]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/SecItemBackup.c
Security-57336.1.9.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 data_data_to_data_error_request(enum SecXPCOperation op, 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_data_data_to_error_request(enum SecXPCOperation op, CFDataRef backup, 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, data_data_to_data_error_request, 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, data_data_to_data_error_request, backupKeybag, password, NULL);
157 });
158 return result;
159 }
160
161 OSStatus _SecKeychainRestoreBackup(CFDataRef backup, CFDataRef backupKeybag,
162 CFDataRef password) {
163 __block OSStatus result;
164 os_activity_initiate("_SecKeychainRestoreBackup", OS_ACTIVITY_FLAG_DEFAULT, ^{
165 result = SecOSStatusWith(^bool (CFErrorRef *error) {
166 return SECURITYD_XPC(sec_keychain_restore, data_data_data_to_error_request, backup, backupKeybag, password, error);
167 });
168 });
169 return result;
170 }
171
172 static int compareDigests(const void *l, const void *r) {
173 return memcmp(l, r, CCSHA1_OUTPUT_SIZE);
174 }
175
176 CFDataRef SecItemBackupCreateManifest(CFDictionaryRef backup, CFErrorRef *error)
177 {
178 CFIndex count = backup ? CFDictionaryGetCount(backup) : 0;
179 CFMutableDataRef manifest = CFDataCreateMutable(kCFAllocatorDefault, CCSHA1_OUTPUT_SIZE * count);
180 if (backup) {
181 CFDictionaryForEach(backup, ^void (const void *key, const void *value) {
182 if (isDictionary(value)) {
183 /* converting key back to binary blob is horrible */
184 CFDataRef sha1 = CFDictionaryGetValue(value, kSecItemBackupHashKey);
185 if (isData(sha1) && CFDataGetLength(sha1) == CCSHA1_OUTPUT_SIZE) {
186 CFDataAppend(manifest, sha1);
187 } else {
188 CFStringRef sha1Hex = CFDataCopyHexString(sha1);
189 secerror("bad hash %@ in backup", sha1Hex);
190 CFReleaseSafe(sha1Hex);
191 // TODO: Drop this key from dictionary (outside the loop)
192 }
193 }
194 });
195 qsort(CFDataGetMutableBytePtr(manifest), CFDataGetLength(manifest) / CCSHA1_OUTPUT_SIZE, CCSHA1_OUTPUT_SIZE, compareDigests);
196 }
197 return manifest;
198 }
199
200 /*
201 client code in CloudServices calls SecItemBackupWithChanges in the loop of SecItemBackupWithRegisteredBackups
202 */
203 __unused static CFDictionaryRef SecItemBackupSyncable(CFDataRef keybag, CFDataRef password, CFDictionaryRef backup_in, CFErrorRef *error)
204 {
205 const CFStringRef backupName = kSOSViewKeychainV0_tomb;
206 __block bool complete = false;
207 __block CFMutableDictionaryRef backup = backup_in ? CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, backup_in) : CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
208 CFDataRef keybagDigest = CFDataCopySHA1Digest(keybag, NULL); // Used to confirm we are in sync keybag wise.
209 do {
210 CFErrorRef localError = NULL;
211 if (!SecItemBackupWithChanges(backupName, &localError, ^(SecBackupEventType et, CFTypeRef key, CFTypeRef item) {
212 CFStringRef hexDigest = key ? CFDataCopyHexString(key) : NULL;
213 complete = false;
214 switch(et) {
215 case kSecBackupEventReset:
216 CFDictionaryRemoveAllValues(backup);
217 break;
218 case kSecBackupEventAdd:
219 CFDictionarySetValue(backup, hexDigest, item);
220 break;
221 case kSecBackupEventRemove:
222 CFDictionaryRemoveValue(backup, hexDigest);
223 break;
224 case kSecBackupEventComplete:
225 complete = true;
226 break;
227 }
228 CFReleaseSafe(hexDigest);
229
230 })) {
231 if (localError && CFEqual(CFErrorGetDomain(localError), kSecErrnoDomain) && CFErrorGetCode(localError) == ENOENT) {
232 // No journal file returned by securityd, nothing left to do, ignore error and exit.
233 CFReleaseNull(localError);
234 } else {
235 CFErrorPropagate(localError, error);
236 CFReleaseNull(backup);
237 }
238 break;
239 }
240
241 CFDataRef mconfirmed = SecItemBackupCreateManifest(backup, error);
242 if (!mconfirmed) {
243 CFReleaseNull(backup);
244 break;
245 }
246 bool ok = SecItemBackupSetConfirmedManifest(backupName, keybagDigest, mconfirmed, error);
247 CFReleaseSafe(mconfirmed);
248 if (!ok) {
249 CFReleaseNull(backup);
250 break;
251 }
252 } while (!complete);
253 CFReleaseSafe(keybagDigest);
254
255 return backup;
256 }
257
258 #if 0 // interim code to call SecItemBackupSyncable
259 OSStatus _SecKeychainBackupSyncable(CFDataRef keybag, CFDataRef password, CFDictionaryRef backup_in, CFDictionaryRef *backup_out)
260 {
261 __block OSStatus result;
262 os_activity_initiate("_SecKeychainBackupSyncable", OS_ACTIVITY_FLAG_DEFAULT, ^{
263 result = SecOSStatusWith(^bool (CFErrorRef *error) {
264 *backup_out = SecItemBackupSyncable(keybag, password, backup_in, error);
265 return *backup_out != NULL;
266 });
267 });
268 return result;
269 }
270 #endif
271
272 OSStatus _SecKeychainBackupSyncable(CFDataRef keybag, CFDataRef password, CFDictionaryRef backup_in, CFDictionaryRef *backup_out)
273 {
274 return SecOSStatusWith(^bool (CFErrorRef *error) {
275 *backup_out = SECURITYD_XPC(sec_keychain_backup_syncable, data_data_dict_to_dict_error_request, backup_in, keybag, password, error);
276 return *backup_out != NULL;
277 });
278 }
279
280 OSStatus _SecKeychainRestoreSyncable(CFDataRef keybag, CFDataRef password, CFDictionaryRef backup_in)
281 {
282 __block OSStatus result;
283 os_activity_initiate("_SecKeychainRestoreSyncable", OS_ACTIVITY_FLAG_DEFAULT, ^{
284 result = SecOSStatusWith(^bool (CFErrorRef *error) {
285 return SECURITYD_XPC(sec_keychain_restore_syncable, dict_data_data_to_error_request, backup_in, keybag, password, error);
286 });
287 });
288 return result;
289 }
290
291 // Client code
292
293 static bool SecKeychainWithBackupFile(CFStringRef backupName, CFErrorRef *error, void(^with)(FILE *bufile)) {
294 int fd = SecItemBackupHandoffFD(backupName, error);
295 if (fd < 0) {
296 secdebug("backup", "SecItemBackupHandoffFD returned %d", fd);
297 return false;
298 }
299 FILE *backup = fdopen(fd, "r");
300 if (!backup) {
301 close(fd);
302 secdebug("backup", "Receiving file for %@ failed, %d", backupName, errno);
303 return SecCheckErrno(!backup, error, CFSTR("fdopen"));
304 } else {
305 secdebug("backup", "Receiving file for %@ with fd %d of size %llu", backupName, fd, lseek(fd, 0, SEEK_END));
306 }
307
308 // Rewind file to start
309 lseek(fd, 0, SEEK_SET);
310 with(backup);
311 fclose(backup);
312 return true;
313 }
314
315 static CFArrayRef SecItemBackupCopyNames(CFErrorRef *error)
316 {
317 __block CFArrayRef result;
318 os_activity_initiate("SecItemBackupCopyNames", OS_ACTIVITY_FLAG_DEFAULT, ^{
319 result = SECURITYD_XPC(sec_item_backup_copy_names, to_array_error_request, error);
320 });
321 return result;
322 }
323
324 bool SecItemBackupWithRegisteredBackups(CFErrorRef *error, void(^backup)(CFStringRef backupName)) {
325 CFArrayRef backupNames = SecItemBackupCopyNames(error);
326 if (!backupNames) return false;
327 CFStringRef name;
328 CFArrayForEachC(backupNames, name) {
329 backup(name);
330 }
331 CFRelease(backupNames);
332 return true;
333 }
334
335 static bool SecItemBackupDoResetEventBody(const uint8_t *der, const uint8_t *der_end, CFErrorRef *error, void (^handleEvent)(SecBackupEventType et, CFTypeRef key, CFTypeRef item)) {
336 size_t sequence_len;
337 const uint8_t *sequence_body = ccder_decode_len(&sequence_len, der, der_end);
338 bool ok = sequence_body;
339 if (ok && sequence_body + sequence_len != der_end) {
340 // Can't ever happen!
341 SecError(errSecDecode, error, CFSTR("trailing junk after reset"));
342 ok = false;
343 }
344 if (ok) {
345 CFDataRef keybag = NULL;
346 if (sequence_body != der_end) {
347 size_t keybag_len = 0;
348 const uint8_t *keybag_start = ccder_decode_tl(CCDER_OCTET_STRING, &keybag_len, sequence_body, der_end);
349 if (!keybag_start) {
350 ok = SecError(errSecDecode, error, CFSTR("failed to decode keybag"));
351 } else if (keybag_start + keybag_len != der_end) {
352 ok = SecError(errSecDecode, error, CFSTR("trailing junk after keybag"));
353 } else {
354 keybag = CFDataCreate(kCFAllocatorDefault, keybag_start, keybag_len);
355 }
356 }
357 handleEvent(kSecBackupEventReset, keybag, NULL);
358 CFReleaseSafe(keybag);
359 }
360
361 return ok;
362 }
363
364 static bool SecItemBackupDoAddEvent(const uint8_t *der, const uint8_t *der_end, CFErrorRef *error, void (^handleEvent)(SecBackupEventType et, CFTypeRef key, CFTypeRef item)) {
365 CFDictionaryRef eventDict = NULL;
366 const uint8_t *der_end_of_dict = der_decode_dictionary(kCFAllocatorDefault, kCFPropertyListImmutable, &eventDict, error, der, der_end);
367 if (der_end_of_dict && der_end_of_dict != der_end) {
368 // Can't ever happen!
369 SecError(errSecDecode, error, CFSTR("trailing junk after add"));
370 der_end_of_dict = NULL;
371 }
372 if (der_end_of_dict) {
373 CFDataRef hash = CFDictionaryGetValue(eventDict, kSecItemBackupHashKey);
374 handleEvent(kSecBackupEventAdd, hash, eventDict);
375 }
376
377 CFReleaseSafe(eventDict);
378 return der_end_of_dict;
379 }
380
381 static bool SecItemBackupDoCompleteEvent(const uint8_t *der, const uint8_t *der_end, CFErrorRef *error, void (^handleEvent)(SecBackupEventType et, CFTypeRef key, CFTypeRef item)) {
382 uint64_t event_num = 0;
383 const uint8_t *der_end_of_num = ccder_decode_uint64(&event_num, der, der_end);
384 if (der_end_of_num && der_end_of_num != der_end) {
385 // Can't ever happen!
386 SecError(errSecDecode, error, CFSTR("trailing junk after complete"));
387 der_end_of_num = NULL;
388 }
389 if (der_end_of_num) {
390 handleEvent(kSecBackupEventComplete, NULL, NULL);
391 }
392 return der_end_of_num;
393 }
394
395 static bool SecItemBackupDoDeleteEventBody(const uint8_t *der, const uint8_t *der_end, CFErrorRef *error, void (^handleEvent)(SecBackupEventType et, CFTypeRef key, CFTypeRef item)) {
396 size_t digest_len = 0;
397 const uint8_t *digest_start = ccder_decode_len(&digest_len, der, der_end);
398 if (digest_start && digest_start + digest_len != der_end) {
399 // Can't ever happen!
400 SecError(errSecDecode, error, CFSTR("trailing junk after delete"));
401 digest_start = NULL;
402 }
403 if (digest_start) {
404 CFDataRef hash = CFDataCreate(kCFAllocatorDefault, digest_start, digest_len);
405 handleEvent(kSecBackupEventRemove, hash, NULL);
406 CFRelease(hash);
407 }
408
409 return digest_start;
410 }
411
412 static void debugDisplayBackupEventTag(ccder_tag tag) {
413 #if !defined(NDEBUG)
414 const char *eventDesc;
415 switch (tag) {
416 case CCDER_CONSTRUCTED_SEQUENCE: eventDesc = "ResetEvent"; break;
417 case CCDER_CONSTRUCTED_SET: eventDesc = "AddEvent"; break;
418 case CCDER_INTEGER: eventDesc = "ResetEvent"; break;
419 case CCDER_OCTET_STRING: eventDesc = "DeleteEvent"; break;
420 default: eventDesc = "UnknownEvent"; break;
421 }
422 secdebug("backup", "processing event %s (tag %08lX)", eventDesc, tag);
423 #endif
424 }
425
426 static bool SecItemBackupDoEvent(const uint8_t *der, const uint8_t *der_end, CFErrorRef *error, void (^handleEvent)(SecBackupEventType et, CFTypeRef key, CFTypeRef item)) {
427 ccder_tag tag;
428 const uint8_t *der_start_of_len = ccder_decode_tag(&tag, der, der_end);
429 debugDisplayBackupEventTag(tag);
430 switch (tag) {
431 case CCDER_CONSTRUCTED_SEQUENCE:
432 return SecItemBackupDoResetEventBody(der_start_of_len, der_end, error, handleEvent);
433 case CCDER_CONSTRUCTED_SET:
434 return SecItemBackupDoAddEvent(der, der_end, error, handleEvent);
435 case CCDER_INTEGER:
436 return SecItemBackupDoCompleteEvent(der, der_end, error, handleEvent);
437 case CCDER_OCTET_STRING:
438 return SecItemBackupDoDeleteEventBody(der_start_of_len, der_end, error, handleEvent);
439 default:
440 return SecError(errSecDecode, error, CFSTR("unsupported event tag: %lu"), tag);
441 }
442 }
443
444 // TODO: Move to ccder and give better name.
445 static const uint8_t *ccder_decode_len_unchecked(size_t *lenp, const uint8_t *der, const uint8_t *der_end) {
446 if (der && der < der_end) {
447 size_t len = *der++;
448 if (len < 0x80) {
449 } else if (len == 0x81) {
450 if (der_end - der < 1) goto errOut;
451 len = *der++;
452 } else if (len == 0x82) {
453 if (der_end - der < 2) goto errOut;
454 len = *(der++) << 8;
455 len += *der++;
456 } else if (len == 0x83) {
457 if (der_end - der < 3) goto errOut;
458 len = *(der++) << 16;
459 len += *(der++) << 8;
460 len += *(der++);
461 } else {
462 goto errOut;
463 }
464 *lenp = len;
465 return der;
466 }
467 errOut:
468 return NULL;
469 }
470
471 static bool SecKeychainWithBackupFileParse(FILE *backup, CFErrorRef *error, void (^handleEvent)(SecBackupEventType et, CFTypeRef key, CFTypeRef item)) {
472 __block bool ok = true;
473 size_t buf_remaining = 0;
474 const size_t read_ahead = 16;
475 size_t buf_len = read_ahead;
476 uint8_t *buf = malloc(buf_len);
477 for (;;) {
478 const size_t bytes_read = fread(buf + buf_remaining, 1, read_ahead - buf_remaining, backup);
479 if (bytes_read <= 0) {
480 if (!feof(backup))
481 ok = SecCheckErrno(true, error, CFSTR("read backup event header"));
482 else if (!buf_remaining) {
483 // Nothing read, nothing in buffer, clean eof.
484 }
485 break;
486 }
487 const size_t buf_avail = bytes_read + buf_remaining;
488
489 const uint8_t *der = buf;
490 const uint8_t *der_end = der + buf_avail;
491 ccder_tag tag;
492 size_t body_len;
493 der = ccder_decode_tag(&tag, der, der_end);
494 der = ccder_decode_len_unchecked(&body_len, der, der_end);
495
496 if (!der) {
497 ok = SecError(errSecDecode, error, CFSTR("failed to decode backup event header"));
498 break;
499 }
500
501 const size_t decoded_len = der - buf;
502 size_t event_len = decoded_len + body_len;
503 if (event_len > buf_avail) {
504 // We need to read the rest of this event, first
505 // ensure we have enough space.
506 if (buf_len < event_len) {
507 // TODO: Think about a max for buf_len here to prevent attacks.
508 uint8_t *new_buf = realloc(buf, event_len);
509 if (!new_buf) {
510 ok = SecError(errSecAllocate, error, CFSTR("realloc buf failed"));
511 break;
512 }
513 buf = new_buf;
514 buf_len = event_len;
515 }
516
517 // Read tail of current event.
518 const size_t tail_len = fread(buf + buf_avail, 1, event_len - buf_avail, backup);
519 if (tail_len < event_len - buf_avail) {
520 if (!feof(backup)) {
521 ok = SecCheckErrno(true, error, CFSTR("failed to read event body"));
522 } else {
523 ok = SecError(errSecDecode, error, CFSTR("unexpected end of event file %zu of %zu bytes read"), tail_len, event_len - buf_avail);
524 }
525 break;
526 }
527 }
528
529 // Adjust der_end to the end of the event.
530 der_end = buf + event_len;
531
532 ok &= SecItemBackupDoEvent(buf, der_end, error, handleEvent);
533
534 if (event_len < buf_avail) {
535 // Shift remaining bytes to start of buffer.
536 buf_remaining = buf_avail - event_len;
537 memmove(buf, der_end, buf_remaining);
538 } else {
539 buf_remaining = 0;
540 }
541 }
542 free(buf);
543 return ok;
544 }
545
546 bool SecItemBackupWithChanges(CFStringRef backupName, CFErrorRef *error, void (^handleEvent)(SecBackupEventType et, CFTypeRef key, CFTypeRef item)) {
547 __block bool ok = true;
548 __block CFErrorRef localError = NULL;
549 ok &= SecKeychainWithBackupFile(backupName, &localError, ^(FILE *backup) {
550 ok &= SecKeychainWithBackupFileParse(backup, &localError, handleEvent);
551 });
552 if (!ok) { // TODO: remove this logging
553 secdebug("backup", "SecItemBackupWithChanges failed: %@", localError);
554 handleEvent(kSecBackupEventComplete, NULL, NULL);
555 CFErrorPropagate(localError, error);
556 }
557
558 return ok;
559 }
560
561 bool SecItemBackupSetConfirmedManifest(CFStringRef backupName, CFDataRef keybagDigest, CFDataRef manifest, CFErrorRef *error) {
562 __block bool result;
563 os_activity_initiate("SecItemBackupSetConfirmedManifest", OS_ACTIVITY_FLAG_DEFAULT, ^{
564 result = SECURITYD_XPC(sec_item_backup_set_confirmed_manifest, string_data_data_to_bool_error_request, backupName, keybagDigest, manifest, error);
565 });
566 return result;
567 }
568
569 void SecItemBackupRestore(CFStringRef backupName, CFStringRef peerID, CFDataRef keybag, CFDataRef secret, CFTypeRef backup, void (^completion)(CFErrorRef error)) {
570 __block CFErrorRef localError = NULL;
571 os_activity_initiate("SecItemBackupRestore", OS_ACTIVITY_FLAG_DEFAULT, ^{
572 SECURITYD_XPC(sec_item_backup_restore, string_string_data_data_data_to_bool_error_request, backupName, peerID, keybag, secret, backup, &localError);
573 });
574 completion(localError);
575 CFReleaseSafe(localError);
576 }
577
578 CFDictionaryRef SecItemBackupCopyMatching(CFDataRef keybag, CFDataRef secret, CFDictionaryRef backup, CFDictionaryRef query, CFErrorRef *error) {
579 SecError(errSecUnimplemented, error, CFSTR("SecItemBackupCopyMatching unimplemented"));
580 return NULL;
581 }
582
583