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