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