]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/SecItemBackup.c
Security-58286.1.32.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 #include <sys/stat.h>
51
52 static CFDataRef client_data_data_to_data_error_request(enum SecXPCOperation op, SecurityClient *client, CFDataRef keybag, CFDataRef passcode, CFErrorRef *error) {
53 __block CFDataRef result = NULL;
54 securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
55 return SecXPCDictionarySetDataOptional(message, kSecXPCKeyKeybag, keybag, error)
56 && SecXPCDictionarySetDataOptional(message, kSecXPCKeyUserPassword, passcode, error);
57 }, ^bool(xpc_object_t response, CFErrorRef *error) {
58 return (result = SecXPCDictionaryCopyData(response, kSecXPCKeyResult, error));
59 });
60 return result;
61 }
62
63 static bool data_client_data_data_to_error_request(enum SecXPCOperation op, CFDataRef backup, SecurityClient *client, CFDataRef keybag, CFDataRef passcode, CFErrorRef *error) {
64 return securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
65 return SecXPCDictionarySetData(message, kSecXPCKeyBackup, backup, error)
66 && SecXPCDictionarySetData(message, kSecXPCKeyKeybag, keybag, error)
67 && SecXPCDictionarySetDataOptional(message, kSecXPCKeyUserPassword, passcode, error);
68 } , NULL);
69 }
70
71 static bool dict_data_data_to_error_request(enum SecXPCOperation op, CFDictionaryRef backup, CFDataRef keybag, CFDataRef passcode, CFErrorRef *error) {
72 return securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
73 return SecXPCDictionarySetPList(message, kSecXPCKeyBackup, backup, error)
74 && SecXPCDictionarySetData(message, kSecXPCKeyKeybag, keybag, error)
75 && SecXPCDictionarySetDataOptional(message, kSecXPCKeyUserPassword, passcode, error);
76 } , NULL);
77 }
78
79 static CFDictionaryRef data_data_dict_to_dict_error_request(enum SecXPCOperation op, CFDictionaryRef backup, CFDataRef keybag, CFDataRef passcode, CFErrorRef *error) {
80 __block CFDictionaryRef dict = NULL;
81 securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
82 return SecXPCDictionarySetPListOptional(message, kSecXPCKeyBackup, backup, error)
83 && SecXPCDictionarySetData(message, kSecXPCKeyKeybag, keybag, error)
84 && SecXPCDictionarySetDataOptional(message, kSecXPCKeyUserPassword, passcode, error);
85 }, ^bool(xpc_object_t response, CFErrorRef *error) {
86 return (dict = SecXPCDictionaryCopyDictionary(response, kSecXPCKeyResult, error));
87 });
88 return dict;
89 }
90
91 static int string_to_fd_error_request(enum SecXPCOperation op, CFStringRef backupName, CFErrorRef *error) {
92 __block int fd = -1;
93 securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
94 return SecXPCDictionarySetString(message, kSecXPCKeyBackup, backupName, error);
95 }, ^bool(xpc_object_t response, CFErrorRef *error) {
96 fd = SecXPCDictionaryDupFileDescriptor(response, kSecXPCKeyResult, error);
97 return true;
98 });
99 return fd;
100 }
101
102 static bool string_data_data_to_bool_error_request(enum SecXPCOperation op, CFStringRef backupName, CFDataRef keybagDigest, CFDataRef manifest, CFErrorRef *error)
103 {
104 return securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
105 return SecXPCDictionarySetString(message, kSecXPCKeyBackup, backupName, error) &&
106 SecXPCDictionarySetDataOptional(message, kSecXPCKeyKeybag, keybagDigest, error) &&
107 SecXPCDictionarySetDataOptional(message, kSecXPCData, manifest, error);
108 }, ^bool(xpc_object_t response, __unused CFErrorRef *error) {
109 return xpc_dictionary_get_bool(response, kSecXPCKeyResult);
110 });
111 }
112
113 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)
114 {
115 return securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
116 return SecXPCDictionarySetString(message, kSecXPCKeyBackup, backupName, error) &&
117 SecXPCDictionarySetStringOptional(message, kSecXPCKeyDigest, peerID, error) &&
118 SecXPCDictionarySetData(message, kSecXPCKeyKeybag, keybag, error) &&
119 SecXPCDictionarySetData(message, kSecXPCKeyUserPassword, secret, error) &&
120 SecXPCDictionarySetData(message, kSecXPCData, backup, error);
121 }, ^bool(xpc_object_t response, __unused CFErrorRef *error) {
122 return xpc_dictionary_get_bool(response, kSecXPCKeyResult);
123 });
124 }
125
126 static CFArrayRef to_array_error_request(enum SecXPCOperation op, CFErrorRef *error)
127 {
128 __block CFArrayRef result = NULL;
129 securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
130 return true;
131 }, ^bool(xpc_object_t response, CFErrorRef *error) {
132 return result = SecXPCDictionaryCopyArray(response, kSecXPCKeyResult, error);
133 });
134 return result;
135 }
136
137 // XPC calls
138
139 static int SecItemBackupHandoffFD(CFStringRef backupName, CFErrorRef *error) {
140 __block int fileDesc = -1;
141 os_activity_initiate("SecItemBackupHandoffFD", OS_ACTIVITY_FLAG_DEFAULT, ^{
142 fileDesc = SECURITYD_XPC(sec_item_backup_handoff_fd, string_to_fd_error_request, backupName, error);
143 });
144 return fileDesc;
145 }
146
147 CFDataRef _SecKeychainCopyOTABackup(void) {
148 __block CFDataRef result;
149 os_activity_initiate("_SecKeychainCopyOTABackup", OS_ACTIVITY_FLAG_DEFAULT, ^{
150 result = SECURITYD_XPC(sec_keychain_backup, client_data_data_to_data_error_request, SecSecurityClientGet(), NULL, NULL, NULL);
151 });
152 return result;
153 }
154
155 CFDataRef _SecKeychainCopyBackup(CFDataRef backupKeybag, CFDataRef password) {
156 __block CFDataRef result;
157 os_activity_initiate("_SecKeychainCopyBackup", OS_ACTIVITY_FLAG_DEFAULT, ^{
158 result = SECURITYD_XPC(sec_keychain_backup, client_data_data_to_data_error_request, SecSecurityClientGet(), backupKeybag, password, NULL);
159 });
160 return result;
161 }
162
163 bool _SecKeychainWriteBackupToFileDescriptor(CFDataRef backupKeybag, CFDataRef password, int fd, CFErrorRef *error) {
164 __block bool result = false;
165 os_activity_initiate("_SecKeychainWriteBackupToFile", OS_ACTIVITY_FLAG_DEFAULT, ^{
166
167 securityd_send_sync_and_do(sec_keychain_backup_id, error, ^bool(xpc_object_t message, CFErrorRef *error) {
168 return SecXPCDictionarySetDataOptional(message, kSecXPCKeyKeybag, backupKeybag, error)
169 && SecXPCDictionarySetDataOptional(message, kSecXPCKeyUserPassword, password, error)
170 && SecXPCDictionarySetFileDescriptor(message, kSecXPCKeyFileDescriptor, fd, error);
171 }, ^bool(xpc_object_t response, CFErrorRef *error) {
172 return (result = SecXPCDictionaryGetBool(response, kSecXPCKeyResult, error));
173 });
174 });
175 return result;
176 }
177
178 bool
179 _SecKeychainRestoreBackupFromFileDescriptor(int fd, CFDataRef backupKeybag, CFDataRef password, CFErrorRef *error)
180 {
181 __block bool result;
182 os_activity_initiate("_SecKeychainRestoreBackup", OS_ACTIVITY_FLAG_DEFAULT, ^{
183 securityd_send_sync_and_do(sec_keychain_restore_id, error, ^bool(xpc_object_t message, CFErrorRef *error) {
184 return SecXPCDictionarySetFileDescriptor(message, kSecXPCKeyFileDescriptor, fd, error)
185 && SecXPCDictionarySetData(message, kSecXPCKeyKeybag, backupKeybag, error)
186 && SecXPCDictionarySetDataOptional(message, kSecXPCKeyUserPassword, password, error);
187 }, ^bool(xpc_object_t response, CFErrorRef *error) {
188 return (result = SecXPCDictionaryGetBool(response, kSecXPCKeyResult, error));
189 });
190 });
191 return result;
192 }
193
194 /*
195 * Current promise is that this is low memory usage, so in the current format, ask securityd
196 * to resolve the item for us.
197 */
198
199 CFStringRef
200 _SecKeychainCopyKeybagUUIDFromFileDescriptor(int fd, CFErrorRef *error)
201 {
202 __block CFStringRef result = NULL;
203 os_activity_initiate("_SecKeychainCopyKeybagUUID", OS_ACTIVITY_FLAG_DEFAULT, ^{
204 securityd_send_sync_and_do(sec_keychain_backup_keybag_uuid_id, error, ^bool(xpc_object_t message, CFErrorRef *error) {
205 return SecXPCDictionarySetFileDescriptor(message, kSecXPCKeyFileDescriptor, fd, error);
206 }, ^bool(xpc_object_t response, CFErrorRef *error) {
207 return (result = SecXPCDictionaryCopyString(response, kSecXPCKeyResult, error));
208 });
209 });
210 return result;
211 }
212
213
214 OSStatus _SecKeychainRestoreBackup(CFDataRef backup, CFDataRef backupKeybag,
215 CFDataRef password) {
216 __block OSStatus result;
217 os_activity_initiate("_SecKeychainRestoreBackup", OS_ACTIVITY_FLAG_DEFAULT, ^{
218 result = SecOSStatusWith(^bool (CFErrorRef *error) {
219 return SECURITYD_XPC(sec_keychain_restore, data_client_data_data_to_error_request, backup, SecSecurityClientGet(), backupKeybag, password, error);
220 });
221 });
222 return result;
223 }
224
225
226 static int compareDigests(const void *l, const void *r) {
227 return memcmp(l, r, CCSHA1_OUTPUT_SIZE);
228 }
229
230 CFDataRef SecItemBackupCreateManifest(CFDictionaryRef backup, CFErrorRef *error)
231 {
232 CFIndex count = backup ? CFDictionaryGetCount(backup) : 0;
233 CFMutableDataRef manifest = CFDataCreateMutable(kCFAllocatorDefault, CCSHA1_OUTPUT_SIZE * count);
234 if (backup) {
235 CFDictionaryForEach(backup, ^void (const void *key, const void *value) {
236 if (isDictionary(value)) {
237 /* converting key back to binary blob is horrible */
238 CFDataRef sha1 = CFDictionaryGetValue(value, kSecItemBackupHashKey);
239 if (isData(sha1) && CFDataGetLength(sha1) == CCSHA1_OUTPUT_SIZE) {
240 CFDataAppend(manifest, sha1);
241 } else {
242 CFStringRef sha1Hex = CFDataCopyHexString(sha1);
243 secerror("bad hash %@ in backup", sha1Hex);
244 CFReleaseSafe(sha1Hex);
245 // TODO: Drop this key from dictionary (outside the loop)
246 }
247 }
248 });
249 qsort(CFDataGetMutableBytePtr(manifest), CFDataGetLength(manifest) / CCSHA1_OUTPUT_SIZE, CCSHA1_OUTPUT_SIZE, compareDigests);
250 }
251 return manifest;
252 }
253
254 OSStatus _SecKeychainBackupSyncable(CFDataRef keybag, CFDataRef password, CFDictionaryRef backup_in, CFDictionaryRef *backup_out)
255 {
256 return SecOSStatusWith(^bool (CFErrorRef *error) {
257 *backup_out = SECURITYD_XPC(sec_keychain_backup_syncable, data_data_dict_to_dict_error_request, backup_in, keybag, password, error);
258 return *backup_out != NULL;
259 });
260 }
261
262 OSStatus _SecKeychainRestoreSyncable(CFDataRef keybag, CFDataRef password, CFDictionaryRef backup_in)
263 {
264 __block OSStatus result;
265 os_activity_initiate("_SecKeychainRestoreSyncable", OS_ACTIVITY_FLAG_DEFAULT, ^{
266 result = SecOSStatusWith(^bool (CFErrorRef *error) {
267 return SECURITYD_XPC(sec_keychain_restore_syncable, dict_data_data_to_error_request, backup_in, keybag, password, error);
268 });
269 });
270 return result;
271 }
272
273 // Client code
274
275 static bool SecKeychainWithBackupFile(CFStringRef backupName, CFErrorRef *error, void(^with)(FILE *bufile)) {
276 int fd = SecItemBackupHandoffFD(backupName, error);
277 if (fd < 0) {
278 secdebug("backup", "SecItemBackupHandoffFD returned %d", fd);
279 return false;
280 }
281
282 // Rewind file to start
283 if (lseek(fd, 0, SEEK_SET)) {
284 secdebug("backup", "Could not seek in fd %d for %@", fd, backupName);
285 return SecCheckErrno(true, error, CFSTR("lseek"));
286 }
287
288 FILE *backup = fdopen(fd, "r");
289 if (!backup) {
290 secdebug("backup", "Receiving file for %@ failed, %d", backupName, errno);
291 SecCheckErrno(!backup, error, CFSTR("fdopen"));
292 if (close(fd)) {
293 secdebug("backup", "Encountered error closing file %@: %d", backupName, errno);
294 SecCheckErrno(true, error, CFSTR("close"));
295 }
296 return false;
297 } else {
298 struct stat sb;
299 if (fstat(fd, &sb)) {
300 secdebug("backup", "Unable to get file metadata for %@, fd %d", backupName, fd);
301 SecCheckErrno(true, error, CFSTR("fstat"));
302 if (fclose(backup)) {
303 secdebug("backup", "Encountered error closing file %@: %d", backupName, errno);
304 SecCheckErrno(true, error, CFSTR("fclose"));
305 }
306 return false;
307 }
308 secdebug("backup", "Receiving file for %@ with fd %d of size %llu", backupName, fd, sb.st_size);
309 }
310
311 with(backup);
312 if (fclose(backup)) {
313 secdebug("backup", "Encountered error %d closing file %@ after backup handler", errno, backupName);
314 SecCheckErrno(true, error, CFSTR("fclose"));
315 // read only file and block has its own error handling for IO failure, no need to return false
316 }
317 return true;
318 }
319
320 static CFArrayRef SecItemBackupCopyNames(CFErrorRef *error)
321 {
322 __block CFArrayRef result;
323 os_activity_initiate("SecItemBackupCopyNames", OS_ACTIVITY_FLAG_DEFAULT, ^{
324 result = SECURITYD_XPC(sec_item_backup_copy_names, to_array_error_request, error);
325 });
326 return result;
327 }
328
329 bool SecItemBackupWithRegisteredBackups(CFErrorRef *error, void(^backup)(CFStringRef backupName)) {
330 CFArrayRef backupNames = SecItemBackupCopyNames(error);
331 if (!backupNames) return false;
332 CFStringRef name;
333 CFArrayForEachC(backupNames, name) {
334 backup(name);
335 }
336 CFRelease(backupNames);
337 return true;
338 }
339
340 static bool SecItemBackupDoResetEventBody(const uint8_t *der, const uint8_t *der_end, CFErrorRef *error, void (^handleEvent)(SecBackupEventType et, CFTypeRef key, CFTypeRef item)) {
341 size_t sequence_len;
342 const uint8_t *sequence_body = ccder_decode_len(&sequence_len, der, der_end);
343 bool ok = sequence_body;
344 if (ok && sequence_body + sequence_len != der_end) {
345 // Can't ever happen!
346 SecError(errSecDecode, error, CFSTR("trailing junk after reset"));
347 ok = false;
348 }
349 if (ok) {
350 CFDataRef keybag = NULL;
351 if (sequence_body != der_end) {
352 size_t keybag_len = 0;
353 const uint8_t *keybag_start = ccder_decode_tl(CCDER_OCTET_STRING, &keybag_len, sequence_body, der_end);
354 if (!keybag_start) {
355 ok = SecError(errSecDecode, error, CFSTR("failed to decode keybag"));
356 } else if (keybag_start + keybag_len != der_end) {
357 ok = SecError(errSecDecode, error, CFSTR("trailing junk after keybag"));
358 } else {
359 keybag = CFDataCreate(kCFAllocatorDefault, keybag_start, keybag_len);
360 }
361 }
362 handleEvent(kSecBackupEventReset, keybag, NULL);
363 CFReleaseSafe(keybag);
364 }
365
366 return ok;
367 }
368
369 static bool SecItemBackupDoAddEvent(const uint8_t *der, const uint8_t *der_end, CFErrorRef *error, void (^handleEvent)(SecBackupEventType et, CFTypeRef key, CFTypeRef item)) {
370 CFDictionaryRef eventDict = NULL;
371 const uint8_t *der_end_of_dict = der_decode_dictionary(kCFAllocatorDefault, kCFPropertyListImmutable, &eventDict, error, der, der_end);
372 if (der_end_of_dict && der_end_of_dict != der_end) {
373 // Can't ever happen!
374 SecError(errSecDecode, error, CFSTR("trailing junk after add"));
375 der_end_of_dict = NULL;
376 }
377 if (der_end_of_dict) {
378 CFDataRef hash = CFDictionaryGetValue(eventDict, kSecItemBackupHashKey);
379 handleEvent(kSecBackupEventAdd, hash, eventDict);
380 }
381
382 CFReleaseSafe(eventDict);
383 return der_end_of_dict;
384 }
385
386 static bool SecItemBackupDoCompleteEvent(const uint8_t *der, const uint8_t *der_end, CFErrorRef *error, void (^handleEvent)(SecBackupEventType et, CFTypeRef key, CFTypeRef item)) {
387 uint64_t event_num = 0;
388 const uint8_t *der_end_of_num = ccder_decode_uint64(&event_num, der, der_end);
389 if (der_end_of_num && der_end_of_num != der_end) {
390 // Can't ever happen!
391 SecError(errSecDecode, error, CFSTR("trailing junk after complete"));
392 der_end_of_num = NULL;
393 }
394 if (der_end_of_num) {
395 handleEvent(kSecBackupEventComplete, NULL, NULL);
396 }
397 return der_end_of_num;
398 }
399
400 static bool SecItemBackupDoDeleteEventBody(const uint8_t *der, const uint8_t *der_end, CFErrorRef *error, void (^handleEvent)(SecBackupEventType et, CFTypeRef key, CFTypeRef item)) {
401 size_t digest_len = 0;
402 const uint8_t *digest_start = ccder_decode_len(&digest_len, der, der_end);
403 if (digest_start && digest_start + digest_len != der_end) {
404 // Can't ever happen!
405 SecError(errSecDecode, error, CFSTR("trailing junk after delete"));
406 digest_start = NULL;
407 }
408 if (digest_start) {
409 CFDataRef hash = CFDataCreate(kCFAllocatorDefault, digest_start, digest_len);
410 handleEvent(kSecBackupEventRemove, hash, NULL);
411 CFRelease(hash);
412 }
413
414 return digest_start;
415 }
416
417 static void debugDisplayBackupEventTag(ccder_tag tag) {
418 #if !defined(NDEBUG)
419 const char *eventDesc;
420 switch (tag) {
421 case CCDER_CONSTRUCTED_SEQUENCE: eventDesc = "ResetEvent"; break;
422 case CCDER_CONSTRUCTED_SET: eventDesc = "AddEvent"; break;
423 case CCDER_INTEGER: eventDesc = "ResetEvent"; break;
424 case CCDER_OCTET_STRING: eventDesc = "DeleteEvent"; break;
425 default: eventDesc = "UnknownEvent"; break;
426 }
427 secdebug("backup", "processing event %s (tag %08lX)", eventDesc, tag);
428 #endif
429 }
430
431 static bool SecItemBackupDoEvent(const uint8_t *der, const uint8_t *der_end, CFErrorRef *error, void (^handleEvent)(SecBackupEventType et, CFTypeRef key, CFTypeRef item)) {
432 ccder_tag tag;
433 const uint8_t *der_start_of_len = ccder_decode_tag(&tag, der, der_end);
434 debugDisplayBackupEventTag(tag);
435 switch (tag) {
436 case CCDER_CONSTRUCTED_SEQUENCE:
437 return SecItemBackupDoResetEventBody(der_start_of_len, der_end, error, handleEvent);
438 case CCDER_CONSTRUCTED_SET:
439 return SecItemBackupDoAddEvent(der, der_end, error, handleEvent);
440 case CCDER_INTEGER:
441 return SecItemBackupDoCompleteEvent(der, der_end, error, handleEvent);
442 case CCDER_OCTET_STRING:
443 return SecItemBackupDoDeleteEventBody(der_start_of_len, der_end, error, handleEvent);
444 default:
445 return SecError(errSecDecode, error, CFSTR("unsupported event tag: %lu"), tag);
446 }
447 }
448
449 // TODO: Move to ccder and give better name.
450 static const uint8_t *ccder_decode_len_unchecked(size_t *lenp, const uint8_t *der, const uint8_t *der_end) {
451 if (der && der < der_end) {
452 size_t len = *der++;
453 if (len < 0x80) {
454 } else if (len == 0x81) {
455 if (der_end - der < 1) goto errOut;
456 len = *der++;
457 } else if (len == 0x82) {
458 if (der_end - der < 2) goto errOut;
459 len = *(der++) << 8;
460 len += *der++;
461 } else if (len == 0x83) {
462 if (der_end - der < 3) goto errOut;
463 len = *(der++) << 16;
464 len += *(der++) << 8;
465 len += *(der++);
466 } else {
467 goto errOut;
468 }
469 *lenp = len;
470 return der;
471 }
472 errOut:
473 return NULL;
474 }
475
476 static bool SecKeychainWithBackupFileParse(FILE *backup, CFErrorRef *error, void (^handleEvent)(SecBackupEventType et, CFTypeRef key, CFTypeRef item)) {
477 __block bool ok = true;
478 size_t buf_remaining = 0;
479 const size_t read_ahead = 16;
480 size_t buf_len = read_ahead;
481 uint8_t *buf = malloc(buf_len);
482 for (;;) {
483 const size_t bytes_read = fread(buf + buf_remaining, 1, read_ahead - buf_remaining, backup);
484 if (bytes_read <= 0) {
485 if (!feof(backup))
486 ok = SecCheckErrno(true, error, CFSTR("read backup event header"));
487 else if (!buf_remaining) {
488 // Nothing read, nothing in buffer, clean eof.
489 }
490 break;
491 }
492 const size_t buf_avail = bytes_read + buf_remaining;
493
494 const uint8_t *der = buf;
495 const uint8_t *der_end = der + buf_avail;
496 ccder_tag tag;
497 size_t body_len;
498 der = ccder_decode_tag(&tag, der, der_end);
499 der = ccder_decode_len_unchecked(&body_len, der, der_end);
500
501 if (!der) {
502 ok = SecError(errSecDecode, error, CFSTR("failed to decode backup event header"));
503 break;
504 }
505
506 const size_t decoded_len = der - buf;
507 size_t event_len = decoded_len + body_len;
508 if (event_len > buf_avail) {
509 // We need to read the rest of this event, first
510 // ensure we have enough space.
511 if (buf_len < event_len) {
512 // TODO: Think about a max for buf_len here to prevent attacks.
513 uint8_t *new_buf = realloc(buf, event_len);
514 if (!new_buf) {
515 ok = SecError(errSecAllocate, error, CFSTR("realloc buf failed"));
516 break;
517 }
518 buf = new_buf;
519 buf_len = event_len;
520 }
521
522 // Read tail of current event.
523 const size_t tail_len = fread(buf + buf_avail, 1, event_len - buf_avail, backup);
524 if (tail_len < event_len - buf_avail) {
525 if (!feof(backup)) {
526 ok = SecCheckErrno(true, error, CFSTR("failed to read event body"));
527 } else {
528 ok = SecError(errSecDecode, error, CFSTR("unexpected end of event file %zu of %zu bytes read"), tail_len, event_len - buf_avail);
529 }
530 break;
531 }
532 }
533
534 // Adjust der_end to the end of the event.
535 der_end = buf + event_len;
536
537 ok &= SecItemBackupDoEvent(buf, der_end, error, handleEvent);
538
539 if (event_len < buf_avail) {
540 // Shift remaining bytes to start of buffer.
541 buf_remaining = buf_avail - event_len;
542 memmove(buf, der_end, buf_remaining);
543 } else {
544 buf_remaining = 0;
545 }
546 }
547 free(buf);
548 return ok;
549 }
550
551 bool SecItemBackupWithChanges(CFStringRef backupName, CFErrorRef *error, void (^handleEvent)(SecBackupEventType et, CFTypeRef key, CFTypeRef item)) {
552 __block bool ok = true;
553 __block CFErrorRef localError = NULL;
554 ok &= SecKeychainWithBackupFile(backupName, &localError, ^(FILE *backup) {
555 ok &= SecKeychainWithBackupFileParse(backup, &localError, handleEvent);
556 });
557 if (!ok) { // TODO: remove this logging
558 secdebug("backup", "SecItemBackupWithChanges failed: %@", localError);
559 handleEvent(kSecBackupEventComplete, NULL, NULL);
560 CFErrorPropagate(localError, error);
561 }
562
563 return ok;
564 }
565
566 bool SecItemBackupSetConfirmedManifest(CFStringRef backupName, CFDataRef keybagDigest, CFDataRef manifest, CFErrorRef *error) {
567 __block bool result;
568 os_activity_initiate("SecItemBackupSetConfirmedManifest", OS_ACTIVITY_FLAG_DEFAULT, ^{
569 result = SECURITYD_XPC(sec_item_backup_set_confirmed_manifest, string_data_data_to_bool_error_request, backupName, keybagDigest, manifest, error);
570 });
571 return result;
572 }
573
574 void SecItemBackupRestore(CFStringRef backupName, CFStringRef peerID, CFDataRef keybag, CFDataRef secret, CFTypeRef backup, void (^completion)(CFErrorRef error)) {
575 __block CFErrorRef localError = NULL;
576 os_activity_initiate("SecItemBackupRestore", OS_ACTIVITY_FLAG_DEFAULT, ^{
577 SECURITYD_XPC(sec_item_backup_restore, string_string_data_data_data_to_bool_error_request, backupName, peerID, keybag, secret, backup, &localError);
578 });
579 completion(localError);
580 CFReleaseSafe(localError);
581 }
582
583 CFDictionaryRef SecItemBackupCopyMatching(CFDataRef keybag, CFDataRef secret, CFDictionaryRef backup, CFDictionaryRef query, CFErrorRef *error) {
584 SecError(errSecUnimplemented, error, CFSTR("SecItemBackupCopyMatching unimplemented"));
585 return NULL;
586 }
587
588 bool SecBackupKeybagAdd(CFDataRef passcode, CFDataRef *identifier, CFURLRef *pathinfo, CFErrorRef *error) {
589 __block bool result = false;
590 os_activity_initiate("_SecServerBackupKeybagAdd", OS_ACTIVITY_FLAG_DEFAULT, ^{
591 securityd_send_sync_and_do(kSecXPCOpBackupKeybagAdd, error, ^bool(xpc_object_t message, CFErrorRef *error) {
592 return SecXPCDictionarySetDataOptional(message, kSecXPCKeyUserPassword, passcode, error);
593 }, ^bool(xpc_object_t response, CFErrorRef *error) {
594 result = SecXPCDictionaryCopyDataOptional(response, kSecXPCKeyBackupKeybagIdentifier, identifier, error) &&
595 SecXPCDictionaryCopyURLOptional(response, kSecXPCKeyBackupKeybagPath, pathinfo, error) &&
596 SecXPCDictionaryGetBool(response, kSecXPCKeyResult, error);
597 return result;
598 });
599 });
600 return result;
601 }
602
603 bool SecBackupKeybagDelete(CFDictionaryRef query, CFErrorRef *error) {
604 __block bool result = false;
605 os_activity_initiate("_SecBackupKeybagDelete", OS_ACTIVITY_FLAG_DEFAULT, ^{
606 securityd_send_sync_and_do(kSecXPCOpBackupKeybagDelete, error, ^bool(xpc_object_t message, CFErrorRef *error) {
607 return SecXPCDictionarySetPList(message, kSecXPCKeyQuery, query, error);
608 }, ^bool(xpc_object_t response, CFErrorRef *error) {
609 result = SecXPCDictionaryGetBool(response, kSecXPCKeyResult, error);
610 return result;
611 });
612 });
613 return result;
614 }
615
616