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