]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/SecItemBackup.c
Security-57740.20.22.tar.gz
[apple/security.git] / OSX / sec / Security / SecItemBackup.c
1 /*
2 * Copyright (c) 2015 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25 /*
26 * SecItemBackup.c - Client side backup interfaces and support code
27 */
28
29 #include <Security/SecItemBackup.h>
30
31 #include <Security/SecItemPriv.h>
32 #include <Security/SecuritydXPC.h>
33 #include <Security/SecFramework.h>
34 #include <securityd/SecItemServer.h>
35 #include <ipc/securityd_client.h>
36 #include <Security/SecureObjectSync/SOSBackupEvent.h>
37 #include <Security/SecureObjectSync/SOSCloudCircle.h>
38 #include <Security/SecureObjectSync/SOSViews.h>
39 #include <corecrypto/ccsha1.h>
40 #include <utilities/SecCFError.h>
41 #include <utilities/SecCFRelease.h>
42 #include <utilities/SecCFCCWrappers.h>
43 #include <utilities/array_size.h>
44 #include <utilities/der_plist.h>
45 #include <utilities/der_plist_internal.h>
46 #include <AssertMacros.h>
47 #include <os/activity.h>
48 #include <notify.h>
49
50 static CFDataRef client_data_data_to_data_error_request(enum SecXPCOperation op, SecurityClient *client, CFDataRef keybag, CFDataRef passcode, CFErrorRef *error) {
51 __block CFDataRef result = NULL;
52 securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
53 return SecXPCDictionarySetDataOptional(message, kSecXPCKeyKeybag, keybag, error)
54 && SecXPCDictionarySetDataOptional(message, kSecXPCKeyUserPassword, passcode, error);
55 }, ^bool(xpc_object_t response, CFErrorRef *error) {
56 return (result = SecXPCDictionaryCopyData(response, kSecXPCKeyResult, error));
57 });
58 return result;
59 }
60
61 static bool data_client_data_data_to_error_request(enum SecXPCOperation op, CFDataRef backup, SecurityClient *client, CFDataRef keybag, CFDataRef passcode, CFErrorRef *error) {
62 return securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
63 return SecXPCDictionarySetData(message, kSecXPCKeyBackup, backup, error)
64 && SecXPCDictionarySetData(message, kSecXPCKeyKeybag, keybag, error)
65 && SecXPCDictionarySetDataOptional(message, kSecXPCKeyUserPassword, passcode, error);
66 } , NULL);
67 }
68
69 static bool dict_data_data_to_error_request(enum SecXPCOperation op, CFDictionaryRef backup, CFDataRef keybag, CFDataRef passcode, CFErrorRef *error) {
70 return securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
71 return SecXPCDictionarySetPList(message, kSecXPCKeyBackup, backup, error)
72 && SecXPCDictionarySetData(message, kSecXPCKeyKeybag, keybag, error)
73 && SecXPCDictionarySetDataOptional(message, kSecXPCKeyUserPassword, passcode, error);
74 } , NULL);
75 }
76
77 static CFDictionaryRef data_data_dict_to_dict_error_request(enum SecXPCOperation op, CFDictionaryRef backup, CFDataRef keybag, CFDataRef passcode, CFErrorRef *error) {
78 __block CFDictionaryRef dict = NULL;
79 securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
80 return SecXPCDictionarySetPListOptional(message, kSecXPCKeyBackup, backup, error)
81 && SecXPCDictionarySetData(message, kSecXPCKeyKeybag, keybag, error)
82 && SecXPCDictionarySetDataOptional(message, kSecXPCKeyUserPassword, passcode, error);
83 }, ^bool(xpc_object_t response, CFErrorRef *error) {
84 return (dict = SecXPCDictionaryCopyDictionary(response, kSecXPCKeyResult, error));
85 });
86 return dict;
87 }
88
89 static int string_to_fd_error_request(enum SecXPCOperation op, CFStringRef backupName, CFErrorRef *error) {
90 __block int fd = -1;
91 securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
92 return SecXPCDictionarySetString(message, kSecXPCKeyBackup, backupName, error);
93 }, ^bool(xpc_object_t response, CFErrorRef *error) {
94 fd = SecXPCDictionaryDupFileDescriptor(response, kSecXPCKeyResult, error);
95 return true;
96 });
97 return fd;
98 }
99
100 static bool string_data_data_to_bool_error_request(enum SecXPCOperation op, CFStringRef backupName, CFDataRef keybagDigest, CFDataRef manifest, CFErrorRef *error)
101 {
102 return securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
103 return SecXPCDictionarySetString(message, kSecXPCKeyBackup, backupName, error) &&
104 SecXPCDictionarySetDataOptional(message, kSecXPCKeyKeybag, keybagDigest, error) &&
105 SecXPCDictionarySetDataOptional(message, kSecXPCData, manifest, error);
106 }, ^bool(xpc_object_t response, __unused CFErrorRef *error) {
107 return xpc_dictionary_get_bool(response, kSecXPCKeyResult);
108 });
109 }
110
111 static bool string_string_data_data_data_to_bool_error_request(enum SecXPCOperation op, CFStringRef backupName, CFStringRef peerID, CFDataRef keybag, CFDataRef secret, CFDataRef backup, CFErrorRef *error)
112 {
113 return securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
114 return SecXPCDictionarySetString(message, kSecXPCKeyBackup, backupName, error) &&
115 SecXPCDictionarySetStringOptional(message, kSecXPCKeyDigest, peerID, error) &&
116 SecXPCDictionarySetData(message, kSecXPCKeyKeybag, keybag, error) &&
117 SecXPCDictionarySetData(message, kSecXPCKeyUserPassword, secret, error) &&
118 SecXPCDictionarySetData(message, kSecXPCData, backup, error);
119 }, ^bool(xpc_object_t response, __unused CFErrorRef *error) {
120 return xpc_dictionary_get_bool(response, kSecXPCKeyResult);
121 });
122 }
123
124 static CFArrayRef to_array_error_request(enum SecXPCOperation op, CFErrorRef *error)
125 {
126 __block CFArrayRef result = NULL;
127 securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
128 return true;
129 }, ^bool(xpc_object_t response, CFErrorRef *error) {
130 return result = SecXPCDictionaryCopyArray(response, kSecXPCKeyResult, error);
131 });
132 return result;
133 }
134
135 // XPC calls
136
137 static int SecItemBackupHandoffFD(CFStringRef backupName, CFErrorRef *error) {
138 __block int fileDesc = -1;
139 os_activity_initiate("SecItemBackupHandoffFD", OS_ACTIVITY_FLAG_DEFAULT, ^{
140 fileDesc = SECURITYD_XPC(sec_item_backup_handoff_fd, string_to_fd_error_request, backupName, error);
141 });
142 return fileDesc;
143 }
144
145 CFDataRef _SecKeychainCopyOTABackup(void) {
146 __block CFDataRef result;
147 os_activity_initiate("_SecKeychainCopyOTABackup", OS_ACTIVITY_FLAG_DEFAULT, ^{
148 result = SECURITYD_XPC(sec_keychain_backup, client_data_data_to_data_error_request, SecSecurityClientGet(), NULL, NULL, NULL);
149 });
150 return result;
151 }
152
153 CFDataRef _SecKeychainCopyBackup(CFDataRef backupKeybag, CFDataRef password) {
154 __block CFDataRef result;
155 os_activity_initiate("_SecKeychainCopyBackup", OS_ACTIVITY_FLAG_DEFAULT, ^{
156 result = SECURITYD_XPC(sec_keychain_backup, client_data_data_to_data_error_request, SecSecurityClientGet(), backupKeybag, password, NULL);
157 });
158 return result;
159 }
160
161 bool _SecKeychainWriteBackupToFileDescriptor(CFDataRef backupKeybag, CFDataRef password, int fd, CFErrorRef *error) {
162 __block bool result = false;
163 os_activity_initiate("_SecKeychainWriteBackupToFile", OS_ACTIVITY_FLAG_DEFAULT, ^{
164
165 securityd_send_sync_and_do(sec_keychain_backup_id, error, ^bool(xpc_object_t message, CFErrorRef *error) {
166 return SecXPCDictionarySetDataOptional(message, kSecXPCKeyKeybag, backupKeybag, error)
167 && SecXPCDictionarySetDataOptional(message, kSecXPCKeyUserPassword, password, error)
168 && SecXPCDictionarySetFileDescriptor(message, kSecXPCKeyFileDescriptor, fd, error);
169 }, ^bool(xpc_object_t response, CFErrorRef *error) {
170 return (result = SecXPCDictionaryGetBool(response, kSecXPCKeyResult, error));
171 });
172 });
173 return result;
174 }
175
176 bool
177 _SecKeychainRestoreBackupFromFileDescriptor(int fd, CFDataRef backupKeybag, CFDataRef password, CFErrorRef *error)
178 {
179 __block bool result;
180 os_activity_initiate("_SecKeychainRestoreBackup", OS_ACTIVITY_FLAG_DEFAULT, ^{
181 securityd_send_sync_and_do(sec_keychain_restore_id, error, ^bool(xpc_object_t message, CFErrorRef *error) {
182 return SecXPCDictionarySetFileDescriptor(message, kSecXPCKeyFileDescriptor, fd, error)
183 && SecXPCDictionarySetData(message, kSecXPCKeyKeybag, backupKeybag, error)
184 && SecXPCDictionarySetDataOptional(message, kSecXPCKeyUserPassword, password, error);
185 }, ^bool(xpc_object_t response, CFErrorRef *error) {
186 return (result = SecXPCDictionaryGetBool(response, kSecXPCKeyResult, error));
187 });
188 });
189 return result;
190 }
191
192 /*
193 * Current promise is that this is low memory usage, so in the current format, ask securityd
194 * to resolve the item for us.
195 */
196
197 CFStringRef
198 _SecKeychainCopyKeybagUUIDFromFileDescriptor(int fd, CFErrorRef *error)
199 {
200 __block CFStringRef result;
201 os_activity_initiate("_SecKeychainCopyKeybagUUID", OS_ACTIVITY_FLAG_DEFAULT, ^{
202 securityd_send_sync_and_do(sec_keychain_backup_keybag_uuid_id, error, ^bool(xpc_object_t message, CFErrorRef *error) {
203 return SecXPCDictionarySetFileDescriptor(message, kSecXPCKeyFileDescriptor, fd, error);
204 }, ^bool(xpc_object_t response, CFErrorRef *error) {
205 return (result = SecXPCDictionaryCopyString(response, kSecXPCKeyResult, error));
206 });
207 });
208 return result;
209 }
210
211
212 OSStatus _SecKeychainRestoreBackup(CFDataRef backup, CFDataRef backupKeybag,
213 CFDataRef password) {
214 __block OSStatus result;
215 os_activity_initiate("_SecKeychainRestoreBackup", OS_ACTIVITY_FLAG_DEFAULT, ^{
216 result = SecOSStatusWith(^bool (CFErrorRef *error) {
217 return SECURITYD_XPC(sec_keychain_restore, data_client_data_data_to_error_request, backup, SecSecurityClientGet(), backupKeybag, password, error);
218 });
219 });
220 return result;
221 }
222
223
224 static int compareDigests(const void *l, const void *r) {
225 return memcmp(l, r, CCSHA1_OUTPUT_SIZE);
226 }
227
228 CFDataRef SecItemBackupCreateManifest(CFDictionaryRef backup, CFErrorRef *error)
229 {
230 CFIndex count = backup ? CFDictionaryGetCount(backup) : 0;
231 CFMutableDataRef manifest = CFDataCreateMutable(kCFAllocatorDefault, CCSHA1_OUTPUT_SIZE * count);
232 if (backup) {
233 CFDictionaryForEach(backup, ^void (const void *key, const void *value) {
234 if (isDictionary(value)) {
235 /* converting key back to binary blob is horrible */
236 CFDataRef sha1 = CFDictionaryGetValue(value, kSecItemBackupHashKey);
237 if (isData(sha1) && CFDataGetLength(sha1) == CCSHA1_OUTPUT_SIZE) {
238 CFDataAppend(manifest, sha1);
239 } else {
240 CFStringRef sha1Hex = CFDataCopyHexString(sha1);
241 secerror("bad hash %@ in backup", sha1Hex);
242 CFReleaseSafe(sha1Hex);
243 // TODO: Drop this key from dictionary (outside the loop)
244 }
245 }
246 });
247 qsort(CFDataGetMutableBytePtr(manifest), CFDataGetLength(manifest) / CCSHA1_OUTPUT_SIZE, CCSHA1_OUTPUT_SIZE, compareDigests);
248 }
249 return manifest;
250 }
251
252 OSStatus _SecKeychainBackupSyncable(CFDataRef keybag, CFDataRef password, CFDictionaryRef backup_in, CFDictionaryRef *backup_out)
253 {
254 return SecOSStatusWith(^bool (CFErrorRef *error) {
255 *backup_out = SECURITYD_XPC(sec_keychain_backup_syncable, data_data_dict_to_dict_error_request, backup_in, keybag, password, error);
256 return *backup_out != NULL;
257 });
258 }
259
260 OSStatus _SecKeychainRestoreSyncable(CFDataRef keybag, CFDataRef password, CFDictionaryRef backup_in)
261 {
262 __block OSStatus result;
263 os_activity_initiate("_SecKeychainRestoreSyncable", OS_ACTIVITY_FLAG_DEFAULT, ^{
264 result = SecOSStatusWith(^bool (CFErrorRef *error) {
265 return SECURITYD_XPC(sec_keychain_restore_syncable, dict_data_data_to_error_request, backup_in, keybag, password, error);
266 });
267 });
268 return result;
269 }
270
271 // Client code
272
273 static bool SecKeychainWithBackupFile(CFStringRef backupName, CFErrorRef *error, void(^with)(FILE *bufile)) {
274 int fd = SecItemBackupHandoffFD(backupName, error);
275 if (fd < 0) {
276 secdebug("backup", "SecItemBackupHandoffFD returned %d", fd);
277 return false;
278 }
279
280 // Rewind file to start
281 lseek(fd, 0, SEEK_SET);
282
283 FILE *backup = fdopen(fd, "r");
284 if (!backup) {
285 close(fd);
286 secdebug("backup", "Receiving file for %@ failed, %d", backupName, errno);
287 return SecCheckErrno(!backup, error, CFSTR("fdopen"));
288 } else {
289 secdebug("backup", "Receiving file for %@ with fd %d of size %llu", backupName, fd, lseek(fd, 0, SEEK_END));
290 }
291
292 with(backup);
293 fclose(backup);
294 return true;
295 }
296
297 static CFArrayRef SecItemBackupCopyNames(CFErrorRef *error)
298 {
299 __block CFArrayRef result;
300 os_activity_initiate("SecItemBackupCopyNames", OS_ACTIVITY_FLAG_DEFAULT, ^{
301 result = SECURITYD_XPC(sec_item_backup_copy_names, to_array_error_request, error);
302 });
303 return result;
304 }
305
306 bool SecItemBackupWithRegisteredBackups(CFErrorRef *error, void(^backup)(CFStringRef backupName)) {
307 CFArrayRef backupNames = SecItemBackupCopyNames(error);
308 if (!backupNames) return false;
309 CFStringRef name;
310 CFArrayForEachC(backupNames, name) {
311 backup(name);
312 }
313 CFRelease(backupNames);
314 return true;
315 }
316
317 static bool SecItemBackupDoResetEventBody(const uint8_t *der, const uint8_t *der_end, CFErrorRef *error, void (^handleEvent)(SecBackupEventType et, CFTypeRef key, CFTypeRef item)) {
318 size_t sequence_len;
319 const uint8_t *sequence_body = ccder_decode_len(&sequence_len, der, der_end);
320 bool ok = sequence_body;
321 if (ok && sequence_body + sequence_len != der_end) {
322 // Can't ever happen!
323 SecError(errSecDecode, error, CFSTR("trailing junk after reset"));
324 ok = false;
325 }
326 if (ok) {
327 CFDataRef keybag = NULL;
328 if (sequence_body != der_end) {
329 size_t keybag_len = 0;
330 const uint8_t *keybag_start = ccder_decode_tl(CCDER_OCTET_STRING, &keybag_len, sequence_body, der_end);
331 if (!keybag_start) {
332 ok = SecError(errSecDecode, error, CFSTR("failed to decode keybag"));
333 } else if (keybag_start + keybag_len != der_end) {
334 ok = SecError(errSecDecode, error, CFSTR("trailing junk after keybag"));
335 } else {
336 keybag = CFDataCreate(kCFAllocatorDefault, keybag_start, keybag_len);
337 }
338 }
339 handleEvent(kSecBackupEventReset, keybag, NULL);
340 CFReleaseSafe(keybag);
341 }
342
343 return ok;
344 }
345
346 static bool SecItemBackupDoAddEvent(const uint8_t *der, const uint8_t *der_end, CFErrorRef *error, void (^handleEvent)(SecBackupEventType et, CFTypeRef key, CFTypeRef item)) {
347 CFDictionaryRef eventDict = NULL;
348 const uint8_t *der_end_of_dict = der_decode_dictionary(kCFAllocatorDefault, kCFPropertyListImmutable, &eventDict, error, der, der_end);
349 if (der_end_of_dict && der_end_of_dict != der_end) {
350 // Can't ever happen!
351 SecError(errSecDecode, error, CFSTR("trailing junk after add"));
352 der_end_of_dict = NULL;
353 }
354 if (der_end_of_dict) {
355 CFDataRef hash = CFDictionaryGetValue(eventDict, kSecItemBackupHashKey);
356 handleEvent(kSecBackupEventAdd, hash, eventDict);
357 }
358
359 CFReleaseSafe(eventDict);
360 return der_end_of_dict;
361 }
362
363 static bool SecItemBackupDoCompleteEvent(const uint8_t *der, const uint8_t *der_end, CFErrorRef *error, void (^handleEvent)(SecBackupEventType et, CFTypeRef key, CFTypeRef item)) {
364 uint64_t event_num = 0;
365 const uint8_t *der_end_of_num = ccder_decode_uint64(&event_num, der, der_end);
366 if (der_end_of_num && der_end_of_num != der_end) {
367 // Can't ever happen!
368 SecError(errSecDecode, error, CFSTR("trailing junk after complete"));
369 der_end_of_num = NULL;
370 }
371 if (der_end_of_num) {
372 handleEvent(kSecBackupEventComplete, NULL, NULL);
373 }
374 return der_end_of_num;
375 }
376
377 static bool SecItemBackupDoDeleteEventBody(const uint8_t *der, const uint8_t *der_end, CFErrorRef *error, void (^handleEvent)(SecBackupEventType et, CFTypeRef key, CFTypeRef item)) {
378 size_t digest_len = 0;
379 const uint8_t *digest_start = ccder_decode_len(&digest_len, der, der_end);
380 if (digest_start && digest_start + digest_len != der_end) {
381 // Can't ever happen!
382 SecError(errSecDecode, error, CFSTR("trailing junk after delete"));
383 digest_start = NULL;
384 }
385 if (digest_start) {
386 CFDataRef hash = CFDataCreate(kCFAllocatorDefault, digest_start, digest_len);
387 handleEvent(kSecBackupEventRemove, hash, NULL);
388 CFRelease(hash);
389 }
390
391 return digest_start;
392 }
393
394 static void debugDisplayBackupEventTag(ccder_tag tag) {
395 #if !defined(NDEBUG)
396 const char *eventDesc;
397 switch (tag) {
398 case CCDER_CONSTRUCTED_SEQUENCE: eventDesc = "ResetEvent"; break;
399 case CCDER_CONSTRUCTED_SET: eventDesc = "AddEvent"; break;
400 case CCDER_INTEGER: eventDesc = "ResetEvent"; break;
401 case CCDER_OCTET_STRING: eventDesc = "DeleteEvent"; break;
402 default: eventDesc = "UnknownEvent"; break;
403 }
404 secdebug("backup", "processing event %s (tag %08lX)", eventDesc, tag);
405 #endif
406 }
407
408 static bool SecItemBackupDoEvent(const uint8_t *der, const uint8_t *der_end, CFErrorRef *error, void (^handleEvent)(SecBackupEventType et, CFTypeRef key, CFTypeRef item)) {
409 ccder_tag tag;
410 const uint8_t *der_start_of_len = ccder_decode_tag(&tag, der, der_end);
411 debugDisplayBackupEventTag(tag);
412 switch (tag) {
413 case CCDER_CONSTRUCTED_SEQUENCE:
414 return SecItemBackupDoResetEventBody(der_start_of_len, der_end, error, handleEvent);
415 case CCDER_CONSTRUCTED_SET:
416 return SecItemBackupDoAddEvent(der, der_end, error, handleEvent);
417 case CCDER_INTEGER:
418 return SecItemBackupDoCompleteEvent(der, der_end, error, handleEvent);
419 case CCDER_OCTET_STRING:
420 return SecItemBackupDoDeleteEventBody(der_start_of_len, der_end, error, handleEvent);
421 default:
422 return SecError(errSecDecode, error, CFSTR("unsupported event tag: %lu"), tag);
423 }
424 }
425
426 // TODO: Move to ccder and give better name.
427 static const uint8_t *ccder_decode_len_unchecked(size_t *lenp, const uint8_t *der, const uint8_t *der_end) {
428 if (der && der < der_end) {
429 size_t len = *der++;
430 if (len < 0x80) {
431 } else if (len == 0x81) {
432 if (der_end - der < 1) goto errOut;
433 len = *der++;
434 } else if (len == 0x82) {
435 if (der_end - der < 2) goto errOut;
436 len = *(der++) << 8;
437 len += *der++;
438 } else if (len == 0x83) {
439 if (der_end - der < 3) goto errOut;
440 len = *(der++) << 16;
441 len += *(der++) << 8;
442 len += *(der++);
443 } else {
444 goto errOut;
445 }
446 *lenp = len;
447 return der;
448 }
449 errOut:
450 return NULL;
451 }
452
453 static bool SecKeychainWithBackupFileParse(FILE *backup, CFErrorRef *error, void (^handleEvent)(SecBackupEventType et, CFTypeRef key, CFTypeRef item)) {
454 __block bool ok = true;
455 size_t buf_remaining = 0;
456 const size_t read_ahead = 16;
457 size_t buf_len = read_ahead;
458 uint8_t *buf = malloc(buf_len);
459 for (;;) {
460 const size_t bytes_read = fread(buf + buf_remaining, 1, read_ahead - buf_remaining, backup);
461 if (bytes_read <= 0) {
462 if (!feof(backup))
463 ok = SecCheckErrno(true, error, CFSTR("read backup event header"));
464 else if (!buf_remaining) {
465 // Nothing read, nothing in buffer, clean eof.
466 }
467 break;
468 }
469 const size_t buf_avail = bytes_read + buf_remaining;
470
471 const uint8_t *der = buf;
472 const uint8_t *der_end = der + buf_avail;
473 ccder_tag tag;
474 size_t body_len;
475 der = ccder_decode_tag(&tag, der, der_end);
476 der = ccder_decode_len_unchecked(&body_len, der, der_end);
477
478 if (!der) {
479 ok = SecError(errSecDecode, error, CFSTR("failed to decode backup event header"));
480 break;
481 }
482
483 const size_t decoded_len = der - buf;
484 size_t event_len = decoded_len + body_len;
485 if (event_len > buf_avail) {
486 // We need to read the rest of this event, first
487 // ensure we have enough space.
488 if (buf_len < event_len) {
489 // TODO: Think about a max for buf_len here to prevent attacks.
490 uint8_t *new_buf = realloc(buf, event_len);
491 if (!new_buf) {
492 ok = SecError(errSecAllocate, error, CFSTR("realloc buf failed"));
493 break;
494 }
495 buf = new_buf;
496 buf_len = event_len;
497 }
498
499 // Read tail of current event.
500 const size_t tail_len = fread(buf + buf_avail, 1, event_len - buf_avail, backup);
501 if (tail_len < event_len - buf_avail) {
502 if (!feof(backup)) {
503 ok = SecCheckErrno(true, error, CFSTR("failed to read event body"));
504 } else {
505 ok = SecError(errSecDecode, error, CFSTR("unexpected end of event file %zu of %zu bytes read"), tail_len, event_len - buf_avail);
506 }
507 break;
508 }
509 }
510
511 // Adjust der_end to the end of the event.
512 der_end = buf + event_len;
513
514 ok &= SecItemBackupDoEvent(buf, der_end, error, handleEvent);
515
516 if (event_len < buf_avail) {
517 // Shift remaining bytes to start of buffer.
518 buf_remaining = buf_avail - event_len;
519 memmove(buf, der_end, buf_remaining);
520 } else {
521 buf_remaining = 0;
522 }
523 }
524 free(buf);
525 return ok;
526 }
527
528 bool SecItemBackupWithChanges(CFStringRef backupName, CFErrorRef *error, void (^handleEvent)(SecBackupEventType et, CFTypeRef key, CFTypeRef item)) {
529 __block bool ok = true;
530 __block CFErrorRef localError = NULL;
531 ok &= SecKeychainWithBackupFile(backupName, &localError, ^(FILE *backup) {
532 ok &= SecKeychainWithBackupFileParse(backup, &localError, handleEvent);
533 });
534 if (!ok) { // TODO: remove this logging
535 secdebug("backup", "SecItemBackupWithChanges failed: %@", localError);
536 handleEvent(kSecBackupEventComplete, NULL, NULL);
537 CFErrorPropagate(localError, error);
538 }
539
540 return ok;
541 }
542
543 bool SecItemBackupSetConfirmedManifest(CFStringRef backupName, CFDataRef keybagDigest, CFDataRef manifest, CFErrorRef *error) {
544 __block bool result;
545 os_activity_initiate("SecItemBackupSetConfirmedManifest", OS_ACTIVITY_FLAG_DEFAULT, ^{
546 result = SECURITYD_XPC(sec_item_backup_set_confirmed_manifest, string_data_data_to_bool_error_request, backupName, keybagDigest, manifest, error);
547 });
548 return result;
549 }
550
551 void SecItemBackupRestore(CFStringRef backupName, CFStringRef peerID, CFDataRef keybag, CFDataRef secret, CFTypeRef backup, void (^completion)(CFErrorRef error)) {
552 __block CFErrorRef localError = NULL;
553 os_activity_initiate("SecItemBackupRestore", OS_ACTIVITY_FLAG_DEFAULT, ^{
554 SECURITYD_XPC(sec_item_backup_restore, string_string_data_data_data_to_bool_error_request, backupName, peerID, keybag, secret, backup, &localError);
555 });
556 completion(localError);
557 CFReleaseSafe(localError);
558 }
559
560 CFDictionaryRef SecItemBackupCopyMatching(CFDataRef keybag, CFDataRef secret, CFDictionaryRef backup, CFDictionaryRef query, CFErrorRef *error) {
561 SecError(errSecUnimplemented, error, CFSTR("SecItemBackupCopyMatching unimplemented"));
562 return NULL;
563 }
564
565