/ OSX / sec / Security / SecItemBackup.c
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