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