2 * Copyright (c) 2015 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 * SecItemBackup.c - Client side backup interfaces and support code
29 #include <Security/SecItemBackup.h>
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>
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
));
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
);
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
);
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
));
92 static int string_to_fd_error_request(enum SecXPCOperation op
, CFStringRef backupName
, CFErrorRef
*error
) {
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
);
103 static bool string_data_data_to_bool_error_request(enum SecXPCOperation op
, CFStringRef backupName
, CFDataRef keybagDigest
, CFDataRef manifest
, CFErrorRef
*error
)
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
);
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
)
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
);
127 static CFStringRef
string_to_string_error_request(enum SecXPCOperation op
, CFStringRef viewName
, CFErrorRef
*error
)
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
);
138 static CFArrayRef
to_array_error_request(enum SecXPCOperation op
, CFErrorRef
*error
)
140 __block CFArrayRef result
= NULL
;
141 securityd_send_sync_and_do(op
, error
, ^bool(xpc_object_t message
, CFErrorRef
*error
) {
143 }, ^bool(xpc_object_t response
, CFErrorRef
*error
) {
144 return result
= SecXPCDictionaryCopyArray(response
, kSecXPCKeyResult
, error
);
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
);
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
);
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
);
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
);
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
, ^{
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
));
199 _SecKeychainRestoreBackupFromFileDescriptor(int fd
, CFDataRef backupKeybag
, CFDataRef password
, CFErrorRef
*error
)
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
));
215 * Current promise is that this is low memory usage, so in the current format, ask securityd
216 * to resolve the item for us.
220 _SecKeychainCopyKeybagUUIDFromFileDescriptor(int fd
, CFErrorRef
*error
)
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
));
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
);
246 static int compareDigests(const void *l
, const void *r
) {
247 return memcmp(l
, r
, CCSHA1_OUTPUT_SIZE
);
250 CFDataRef
SecItemBackupCreateManifest(CFDictionaryRef backup
, CFErrorRef
*error
)
252 CFIndex count
= backup
? CFDictionaryGetCount(backup
) : 0;
253 CFMutableDataRef manifest
= CFDataCreateMutable(kCFAllocatorDefault
, CCSHA1_OUTPUT_SIZE
* count
);
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
);
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)
269 qsort(CFDataGetMutableBytePtr(manifest
), CFDataGetLength(manifest
) / CCSHA1_OUTPUT_SIZE
, CCSHA1_OUTPUT_SIZE
, compareDigests
);
274 OSStatus
_SecKeychainBackupSyncable(CFDataRef keybag
, CFDataRef password
, CFDictionaryRef backup_in
, CFDictionaryRef
*backup_out
)
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
;
282 OSStatus
_SecKeychainRestoreSyncable(CFDataRef keybag
, CFDataRef password
, CFDictionaryRef backup_in
)
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
);
295 static bool SecKeychainWithBackupFile(CFStringRef backupName
, CFErrorRef
*error
, void(^with
)(FILE *bufile
)) {
296 int fd
= SecItemBackupHandoffFD(backupName
, error
);
298 secdebug("backup", "SecItemBackupHandoffFD returned %d", fd
);
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"));
308 FILE *backup
= fdopen(fd
, "r");
310 secdebug("backup", "Receiving file for %@ failed, %d", backupName
, errno
);
311 SecCheckErrno(!backup
, error
, CFSTR("fdopen"));
313 secdebug("backup", "Encountered error closing file %@: %d", backupName
, errno
);
314 SecCheckErrno(true, error
, CFSTR("close"));
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"));
328 secdebug("backup", "Receiving file for %@ with fd %d of size %llu", backupName
, fd
, sb
.st_size
);
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
340 static CFArrayRef
SecItemBackupCopyNames(CFErrorRef
*error
)
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
);
349 bool SecItemBackupWithRegisteredBackups(CFErrorRef
*error
, void(^backup
)(CFStringRef backupName
)) {
350 CFArrayRef backupNames
= SecItemBackupCopyNames(error
);
351 if (!backupNames
) return false;
353 CFArrayForEachC(backupNames
, name
) {
356 CFRelease(backupNames
);
360 static CFStringRef
SecItemBackupViewAndCopyBackupPeerID(CFStringRef viewName
, CFErrorRef
*error
)
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
);
369 bool SecItemBackupWithRegisteredViewBackup(CFStringRef viewName
, CFErrorRef
*error
) {
370 CFStringRef backupName
= SecItemBackupViewAndCopyBackupPeerID(viewName
, error
);
371 if(backupName
== NULL
) {
374 CFReleaseNull(backupName
);
379 static bool SecItemBackupDoResetEventBody(const uint8_t *der
, const uint8_t *der_end
, CFErrorRef
*error
, void (^handleEvent
)(SecBackupEventType et
, CFTypeRef key
, CFTypeRef item
)) {
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"));
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
);
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"));
398 keybag
= CFDataCreate(kCFAllocatorDefault
, keybag_start
, keybag_len
);
401 handleEvent(kSecBackupEventReset
, keybag
, NULL
);
402 CFReleaseSafe(keybag
);
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
;
416 if (der_end_of_dict
) {
417 CFDataRef hash
= CFDictionaryGetValue(eventDict
, kSecItemBackupHashKey
);
418 handleEvent(kSecBackupEventAdd
, hash
, eventDict
);
421 CFReleaseSafe(eventDict
);
422 return der_end_of_dict
;
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
;
433 if (der_end_of_num
) {
434 handleEvent(kSecBackupEventComplete
, NULL
, NULL
);
436 return der_end_of_num
;
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"));
448 CFDataRef hash
= CFDataCreate(kCFAllocatorDefault
, digest_start
, digest_len
);
449 handleEvent(kSecBackupEventRemove
, hash
, NULL
);
456 static void debugDisplayBackupEventTag(ccder_tag tag
) {
458 const char *eventDesc
;
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;
466 secdebug("backup", "processing event %s (tag %08lX)", eventDesc
, tag
);
470 static bool SecItemBackupDoEvent(const uint8_t *der
, const uint8_t *der_end
, CFErrorRef
*error
, void (^handleEvent
)(SecBackupEventType et
, CFTypeRef key
, CFTypeRef item
)) {
472 const uint8_t *der_start_of_len
= ccder_decode_tag(&tag
, der
, der_end
);
473 debugDisplayBackupEventTag(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
);
480 return SecItemBackupDoCompleteEvent(der
, der_end
, error
, handleEvent
);
481 case CCDER_OCTET_STRING
:
482 return SecItemBackupDoDeleteEventBody(der_start_of_len
, der_end
, error
, handleEvent
);
484 return SecError(errSecDecode
, error
, CFSTR("unsupported event tag: %lu"), tag
);
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
) {
493 } else if (len
== 0x81) {
494 if (der_end
- der
< 1) goto errOut
;
496 } else if (len
== 0x82) {
497 if (der_end
- der
< 2) goto errOut
;
500 } else if (len
== 0x83) {
501 if (der_end
- der
< 3) goto errOut
;
502 len
= *(der
++) << 16;
503 len
+= *(der
++) << 8;
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
);
522 const size_t bytes_read
= fread(buf
+ buf_remaining
, 1, read_ahead
- buf_remaining
, backup
);
523 if (bytes_read
<= 0) {
525 ok
= SecCheckErrno(true, error
, CFSTR("read backup event header"));
526 else if (!buf_remaining
) {
527 // Nothing read, nothing in buffer, clean eof.
531 const size_t buf_avail
= bytes_read
+ buf_remaining
;
533 const uint8_t *der
= buf
;
534 const uint8_t *der_end
= der
+ buf_avail
;
537 der
= ccder_decode_tag(&tag
, der
, der_end
);
538 der
= ccder_decode_len_unchecked(&body_len
, der
, der_end
);
541 ok
= SecError(errSecDecode
, error
, CFSTR("failed to decode backup event header"));
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
);
554 ok
= SecError(errSecAllocate
, error
, CFSTR("realloc buf failed"));
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
) {
565 ok
= SecCheckErrno(true, error
, CFSTR("failed to read event body"));
567 ok
= SecError(errSecDecode
, error
, CFSTR("unexpected end of event file %zu of %zu bytes read"), tail_len
, event_len
- buf_avail
);
573 // Adjust der_end to the end of the event.
574 der_end
= buf
+ event_len
;
576 ok
&= SecItemBackupDoEvent(buf
, der_end
, error
, handleEvent
);
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
);
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
);
596 if (!ok
) { // TODO: remove this logging
597 secdebug("backup", "SecItemBackupWithChanges failed: %@", localError
);
598 handleEvent(kSecBackupEventComplete
, NULL
, NULL
);
599 CFErrorPropagate(localError
, error
);
605 bool SecItemBackupSetConfirmedManifest(CFStringRef backupName
, CFDataRef keybagDigest
, CFDataRef manifest
, CFErrorRef
*error
) {
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
);
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
);
618 completion(localError
);
619 CFReleaseSafe(localError
);
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
);
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
);