/ keychain / SecureObjectSync / SOSEngine.c
SOSEngine.c
   1  /*
   2   * Copyright (c) 2012-2017 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   * SOSEngine.c -  Implementation of a secure object syncing engine
  27   */
  28  
  29  #import "keychain/SecureObjectSync/SOSChangeTracker.h"
  30  #include "keychain/SecureObjectSync/SOSEnginePriv.h"
  31  #include "keychain/SecureObjectSync/SOSDigestVector.h"
  32  #include "keychain/SecureObjectSync/SOSInternal.h"
  33  #include "keychain/SecureObjectSync/SOSPeer.h"
  34  #include <Security/SecureObjectSync/SOSViews.h>
  35  #include "keychain/SecureObjectSync/SOSBackupEvent.h"
  36  #include "keychain/SecureObjectSync/SOSPersist.h"
  37  #include <Security/SecureObjectSync/SOSCloudCircleInternal.h>
  38  
  39  #include <corecrypto/ccder.h>
  40  #include <stdlib.h>
  41  #include <stdbool.h>
  42  #include <utilities/array_size.h>
  43  #include <utilities/SecCFCCWrappers.h>
  44  #include <utilities/SecCFError.h>
  45  #include <utilities/SecCFRelease.h>
  46  #include <utilities/SecCFWrappers.h>
  47  #include <utilities/der_plist.h>
  48  #include <utilities/der_plist_internal.h>
  49  #include <utilities/debugging.h>
  50  #include <utilities/iCloudKeychainTrace.h>
  51  #include <utilities/SecCoreCrypto.h>
  52  #include <utilities/SecFileLocations.h>
  53  #include <utilities/SecTrace.h>
  54  #include "utilities/SecCoreAnalytics.h"
  55  
  56  #include <AssertMacros.h>
  57  #include <CoreFoundation/CoreFoundation.h>
  58  
  59  #include "keychain/securityd/SecItemServer.h"    // TODO: We can't leave this here.
  60  #include "keychain/securityd/SOSCloudCircleServer.h" // TODO: We can't leave this here.
  61  #include <Security/SecItem.h>           // TODO: We can't leave this here.
  62  #include <Security/SecItemPriv.h>       // TODO: We can't leave this here.
  63  #include "keychain/securityd/SecItemSchema.h"
  64  #include "keychain/securityd/iCloudTrace.h"
  65  
  66  #include <keychain/ckks/CKKS.h>
  67  
  68  #include <CoreFoundation/CFURL.h>
  69  
  70  #include "keychain/SecureObjectSync/SOSEnsureBackup.h"
  71  
  72  //
  73  // MARK: SOSEngine The Keychain database with syncable keychain support.
  74  //
  75  
  76  //----------------------------------------------------------------------------------------
  77  // MARK: Engine state v0
  78  //----------------------------------------------------------------------------------------
  79  
  80  // Key in dataSource for general engine state file.
  81  // This file only has digest entries in it, no manifests.
  82  static const CFStringRef kSOSEngineState = CFSTR("engine-state");
  83  
  84  // Keys in state dictionary
  85  static CFStringRef kSOSEngineManifestCacheKey = CFSTR("manifestCache");
  86  static CFStringRef kSOSEnginePeerStateKey = CFSTR("peerState");
  87  static CFStringRef kSOSEnginePeerIDsKey = CFSTR("peerIDs");
  88  static CFStringRef kSOSEngineIDKey = CFSTR("id");
  89  static CFStringRef kSOSEngineTraceDateKey = CFSTR("traceDate");
  90  
  91  //----------------------------------------------------------------------------------------
  92  // MARK: Engine state v2
  93  //----------------------------------------------------------------------------------------
  94  
  95  #if !TARGET_OS_SIMULATOR
  96  static const CFIndex kCurrentEngineVersion = 2;
  97  #endif
  98  // Keychain/datasource items
  99  // Used for the kSecAttrAccount when saving in the datasource with dsSetStateWithKey
 100  // Class D [kSecAttrAccessibleAlwaysPrivate/kSecAttrAccessibleAlwaysThisDeviceOnly]
 101  CFStringRef kSOSEngineStatev2 = CFSTR("engine-state-v2");
 102  CFStringRef kSOSEnginePeerStates = CFSTR("engine-peer-states");
 103  CFStringRef kSOSEngineManifestCache = CFSTR("engine-manifest-cache");
 104  CFStringRef kSOSEngineCoders = CFSTR("engine-coders");
 105  #define kSOSEngineProtectionDomainClassA kSecAttrAccessibleWhenUnlockedThisDeviceOnly
 106  
 107  // Keys for individual dictionaries
 108  //  engine-state-v2
 109  CFStringRef kSOSEngineStateVersionKey = CFSTR("engine-stateVersion");
 110  
 111  // Current save/load routines
 112  // SOSEngineCreate/SOSEngineLoad/SOSEngineSetState
 113  // SOSEngineSave/SOSEngineDoSave/SOSEngineCopyState
 114  // no save/load functions external to this file
 115  
 116  /*
 117      Divide engine state into five pieces:
 118  
 119   - General engine state
 120      - My peer ID
 121      - List of other (trusted) peer IDs
 122  
 123   - Coder data (formerly in peer state)
 124   - Backup Keybags (backup peers only)
 125   - Peer state (including manifest hashes -- just keys into ManifestCache)
 126      [__OpaqueSOSPeer/SOSPeerRef]
 127      must-send
 128      send-objects
 129      sequence-number
 130      Peer object states:
 131          pending-objects
 132          unwanted-manifest
 133          confirmed-manifest
 134          local-manifest
 135          pending-manifest
 136      Views
 137  
 138   - Manifest Cache
 139      - local manifest hashes (copy of local keychain)
 140      - peer manifest hashes
 141  
 142   These divisions are based on size, frequency of update, and protection domain
 143  
 144      The Manifest Cache is a dictionary where each key is a hash over its entry,
 145      which is a concatenation of 20 byte hashes of the keychain items. The local
 146      keychain is present as one entry. The other entries are subsets of that, one
 147      for each confirmed/pending/missing/unwanted shared with a peer. The local
 148      keychain entry can be re-created by iterating over the databse, whereas the
 149      others are built up through communicating with other peers.
 150  
 151   83:d=2  hl=2 l=  13 prim:   UTF8STRING        :manifestCache
 152   98:d=2  hl=4 l= 912 cons:   SET
 153   102:d=3  hl=2 l=  24 cons:    SEQUENCE
 154   104:d=4  hl=2 l=  20 prim:     OCTET STRING      [HEX DUMP]:DA39A3EE5E6B4B0D3255BFEF95601890AFD80709
 155   126:d=4  hl=2 l=   0 prim:     OCTET STRING
 156   128:d=3  hl=2 l= 124 cons:    SEQUENCE
 157   130:d=4  hl=2 l=  20 prim:     OCTET STRING      [HEX DUMP]:F9B59370A4733F0D174E8D220C5BE3AF062C775B
 158   152:d=4  hl=2 l= 100 prim:     OCTET STRING      [HEX DUMP]:5A574BB4EC90C3BBCC69EE73CBFE039133AE807265D6A58003B8D205997EAB96390AAB207E63A2E270A476CAB5B2D9D2F7B0E55512AA957B58D5658E7EF907B069B83AA6BA941790A3C3C4A68292D59DABA3CA342966EFF82E1ACAEB691FD6E20772E17E
 159   254:d=3  hl=4 l= 366 cons:    SEQUENCE
 160   258:d=4  hl=2 l=  20 prim:     OCTET STRING      [HEX DUMP]:2E69C2F7F3E014075B30004CE0EC6C1AD419EBF5
 161   280:d=4  hl=4 l= 340 prim:     OCTET STRING      [HEX DUMP]:07571E9678FD7D68812E409CC96C1F54834A099A0C3A2D12CCE2EA95F4505EA52F2C982B2ADEE3DA14D4712C000309BF63D54A98B61AA1D963C40E0E2531C83B28CA5BE6DA0D26400C3C77A618F711DD3CC0BF86CCBAF8AA3332973268B30EEBF21CD8184D9C8427CA13DECCC7BB83C80009A2EF45CCC07F586315C80CEEEEF5D5352FD000AAE6D9CBB4294D5959FD00198225AF9ABD09B341A2FDC278E9FD1465D6A58003B8D205997EAB96390AAB207E63A2E270A476CAB5B2D9D2F7B0E55512AA957B58D5658E7EF907B069B83AA6BA941790A3C3C4A68292D59D95C9D4D8A8BCA2E8242AB0D409F671F298B6DCAE9BC4238C09E07548CEFB300098606F9E4F230C99ABA3CA342966EFF82E1ACAEB691FD6E20772E17EB4FEFB84F8CF75C0C69C59532C354D175A59F961BA4D4DFA017FD8192288F14278AE76712E127D65FE616C7E4FD0713644F7C9A7ABA1CE065694A968
 162   624:d=3  hl=4 l= 386 cons:    SEQUENCE
 163   628:d=4  hl=2 l=  20 prim:     OCTET STRING      [HEX DUMP]:CCF179FF718C10F151E7409EDF1A06F0DF10DCAD
 164   650:d=4  hl=4 l= 360 prim:     OCTET STRING      [HEX DUMP]:07571E9678FD7D68812E409CC96C1F54834A099A0C3A2D12CCE2EA95F4505EA52F2C982B2ADEE3DA14D4712C000309BF63D54A98B61AA1D963C40E0E2531C83B28CA5BE6DA0D26400C3C77A618F711DD3CC0BF86CCBAF8AA3332973268B30EEBF21CD8184D9C8427CA13DECCC7BB83C80009A2EF45CCC07F586315C80CEEEEF5D5352FD000AAE6D9CBB4294D5959FD00198225AF9ABD09B341A2FDC278E9FD145A574BB4EC90C3BBCC69EE73CBFE039133AE807265D6A58003B8D205997EAB96390AAB207E63A2E270A476CAB5B2D9D2F7B0E55512AA957B58D5658E7EF907B069B83AA6BA941790A3C3C4A68292D59D95C9D4D8A8BCA2E8242AB0D409F671F298B6DCAE9BC4238C09E07548CEFB300098606F9E4F230C99ABA3CA342966EFF82E1ACAEB691FD6E20772E17EB4FEFB84F8CF75C0C69C59532C354D175A59F961BA4D4DFA017FD8192288F14278AE76712E127D65FE616C7E4FD0713644F7C9A7ABA1CE065694A968
 165  
 166   */
 167  
 168  
 169  
 170  static bool SOSEngineLoad(SOSEngineRef engine, SOSTransactionRef txn, CFErrorRef *error);
 171  static bool SOSEngineSetPeers_locked(SOSEngineRef engine, SOSPeerMetaRef myPeerMeta, CFArrayRef trustedPeerMetas, CFArrayRef untrustedPeerMetas);
 172  static void SOSEngineApplyPeerState(SOSEngineRef engine, CFDictionaryRef peerStateMap);
 173  static void SOSEngineSynthesizePeerMetas(SOSEngineRef engine, CFMutableArrayRef trustedPeersMetas, CFMutableArrayRef untrustedPeers);
 174  static bool SOSEngineLoadCoders(SOSEngineRef engine, SOSTransactionRef txn, CFErrorRef *error);
 175  #if !TARGET_OS_SIMULATOR
 176  static bool SOSEngineDeleteV0State(SOSEngineRef engine, SOSTransactionRef txn, CFErrorRef *error);
 177  #endif
 178  
 179  static CFStringRef SOSPeerIDArrayCreateString(CFArrayRef peerIDs) {
 180      return peerIDs ? CFStringCreateByCombiningStrings(kCFAllocatorDefault, peerIDs, CFSTR(" ")) : CFSTR("");
 181  }
 182   
 183  static CFStringRef SOSEngineCopyFormattingDesc(CFTypeRef cf, CFDictionaryRef formatOptions) {
 184      SOSEngineRef engine = (SOSEngineRef)cf;
 185      CFStringRef tpDesc = SOSPeerIDArrayCreateString(engine->peerIDs);
 186      CFStringRef desc = CFStringCreateWithFormat(kCFAllocatorDefault, formatOptions, CFSTR("<Engine %@ peers %@ MC[%d] PS[%d]>"), engine->myID, tpDesc, engine->manifestCache ? (int)CFDictionaryGetCount(engine->manifestCache) : 0, engine->peerMap ? (int)CFDictionaryGetCount(engine->peerMap) : 0);
 187      CFReleaseSafe(tpDesc);
 188      return desc;
 189   }
 190   
 191  static CFStringRef SOSEngineCopyDebugDesc(CFTypeRef cf) {
 192      return SOSEngineCopyFormattingDesc(cf, NULL);
 193   }
 194   
 195  static dispatch_queue_t sEngineQueue;
 196  static CFDictionaryRef sEngineMap;
 197   
 198  CFGiblisWithFunctions(SOSEngine, NULL, NULL, NULL, NULL, NULL, SOSEngineCopyFormattingDesc, SOSEngineCopyDebugDesc, NULL, NULL, ^{
 199      sEngineQueue = dispatch_queue_create("SOSEngine queue", DISPATCH_QUEUE_SERIAL);
 200      sEngineMap = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
 201  });
 202  
 203  #define _LOG_RAW_MESSAGES 0
 204  void logRawMessage(CFDataRef message, bool sending, uint64_t seqno)
 205  {
 206  #if _LOG_RAW_MESSAGES
 207      CFStringRef hexMessage = NULL;
 208      if (message) {
 209          hexMessage = CFDataCopyHexString(message);
 210          if (sending)
 211              secnoticeq("engine", "%s RAW%1d %@", sending ? "send" : "recv", seqno?2:0, hexMessage);
 212          else
 213              secnoticeq("engine", "%s RAWx %@", sending ? "send" : "recv", hexMessage);  // we don't know vers of received msg here
 214      }
 215      CFReleaseSafe(hexMessage);
 216  #endif
 217  }
 218  
 219  //
 220  // Peer state layout.  WRONG! It's an array now
 221  // The peer state is an array.
 222  // The first element of the array is a dictionary with any number of keys and
 223  // values in it (for future expansion) such as changing the digest size or type
 224  // or remembering boolean flags for a peers sake.
 225  // The next three are special in that they are manifest digests with special
 226  // meaning and rules as to how they are treated (These are dynamically updated
 227  // based on database activity so they have a fully history of all changes made
 228  // to the local db. The first is the manifest representing the pendingObjects
 229  // to send to the other peer.  This is normally only ever appending to, and in
 230  // particular with transactions originating from the Keychain API that affect
 231  // syncable items will need to add the new objects digests to the pendingObjects list
 232  // while adding the digests of any tombstones encountered to the extra list.
 233  
 234  CFStringRef SOSEngineGetMyID(SOSEngineRef engine) {
 235      // TODO: this should not be needed
 236      return engine->myID;
 237  }
 238  
 239  // TEMPORARY: Get the list of IDs for cleanup, this shouldn't be used instead it should iterate KVS.
 240  CFArrayRef SOSEngineGetPeerIDs(SOSEngineRef engine) {
 241      if(!engine) return NULL;
 242      return engine->peerIDs;
 243  }
 244  
 245  void SOSEngineClearCache(SOSEngineRef engine){
 246      CFReleaseNull(engine->manifestCache);
 247      CFReleaseNull(engine->localMinusUnreadableDigest);
 248      if (engine->save_timer)
 249          dispatch_source_cancel(engine->save_timer);
 250      dispatch_release(engine->queue);
 251      engine->queue = NULL;
 252  }
 253  
 254  static SOSPeerRef SOSEngineCopyPeerWithMapEntry_locked(SOSEngineRef engine, CFStringRef peerID, CFTypeRef mapEntry, CFErrorRef *error) {
 255      SOSPeerRef peer = NULL;
 256      if (mapEntry && CFGetTypeID(mapEntry) == SOSPeerGetTypeID()) {
 257          // The mapEntry is an SOSPeer, so we're done.
 258          peer = (SOSPeerRef)CFRetain(mapEntry);
 259      } else {
 260          // The mapEntry is a peerState, attempt to initialize a new
 261          // peer iff peerID is in the set of trusted peerIDs
 262          if (engine->peerIDs && CFArrayContainsValue(engine->peerIDs, CFRangeMake(0, CFArrayGetCount(engine->peerIDs)), peerID)) {
 263              CFErrorRef localError = NULL;
 264              peer = SOSPeerCreateWithState(engine, peerID, mapEntry, &localError);
 265              if (!peer) {
 266                  secerror("error inflating peer: %@: %@ from state: %@", peerID, localError, mapEntry);
 267                  CFReleaseNull(localError);
 268                  peer = SOSPeerCreateWithState(engine, peerID, NULL, error);
 269              }
 270              if (peer) {
 271                  // Replace the map entry with the inflated peer.
 272                  CFDictionarySetValue(engine->peerMap, peerID, peer);
 273              }
 274          } else {
 275              SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("peer: %@ is untrusted inflating not allowed"), peerID);
 276          }
 277      }
 278      return peer;
 279  }
 280  
 281  static SOSPeerRef SOSEngineCopyPeerWithID_locked(SOSEngineRef engine, CFStringRef peerID, CFErrorRef *error) {
 282      CFTypeRef mapEntry = CFDictionaryGetValue(engine->peerMap, peerID);
 283      SOSPeerRef peer = NULL;
 284      if (mapEntry) {
 285          peer = SOSEngineCopyPeerWithMapEntry_locked(engine, peerID, mapEntry, error);
 286      } else {
 287          peer = NULL;
 288          secerror("peer: %@ not found, peerMap: %@, engine: %@", peerID, engine->peerMap, engine);
 289          SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("peer: %@ not found"), peerID);
 290      }
 291      return peer;
 292  }
 293  
 294  struct SOSEngineWithPeerContext {
 295      SOSEngineRef engine;
 296      void (^with)(SOSPeerRef peer);
 297  };
 298  
 299  static void SOSEngineWithPeerMapEntry_locked(const void *peerID, const void *mapEntry, void *context) {
 300      struct SOSEngineWithPeerContext *ewp = context;
 301      SOSPeerRef peer = SOSEngineCopyPeerWithMapEntry_locked(ewp->engine, peerID, mapEntry, NULL);
 302      if (peer) {
 303          ewp->with(peer);
 304          CFRelease(peer);
 305      }
 306  }
 307  
 308  static void SOSEngineForEachPeer_locked(SOSEngineRef engine, void (^with)(SOSPeerRef peer)) {
 309      struct SOSEngineWithPeerContext ewp = { .engine = engine, .with = with };
 310      CFDictionaryRef peerMapCopy = CFDictionaryCreateCopy(NULL, engine->peerMap);
 311      CFDictionaryApplyFunction(peerMapCopy, SOSEngineWithPeerMapEntry_locked, &ewp);
 312      CFRelease(peerMapCopy);
 313  }
 314  
 315  static void SOSEngineWithBackupPeerMapEntry_locked(const void *peerID, const void *mapEntry, void *context) {
 316      struct SOSEngineWithPeerContext *ewp = context;
 317      // v0 backup peer is always in map but we only consider it a backup peer if it has a keybag.
 318      if (SOSPeerMapEntryIsBackup(mapEntry)) {
 319          SOSPeerRef peer = SOSEngineCopyPeerWithMapEntry_locked(ewp->engine, peerID, mapEntry, NULL);
 320          if (peer) {
 321              ewp->with(peer);
 322              CFRelease(peer);
 323          }
 324      }
 325  }
 326  
 327  static void SOSEngineForEachBackupPeer_locked(SOSEngineRef engine, void (^with)(SOSPeerRef peer)) {
 328      struct SOSEngineWithPeerContext ewp = { .engine = engine, .with = with };
 329      CFDictionaryRef peerMapCopy = CFDictionaryCreateCopy(NULL, engine->peerMap);
 330      CFDictionaryApplyFunction(peerMapCopy, SOSEngineWithBackupPeerMapEntry_locked, &ewp);
 331      CFRelease(peerMapCopy);
 332  }
 333  
 334  static void SOSEngineForBackupPeer_locked(SOSEngineRef engine, CFStringRef backupPeerID, void (^with)(SOSPeerRef peer)) {
 335      struct SOSEngineWithPeerContext ewp = { .engine = engine, .with = with };
 336      CFMutableDictionaryRef singleEntryMap = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
 337      CFDictionaryRef peerMapCopy = CFDictionaryCreateCopy(NULL, engine->peerMap);
 338      SOSPeerRef peer = (SOSPeerRef)CFDictionaryGetValue(peerMapCopy, backupPeerID);
 339      if(peer != NULL) {
 340          CFDictionaryAddValue(singleEntryMap, backupPeerID, peer);
 341          CFDictionaryApplyFunction(singleEntryMap, SOSEngineWithBackupPeerMapEntry_locked, &ewp);
 342      }
 343      CFRelease(peerMapCopy);
 344      CFRelease(singleEntryMap);
 345  }
 346  
 347  //
 348  // Manifest cache
 349  //
 350  SOSManifestRef SOSEngineGetManifestForDigest(SOSEngineRef engine, CFDataRef digest) {
 351      if (!engine->manifestCache || !digest) return NULL;
 352      SOSManifestRef manifest = (SOSManifestRef)CFDictionaryGetValue(engine->manifestCache, digest);
 353      if (!manifest) return NULL;
 354      if (CFGetTypeID(manifest) != SOSManifestGetTypeID()) {
 355          secerror("dropping corrupt manifest for %@ from cache", digest);
 356          CFDictionaryRemoveValue(engine->manifestCache, digest);
 357          return NULL;
 358      }
 359      
 360      return manifest;
 361  }
 362  
 363  void SOSEngineAddManifest(SOSEngineRef engine, SOSManifestRef manifest) {
 364      CFDataRef digest = SOSManifestGetDigest(manifest, NULL);
 365      if (digest) {
 366          if (!engine->manifestCache)
 367              engine->manifestCache = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
 368          CFDictionaryAddValue(engine->manifestCache, digest, manifest);
 369      }
 370  }
 371  
 372  CFDataRef SOSEnginePatchRecordAndCopyDigest(SOSEngineRef engine, SOSManifestRef base, SOSManifestRef removals, SOSManifestRef additions, CFErrorRef *error) {
 373      CFDataRef digest = NULL;
 374      SOSManifestRef manifest = SOSManifestCreateWithPatch(base, removals, additions, error);
 375      if (manifest) {
 376          SOSEngineAddManifest(engine, manifest);
 377          digest = CFRetainSafe(SOSManifestGetDigest(manifest, NULL));
 378      }
 379      CFReleaseSafe(manifest);
 380      return digest;
 381  }
 382  
 383  SOSManifestRef SOSEngineCopyPersistedManifest(SOSEngineRef engine, CFDictionaryRef persisted, CFStringRef key) {
 384      return CFRetainSafe(SOSEngineGetManifestForDigest(engine, asData(CFDictionaryGetValue(persisted, key), NULL)));
 385  }
 386  
 387  CFMutableArrayRef SOSEngineCopyPersistedManifestArray(SOSEngineRef engine, CFDictionaryRef persisted, CFStringRef key, CFErrorRef *error) {
 388      CFMutableArrayRef manifests = NULL;
 389      CFArrayRef digests = NULL;
 390      CFDataRef digest;
 391      if (asArrayOptional(CFDictionaryGetValue(persisted, key), &digests, error))
 392          manifests = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
 393      if (digests) CFArrayForEachC(digests, digest) {
 394          SOSManifestRef manifest = SOSEngineGetManifestForDigest(engine, digest);
 395          if (manifest)
 396              CFArrayAppendValue(manifests, manifest);
 397      }
 398      return manifests;
 399  }
 400  
 401  #if !TARGET_OS_SIMULATOR
 402  static CFDictionaryRef SOSEngineCopyEncodedManifestCache_locked(SOSEngineRef engine, CFErrorRef *error) {
 403      CFMutableDictionaryRef mfc = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
 404      SOSEngineForEachPeer_locked(engine, ^(SOSPeerRef peer) {
 405          SOSPeerAddManifestsInUse(peer, mfc);
 406      });
 407      return mfc;
 408  }
 409  #endif
 410  
 411  //
 412  // End of Manifest cache
 413  //
 414  
 415  //----------------------------------------------------------------------------------------
 416  // MARK: Coders
 417  //----------------------------------------------------------------------------------------
 418  
 419  /*
 420   Each peer has an associated coder, whcih the engine keeps track of in a
 421   CFDictionary indexed by peerID. The coders are read from disk when first needed,
 422   then kept in memory as SOSCoders.
 423  
 424   N.B. Don't rollback coder in memory if a transaction is rolled back, since this
 425   might lead to reuse of an IV.
 426  */
 427  
 428  static bool SOSEngineCopyCoderData(SOSEngineRef engine, CFStringRef peerID, CFDataRef *coderData, CFErrorRef *error) {
 429      bool ok = true;
 430      SOSCoderRef coder = (SOSCoderRef)CFDictionaryGetValue(engine->coders, peerID);
 431      if (coder && (CFGetTypeID(coder) == SOSCoderGetTypeID())) {
 432          CFErrorRef localError = NULL;
 433          ok = *coderData = SOSCoderCopyDER(coder, &localError);
 434          if (!ok) {
 435              secerror("failed to der encode coder for peer %@, dropping it: %@", peerID, localError);
 436              CFDictionaryRemoveValue(engine->coders, peerID);
 437              CFErrorPropagate(localError, error);
 438          }
 439      } else {
 440          *coderData = NULL;
 441      }
 442      return ok;
 443  }
 444  
 445  static SOSCoderRef SOSEngineGetCoderInTx_locked(SOSEngineRef engine, SOSTransactionRef txn, CFStringRef peerID, CFErrorRef *error) {
 446      if (!engine->haveLoadedCoders) {
 447          engine->haveLoadedCoders = SOSEngineLoadCoders(engine, txn, error);
 448  
 449          if (!engine->haveLoadedCoders) {
 450              return NULL;
 451          }
 452      }
 453  
 454      SOSCoderRef coder = (SOSCoderRef)CFDictionaryGetValue(engine->coders, peerID);
 455      if (!coder || (CFGetTypeID(coder) != SOSCoderGetTypeID())) {
 456          SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("No coder for peer: %@"), peerID);
 457      }
 458      return coder;
 459  }
 460  
 461  static bool SOSEngineEnsureCoder_locked(SOSEngineRef engine, SOSTransactionRef txn, CFStringRef peerID, SOSFullPeerInfoRef myPeerInfo, SOSPeerInfoRef peerInfo, SOSCoderRef ourCoder, CFErrorRef *error) {
 462      //have to have caused coder loading, transactions do this.
 463      if (!ourCoder || !SOSCoderIsFor(ourCoder, peerInfo, myPeerInfo)) {
 464          secinfo("coder", "New coder for id %@.", peerID);
 465          CFErrorRef localError = NULL;
 466          SOSCoderRef coder = SOSCoderCreate(peerInfo, myPeerInfo, kCFBooleanFalse, &localError);
 467          if (!coder) {
 468              secerror("Failed to create coder for %@: %@", peerID, localError);
 469              CFErrorPropagate(localError, error);
 470              return false;
 471          }
 472          CFDictionarySetValue(engine->coders, peerID, coder);
 473          secdebug("coder", "setting coder for peerid: %@, coder: %@", peerID, coder);
 474          CFReleaseNull(coder);
 475          engine->codersNeedSaving = true;
 476      }
 477      return true;
 478  }
 479  
 480  bool SOSEngineInitializePeerCoder(SOSEngineRef engine, SOSFullPeerInfoRef myPeerInfo, SOSPeerInfoRef peerInfo, CFErrorRef *error) {
 481      __block bool ok = true;
 482      CFStringRef peerID = SOSPeerInfoGetPeerID(peerInfo);
 483  
 484      ok &= SOSEngineWithPeerID(engine, peerID, error, ^(SOSPeerRef peer, SOSCoderRef coder, SOSDataSourceRef dataSource, SOSTransactionRef txn, bool *forceSaveState) {
 485          ok = SOSEngineEnsureCoder_locked(engine, txn, peerID, myPeerInfo, peerInfo, coder, error);
 486          // Only set if the codersNeedSaving state gets set.
 487          *forceSaveState = engine->codersNeedSaving;
 488      });
 489  
 490      return ok;
 491  }
 492  
 493  static bool SOSEngineGCPeerState_locked(SOSEngineRef engine, CFErrorRef *error) {
 494      bool ok = true;
 495      
 496      //require_quiet(ok = SOSEngineGCManifests_locked(engine, error), exit);
 497  
 498  //exit:
 499      return ok;
 500  }
 501  #if !TARGET_OS_SIMULATOR
 502  static CFMutableDictionaryRef SOSEngineCopyPeerState_locked(SOSEngineRef engine, CFErrorRef *error) {
 503      CFMutableDictionaryRef peerState = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
 504      CFDictionaryForEach(engine->peerMap, ^(const void *key, const void *value) {
 505          CFDictionaryRef state = NULL;
 506          if (value && CFGetTypeID(value) == SOSPeerGetTypeID()) {
 507              CFErrorRef localError = NULL;
 508              // Inflated peer
 509              state = SOSPeerCopyState((SOSPeerRef)value, &localError);
 510              if (!state)
 511                  secnotice("engine", "%@ failed to encode peer: %@", key, localError);
 512              CFReleaseNull(localError);
 513              // TODO: Potentially replace inflated peer with deflated peer in peerMap
 514          } else if (value) {
 515              // We have a deflated peer.
 516              state = CFRetainSafe(value);
 517          }
 518  
 519          if (state) {
 520              CFDictionarySetValue(peerState, key, state);
 521              CFReleaseSafe(state);
 522          }
 523      });
 524      return peerState;
 525  }
 526  #endif
 527  static CFMutableDictionaryRef SOSEngineCopyPeerCoders_locked(SOSEngineRef engine, CFErrorRef *error) {
 528      CFMutableDictionaryRef coders = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
 529      CFDictionaryForEach(engine->peerMap, ^(const void *key, const void *value) {
 530          CFDataRef coderData = NULL;
 531          CFErrorRef localError = NULL;
 532          bool ok = SOSEngineCopyCoderData(engine, (CFStringRef)key, &coderData, &localError);
 533  
 534          if (!ok) {
 535              secnotice("engine", "%@ no coder for peer: %@", key, localError);
 536          }
 537          if (ok && coderData) {
 538              CFDictionarySetValue(coders, key, coderData);
 539          }
 540          CFReleaseNull(coderData);
 541          CFReleaseNull(localError);
 542      });
 543      return coders;
 544  }
 545  
 546  //----------------------------------------------------------------------------------------
 547  // MARK: Engine state v2 Save
 548  //----------------------------------------------------------------------------------------
 549  
 550  // Coders and keybags
 551  
 552  static CFDataRef SOSEngineCopyCoders(SOSEngineRef engine, CFErrorRef *error) {
 553      // Copy the CFDataRef version of the coders into a dictionary, which is then DER-encoded for saving
 554      CFDictionaryRef coders = SOSEngineCopyPeerCoders_locked(engine, error);
 555      secdebug("coders", "copying coders! %@", coders);
 556      CFDataRef der = CFPropertyListCreateDERData(kCFAllocatorDefault, coders, error);
 557      CFReleaseSafe(coders);
 558      return der;
 559  }
 560  
 561  #pragma clang diagnostic push
 562  #pragma clang diagnostic fatal "-Wshadow"
 563  static bool SOSEngineSaveCoders(SOSEngineRef engine, SOSTransactionRef txn, CFErrorRef *error) {
 564      // MUST hold engine lock
 565      // Device must be unlocked for this to succeed
 566  
 567      if(!engine->haveLoadedCoders){
 568          secdebug("coders", "attempting to save coders before we have loaded them!");
 569      }
 570      
 571      bool ok = true;
 572      if (engine->codersNeedSaving) {
 573          CFErrorRef localError = NULL;
 574          CFDataRef derCoders = SOSEngineCopyCoders(engine, &localError);
 575          ok = derCoders && SOSDataSourceSetStateWithKey(engine->dataSource, txn, kSOSEngineCoders,
 576                                                              kSOSEngineProtectionDomainClassA, derCoders, &localError);
 577          if (ok) {
 578              engine->codersNeedSaving = false;
 579              secnotice("coder", "saved coders: %@", engine->coders);
 580          } else {
 581              if(error) CFTransferRetained(*error, localError);
 582              secnotice("coder", "failed to save coders: %@ (%@)", engine->coders, localError);
 583          }
 584          CFReleaseSafe(derCoders);
 585          CFReleaseSafe(localError);
 586      }
 587      return ok;
 588  }
 589  #pragma clang diagnostic pop
 590  
 591  bool SOSTestEngineSaveCoders(CFTypeRef engine, SOSTransactionRef txn, CFErrorRef *error){
 592      return SOSEngineSaveCoders((SOSEngineRef)engine, txn, error);
 593  }
 594  #if !TARGET_OS_SIMULATOR
 595  
 596  static CFDictionaryRef SOSEngineCopyBasicState(SOSEngineRef engine, CFErrorRef *error) {
 597      // Create a version of the in-memory engine state for saving to disk
 598      CFMutableDictionaryRef state = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
 599      if (engine->myID)
 600          CFDictionarySetValue(state, kSOSEngineIDKey, engine->myID);
 601      if (engine->peerIDs)
 602          CFDictionarySetValue(state, kSOSEnginePeerIDsKey, engine->peerIDs);
 603      if (engine->lastTraceDate)
 604          CFDictionarySetValue(state, kSOSEngineTraceDateKey, engine->lastTraceDate);
 605  
 606      SOSPersistCFIndex(state, kSOSEngineStateVersionKey, kCurrentEngineVersion);
 607      return state;
 608  }
 609  
 610  static bool SOSEngineDoSaveOneState(SOSEngineRef engine, SOSTransactionRef txn, CFStringRef key, CFStringRef pdmn,
 611                                      CFDictionaryRef state, CFErrorRef *error) {
 612      CFDataRef derState = CFPropertyListCreateDERData(kCFAllocatorDefault, state, error);
 613      bool ok = derState && SOSDataSourceSetStateWithKey(engine->dataSource, txn, key, pdmn, derState, error);
 614      CFReleaseSafe(derState);
 615      return ok;
 616  }
 617  
 618  static bool SOSEngineDoSave(SOSEngineRef engine, SOSTransactionRef txn, CFErrorRef *error) {
 619      bool ok = true;
 620  
 621      CFDictionaryRef state = SOSEngineCopyBasicState(engine, error);
 622      ok &= state && SOSEngineDoSaveOneState(engine, txn, kSOSEngineStatev2, kSOSEngineProtectionDomainClassD, state, error);
 623      CFReleaseNull(state);
 624  
 625      state = SOSEngineCopyPeerState_locked(engine, error);
 626      ok &= state && SOSEngineDoSaveOneState(engine, txn, kSOSEnginePeerStates, kSOSEngineProtectionDomainClassD, state, error);
 627      CFReleaseNull(state);
 628  
 629      state = SOSEngineCopyEncodedManifestCache_locked(engine, error);
 630      ok &= state && SOSEngineDoSaveOneState(engine, txn, kSOSEngineManifestCache, kSOSEngineProtectionDomainClassD, state, error);
 631      CFReleaseNull(state);
 632  
 633      ok &= SOSEngineSaveCoders(engine, txn, error);
 634  
 635      SOSEngineDeleteV0State(engine, txn, NULL);
 636  
 637      return ok;
 638  }
 639  #endif
 640  
 641  static bool SOSEngineSave(SOSEngineRef engine, SOSTransactionRef txn, CFErrorRef *error) {
 642      // Don't save engine state from tests
 643      if (!engine->dataSource)
 644          return true;
 645  #if !TARGET_OS_SIMULATOR
 646      return SOSEngineDoSave(engine, txn, error);
 647  #endif
 648      return true;
 649  }
 650  
 651  //----------------------------------------------------------------------------------------
 652  // MARK: Engine state v2 Load/Restore
 653  //----------------------------------------------------------------------------------------
 654  
 655  // Restore the in-memory state of engine from saved state loaded from the db
 656  static bool SOSEngineSetManifestCacheWithDictionary(SOSEngineRef engine, CFDictionaryRef manifestCache, CFErrorRef *error) {
 657      __block bool ok = true;
 658      CFReleaseNull(engine->manifestCache);
 659      if (manifestCache) {
 660          engine->manifestCache = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
 661          CFDictionaryForEach(manifestCache, ^(const void *key, const void *value) {
 662              CFDataRef data = (CFDataRef)value;
 663              if (isData(data)) {
 664                  SOSManifestRef mf = SOSManifestCreateWithData(data, NULL);
 665                  if (mf)
 666                      CFDictionarySetValue(engine->manifestCache, key, mf);
 667                  CFReleaseSafe(mf);
 668              }
 669          });
 670      }
 671  
 672      return ok;
 673  }
 674  
 675  static bool SOSEngineUpdateStateWithDictionary(SOSEngineRef engine, CFDictionaryRef stateDict, CFErrorRef *error) {
 676      bool ok = true;
 677  #if 0
 678      if (stateDict) {
 679          // If kCurrentEngineVersion > 2, uncomment and fill in code below
 680          CFIndex engineVersion = 0 ;
 681          bool versionPresent = SOSPeerGetOptionalPersistedCFIndex(stateDict, kSOSEngineStateVersionKey, &engineVersion);
 682          if (versionPresent && (engineVersion != kCurrentEngineVersion)) {
 683              // need migration
 684          }
 685      }
 686  #endif
 687      return ok;
 688  }
 689  
 690  static bool SOSEngineSetStateWithDictionary(SOSEngineRef engine, CFDictionaryRef stateDict, CFErrorRef *error) {
 691      bool ok = true;
 692      if (stateDict) {
 693          SOSEngineUpdateStateWithDictionary(engine, stateDict, error);
 694          CFRetainAssign(engine->myID, asString(CFDictionaryGetValue(stateDict, kSOSEngineIDKey), NULL));
 695          CFRetainAssign(engine->peerIDs, asArray(CFDictionaryGetValue(stateDict, kSOSEnginePeerIDsKey), NULL));
 696          CFRetainAssign(engine->lastTraceDate, asDate(CFDictionaryGetValue(stateDict, kSOSEngineTraceDateKey), NULL));
 697  
 698      }
 699      secnotice("engine", "%@", engine);
 700      return ok;
 701  }
 702  
 703  static bool SOSEngineSetPeerStateWithDictionary(SOSEngineRef engine, CFDictionaryRef peerStateDict, CFErrorRef *error) {
 704      // Set the in-memory peer state using the dictionary version of the DER-encoded version from disk
 705      CFMutableArrayRef untrustedPeers = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
 706      CFMutableArrayRef trustedPeersMetas = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
 707      SOSEngineApplyPeerState(engine, asDictionary(peerStateDict, NULL));
 708      SOSEngineSynthesizePeerMetas(engine, trustedPeersMetas, untrustedPeers);
 709      SOSEngineSetPeers_locked(engine, engine->myID, trustedPeersMetas, untrustedPeers);
 710      CFReleaseNull(trustedPeersMetas);
 711      CFReleaseNull(untrustedPeers);
 712      return true;
 713  }
 714  
 715  CFMutableDictionaryRef derStateToDictionaryCopy(CFDataRef state, CFErrorRef *error) {
 716      bool ok = true;
 717      CFMutableDictionaryRef stateDict = NULL;
 718      if (state) {
 719          const uint8_t *der = CFDataGetBytePtr(state);
 720          const uint8_t *der_end = der + CFDataGetLength(state);
 721          ok = der = der_decode_dictionary(kCFAllocatorDefault, (CFDictionaryRef *)&stateDict, error, der, der_end);
 722          if (der && der != der_end) {
 723              ok = SOSErrorCreate(kSOSErrorDecodeFailure, error, NULL, CFSTR("trailing %td bytes at end of state"), der_end - der);
 724          }
 725          if (!ok) {
 726              CFReleaseNull(stateDict);
 727          }
 728      }
 729      return stateDict;
 730  }
 731  bool TestSOSEngineLoadCoders(CFTypeRef engine, SOSTransactionRef txn, CFErrorRef *error)
 732  {
 733      return SOSEngineLoadCoders((SOSEngineRef)engine, txn, error);
 734  }
 735  
 736  static bool SOSEngineLoadCoders(SOSEngineRef engine, SOSTransactionRef txn, CFErrorRef *error) {
 737      // Read the serialized engine state from the datasource (aka keychain) and populate the in-memory engine
 738      __block bool needPeerRegistration = false;
 739      bool ok = true;
 740      CFDataRef derCoders = NULL;
 741      CFMutableDictionaryRef codersDict = NULL;
 742      derCoders = SOSDataSourceCopyStateWithKey(engine->dataSource, kSOSEngineCoders, kSOSEngineProtectionDomainClassA, txn, error);
 743      require_quiet(derCoders, xit);
 744      codersDict = derStateToDictionaryCopy(derCoders, error);
 745      require_quiet(codersDict, xit);
 746  
 747      /*
 748       * Make sure all peer have coders
 749       */
 750      CFDictionaryForEach(engine->peerMap, ^(const void *peerID, const void *peerState) {
 751          /*
 752           * Skip backup peer since they will never have coders
 753           */
 754          if (isString(peerID) && CFStringHasSuffix(peerID, CFSTR("-tomb"))) {
 755              secnotice("coder", "Skipping coder check for peer: %@", peerID);
 756              return;
 757          }
 758  
 759          CFTypeRef coderRef = CFDictionaryGetValue(codersDict, peerID);
 760          if (coderRef) {
 761              CFDataRef coderData = asData(coderRef, NULL);
 762              if (coderData) {
 763                  CFErrorRef createError = NULL;
 764                  SOSCoderRef coder = SOSCoderCreateFromData(coderData, &createError);
 765                  if (coder) {
 766                      CFDictionaryAddValue(engine->coders, peerID, coder);
 767                      secnotice("coder", "adding coder: %@ for peerid: %@", coder, peerID);
 768                  } else {
 769                      secnotice("coder", "Coder for '%@' failed to create: %@", peerID, createError);
 770                  }
 771                  CFReleaseNull(createError);
 772                  CFReleaseNull(coder);
 773              } else {
 774                  // Needed a coder, didn't find one, notify the account to help us out.
 775                  // Next attempt to sync will fix this
 776                  secnotice("coder", "coder for %@ was not cf data: %@", peerID, coderData);
 777                  needPeerRegistration = true;
 778              }
 779          } else{
 780              secnotice("coder", "didn't find coder for peer: %@ engine dictionary: %@", peerID, codersDict);
 781              needPeerRegistration = true;
 782          }
 783      });
 784  
 785      secnotice("coder", "Will force peer registration: %s",needPeerRegistration ? "yes" : "no");
 786  
 787      if (needPeerRegistration) {
 788          dispatch_queue_t queue = dispatch_get_global_queue(SOS_ENGINE_PRIORITY, 0);
 789  
 790          dispatch_async(queue, ^{
 791              CFErrorRef eprError = NULL;
 792              if (!SOSCCProcessEnsurePeerRegistration_Server(&eprError)) {
 793                  secnotice("coder", "SOSCCProcessEnsurePeerRegistration failed with: %@", eprError);
 794              }
 795              CFReleaseNull(eprError);
 796          });
 797      }
 798  
 799      engine->haveLoadedCoders = true;
 800  
 801  xit:
 802      CFReleaseNull(derCoders);
 803      CFReleaseNull(codersDict);
 804      return ok;
 805  }
 806  #if !TARGET_OS_SIMULATOR
 807  static bool SOSEngineDeleteV0State(SOSEngineRef engine, SOSTransactionRef txn, CFErrorRef *error) {
 808  //    SOSDataSourceDeleteStateWithKey(engine->dataSource, kSOSEngineState, kSOSEngineProtectionDomainClassD, txn, error);
 809  
 810      // Create effectively empty state until delete is working
 811      CFMutableDictionaryRef state = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
 812      if (engine->myID)
 813          CFDictionarySetValue(state, kSOSEngineIDKey, engine->myID);
 814      CFDataRef derState = CFPropertyListCreateDERData(kCFAllocatorDefault, state, error);
 815      CFReleaseNull(state);
 816  
 817      bool ok = derState && SOSDataSourceSetStateWithKey(engine->dataSource, txn, kSOSEngineState, kSOSEngineProtectionDomainClassD, derState, error);
 818      CFReleaseSafe(derState);
 819      return ok;
 820  }
 821  #endif
 822  static bool SOSEngineLoad(SOSEngineRef engine, SOSTransactionRef txn, CFErrorRef *error) {
 823      // Read the serialized engine state from the datasource (aka keychain) and populate the in-memory engine
 824      bool ok = true;
 825      CFDataRef basicEngineState = NULL;
 826      CFMutableDictionaryRef engineState = NULL;
 827      CFDictionaryRef manifestCache = NULL;
 828      CFDictionaryRef peerStateDict = NULL;
 829      CFMutableDictionaryRef codersDict = NULL;
 830      // Look for the v2 engine state first
 831      basicEngineState = SOSDataSourceCopyStateWithKey(engine->dataSource, kSOSEngineStatev2, kSOSEngineProtectionDomainClassD, txn, error);
 832      if (basicEngineState) {
 833          CFDataRef data = NULL;
 834          engineState = derStateToDictionaryCopy(basicEngineState, error);
 835  
 836          data = SOSDataSourceCopyStateWithKey(engine->dataSource, kSOSEngineManifestCache, kSOSEngineProtectionDomainClassD, txn, error);
 837          manifestCache = derStateToDictionaryCopy(data, error);
 838          CFReleaseNull(data);
 839  
 840          data = SOSDataSourceCopyStateWithKey(engine->dataSource, kSOSEnginePeerStates, kSOSEngineProtectionDomainClassD, txn, error);
 841          peerStateDict = derStateToDictionaryCopy(data, error);
 842          CFReleaseNull(data);
 843      } else {
 844          // Look for original V0 engine state next
 845          CFDataRef v0EngineStateData = SOSDataSourceCopyStateWithKey(engine->dataSource, kSOSEngineState, kSOSEngineProtectionDomainClassD, txn, error);
 846          if (v0EngineStateData) {
 847              engineState = derStateToDictionaryCopy(v0EngineStateData, error);
 848              if (engineState) {
 849                  manifestCache = CFRetainSafe(asDictionary(CFDictionaryGetValue(engineState, kSOSEngineManifestCacheKey), NULL));
 850                  peerStateDict = CFRetainSafe(asDictionary(CFDictionaryGetValue(engineState, kSOSEnginePeerStateKey), NULL));
 851              }
 852              CFReleaseNull(v0EngineStateData);
 853          }
 854          secnotice("coder", "Migrating from v0 engine state; dropping coders and forcing re-negotiation");
 855          SOSCCEnsurePeerRegistration();
 856  
 857          if (engine->peerIDs) {
 858              SOSCCRequestSyncWithPeersList(engine->peerIDs);
 859          }
 860      }
 861  
 862      ok = engineState && SOSEngineSetStateWithDictionary(engine, engineState, error);
 863  
 864      ok &= SOSEngineSetManifestCacheWithDictionary(engine, manifestCache, error);
 865  
 866      ok &= peerStateDict && SOSEngineSetPeerStateWithDictionary(engine, peerStateDict, error);
 867  
 868      CFReleaseSafe(basicEngineState);
 869      CFReleaseSafe(engineState);
 870      CFReleaseSafe(manifestCache);
 871      CFReleaseSafe(peerStateDict);
 872      CFReleaseSafe(codersDict);
 873      return ok;
 874  }
 875  
 876  bool SOSTestEngineSaveWithDER(SOSEngineRef engine, CFDataRef derState, CFErrorRef *error) {
 877      assert(true);
 878      return true;
 879  }
 880  
 881  bool SOSTestEngineSave(SOSEngineRef engine, SOSTransactionRef txn, CFErrorRef *error) {
 882      bool bx = SOSEngineSave(engine, txn, error);
 883      secnotice("test", "saved engine: %@", engine);
 884      return bx;
 885  }
 886  
 887  bool SOSTestEngineLoad(SOSEngineRef engine, SOSTransactionRef txn, CFErrorRef *error) {
 888      bool bx = SOSEngineLoad(engine, txn, error);
 889      secnotice("test", "loaded engine: %@", engine);
 890      return bx;
 891  }
 892  
 893  //----------------------------------------------------------------------------------------
 894  // MARK: Change Trackers and Peer Manifests
 895  //----------------------------------------------------------------------------------------
 896  
 897  static SOSManifestRef SOSEngineCreateManifestWithViewNameSet_locked(SOSEngineRef engine, CFSetRef viewNameSet, CFErrorRef *error) {
 898      // TODO: Potentially tell all changeTrackers to track manifests (    //forall ct do SOSChangeTrackerSetConcrete(ct, true);
 899      // and read the entire dataSource and pass all objects though the filter here, instead of
 900      // forcing the datasource to be able to do "smart" queries
 901      return SOSDataSourceCopyManifestWithViewNameSet(engine->dataSource, viewNameSet, error);
 902  }
 903  
 904  static SOSChangeTrackerRef SOSEngineCopyChangeTrackerWithViewNameSet_locked(SOSEngineRef engine, CFSetRef viewNameSet, CFErrorRef *error) {
 905      SOSChangeTrackerRef ct = (SOSChangeTrackerRef)CFDictionaryGetValue(engine->viewNameSet2ChangeTracker, viewNameSet);
 906      if (!ct)
 907          SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("no change tracker for view set %@"), viewNameSet);
 908      return CFRetainSafe(ct);
 909  }
 910  
 911  static SOSManifestRef SOSEngineCopyManifestWithViewNameSet_locked(SOSEngineRef engine, CFSetRef viewNameSet, CFErrorRef *error) {
 912      SOSChangeTrackerRef ct = SOSEngineCopyChangeTrackerWithViewNameSet_locked(engine, viewNameSet, error);
 913      if (!ct)
 914          return NULL;
 915  
 916      SOSManifestRef manifest = SOSChangeTrackerCopyManifest(ct, NULL);
 917      if (!manifest) {
 918          manifest = SOSEngineCreateManifestWithViewNameSet_locked(engine, viewNameSet, error); // Do the SQL query
 919          SOSChangeTrackerSetManifest(ct, manifest);
 920      }
 921      CFReleaseSafe(ct);
 922      return manifest;
 923  }
 924  
 925  SOSManifestRef SOSEngineCopyLocalPeerManifest_locked(SOSEngineRef engine, SOSPeerRef peer, CFErrorRef *error) {
 926      return SOSEngineCopyManifestWithViewNameSet_locked(engine, SOSPeerGetViewNameSet(peer), error);
 927  }
 928  
 929  #define withViewAndBackup(VIEW)  do { with(VIEW); if (!isTomb) with(VIEW ## _tomb); } while(0)
 930  
 931  
 932  // Invoke with once for each view an object is in.
 933  // TODO: Move this function into the DataSource
 934  static void SOSEngineObjectWithView(SOSEngineRef engine, SOSObjectRef object, void (^with)(CFStringRef view)) {
 935      // Filter items into v0 only view here
 936      SecDbItemRef item = (SecDbItemRef)object; // TODO: Layer violation, breaks tests
 937      if (isDictionary(object)) {
 938          CFTypeRef isTombValue = CFDictionaryGetValue((CFDictionaryRef)object, kSecAttrTombstone);
 939          bool isTomb = isTombValue && CFBooleanGetValue(isTombValue);
 940          // We are in the test just assume v0 and v2 views.
 941          withViewAndBackup(kSOSViewKeychainV0);
 942      } else if (SecDbItemIsSyncableOrCorrupted(item)) {
 943          const SecDbClass *iclass = SecDbItemGetClass(item);
 944          CFTypeRef pdmn = SecDbItemGetCachedValueWithName(item, kSecAttrAccessible);
 945          if ((iclass == genp_class() || iclass == inet_class() || iclass == keys_class() || iclass == cert_class())
 946              && isString(pdmn)
 947              && (CFEqual(pdmn, kSecAttrAccessibleWhenUnlocked)
 948                  || CFEqual(pdmn, kSecAttrAccessibleAfterFirstUnlock)
 949                  || CFEqual(pdmn, kSecAttrAccessibleAlwaysPrivate)
 950                  || CFEqual(pdmn, kSecAttrAccessibleWhenUnlockedThisDeviceOnly)
 951                  || CFEqual(pdmn, kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly)
 952                  || CFEqual(pdmn, kSecAttrAccessibleAlwaysThisDeviceOnlyPrivate)))
 953          {
 954              CFTypeRef tomb = SecDbItemGetCachedValueWithName(item, kSecAttrTombstone);
 955              char cvalue = 0;
 956              bool isTomb = (isNumber(tomb) && CFNumberGetValue(tomb, kCFNumberCharType, &cvalue) && cvalue == 1);
 957              CFStringRef viewHint = SecDbItemGetCachedValueWithName(item, kSecAttrSyncViewHint);
 958  
 959              // check that view hint is a string, if its unset it will be kCFNull
 960              if (!isString(viewHint)) {
 961                  viewHint = NULL;
 962              }
 963  
 964              // Intecept CKKS-handled items here and short-circuit function
 965              if(SOSViewHintInCKKSSystem(viewHint)) {
 966                  return;
 967              }
 968  
 969              if (viewHint == NULL) {
 970                  if (iclass == cert_class()) {
 971                      withViewAndBackup(kSOSViewOtherSyncable);
 972                  } else {
 973                      if (!SecDbItemGetCachedValueWithName(item, kSecAttrTokenID)) {
 974                          withViewAndBackup(kSOSViewKeychainV0);
 975                      }
 976                      CFTypeRef agrp = SecDbItemGetCachedValueWithName(item, kSecAttrAccessGroup);
 977                      if (iclass == keys_class() && CFEqualSafe(agrp, CFSTR("com.apple.security.sos"))) {
 978                          withViewAndBackup(kSOSViewiCloudIdentity);
 979                      } else if (CFEqualSafe(agrp, CFSTR("com.apple.cfnetwork"))) {
 980                          withViewAndBackup(kSOSViewAutofillPasswords);
 981                      } else if (CFEqualSafe(agrp, CFSTR("com.apple.safari.credit-cards"))) {
 982                          withViewAndBackup(kSOSViewSafariCreditCards);
 983                      } else if (iclass == genp_class()) {
 984                          if (CFEqualSafe(agrp, CFSTR("apple")) &&
 985                              CFEqualSafe(SecDbItemGetCachedValueWithName(item, kSecAttrService), CFSTR("AirPort"))) {
 986                              withViewAndBackup(kSOSViewWiFi);
 987                          } else if (CFEqualSafe(agrp, CFSTR("com.apple.sbd"))) {
 988                              withViewAndBackup(kSOSViewBackupBagV0);
 989                          } else {
 990                              withViewAndBackup(kSOSViewOtherSyncable); // (genp)
 991                          }
 992                      } else {
 993                          withViewAndBackup(kSOSViewOtherSyncable); // (inet || keys)
 994                      }
 995                  }
 996              } else {
 997                  with(viewHint);
 998                  if (!isTomb) {
 999                      CFStringRef viewHintTomb = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@-tomb"), viewHint);
1000                      if (viewHintTomb) {
1001                          with(viewHintTomb);
1002                          CFRelease(viewHintTomb);
1003                      }
1004                  }
1005              }
1006          }
1007      } else {
1008          // TODO: general queries
1009  #if 0
1010          SOSViewRef view;
1011          CFArrayForEachC(engine->views, view) {
1012              bool inView = SOSViewQueryMatchItem(view, item);
1013              if (inView) {
1014                  CFStringRef viewName = SOSViewCopyName(view);
1015                  with(viewName);
1016                  CFReleaseSafe(viewName);
1017              }
1018          }
1019  #endif
1020      }
1021  }
1022  
1023  //
1024  // Deliver delayed notifiations of changes in keychain
1025  //
1026  
1027  static void
1028  SOSSendViewNotification(CFSetRef viewNotifications)
1029  {
1030      CFNotificationCenterRef center = CFNotificationCenterGetDarwinNotifyCenter();
1031  
1032      CFSetForEach(viewNotifications, ^(const void *value) {
1033          secinfo("view", "Sending view notification for view %@", value);
1034  
1035          CFStringRef str = CFStringCreateWithFormat(NULL, NULL, CFSTR("com.apple.security.view-change.%@"), value);
1036          if (str == NULL)
1037              return;
1038  
1039          CFNotificationCenterPostNotificationWithOptions(center, str, NULL, NULL, 0);
1040          CFRelease(str);
1041  
1042      });
1043  }
1044  
1045  static void
1046  SOSArmViewNotificationEvents(CFSetRef viewNotifications)
1047  {
1048      static CFMutableSetRef pendingViewNotifications;
1049      static dispatch_once_t onceToken;
1050      static dispatch_queue_t queue;
1051  
1052      dispatch_once(&onceToken, ^{
1053          queue = dispatch_queue_create("ViewNotificationQueue", NULL);
1054      });
1055      if (queue == NULL || CFSetGetCount(viewNotifications) == 0)
1056          return;
1057  
1058      /*
1059       * PendingViewNotifications is only modified on queue.
1060       * PendingViewNotifications is used as a signal if a timer is running.
1061       *
1062       * If a timer is running, new events are just added to the existing
1063       * pendingViewNotifications.
1064       */
1065  
1066  #define DELAY_OF_NOTIFICATION_IN_NS    (NSEC_PER_SEC)
1067  
1068      CFRetain(viewNotifications);
1069  
1070      dispatch_async(queue, ^{
1071          if (pendingViewNotifications == NULL) {
1072              pendingViewNotifications = CFSetCreateMutableCopy(NULL, 0, viewNotifications);
1073  
1074              dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)DELAY_OF_NOTIFICATION_IN_NS), queue, ^{
1075                  SOSSendViewNotification(pendingViewNotifications);
1076  
1077                  // when timer hits, clear out set of modified views
1078                  CFRelease(pendingViewNotifications);
1079                  pendingViewNotifications = NULL;
1080              });
1081          } else {
1082              CFSetUnion(pendingViewNotifications, viewNotifications);
1083          }
1084          CFRelease(viewNotifications);
1085      });
1086  }
1087  
1088  
1089  //
1090  // SOSChangeMapper - Helper for SOSEngineUpdateChanges_locked
1091  //
1092  struct SOSChangeMapper {
1093      SOSEngineRef engine;
1094      SOSTransactionRef txn;
1095      SOSDataSourceTransactionPhase phase;
1096      SOSDataSourceTransactionSource source;
1097      CFMutableDictionaryRef ct2changes;
1098      CFMutableSetRef viewNotifications;
1099  };
1100  
1101  static void SOSChangeMapperInit(struct SOSChangeMapper *cm, SOSEngineRef engine, SOSTransactionRef txn, SOSDataSourceTransactionPhase phase, SOSDataSourceTransactionSource source) {
1102      cm->engine = engine;
1103      cm->txn = txn;
1104      cm->phase = phase;
1105      cm->source = source;
1106      cm->ct2changes = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1107      cm->viewNotifications = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
1108  }
1109  
1110  static void SOSChangeMapperSendNotifications(struct SOSChangeMapper *cm)
1111  {
1112      SOSArmViewNotificationEvents(cm->viewNotifications);
1113  }
1114  
1115  static void SOSChangeMapperFree(struct SOSChangeMapper *cm) {
1116      CFReleaseSafe(cm->ct2changes);
1117      CFReleaseSafe(cm->viewNotifications);
1118  }
1119  
1120  static void SOSChangeMapperAddViewNotification(struct SOSChangeMapper *cm, CFStringRef view)
1121  {
1122      assert(isString(view));
1123  
1124      // aggregate the PCS view into one notification
1125      if (CFStringHasPrefix(view, CFSTR("PCS-"))) {
1126          view = CFSTR("PCS");
1127      }
1128      CFSetSetValue(cm->viewNotifications, view);
1129  }
1130  
1131  static void SOSChangeMapperAppendObject(struct SOSChangeMapper *cm, SOSChangeTrackerRef ct, bool isAdd, CFTypeRef object) {
1132      CFMutableArrayRef changes = (CFMutableArrayRef)CFDictionaryGetValue(cm->ct2changes, ct);
1133      if (!changes) {
1134          changes = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1135          CFDictionarySetValue(cm->ct2changes, ct, changes);
1136          CFReleaseSafe(changes);
1137      }
1138      isAdd ? SOSChangesAppendAdd(changes, object) : SOSChangesAppendDelete(changes, object);
1139  }
1140  
1141  static bool SOSChangeMapperIngestChange(struct SOSChangeMapper *cm, bool isAdd, CFTypeRef change) {
1142      bool someoneCares = false;
1143      if (isData(change)) {
1144          // TODO: Reenable assertion once the tests have been updated
1145          //assert(!isAdd);
1146          // We got a digest for a deleted object. Our dataSource probably couldn't find
1147          // an object with this digest, probably because it went missing, or it was
1148          // discovered to be corrupted.
1149          // Tell all our changeTrackers about this digest since we don't know who might need it.
1150          CFDictionaryForEach(cm->engine->viewNameSet2ChangeTracker, ^(const void *viewNameSet, const void *ct) {
1151              SOSChangeMapperAppendObject(cm, (SOSChangeTrackerRef)ct, isAdd, change);
1152          });
1153          someoneCares = CFDictionaryGetCount(cm->engine->viewNameSet2ChangeTracker);
1154      } else {
1155          // We got an object let's figure out which views it's in and schedule it for
1156          // delivery to all changeTrackers interested in any of those views.
1157          SOSObjectRef object = (SOSObjectRef)change;
1158          CFMutableSetRef changeTrackerSet = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
1159          // First gather all the changeTrackers interested in this object (eliminating dupes by collecting them in a set)
1160          SOSEngineObjectWithView(cm->engine, object, ^(CFStringRef viewName) {
1161              const void *ctorset = CFDictionaryGetValue(cm->engine->viewName2ChangeTracker, viewName);
1162              if (isSet(ctorset)) {
1163                  CFSetForEach((CFSetRef)ctorset, ^(const void *ct) { CFSetAddValue(changeTrackerSet, ct); });
1164              } else if (ctorset) {
1165                  CFSetAddValue(changeTrackerSet, ctorset);
1166              }
1167  
1168  
1169              SOSChangeMapperAddViewNotification(cm, viewName);
1170          });
1171          // Then append the object to the changes array in the ct2changes dictionary keyed by viewSet
1172          CFSetForEach(changeTrackerSet, ^(const void *ct) {
1173              SOSChangeMapperAppendObject(cm, (SOSChangeTrackerRef)ct, isAdd, object);
1174          });
1175          someoneCares = CFSetGetCount(changeTrackerSet);
1176          CFReleaseSafe(changeTrackerSet);
1177      }
1178      return someoneCares;
1179  }
1180  
1181  static bool SOSChangeMapperSend(struct SOSChangeMapper *cm, CFErrorRef *error) {
1182      __block bool ok = true;
1183      CFDictionaryForEach(cm->ct2changes, ^(const void *ct, const void *changes) {
1184          ok &= SOSChangeTrackerTrackChanges((SOSChangeTrackerRef)ct, cm->engine, cm->txn, cm->source, cm->phase, (CFArrayRef)changes, error);
1185      });
1186      return ok;
1187  }
1188  
1189  static bool SOSEngineUpdateChanges_locked(SOSEngineRef engine, SOSTransactionRef txn, SOSDataSourceTransactionPhase phase, SOSDataSourceTransactionSource source, CFArrayRef changes, CFErrorRef *error)
1190  {
1191      secnoticeq("engine", "%@: %s %s %ld changes, txn=%@, %p", engine->myID, phase == kSOSDataSourceTransactionWillCommit ? "will-commit" : phase == kSOSDataSourceTransactionDidCommit ? "did-commit" : "did-rollback",
1192                 source == kSOSDataSourceSOSTransaction ? "sos" :
1193                 source == kSOSDataSourceCKKSTransaction ? "ckks" :
1194                 source == kSOSDataSourceAPITransaction ? "api" :
1195                 "unknown",
1196                 CFArrayGetCount(changes), txn, txn);
1197      bool ok = true;
1198      switch (phase) {
1199          case kSOSDataSourceTransactionDidRollback:
1200              ok &= SOSEngineLoad(engine, txn, error);
1201              break;
1202          case kSOSDataSourceTransactionDidCommit: // Corruption causes us to process items at DidCommit
1203          case kSOSDataSourceTransactionWillCommit:
1204          {
1205              bool mappedItemChanged = false;
1206  
1207              struct SOSChangeMapper cm;
1208              SOSChangeMapperInit(&cm, engine, txn, phase, source);
1209              SecDbEventRef event;
1210              CFArrayForEachC(changes, event) {
1211                  CFTypeRef deleted = NULL;
1212                  CFTypeRef inserted = NULL;
1213                  SecDbEventGetComponents(event, &deleted, &inserted, error);
1214                  if (deleted) {
1215                      bool someoneCares = SOSChangeMapperIngestChange(&cm, false, deleted);
1216                      if (someoneCares) {
1217                          mappedItemChanged = true;
1218                      }
1219                  }
1220                  if (inserted) {
1221                      bool someoneCares = SOSChangeMapperIngestChange(&cm, true, inserted);
1222                      if (someoneCares) {
1223                          mappedItemChanged = true;
1224                      }
1225                      if (!someoneCares && !isData(inserted) && SecDbItemIsTombstone((SecDbItemRef)inserted) && !CFEqualSafe(SecDbItemGetValue((SecDbItemRef)inserted, &v7utomb, NULL), kCFBooleanTrue)) {
1226                          CFErrorRef localError = NULL;
1227                          // A tombstone was inserted but there is no changetracker that
1228                          // cares about it.
1229                          if (!SecDbItemDoDeleteSilently((SecDbItemRef)inserted, (SecDbConnectionRef)txn, &localError)) {
1230                              secerror("failed to delete tombstone %@ that no one cares about: %@", inserted, localError);
1231                              CFReleaseNull(localError);
1232                          }
1233                      }
1234                  }
1235              }
1236  
1237              ok &= SOSChangeMapperSend(&cm, error);
1238              SOSChangeMapperSendNotifications(&cm); // Trigger notifications for view that changes changed
1239              SOSChangeMapperFree(&cm);
1240  
1241              if (ok && phase == kSOSDataSourceTransactionWillCommit) {
1242                  // Only consider writing if we're in the WillCommit phase.
1243                  // DidCommit phases happen outside the database lock and
1244                  // writing to the DBConn will cause deadlocks.
1245                  if (mappedItemChanged || source == kSOSDataSourceSOSTransaction) {
1246                      // Write SOSEngine and SOSPeer state to disk
1247  #if OCTAGON
1248                      if(!SecCKKSTestDisableSOS()) {
1249  #endif
1250                          secnotice("engine", "saving engine state");
1251                          ok &= SOSEngineSave(engine, txn, error);
1252                      
1253                          if (kSOSDataSourceAPITransaction == source || kSOSDataSourceCKKSTransaction == source)
1254                              SOSCCRequestSyncWithPeersList(engine->peerIDs);
1255  #if OCTAGON
1256                      }
1257  #endif
1258                  } else {
1259                      secinfo("engine", "Not saving engine state, nothing changed.");
1260                  }
1261              }
1262              
1263              break;
1264          }
1265      }
1266      return ok;
1267  }
1268  
1269  static void SOSEngineSetNotifyPhaseBlock(SOSEngineRef engine) {
1270      SOSDataSourceAddNotifyPhaseBlock(engine->dataSource, ^(SOSDataSourceRef ds, SOSTransactionRef txn, SOSDataSourceTransactionPhase phase, SOSDataSourceTransactionSource source, CFArrayRef changes) {
1271          dispatch_sync(engine->queue, ^{
1272              CFErrorRef localError = NULL;
1273              if (!SOSEngineUpdateChanges_locked(engine, txn, phase, source, changes, &localError)) {
1274                  secerror("updateChanged failed: %@", localError);
1275              }
1276              CFReleaseSafe(localError);
1277          });
1278      });
1279  }
1280  
1281  static SOSChangeTrackerRef SOSReferenceAndGetChangeTracker(CFDictionaryRef lookup, CFMutableDictionaryRef referenced, CFSetRef viewNameSet) {
1282      SOSChangeTrackerRef ct = (SOSChangeTrackerRef)CFDictionaryGetValue(referenced, viewNameSet);
1283      if (!ct) {
1284          ct = (SOSChangeTrackerRef)CFDictionaryGetValue(lookup, viewNameSet);
1285          if (ct) {
1286              SOSChangeTrackerResetRegistration(ct);
1287              CFDictionarySetValue(referenced, viewNameSet, ct);
1288          } else {
1289              ct = SOSChangeTrackerCreate(kCFAllocatorDefault, false, NULL, NULL);
1290              CFDictionarySetValue(referenced, viewNameSet, ct);
1291              CFReleaseSafe(ct);
1292          }
1293      }
1294      return ct;
1295  }
1296  
1297  static void CFStringAppendPeerIDAndViews(CFMutableStringRef desc, CFStringRef peerID, CFSetRef vns) {
1298      CFStringSetPerformWithDescription(vns, ^(CFStringRef description) {
1299          CFStringAppendFormat(desc, NULL, CFSTR(" %@ (%@)"), peerID, description);
1300      });
1301  }
1302  
1303  // Must be called after updating viewNameSet2ChangeTracker
1304  static void SOSEngineUpdateViewName2ChangeTracker(SOSEngineRef engine) {
1305      // Create the mapping from viewName -> ChangeTracker used for lookup during change notification
1306      CFMutableDictionaryRef newViewName2ChangeTracker = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1307      CFDictionaryForEach(engine->viewNameSet2ChangeTracker, ^(const void *viewNameSet, const void *ct) {
1308          CFSetForEach(viewNameSet, ^(const void *viewName) {
1309              const void *ctorset = NULL;
1310              if (CFDictionaryGetValueIfPresent(newViewName2ChangeTracker, viewName, &ctorset)) {
1311                  if (isSet(ctorset)) {
1312                      CFSetAddValue((CFMutableSetRef)ctorset, ct);
1313                  } else if (!CFEqual(ct, ctorset)) {
1314                      CFMutableSetRef set = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
1315                      CFSetAddValue(set, ctorset);
1316                      CFSetAddValue(set, ct);
1317                      CFDictionaryReplaceValue(newViewName2ChangeTracker, viewName, set);
1318                      CFRelease(set);
1319                  }
1320              } else {
1321                  CFDictionarySetValue(newViewName2ChangeTracker, viewName, ct);
1322              }
1323          });
1324      });
1325      CFAssignRetained(engine->viewName2ChangeTracker, newViewName2ChangeTracker);
1326  }
1327  
1328  static void SOSEngineSetBackupBag(SOSEngineRef engine, SOSObjectRef bagItem);
1329  
1330  // This is called only if we are in a circle and we should listen for keybag changes
1331  static void SOSEngineRegisterBackupBagV0Tracker(SOSEngineRef engine, CFMutableDictionaryRef newViewNameSet2ChangeTracker, CFMutableStringRef desc) {
1332      SOSChangeTrackerRef bbct = SOSReferenceAndGetChangeTracker(engine->viewNameSet2ChangeTracker, newViewNameSet2ChangeTracker, SOSViewsGetV0BackupBagViewSet());
1333      SOSChangeTrackerRegisterChangeUpdate(bbct, ^bool(SOSChangeTrackerRef ct, SOSEngineRef engine, SOSTransactionRef txn, SOSDataSourceTransactionSource source, SOSDataSourceTransactionPhase phase, CFArrayRef changes, CFErrorRef *error) {
1334          SOSChangeRef change;
1335          CFArrayForEachC(changes, change) {
1336              CFTypeRef object = NULL;
1337              bool isAdd = SOSChangeGetObject(change, &object);
1338              SecDbItemRef dbi = (SecDbItemRef)object;
1339              if (!isData(object) &&
1340                  CFEqualSafe(SecDbItemGetCachedValueWithName(dbi, kSecAttrService), CFSTR("SecureBackupService")) &&
1341                  CFEqualSafe(SecDbItemGetCachedValueWithName(dbi, kSecAttrAccessible), kSecAttrAccessibleWhenUnlocked) &&
1342                  CFEqualSafe(SecDbItemGetCachedValueWithName(dbi, kSecAttrAccount), CFSTR("SecureBackupPublicKeybag"))) {
1343                  SOSEngineSetBackupBag(engine, isAdd ? (SOSObjectRef)object : NULL);
1344              }
1345          }
1346          return true;
1347      });
1348  }
1349  
1350  static void SOSEngineReferenceBackupPeer(SOSEngineRef engine, CFStringRef peerID, CFSetRef viewNameSet, CFDataRef keyBag, CFMutableDictionaryRef newViewNameSet2ChangeTracker, CFMutableDictionaryRef newPeerMap) {
1351      CFTypeRef oldEntry = CFDictionaryGetValue(engine->peerMap, peerID);
1352      CFTypeRef newEntry = SOSPeerOrStateSetViewsKeyBagAndCreateCopy(oldEntry, viewNameSet, keyBag);
1353      if (newEntry) {
1354          if (isDictionary(newEntry)) {
1355              // Backup peers, are always inflated
1356              CFAssignRetained(newEntry, SOSPeerCreateWithState(engine, peerID, newEntry, NULL));
1357              // If !oldEntry this is an edge (first creation of a peer).
1358              if (!oldEntry) {
1359                  SOSPeerKeyBagDidChange((SOSPeerRef)newEntry);
1360              }
1361          }
1362          CFDictionarySetValue(newPeerMap, peerID, newEntry);
1363          CFRelease(newEntry);
1364  
1365          if (keyBag) {
1366              SOSChangeTrackerRef ct = SOSReferenceAndGetChangeTracker(engine->viewNameSet2ChangeTracker, newViewNameSet2ChangeTracker, viewNameSet);
1367              
1368              SOSChangeTrackerUpdatesChanges child = Block_copy(^bool(SOSChangeTrackerRef ct, SOSEngineRef engine, SOSTransactionRef txn, SOSDataSourceTransactionSource source, SOSDataSourceTransactionPhase phase, CFArrayRef changes, CFErrorRef *error) {
1369                  return SOSPeerDataSourceWillChange((SOSPeerRef)newEntry, SOSEngineGetDataSource(engine), source, changes, error);
1370              });
1371             
1372              SOSChangeTrackerRegisterChangeUpdate(ct, child);
1373              Block_release(child);
1374          }
1375      }
1376  }
1377  
1378  static void SOSEngineReferenceSyncPeer(SOSEngineRef engine, CFStringRef peerID, CFSetRef viewNameSet, CFMutableDictionaryRef newViewNameSet2ChangeTracker, CFMutableDictionaryRef newPeerMap) {
1379      CFTypeRef newEntry = SOSPeerOrStateSetViewsKeyBagAndCreateCopy(CFDictionaryGetValue(engine->peerMap, peerID), viewNameSet, NULL);
1380      if (newEntry) {
1381          SOSChangeTrackerRef ct = SOSReferenceAndGetChangeTracker(engine->viewNameSet2ChangeTracker, newViewNameSet2ChangeTracker, viewNameSet);
1382          // Standard peer, inflated on demand
1383          SOSChangeTrackerUpdatesManifests trackManifest;
1384          if (isDictionary(newEntry)) {
1385              // Uninflated peer, inflate on first notification.
1386              trackManifest = ^bool(SOSChangeTrackerRef ct, SOSEngineRef engine, SOSTransactionRef txn, SOSDataSourceTransactionSource source, SOSDataSourceTransactionPhase phase, SOSManifestRef removals, SOSManifestRef additions, CFErrorRef *error) {
1387                  CFErrorRef localError = NULL;
1388                  SOSPeerRef peer = SOSEngineCopyPeerWithID_locked(engine, peerID, &localError);
1389                  bool ok;
1390                  if (!peer) {
1391                      secerror("%@: peer failed to inflate: %@", peerID, localError);
1392                      CFReleaseSafe(localError);
1393                      ok = false;
1394                  } else {
1395                      ok = SOSPeerDataSourceWillCommit(peer, source, removals, additions, error);
1396                  }
1397                  CFReleaseSafe(peer);
1398                  return ok;
1399              };
1400          } else {
1401              // Inflated peer, just forward the changes to the peer
1402              trackManifest = ^bool(SOSChangeTrackerRef ct, SOSEngineRef engine, SOSTransactionRef txn, SOSDataSourceTransactionSource source, SOSDataSourceTransactionPhase phase, SOSManifestRef removals, SOSManifestRef additions, CFErrorRef *error) {
1403                  return SOSPeerDataSourceWillCommit((SOSPeerRef)newEntry, source, removals, additions, error);
1404              };
1405          }
1406          SOSChangeTrackerUpdatesManifests trackManifestCopy = Block_copy(trackManifest);
1407          SOSChangeTrackerRegisterManifestUpdate(ct, trackManifestCopy);
1408          Block_release(trackManifestCopy);
1409  
1410          CFDictionarySetValue(newPeerMap, peerID, newEntry);
1411          CFRelease(newEntry);
1412      }
1413  }
1414  
1415  
1416  static void SOSEngineReferenceTrustedPeer(SOSEngineRef engine, SOSPeerMetaRef peerMeta, CFMutableDictionaryRef newViewNameSet2ChangeTracker, CFMutableDictionaryRef newPeerMap, CFMutableArrayRef peerIDs, CFMutableStringRef desc) {
1417      CFSetRef viewNameSet = NULL;
1418      CFDataRef keyBag = NULL;
1419      CFStringRef peerID = SOSPeerMetaGetComponents(peerMeta, &viewNameSet, &keyBag, NULL);
1420      // We trust peerID so append it to peerIDs
1421      CFArrayAppendValue(peerIDs, peerID);
1422      if (desc) CFStringAppendPeerIDAndViews(desc, peerID, viewNameSet);
1423      // Update the viewNameSet for this peer, to appease tests, default to a viewset of the V0 view.
1424      if (!viewNameSet)
1425          viewNameSet = SOSViewsGetV0ViewSet();
1426  
1427      // Always inflate backup peers, since they need to register with their changeTrackers right away.
1428      if (keyBag) {
1429          SOSEngineReferenceBackupPeer(engine, peerID, viewNameSet, keyBag, newViewNameSet2ChangeTracker, newPeerMap);
1430      } else {
1431          SOSEngineReferenceSyncPeer(engine, peerID, viewNameSet, newViewNameSet2ChangeTracker, newPeerMap);
1432      }
1433  }
1434  
1435  static CFDataRef SOSEngineCopyV0KeyBag(SOSEngineRef engine, CFErrorRef *error) {
1436      // Return the keybag for the given peerID.
1437      /*
1438       Values for V0 are:
1439       kSecAttrAccessGroup ==> CFSTR("com.apple.sbd")
1440       kSecAttrAccessible  ==> kSecAttrAccessibleWhenUnlocked
1441       kSecAttrAccount     ==> CFSTR("SecureBackupPublicKeybag")
1442       kSecAttrService     ==> CFSTR("SecureBackupService")
1443       */
1444  
1445      CFMutableDictionaryRef keys = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault,
1446                                              kSecAttrAccessGroup, CFSTR("com.apple.sbd"),
1447                                              kSecAttrAccount, CFSTR("SecureBackupPublicKeybag"),
1448                                              kSecAttrService, CFSTR("SecureBackupService"),
1449                                              kSecAttrAccessible, kSecAttrAccessibleWhenUnlocked,
1450                                              kSecAttrSynchronizable, kCFBooleanTrue,
1451                                              NULL);
1452  
1453      CFDataRef keybag = engine->dataSource->dsCopyItemDataWithKeys(engine->dataSource, keys, error);
1454      CFReleaseSafe(keys);
1455  
1456      return keybag;
1457  }
1458  
1459  static void SOSEngineReferenceBackupV0Peer(SOSEngineRef engine, CFMutableDictionaryRef newViewNameSet2ChangeTracker, CFMutableDictionaryRef newPeerMap, CFMutableArrayRef newPeerIDs, CFMutableStringRef desc) {
1460      SOSPeerRef backupPeer = (SOSPeerRef)CFDictionaryGetValue(engine->peerMap, kSOSViewKeychainV0_tomb);
1461      CFDataRef bag = NULL;
1462      if (backupPeer && CFGetTypeID(backupPeer) == SOSPeerGetTypeID()) {
1463          bag = CFRetainSafe(SOSPeerGetKeyBag(backupPeer));
1464      } else {
1465          CFErrorRef localError = NULL;
1466          bag = SOSEngineCopyV0KeyBag(engine, &localError);
1467          if (!bag) {
1468              secnotice("engine", "No keybag found for v0 backup peer: %@", localError);
1469              CFReleaseSafe(localError);
1470          }
1471      }
1472      SOSEngineReferenceBackupPeer(engine, kSOSViewKeychainV0_tomb, SOSViewsGetV0BackupViewSet(), bag, newViewNameSet2ChangeTracker, newPeerMap);
1473      CFReleaseNull(bag);
1474  }
1475  
1476  static void SOSEngineReferenceTrustedPeers(SOSEngineRef engine, CFMutableDictionaryRef newViewNameSet2ChangeTracker, CFMutableDictionaryRef newPeerMap, CFMutableArrayRef newPeerIDs, CFArrayRef trustedPeerMetas, CFMutableStringRef desc) {
1477      // Then update the views for all trusted peers and add them to newPeerMap.
1478      if (trustedPeerMetas != NULL && CFArrayGetCount(trustedPeerMetas) != 0) {
1479          if (desc) CFStringAppend(desc, CFSTR(" trusted"));
1480          // Remake engine->peerIDs
1481          SOSPeerMetaRef peerMeta;
1482          CFArrayForEachC(trustedPeerMetas, peerMeta) {
1483              SOSEngineReferenceTrustedPeer(engine, peerMeta, newViewNameSet2ChangeTracker, newPeerMap, newPeerIDs, desc);
1484          }
1485      }
1486  }
1487  
1488  static void SOSEngineReferenceUntrustedPeers(SOSEngineRef engine, CFMutableDictionaryRef newPeerMap, CFArrayRef untrustedPeerMetas, CFMutableStringRef description) {
1489      // Copy any untrustedPeers to newPeerMap as well if we have a state
1490      // for them, if not no big deal.  We also serialize all the untrustedPeers
1491      // since they don't need to be deserializable
1492      if (untrustedPeerMetas != NULL && CFArrayGetCount(untrustedPeerMetas) != 0) {
1493          if (description) CFStringAppend(description, CFSTR(" untrusted"));
1494          SOSPeerMetaRef peerMeta;
1495          CFArrayForEachC(untrustedPeerMetas, peerMeta) {
1496              CFSetRef views = NULL;
1497              CFStringRef peerID = SOSPeerMetaGetComponents(peerMeta, &views, NULL, NULL);
1498              if (description) CFStringAppendPeerIDAndViews(description, peerID, views);
1499              CFSetRef nviews = NULL;
1500              if (!views)
1501                  views = nviews = CFSetCreate(kCFAllocatorDefault, NULL, 0, &kCFTypeSetCallBacks);
1502              CFTypeRef newEntry = SOSPeerOrStateSetViewsAndCopyState(CFDictionaryGetValue(engine->peerMap, peerID), views);
1503              CFReleaseSafe(nviews);
1504              if (newEntry) {
1505                  CFDictionarySetValue(newPeerMap, peerID, newEntry);
1506                  CFReleaseSafe(newEntry);
1507              }
1508          }
1509      }
1510  }
1511  
1512  static void SOSEngineReferenceChangeTrackers(SOSEngineRef engine, CFArrayRef trustedPeerMetas, CFArrayRef untrustedPeerMetas, CFMutableStringRef desc) {
1513      CFMutableArrayRef newPeerIDs = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1514      CFMutableDictionaryRef newPeerMap = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1515      CFMutableDictionaryRef newViewNameSet2ChangeTracker = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1516  
1517      if (engine->myID) {
1518          // We have an engineID => in a circle (with 0 or more peers)
1519          // Ensure we have a v0 backup peer and it's listening for backup bag changes
1520          SOSEngineReferenceBackupV0Peer(engine, newViewNameSet2ChangeTracker, newPeerMap, newPeerIDs, desc);
1521          SOSEngineRegisterBackupBagV0Tracker(engine, newViewNameSet2ChangeTracker, desc);
1522      }
1523      SOSEngineReferenceTrustedPeers(engine, newViewNameSet2ChangeTracker, newPeerMap, newPeerIDs, trustedPeerMetas, desc);
1524      SOSEngineReferenceUntrustedPeers(engine, newPeerMap, untrustedPeerMetas, desc);
1525  
1526      CFAssignRetained(engine->peerIDs, newPeerIDs);
1527      CFAssignRetained(engine->peerMap, newPeerMap);
1528      CFAssignRetained(engine->viewNameSet2ChangeTracker, newViewNameSet2ChangeTracker);
1529      SOSEngineUpdateViewName2ChangeTracker(engine);
1530  }
1531  
1532  // Return true iff peers or views changed
1533  static bool SOSEngineSetPeers_locked(SOSEngineRef engine, SOSPeerMetaRef myPeerMeta, CFArrayRef trustedPeerMetas, CFArrayRef untrustedPeerMetas) {
1534      CFErrorRef error = NULL;
1535      CFSetRef myViews = NULL;
1536      CFDataRef myKeyBag = NULL;
1537      CFMutableStringRef desc = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, CFSTR("me"));
1538      CFStringRef myPeerID = myPeerMeta ? SOSPeerMetaGetComponents(myPeerMeta, &myViews, &myKeyBag, &error) : NULL;
1539      if (desc) CFStringAppendPeerIDAndViews(desc, myPeerID, myViews);
1540  
1541      // Start with no coders
1542      CFMutableDictionaryRef codersToKeep = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1543  
1544      if(engine->haveLoadedCoders){
1545          // If we're the same peerID we keep known peers (both trusted and untrusted)
1546          if (CFEqualSafe(myPeerID, engine->myID)) {
1547              void (^copyPeerMetasCoder)(const void *value) = ^(const void*element) {
1548                  SOSPeerMetaRef peerMeta = (SOSPeerMetaRef) element;
1549  
1550                  CFStringRef currentID = SOSPeerMetaGetComponents(peerMeta, NULL, NULL, NULL);
1551                  if (currentID) {
1552                      SOSCoderRef coder = (SOSCoderRef) CFDictionaryGetValue(engine->coders, currentID);
1553                      if (coder) {
1554                          CFDictionarySetValue(codersToKeep, currentID, coder);
1555                      }
1556                  }
1557              };
1558  
1559              if (trustedPeerMetas) {
1560                  CFArrayForEach(trustedPeerMetas, copyPeerMetasCoder);
1561              }
1562              if (untrustedPeerMetas) {
1563                  CFArrayForEach(untrustedPeerMetas, copyPeerMetasCoder);
1564              }
1565          }
1566  
1567          engine->codersNeedSaving = true;
1568      }
1569      CFRetainAssign(engine->myID, myPeerID);
1570      CFTransferRetained(engine->coders, codersToKeep);
1571  
1572      // Remake engine->peerMap from both trusted and untrusted peers
1573      SOSEngineReferenceChangeTrackers(engine, trustedPeerMetas, untrustedPeerMetas, desc);
1574  
1575      secnotice("engine", "%@", desc);
1576      CFReleaseSafe(desc);
1577      return true;
1578  }
1579  
1580  static void SOSEngineApplyPeerState(SOSEngineRef engine, CFDictionaryRef peerStateMap) {
1581      if (peerStateMap) CFDictionaryForEach(peerStateMap, ^(const void *peerID, const void *peerState) {
1582          CFTypeRef mapEntry = CFDictionaryGetValue(engine->peerMap, peerID);
1583          if (mapEntry && CFGetTypeID(mapEntry) == SOSPeerGetTypeID()) {
1584              // Update the state of any already inflated peers
1585              SOSPeerRef peer = (SOSPeerRef)mapEntry;
1586              CFErrorRef localError = NULL;
1587              if (!SOSPeerSetState(peer, engine, peerState, &localError)) {
1588                  CFStringRef stateHex = NULL;
1589                  stateHex = CFDataCopyHexString(peerState);
1590                  secerror("peer: %@: bad state: %@ in engine state: %@", peerID, localError, stateHex);
1591                  CFReleaseSafe(stateHex);
1592                  CFReleaseNull(localError);
1593                  // Possibly ask for an ensurePeerRegistration so we have a good list of peers again.
1594              }
1595          } else {
1596              // Just record the state for non inflated peers for now.
1597              CFDictionarySetValue(engine->peerMap, peerID, peerState);
1598          }
1599      });
1600  }
1601  
1602  static void SOSEngineSynthesizePeerMetas(SOSEngineRef engine, CFMutableArrayRef trustedPeersMetas, CFMutableArrayRef untrustedPeers) {
1603      CFSetRef trustedPeerSet = engine->peerIDs ? CFSetCreateCopyOfArrayForCFTypes(engine->peerIDs) : NULL;
1604      CFDictionaryForEach(engine->peerMap, ^(const void *peerID, const void *peerState) {
1605          SOSPeerMetaRef meta = NULL;
1606          if (peerState && CFGetTypeID(peerState) == SOSPeerGetTypeID()) {
1607              SOSPeerRef peer = (SOSPeerRef)peerState;
1608              meta = SOSPeerMetaCreateWithComponents(peerID, SOSPeerGetViewNameSet(peer), SOSPeerGetKeyBag(peer));
1609          } else {
1610              // We don't need to add the meta for the backup case, since
1611              //   SOSEngineReferenceBackupV0Peer will do the right thing
1612              if (!CFEqualSafe(peerID, kSOSViewKeychainV0_tomb)) {
1613                  meta = SOSPeerMetaCreateWithState(peerID, peerState);
1614              }
1615          }
1616          // Any peer in peerStateMap that is not in trustedPeers is an untrustedPeer unless it's the v0 backup peer
1617          if ((trustedPeerSet && CFSetContainsValue(trustedPeerSet, peerID)) || CFEqualSafe(peerID, kSOSViewKeychainV0_tomb)) {
1618              if (meta) {
1619                  CFArrayAppendValue(trustedPeersMetas, meta);
1620              }
1621          } else {
1622              CFArrayAppendValue(untrustedPeers, peerID);
1623          }
1624          CFReleaseNull(meta);
1625      });
1626      CFReleaseNull(trustedPeerSet);
1627  }
1628  
1629  static void SOSEngineSetBackupBag(SOSEngineRef engine, SOSObjectRef bagItem) {
1630      CFMutableStringRef desc = NULL;
1631      SOSPeerRef backupPeer = SOSEngineCopyPeerWithID_locked(engine, kSOSViewKeychainV0_tomb, NULL);
1632      CFDataRef keybag = NULL;
1633      if (bagItem) {
1634          keybag = SecDbItemGetValue((SecDbItemRef)bagItem, &v6v_Data, NULL);
1635      }
1636  
1637      // Since SOSPeerSetKeyBag() doesn't notify on the edge from NULL->initial keybag, since
1638      // that is the right behaviour for non v0 backup peers, we need to do it here for the v0 peer.
1639      bool hadBag = SOSPeerGetKeyBag(backupPeer);
1640      SOSPeerSetKeyBag(backupPeer, keybag);
1641      if (!hadBag)
1642          SOSPeerKeyBagDidChange(backupPeer);
1643  
1644      CFReleaseSafe(backupPeer);
1645  
1646      CFMutableArrayRef untrustedPeerMetas = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1647      CFMutableArrayRef trustedPeersMetas = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1648      SOSEngineSynthesizePeerMetas(engine, trustedPeersMetas, untrustedPeerMetas);
1649      SOSEngineReferenceChangeTrackers(engine, trustedPeersMetas, untrustedPeerMetas, desc);
1650      CFReleaseSafe(trustedPeersMetas);
1651      CFReleaseSafe(untrustedPeerMetas);
1652  }
1653  
1654  static bool SOSEngineCircleChanged_locked(SOSEngineRef engine, SOSPeerMetaRef myPeerMeta, CFArrayRef trustedPeers, CFArrayRef untrustedPeers) {
1655      // Sanity check params
1656  //    SOSEngineCircleChanged_sanitycheck(engine, myPeerID, trustedPeers, untrustedPeers);
1657  
1658      // Transform from SOSPeerInfoRefs to CFDictionaries with the info we want per peer.
1659      // Or, Tell the real SOSPeerRef what the SOSPeerInfoRef is and have it copy out the data it needs.
1660      bool peersOrViewsChanged = SOSEngineSetPeers_locked(engine, myPeerMeta, trustedPeers, untrustedPeers);
1661  
1662      // Run though all peers and only cache manifests for peers we still have
1663      CFErrorRef localError = NULL;
1664      if (!SOSEngineGCPeerState_locked(engine, &localError)) {
1665          secerror("SOSEngineGCPeerState_locked failed: %@", localError);
1666          CFReleaseNull(localError);
1667      }
1668      return peersOrViewsChanged;
1669  }
1670  
1671  // Initialize the engine if a load fails.  Basically this is our first time setup
1672  static bool SOSEngineInit(SOSEngineRef engine, CFErrorRef *error) {
1673      bool ok = true;
1674      secnotice("engine", "new engine for datasource named %@", SOSDataSourceGetName(engine->dataSource));
1675      CFAssignRetained(engine->peerMap, CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault));
1676      CFAssignRetained(engine->viewNameSet2ChangeTracker, CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault));
1677      CFAssignRetained(engine->viewName2ChangeTracker, CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault));
1678      CFReleaseNull(engine->manifestCache);
1679      CFReleaseNull(engine->peerIDs);
1680      // TODO: We shouldn't need to load the backup bag if there was no engine
1681      // state (load failed), since that means there was no circle nor were we an applicant.
1682  
1683      // Set up change trackers so we know when a backup peer needs to be created?
1684      // no, since myID is not set, we are not in a circle, so no need to back up
1685      SOSEngineSetPeers_locked(engine, NULL, NULL, NULL);
1686      return ok;
1687  }
1688  
1689  // Called by our DataSource in its constructor
1690  SOSEngineRef SOSEngineCreate(SOSDataSourceRef dataSource, CFErrorRef *error) {
1691      SOSEngineRef engine = NULL;
1692      engine = CFTypeAllocate(SOSEngine, struct __OpaqueSOSEngine, kCFAllocatorDefault);
1693      engine->dataSource = dataSource;
1694      engine->queue = dispatch_queue_create("engine", DISPATCH_QUEUE_SERIAL);
1695  
1696      engine->peerMap = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1697      engine->viewNameSet2ChangeTracker = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1698      engine->viewName2ChangeTracker = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1699      //engine->syncCompleteQueue = NULL;
1700      engine->syncCompleteListener = NULL;
1701      engine->coders = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1702      engine->haveLoadedCoders = false;
1703      engine->codersNeedSaving = false;
1704      
1705      CFErrorRef engineError = NULL;
1706      if (!SOSEngineLoad(engine, NULL, &engineError)) {
1707          secwarning("engine failed load state starting with nothing %@", engineError);
1708          CFReleaseNull(engineError);
1709          if (!SOSEngineInit(engine, error)) {
1710              secerror("engine failed to initialze %@ giving up", error ? *error : NULL);
1711          }
1712      }
1713      SOSEngineSetNotifyPhaseBlock(engine);
1714      return engine;
1715  }
1716  
1717  // --- Called from off the queue, need to move to on the queue
1718  
1719  static void SOSEngineDoOnQueue(SOSEngineRef engine, dispatch_block_t action)
1720  {
1721      dispatch_sync(engine->queue, action);
1722  }
1723  
1724  static bool SOSEngineDoTxnOnQueue(SOSEngineRef engine, CFErrorRef *error, void(^transaction)(SOSTransactionRef txn, bool *commit))
1725  {
1726      return SOSDataSourceWithCommitQueue(engine->dataSource, error, ^(SOSTransactionRef txn, bool *commit) {
1727          SOSEngineDoOnQueue(engine, ^{ transaction(txn, commit); });
1728      });
1729  }
1730  
1731  //
1732  // MARK: SOSEngine API
1733  //
1734  
1735  void SOSEngineDispose(SOSEngineRef engine) {
1736      // NOOP Engines stick around forever to monitor dataSource changes.
1737      engine->dataSource = NULL;
1738      CFReleaseNull(engine->coders);
1739  }
1740  
1741  void SOSEngineForEachPeer(SOSEngineRef engine, void (^with)(SOSPeerRef peer)) {
1742      SOSEngineDoOnQueue(engine, ^{
1743          SOSEngineForEachPeer_locked(engine, with);
1744      });
1745  }
1746  
1747  static void SOSEngineForEachBackupPeer(SOSEngineRef engine, void (^with)(SOSPeerRef peer)) {
1748      SOSEngineDoOnQueue(engine, ^{
1749          SOSEngineForEachBackupPeer_locked(engine, with);
1750      });
1751  }
1752  
1753  static void SOSEngineForBackupPeer(SOSEngineRef engine, CFStringRef backupPeerID, void (^with)(SOSPeerRef peer)) {
1754      SOSEngineDoOnQueue(engine, ^{
1755          SOSEngineForBackupPeer_locked(engine, backupPeerID, with);
1756      });
1757  }
1758  
1759  static const CFStringRef kSecADSecurityNewItemSyncTimeKey = CFSTR("com.apple.security.secureobjectsync.itemtime.new");
1760  static const CFStringRef kSecADSecurityKnownItemSyncTimeKey = CFSTR("com.apple.security.secureobjectsync.itemtime.known");
1761  
1762  
1763  static void ReportItemSyncTime(SOSDataSourceRef ds, bool known, SOSObjectRef object)
1764  {
1765      CFDateRef itemModDate = SOSObjectCopyModificationDate(ds, object, NULL);
1766      if (itemModDate) {
1767          CFAbsoluteTime syncTime = 0;
1768          CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
1769  
1770          CFAbsoluteTime peerModificationAbsoluteTime = CFDateGetAbsoluteTime(itemModDate);
1771          if (peerModificationAbsoluteTime > now) {
1772              syncTime = now - peerModificationAbsoluteTime;
1773          }
1774  
1775          SecCoreAnalyticsSendValue(known ? kSecADSecurityKnownItemSyncTimeKey : kSecADSecurityNewItemSyncTimeKey,
1776                                    SecBucket2Significant(syncTime));
1777      }
1778      CFReleaseNull(itemModDate);
1779  }
1780  
1781  /* Handle incoming message from peer p.  Return false if there was an error, true otherwise. */
1782  bool SOSEngineHandleMessage_locked(SOSEngineRef engine, CFStringRef peerID, SOSMessageRef message,
1783                                     SOSTransactionRef txn, bool *commit, bool *somethingChanged, CFErrorRef *error) {
1784      SOSPeerRef peer = SOSEngineCopyPeerWithID_locked(engine, peerID, error);
1785      if (!peer) return false;
1786  
1787      CFStringRef peerDesc = NULL;
1788      SOSManifestRef localManifest = NULL;
1789      SOSManifestRef allAdditions = NULL;
1790      SOSManifestRef unwanted = NULL;
1791      SOSManifestRef confirmed = NULL;
1792      SOSManifestRef base = NULL;
1793      SOSManifestRef confirmedRemovals = NULL, confirmedAdditions = NULL;
1794      __block struct SOSDigestVector receivedObjects = SOSDigestVectorInit;
1795      __block struct SOSDigestVector unwantedObjects = SOSDigestVectorInit;
1796  
1797      // Check for unknown criticial extensions in the message, and handle
1798      // any other extensions we support
1799      __block bool ok = true;
1800      CFMutableArrayRef changes = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1801  
1802      require_action_quiet(peer, exit, ok = SOSErrorCreate(errSecParam, error, NULL, CFSTR("Couldn't create peer with Engine for %@"), peerID));
1803      peerDesc = CFCopyDescription(peer);
1804  
1805      bool hadBeenInSyncAtStart = SOSPeerHasBeenInSync(peer);
1806  
1807      SOSMessageWithExtensions(message, true, ^(CFDataRef oid, bool isCritical, CFDataRef extension, bool *stop) {
1808          // OMFG a Critical extension what shall I do!
1809          ok = SOSErrorCreate(kSOSErrorNotReady, error, NULL, CFSTR("Unknown criticial extension in peer message"));
1810          *stop = true;
1811      });
1812      require_quiet(ok, exit);
1813  
1814      // Merge Objects from the message into our DataSource.
1815      // Should we move the transaction to the SOSAccount level?
1816      // TODO: Filter incoming objects
1817      //if (!SOSDataSourceForEachObjectInViewSet(engine->dataSource, pendingObjects, SOSPeerGetViewNameSet(peer), error, ^void(CFDataRef key, SOSObjectRef object, bool *stop) {
1818      require_quiet(ok &= SOSMessageWithSOSObjects(message, engine->dataSource, error, ^(SOSObjectRef peersObject, bool *stop) {
1819          CFDataRef digest = SOSObjectCopyDigest(engine->dataSource, peersObject, error);
1820          if (!digest) {
1821              *stop = true;
1822              *commit = false;
1823              secerror("%@ peer sent bad object: %@, rolling back changes", SOSPeerGetID(peer), error ? *error : NULL);
1824              return;
1825          }
1826          SOSDigestVectorAppend(&receivedObjects, CFDataGetBytePtr(digest));
1827          SOSObjectRef mergedObject = NULL;
1828          SOSMergeResult mr = SOSDataSourceMergeObject(engine->dataSource, txn, peersObject, &mergedObject, error);
1829          // TODO: If the mr is kSOSMergeLocalObject most of the time (or all of the time),
1830          // consider asking the peer to stop sending us objects, and send it objects instead.
1831          ok &= (mr != kSOSMergeFailure);
1832          if (!ok) {
1833              *stop = true;
1834              *commit = false;
1835              // TODO: Might want to change to warning since the race of us locking after ckd sends us a message could cause db locked errors here.
1836              secerror("%@ SOSDataSourceMergeObject failed %@ rolling back changes", SOSPeerGetID(peer), error ? *error : NULL);
1837          } else if (mr==kSOSMergePeersObject || mr==kSOSMergeCreatedObject) {
1838              *somethingChanged = true;
1839          } else {
1840              // mr == kSOSMergeLocalObject
1841              if (!CFEqual(mergedObject, peersObject)) {
1842                  // Record this object as something we don't want peer to ever send us again.  By adding it to
1843                  // unwantedObjects we'll falsely claim to peer we have it until they tell us they don't have it anymore.
1844                  SOSDigestVectorAppend(&unwantedObjects, CFDataGetBytePtr(digest));
1845              }
1846              // Ensure localObject is in local manifest (possible corruption) by posting an update when we are done.
1847              SOSChangesAppendAdd(changes, mergedObject);
1848          }
1849  
1850          if (ok && hadBeenInSyncAtStart) {
1851              ReportItemSyncTime(engine->dataSource,
1852                                 mr == kSOSMergeLocalObject,
1853                                 peersObject);
1854          }
1855  
1856          CFReleaseSafe(mergedObject);
1857          CFReleaseSafe(digest);
1858      }), exit);
1859      struct SOSDigestVector dvunion = SOSDigestVectorInit;
1860      SOSDigestVectorSort(&receivedObjects);
1861      SOSDigestVectorUnionSorted(SOSManifestGetDigestVector(SOSMessageGetAdditions(message)), &receivedObjects, &dvunion);
1862      allAdditions = SOSManifestCreateWithDigestVector(&dvunion, error);
1863      SOSDigestVectorFree(&receivedObjects);
1864      SOSDigestVectorFree(&dvunion);
1865  
1866      unwanted = SOSManifestCreateWithDigestVector(&unwantedObjects, error);
1867      SOSDigestVectorFree(&unwantedObjects);
1868  
1869      if (CFArrayGetCount(changes)) {
1870          // NOTE: This is always notifiying of all additions that end up choosing local, which should be rare, since we shouldn't
1871          // be receiving objects we already have.   When we do we tell ourselves to add them all again so our views will properly
1872          // reflect that we actually have these objects if we didn't already.
1873  
1874          // Ensure any objects that we received and have locally already are actually in our local manifest
1875          SOSEngineUpdateChanges_locked(engine, txn, kSOSDataSourceTransactionDidCommit, kSOSDataSourceSOSTransaction, changes, error);
1876      }
1877      CFReleaseSafe(changes);
1878  
1879      // ---- Don't use local or peer manifests from above this line,
1880      // ---- since commiting the SOSDataSourceWith transaction might change them ---
1881  
1882      // Take a snapshot of our dataSource's local manifest.
1883      require_quiet(ok = localManifest = SOSEngineCopyLocalPeerManifest_locked(engine, peer, error), exit);
1884  
1885      CFDataRef baseDigest = SOSMessageGetBaseDigest(message);
1886      CFDataRef proposedDigest = SOSMessageGetProposedDigest(message);
1887      
1888  #if 0
1889      // I believe this is no longer needed now that we have eliminated extra,
1890      // since this is handled below once we get a confirmed manifest from our
1891      // peer.
1892      
1893      // If we just received a L00 reset pendingObjects to localManifest
1894      if (!baseDigest && !proposedDigest) {
1895          // TODO: This is definitely busted for v0 peers since v0 peers always send a
1896          // L00 (ManifestDigestMessage as an ack) whereas in v2 this is a protocol restart
1897          // However if we can still find a confirmed manifest below we probably
1898          // don't want to do this even for v2.
1899          // Also I don't think we will ever send a ManifestMessage right now in
1900          // response to a ManifestDigest
1901          SOSPeerSetPendingObjects(peer, localManifest);
1902          secnoticeq("engine", "%@:%@ SOSPeerSetPendingObjects: %@", engine->myID, peerID, localManifest);
1903      }
1904  #endif
1905  
1906      base = SOSPeerCopyManifestForDigest(peer, baseDigest);
1907  
1908      // Note that the sender digest will only exist if we receive a SOSManifestDigestMessageType (since we never receive v2 messages)
1909      confirmed = SOSPeerCopyManifestForDigest(peer, SOSMessageGetSenderDigest(message));
1910      if (!confirmed) {
1911          if (SOSManifestGetCount(SOSMessageGetRemovals(message)) || SOSManifestGetCount(allAdditions)) {
1912              if (base || !baseDigest) {
1913  
1914                  secnotice("engine", "SOSEngineHandleMessage_locked (%@): creating a confirmed manifest via a patch (base %zu %@, +%zu, -%zu)", SOSPeerGetID(peer),
1915                            SOSManifestGetCount(base), SOSManifestGetDigest(base, NULL),
1916                            SOSManifestGetCount(allAdditions), SOSManifestGetCount(SOSMessageGetRemovals(message)));
1917  
1918                  confirmed = SOSManifestCreateWithPatch(base, SOSMessageGetRemovals(message), allAdditions, error);
1919              }
1920              if (!confirmed) {
1921                  confirmedRemovals = CFRetainSafe(SOSMessageGetRemovals(message));
1922                  confirmedAdditions = CFRetainSafe(allAdditions);
1923              }
1924          } else if (baseDigest) {
1925              confirmed = CFRetainSafe(base);
1926              secerror("%@:%@ Protocol error send L00 - figure out later base: %@", engine->myID, peerID, base);
1927          }
1928  
1929      } else {
1930          secnotice("engine", "SOSEngineHandleMessage_locked (%@): got a confirmed manifest by digest: (%zu, %@)", SOSPeerGetID(peer), SOSManifestGetCount(confirmed), SOSMessageGetSenderDigest(message));
1931      }
1932      secnoticeq("engine", "%@:%@ confirmed: %@ base: %@", engine->myID, peerID, confirmed, base);
1933      if (confirmed) {
1934          ok &= SOSManifestDiff(SOSPeerGetConfirmedManifest(peer), confirmed, &confirmedRemovals, &confirmedAdditions, error);
1935          if (SOSManifestGetCount(SOSMessageGetRemovals(message)))
1936              CFAssignRetained(confirmedRemovals, SOSManifestCreateUnion(confirmedRemovals, SOSMessageGetRemovals(message), error));
1937      }
1938      if (SOSManifestGetCount(confirmedRemovals) || SOSManifestGetCount(confirmedAdditions) || SOSManifestGetCount(unwanted)) {
1939          ok &= SOSPeerDidReceiveRemovalsAndAdditions(peer, confirmedRemovals, confirmedAdditions, unwanted, localManifest, error);
1940      }
1941  
1942  
1943      // TODO: We should probably remove the if below and always call SOSPeerSetConfirmedManifest,
1944      // since having a NULL confirmed will force us to send a manifest message to get in sync again.
1945      if (confirmed) {
1946  
1947          SOSManifestRef previousConfirmedManifest = SOSPeerGetConfirmedManifest(peer);
1948          if(previousConfirmedManifest) {
1949              secnotice("engine", "SOSEngineHandleMessage_locked (%@): new confirmed manifest (%zu, %@) will replace existing confirmed manifest (%zu, %@)", SOSPeerGetID(peer),
1950                        SOSManifestGetCount(confirmed), SOSManifestGetDigest(confirmed, NULL),
1951                        SOSManifestGetCount(previousConfirmedManifest), SOSManifestGetDigest(previousConfirmedManifest, NULL));
1952          } else {
1953              secnotice("engine", "SOSEngineHandleMessage_locked (%@): new confirmed manifest (%zu, %@) is first manifest for peer", SOSPeerGetID(peer),
1954                        SOSManifestGetCount(confirmed), SOSManifestGetDigest(confirmed, NULL));
1955          }
1956  
1957          SOSPeerSetConfirmedManifest(peer, confirmed);
1958      } else if (SOSPeerGetConfirmedManifest(peer)) {
1959          secnoticeq("engine", "%@:%@ unable to find confirmed in %@, sync protocol reset", engine->myID, peer, message);
1960  
1961          SOSPeerSetConfirmedManifest(peer, NULL);
1962          //SOSPeerSetSendObjects(peer, true);
1963      }
1964  
1965      // ---- SendObjects and extra->pendingObjects promotion dance ----
1966  
1967      // The first block of code below sets peer.sendObjects to true when we receive a L00 and the second block
1968      // moves extra to pendingObjects once we receive a confirmed manifest in or after the L00.
1969      if (!baseDigest && !proposedDigest) {
1970          SOSPeerSetSendObjects(peer, true);
1971      }
1972  
1973      if (0 /* confirmed && SOSPeerSendObjects(peer) */) {
1974          SOSManifestRef allExtra = NULL;
1975          ok &= SOSManifestDiff(confirmed, localManifest, NULL, &allExtra, error);
1976          secnoticeq("engine", "%@:%@ confirmed %@ (re)setting O:%@", engine->myID, SOSPeerGetID(peer), confirmed, allExtra);
1977          SOSPeerSetPendingObjects(peer, allExtra);
1978          CFReleaseSafe(allExtra);
1979      }
1980  
1981  exit:
1982      secnotice("engine", "recv %@:%@ %@", engine->myID, SOSPeerGetID(peer), message);
1983      secnotice("peer", "recv %@ -> %@", peerDesc, peer);
1984  
1985      CFReleaseNull(base);
1986      CFReleaseSafe(confirmed);
1987      CFReleaseSafe(localManifest);
1988      CFReleaseSafe(peerDesc);
1989      CFReleaseSafe(allAdditions);
1990      CFReleaseSafe(unwanted);
1991      CFReleaseSafe(confirmedRemovals);
1992      CFReleaseSafe(confirmedAdditions);
1993      CFReleaseSafe(peer);
1994      return ok;
1995  }
1996  
1997  static CFDataRef SOSEngineCopyObjectDER(SOSEngineRef engine, SOSObjectRef object, CFErrorRef *error) {
1998      CFDataRef der = NULL;
1999      CFDictionaryRef plist = SOSObjectCopyPropertyList(engine->dataSource, object, error);
2000      if (plist) {
2001          der = CFPropertyListCreateDERData(kCFAllocatorDefault, plist, error);
2002          CFRelease(plist);
2003      }
2004      return der;
2005  }
2006  
2007  
2008  /*
2009  
2010              +-----------------------------+_
2011              |            |                | \
2012              |      A      |    T          |  \
2013              |              |              |   \
2014             _+=============================+    } L
2015            / |                             |   /
2016           /  |              S              |  /
2017          /   |                             |_/
2018         /    +==============================
2019        /     |                             |
2020     C {      |                             |
2021        \     |        M       +------------|
2022         \    |               |             |
2023          \   |              |      U       |
2024           \  |             |               |
2025            \_+-------------+---------------+
2026  
2027  A assumed
2028  T to be sent
2029  S shared
2030  M missing
2031  U unwanted
2032  L local
2033  C confirmed
2034  
2035  */
2036  
2037  void SOSEngineSetSyncCompleteListener(SOSEngineRef engine, SOSEnginePeerInSyncBlock notify_block) {
2038      SOSEngineDoOnQueue(engine, ^{
2039          CFAssignRetained(engine->syncCompleteListener, Block_copy(notify_block));
2040      });
2041  }
2042  
2043  void SOSEngineSetSyncCompleteListenerQueue(SOSEngineRef engine, dispatch_queue_t notify_queue) {
2044      SOSEngineDoOnQueue(engine, ^{
2045          CFRetainAssign(engine->syncCompleteQueue, notify_queue);
2046      });
2047  }
2048  
2049  static void SOSEngineCompletedSyncWithPeer(SOSEngineRef engine, SOSPeerRef peer) {
2050      SOSEnginePeerInSyncBlock block_to_call = engine->syncCompleteListener;
2051  
2052      if (block_to_call && engine->syncCompleteQueue) {
2053          CFStringRef ID = CFRetainSafe(SOSPeerGetID(peer));
2054          CFSetRef views = CFRetainSafe(SOSPeerGetViewNameSet(peer));
2055          CFRetainSafe(block_to_call);
2056  
2057          dispatch_async(engine->syncCompleteQueue, ^{
2058              block_to_call(ID, views);
2059              CFReleaseSafe(ID);
2060              CFReleaseSafe(views);
2061              CFReleaseSafe(block_to_call);
2062          });
2063      }
2064  
2065      SOSPeerSetHasBeenInSync(peer, true);
2066  }
2067  
2068  
2069  CFDataRef SOSEngineCreateMessage_locked(SOSEngineRef engine, SOSTransactionRef txn, SOSPeerRef peer,
2070                                          CFMutableArrayRef *attributeList, CFErrorRef *error, SOSEnginePeerMessageSentCallback **sent) {
2071      SOSManifestRef local = SOSEngineCopyLocalPeerManifest_locked(engine, peer, error);
2072      __block SOSMessageRef message = SOSMessageCreate(kCFAllocatorDefault, SOSPeerGetMessageVersion(peer), error);
2073      SOSManifestRef confirmed = SOSPeerGetConfirmedManifest(peer);
2074      SOSManifestRef pendingObjects = SOSPeerGetPendingObjects(peer);
2075      SOSManifestRef objectsSent = NULL;
2076      SOSManifestRef proposed = NULL;
2077      SOSManifestRef allMissing = NULL;
2078      SOSManifestRef allExtra = NULL;
2079      SOSManifestRef extra = NULL;
2080      SOSManifestRef excessPending = NULL;
2081      SOSManifestRef missing = NULL;
2082      SOSManifestRef unwanted = SOSPeerGetUnwantedManifest(peer);
2083      SOSManifestRef excessUnwanted = NULL;
2084      CFDataRef result = NULL;
2085  
2086      // Given (C, L, T, U) compute (T, U, M, A)
2087      // (C \ L) \ U => M
2088      // (L \ C) \ T => A
2089      // we also compute
2090      // U \ (C \ L) => EU
2091      // T \ (L \ C) => ET
2092      // And assert that both EU and ET are empty and if not remove them from U and T respectively
2093      SOSManifestDiff(confirmed, local, &allMissing, &allExtra, error);
2094      SOSManifestDiff(allExtra, pendingObjects, &extra, &excessPending, error);
2095      if (SOSManifestGetCount(excessPending)) {
2096          // T \ (L \ C) => excessPending (items both in L and C or in neither that are still pending)
2097          // Can only happen if a member of T was removed from L without us having a chance to update T
2098          secerror("%@ ASSERTION FAILURE purging excess pendingObjects: %@", peer, excessPending);
2099          SOSManifestRef newPendingObjects = SOSManifestCreateComplement(excessPending, pendingObjects, error);
2100          SOSPeerSetPendingObjects(peer, newPendingObjects);
2101          CFReleaseSafe(newPendingObjects);
2102          pendingObjects = SOSPeerGetPendingObjects(peer);
2103      }
2104      SOSManifestDiff(allMissing, unwanted, &missing, &excessUnwanted, error);
2105      if (SOSManifestGetCount(excessUnwanted)) {
2106          // U \ (C \ L) => excessUnwanted (items both in L and C or in neither that are still unwanted)
2107          // Can only happen if a member of U was added to L without us having a chance to update U.
2108          // Since U only contains items the conflict resolver rejected, this implies L somehow got rolled back
2109          // The other option (and more likely) is a member of U was removed from C and not from U.
2110          secerror("%@ ASSERTION FAILURE purging excess unwanted: %@", peer, excessUnwanted);
2111          SOSManifestRef newUnwanted = SOSManifestCreateComplement(excessUnwanted, unwanted, error);
2112          SOSPeerSetUnwantedManifest(peer, newUnwanted);
2113          CFReleaseSafe(newUnwanted);
2114          unwanted = SOSPeerGetUnwantedManifest(peer);
2115      }
2116  
2117      CFReleaseNull(allExtra);
2118      CFReleaseNull(excessPending);
2119      CFReleaseNull(allMissing);
2120      CFReleaseNull(excessUnwanted);
2121  
2122      secnoticeq("engine", "%@:%@: send state for peer [%s%s%s][%s%s] local:%zu confirmed:%zu pending:%zu, extra:%zu, missing:%zu unwanted:%zu", engine->myID, SOSPeerGetID(peer),
2123                 local ? "L":"l",
2124                 confirmed ? "C":"0",
2125                 pendingObjects ? "P":"0",
2126                 SOSPeerSendObjects(peer) ? "O":"o",
2127                 SOSPeerMustSendMessage(peer) ? "S":"s",
2128                 SOSManifestGetCount(local),
2129                 SOSManifestGetCount(confirmed),
2130                 SOSManifestGetCount(pendingObjects),
2131                 SOSManifestGetCount(extra),
2132                 SOSManifestGetCount(missing),
2133                 SOSManifestGetCount(unwanted)
2134                 );
2135  
2136      if (confirmed) {
2137          // TODO: Because of not letting things terminate while we have extra left
2138          // we might send objects when we didn't need to, but there is always an
2139          // extra roundtrip required for objects that we assume the other peer
2140          // should have already.
2141          // TODO: If there are extra objects left, calling this function is not
2142          // idempotent we should check if pending is what we are about to send and not send anything in this case.
2143          if (SOSManifestGetCount(pendingObjects) == 0 && SOSManifestGetCount(extra) == 0)
2144              SOSPeerSetSendObjects(peer, false);
2145  
2146          // If we aren't missing anything, we've gotten all their data, so we're sync even if they haven't seen ours.
2147          if (missing && SOSManifestGetCount(missing) == 0) {
2148              SOSEngineCompletedSyncWithPeer(engine, peer);
2149          }
2150  
2151          if (CFEqualSafe(local, SOSPeerGetProposedManifest(peer)) && !SOSPeerMustSendMessage(peer)) {
2152              bool send = false;
2153              if (CFEqual(confirmed, local)) {
2154                  secnoticeq("engine", "synced <No MSG> %@:%@", engine->myID,  peer);
2155              } else if (SOSManifestGetCount(pendingObjects) == 0 /* TODO: No entries moved from extra to pendingObjects. */
2156                         && SOSManifestGetCount(missing) == 0) {
2157                  secnoticeq("engine", "waiting <MSG not resent> %@:%@ extra: %@", engine->myID, peer, extra);
2158              } else {
2159                  send = true;
2160              }
2161              if (!send) {
2162                  CFReleaseNull(local);
2163                  CFReleaseNull(message);
2164                  CFReleaseNull(extra);
2165                  CFReleaseNull(missing);
2166                  return CFDataCreate(kCFAllocatorDefault, NULL, 0);
2167              }
2168          }
2169  
2170          if (SOSManifestGetCount(pendingObjects)) {
2171              // If we have additions and we need to send objects, do so.
2172              __block size_t objectsSize = 0;
2173              __block struct SOSDigestVector dv = SOSDigestVectorInit;
2174              CFMutableArrayRef changes = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
2175              __block CFErrorRef dsfeError = NULL;
2176  
2177              if (!SOSDataSourceForEachObject(engine->dataSource, txn, pendingObjects, &dsfeError, ^void(CFDataRef key, SOSObjectRef object, bool *stop) {
2178                  CFErrorRef localError = NULL;
2179                  CFDataRef digest = NULL;
2180                  CFDataRef der = NULL;
2181  #if !defined(NDEBUG)
2182                  const uint8_t *d = CFDataGetBytePtr(key);
2183  #endif
2184                  secdebug("engine", "%@:%@ object %02X%02X%02X%02X error from SOSDataSourceForEachObject: %@",
2185                             engine->myID, SOSPeerGetID(peer), d[0], d[1], d[2], d[3], dsfeError);
2186                  if (!object) {
2187                      const uint8_t *d = CFDataGetBytePtr(key);
2188                      secerror("%@:%@ object %02X%02X%02X%02X dropping from manifest: not found in datasource: %@",
2189                               engine->myID, SOSPeerGetID(peer), d[0], d[1], d[2], d[3], dsfeError);
2190                      SOSChangesAppendDelete(changes, key);
2191                  } else if (!(der = SOSEngineCopyObjectDER(engine, object, &localError))
2192                             || !(digest = SOSObjectCopyDigest(engine->dataSource, object, &localError))) {
2193                      if (SecErrorGetOSStatus(localError) == errSecDecode) {
2194                          // Decode error, we need to drop these objects from our manifests
2195                          const uint8_t *d = CFDataGetBytePtr(key);
2196                          secnoticeq("engine", "%@:%@ object %02X%02X%02X%02X dropping from manifest: %@",
2197                                     engine->myID, SOSPeerGetID(peer), d[0], d[1], d[2], d[3], localError);
2198                          SOSChangesAppendDelete(changes, key);
2199                          CFRelease(localError);
2200                      } else {
2201                          // Stop iterating and propagate out all other errors.
2202                          const uint8_t *d = CFDataGetBytePtr(key);
2203                          secnoticeq("engine", "%@:%@ object %02X%02X%02X%02X in SOSDataSourceForEachObject: %@",
2204                                     engine->myID, SOSPeerGetID(peer), d[0], d[1], d[2], d[3], localError);
2205                          *stop = true;
2206                          CFErrorPropagate(localError, error);
2207                          CFReleaseNull(message);
2208                      }
2209                  } else {
2210                      if (!CFEqual(key, digest)) {
2211                          const uint8_t *d = CFDataGetBytePtr(key);
2212                          const uint8_t *e = CFDataGetBytePtr(digest);
2213                          secnoticeq("engine", "%@:%@ object %02X%02X%02X%02X is really %02X%02X%02X%02X dropping from local manifest",
2214                                     engine->myID, SOSPeerGetID(peer), d[0], d[1], d[2], d[3], e[0], e[1], e[2], e[3]);
2215                          SOSChangesAppendDelete(changes, key);
2216                          SOSChangesAppendAdd(changes, object); // This is new behaviour but we think it's more correct
2217                      }
2218                      
2219                      size_t objectLen = (size_t)CFDataGetLength(der);
2220                      if (SOSMessageAppendObject(message, der, &localError)) {
2221                          SOSDigestVectorAppend(&dv, CFDataGetBytePtr(digest));
2222                          if(!*attributeList)
2223                              *attributeList = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
2224                          CFDictionaryRef itemPlist = SOSObjectCopyPropertyList(engine->dataSource, object, &localError);
2225                          if(itemPlist && !CFArrayContainsValue(*attributeList, CFRangeMake(0, CFArrayGetCount(*attributeList)), (CFStringRef)CFDictionaryGetValue(itemPlist, kSecAttrAccessGroup))){
2226                              CFArrayAppendValue(*attributeList, (CFStringRef)CFDictionaryGetValue(itemPlist, kSecAttrAccessGroup));
2227                          }//copy access group to array
2228                          CFReleaseNull(itemPlist);
2229                      } else {
2230                          const uint8_t *d = CFDataGetBytePtr(digest);
2231                          CFStringRef hexder = CFDataCopyHexString(der);
2232                          secnoticeq("engine", "%@:%@ object %02X%02X%02X%02X der: %@ dropping from manifest: %@",
2233                                     engine->myID, SOSPeerGetID(peer), d[0], d[1], d[2], d[3], hexder, localError);
2234                          CFReleaseNull(hexder);
2235                          CFReleaseNull(message);
2236                          // Since we can't send these objects let's assume they are bad too?
2237                          SOSChangesAppendDelete(changes, digest);
2238                      }
2239                      
2240                      objectsSize += objectLen;
2241                      if (objectsSize > kSOSMessageMaxObjectsSize)
2242                          *stop = true;
2243                  }
2244                  CFErrorPropagate(dsfeError, error); // this also releases dsfeError
2245                  dsfeError = NULL;
2246                  CFReleaseSafe(der);
2247                  CFReleaseSafe(digest);
2248              })) {
2249                  CFReleaseNull(message);
2250              }
2251              if (dv.count){
2252                  objectsSent = SOSManifestCreateWithDigestVector(&dv, error);
2253              }
2254              if (CFArrayGetCount(changes)) {
2255                  CFErrorRef localError = NULL;
2256                  if (!SOSEngineUpdateChanges_locked(engine, NULL, kSOSDataSourceTransactionDidCommit, kSOSDataSourceSOSTransaction, changes, &localError))
2257                      secerror("SOSEngineUpdateChanges_locked: %@ failed: %@", changes, localError);
2258                  CFReleaseSafe(localError);
2259                  CFAssignRetained(local, SOSEngineCopyLocalPeerManifest_locked(engine, peer, error));
2260              }
2261              CFReleaseSafe(changes);
2262              SOSDigestVectorFree(&dv);
2263              CFReleaseNull(dsfeError);
2264          }
2265      } else {
2266          // If we have no confirmed manifest, we want all pendedObjects going out as a manifest
2267          objectsSent = CFRetainSafe(pendingObjects);
2268      }
2269  
2270      if (confirmed || SOSManifestGetCount(missing) || SOSManifestGetCount(extra) || objectsSent) {
2271          SOSManifestRef allExtra = SOSManifestCreateUnion(extra, objectsSent, error);
2272          proposed = SOSManifestCreateWithPatch(confirmed, missing, allExtra, error);
2273          CFReleaseNull(allExtra);
2274      }
2275  
2276      SOSManifestRef sender = local;
2277      // We actually send the remote peer its own digest.
2278      // Note that both pendingObjects and unwanted may have been changed, so we get them again
2279      if (SOSManifestGetCount(SOSPeerGetPendingObjects(peer))==0 && SOSManifestGetCount(extra)==0 &&
2280          SOSManifestGetCount(missing)==0 && SOSManifestGetCount(SOSPeerGetUnwantedManifest(peer))!=0) {
2281          secnoticeq("engine", "%@:%@: only have differences in unwanted set; lying to peer to stop sync",engine->myID, SOSPeerGetID(peer));
2282          sender = confirmed;
2283      }
2284  
2285      if (!SOSMessageSetManifests(message, sender, confirmed, proposed, proposed, confirmed ? objectsSent : NULL, error)) {
2286          secnoticeq("engine", "%@:%@: failed to set message manifests",engine->myID, SOSPeerGetID(peer));
2287          CFReleaseNull(message);
2288      }
2289  
2290      CFReleaseNull(objectsSent);
2291  
2292      if (message) {
2293          result = SOSMessageCreateData(message, SOSPeerNextSequenceNumber(peer), error);
2294      }
2295      
2296      if (result) {
2297          SOSEnginePeerMessageSentCallback* pmsc = malloc(sizeof(SOSEnginePeerMessageSentCallback));
2298          memset(pmsc, 0, sizeof(SOSEnginePeerMessageSentCallback));
2299          pmsc->engine    = engine; CFRetain(pmsc->engine);
2300          pmsc->peer      = CFRetainSafe(peer);
2301          pmsc->local     = CFRetainSafe(local);
2302          pmsc->proposed  = CFRetainSafe(proposed);
2303          pmsc->message   = CFRetainSafe(message);
2304          pmsc->confirmed = CFRetainSafe(confirmed);
2305  
2306          SOSEngineMessageCallbackSetCallback(pmsc, ^(bool success) {
2307              // Have to copy pmsc so it'll still be around during the dispatch_async
2308              SOSEnginePeerMessageSentCallback* pmsc2 = malloc(sizeof(SOSEnginePeerMessageSentCallback));
2309              memset(pmsc2, 0, sizeof(SOSEnginePeerMessageSentCallback));
2310              pmsc2->engine    = pmsc->engine; CFRetain(pmsc2->engine);
2311              pmsc2->peer      = CFRetainSafe(pmsc->peer);
2312              pmsc2->local     = CFRetainSafe(pmsc->local);
2313              pmsc2->proposed  = CFRetainSafe(pmsc->proposed);
2314              pmsc2->message   = CFRetainSafe(pmsc->message);
2315              pmsc2->confirmed = CFRetainSafe(pmsc->confirmed);
2316  
2317              dispatch_async(pmsc->engine->queue, ^{
2318                  if (success) {
2319                      SOSPeerSetMustSendMessage(pmsc2->peer, false);
2320                      if (!pmsc2->confirmed && !pmsc2->proposed) {
2321                          SOSPeerSetSendObjects(pmsc2->peer, true);
2322                          secnoticeq("engine", "%@:%@ sendObjects=true L:%@", pmsc2->engine->myID, SOSPeerGetID(pmsc2->peer), pmsc2->local);
2323                      }
2324                      SOSPeerAddLocalManifest(pmsc2->peer, pmsc2->local);
2325                      SOSPeerAddProposedManifest(pmsc2->peer, pmsc2->proposed);
2326                      secnoticeq("engine", "send %@:%@ %@", pmsc2->engine->myID, SOSPeerGetID(pmsc2->peer), pmsc2->message);
2327                  } else {
2328                      secerror("%@:%@ failed to send %@", pmsc2->engine->myID, SOSPeerGetID(pmsc2->peer), pmsc2->message);
2329                  }
2330                  SOSEngineFreeMessageCallback(pmsc2);
2331              });
2332          });
2333  
2334          *sent = pmsc;
2335      }
2336  
2337      CFReleaseNull(local);
2338      CFReleaseNull(extra);
2339      CFReleaseNull(missing);
2340      CFReleaseNull(message);
2341      CFReleaseNull(proposed);
2342      if (error && *error)
2343          secerror("%@:%@ error in send: %@", engine->myID, SOSPeerGetID(peer), *error);
2344  
2345      return result;
2346  }
2347  
2348  void SOSEngineMessageCallbackSetCallback(SOSEnginePeerMessageSentCallback *sent, SOSEnginePeerMessageSentBlock block) {
2349      if(sent) {
2350          sent->block = Block_copy(block);
2351      }
2352  }
2353  
2354  
2355  void SOSEngineMessageCallCallback(SOSEnginePeerMessageSentCallback *sent, bool ok) {
2356      if (sent && sent->block) {
2357          (sent->block)(ok);
2358      }
2359  }
2360  
2361  void SOSEngineFreeMessageCallback(SOSEnginePeerMessageSentCallback* psmc) {
2362      if(psmc) {
2363          CFReleaseNull(psmc->engine);
2364          CFReleaseNull(psmc->peer);
2365          CFReleaseNull(psmc->coder);
2366          CFReleaseNull(psmc->local);
2367          CFReleaseNull(psmc->proposed);
2368          CFReleaseNull(psmc->message);
2369          CFReleaseNull(psmc->confirmed);
2370  
2371          if(psmc->block) {
2372              Block_release(psmc->block);
2373          }
2374  
2375          free(psmc);
2376      }
2377  }
2378  
2379  static void SOSEngineLogItemError(SOSEngineRef engine, CFStringRef peerID, CFDataRef key, CFDataRef optionalDigest, const char *where, CFErrorRef error) {
2380      if (!optionalDigest) {
2381          const uint8_t *d = CFDataGetBytePtr(key);
2382          secwarning("%@:%@ object %02X%02X%02X%02X %s: %@", engine->myID, peerID, d[0], d[1], d[2], d[3], where, error ? (CFTypeRef)error : CFSTR(""));
2383      } else {
2384          const uint8_t *d = CFDataGetBytePtr(key);
2385          const uint8_t *e = CFDataGetBytePtr(optionalDigest);
2386          secwarning("%@:%@ object %02X%02X%02X%02X is really %02X%02X%02X%02X dropping from local manifest", engine->myID, peerID, d[0], d[1], d[2], d[3], e[0], e[1], e[2], e[3]);
2387      }
2388  }
2389  
2390  static bool SOSEngineWriteToBackup_locked(SOSEngineRef engine, SOSPeerRef peer, bool rewriteComplete, bool *didWrite, bool *incomplete, CFErrorRef *error) {
2391      __block bool ok = SOSPeerWritePendingReset(peer, error);
2392      if (!ok || !SOSPeerGetKeyBag(peer))
2393          return ok;
2394      __block SOSManifestRef local = SOSEngineCopyLocalPeerManifest_locked(engine, peer, error);
2395      __block SOSManifestRef proposed = SOSPeerGetProposedManifest(peer);
2396      __block bool notify = true;
2397      SOSManifestRef pendingObjects = NULL;
2398      SOSManifestRef missing = NULL;
2399      CFStringRef peerID = SOSPeerGetID(peer);
2400  
2401      ok &= SOSManifestDiff(proposed, local, &missing, &pendingObjects, error);
2402  
2403      secnoticeq("engine", "%@:%@: Send state for peer [%s%s%s] O: %zu, M: %zu", engine->myID, peerID,
2404                 local ? "L":"l",
2405                 proposed ? "P":"0",
2406                 pendingObjects ? "O":"0",
2407                 SOSManifestGetCount(pendingObjects),
2408                 SOSManifestGetCount(missing));
2409  
2410      if (SOSManifestGetCount(missing) == 0 && SOSManifestGetCount(pendingObjects) == 0) {
2411          // proposed == local (faster test than CFEqualSafe above), since we
2412          // already did the SOSManifestDiff
2413          if (rewriteComplete) {
2414              notify = false;
2415          } else {
2416              secnoticeq("engine", "%@:%@ backup still done", engine->myID, peer);
2417              goto done;
2418          }
2419      }
2420      ok &= SOSPeerAppendToJournal(peer, error, ^(FILE *journalFile, keybag_handle_t kbhandle) {
2421          SOSManifestRef objectsSent = NULL;
2422          __block struct SOSDigestVector dvdel = SOSDigestVectorInit;
2423          __block struct SOSDigestVector dvadd = SOSDigestVectorInit;
2424          SOSManifestForEach(missing, ^(CFDataRef key, bool *stop) {
2425              CFErrorRef localError = NULL;
2426              if (ftello(journalFile) > kSOSBackupMaxFileSize) {
2427                  // Highwatermark hit on file.
2428                  *stop = true;
2429              } else if (SOSBackupEventWriteDelete(journalFile, key, &localError)) {
2430                  SOSDigestVectorAppend(&dvdel, CFDataGetBytePtr(key));
2431              } else {
2432                  SOSEngineLogItemError(engine, peerID, key, NULL, "in SOSPeerWriteDelete", localError);
2433                  CFErrorPropagate(localError, error);
2434                  // TODO: Update of missing so proposed is updated properly
2435                  *stop = true; // Disk full?
2436                  ok = false;
2437              }
2438          });
2439          if (ok && SOSManifestGetCount(pendingObjects)) {
2440              CFMutableArrayRef changes = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
2441              ok &= SOSDataSourceForEachObject(engine->dataSource, NULL, pendingObjects, error, ^void(CFDataRef key, SOSObjectRef object, bool *stop) {
2442                  CFErrorRef localError = NULL;
2443                  CFDataRef digest = NULL;
2444                  CFDictionaryRef backupItem = NULL;
2445                  if (ftello(journalFile) > kSOSBackupMaxFileSize) {
2446                      // Highwatermark hit on file.
2447                      *stop = true;
2448                  } else if (!object) {
2449                      SOSEngineLogItemError(engine, peerID, key, NULL, "dropping from manifest: not found in datasource", localError);
2450                      SOSChangesAppendDelete(changes, key);
2451                  } else if (!(backupItem = SOSObjectCopyBackup(engine->dataSource, object, kbhandle, &localError))
2452                             || !(digest = SOSObjectCopyDigest(engine->dataSource, object, &localError))) {
2453                      if (SecErrorGetOSStatus(localError) == errSecDecode) {
2454                          // Decode error, we need to drop these objects from our manifests
2455                          SOSEngineLogItemError(engine, peerID, key, NULL, "dropping from manifest", localError);
2456                          SOSChangesAppendDelete(changes, key);
2457                          CFRelease(localError);
2458                      } else {
2459                          // Stop iterating and propagate out all other errors.
2460                          SOSEngineLogItemError(engine, peerID, key, NULL, "in SOSDataSourceForEachObject", localError);
2461                          *stop = true;
2462                          CFErrorPropagate(localError, error);
2463                          ok = false;
2464                      }
2465                  } else {
2466                      if (!CFEqual(key, digest)) {
2467                          SOSEngineLogItemError(engine, peerID, key, digest, "", NULL);
2468                          SOSChangesAppendDelete(changes, key);
2469                          SOSChangesAppendAdd(changes, object); // This is new behaviour but we think it's more correct
2470                      }
2471  
2472                      if (SOSBackupEventWriteAdd(journalFile, backupItem, &localError)) {
2473                          SOSDigestVectorAppend(&dvadd, CFDataGetBytePtr(digest));
2474                      } else {
2475                          SOSEngineLogItemError(engine, peerID, key, NULL, "in SOSPeerWriteAdd", localError);
2476                          *stop = true; // Disk full?
2477                          CFErrorPropagate(localError, error);
2478                          ok = false;
2479                      }
2480                  }
2481                  CFReleaseSafe(backupItem);
2482                  CFReleaseSafe(digest);
2483              });
2484              if (CFArrayGetCount(changes)) {
2485                  CFErrorRef localError = NULL;
2486                  if (!SOSEngineUpdateChanges_locked(engine, NULL, kSOSDataSourceTransactionDidCommit, kSOSDataSourceSOSTransaction, changes, &localError))
2487                      secerror("SOSEngineUpdateChanges_locked: %@ failed: %@", changes, localError);
2488                  CFReleaseSafe(localError);
2489                  // Since calling SOSEngineUpdateChanges_locked might cause local to change and might cause the backup peer to update proposed, refetch them here.
2490                  CFAssignRetained(local, SOSEngineCopyLocalPeerManifest_locked(engine, peer, error));
2491                  proposed = SOSPeerGetProposedManifest(peer);
2492              }
2493              CFReleaseSafe(changes);
2494          }
2495  
2496          if (dvadd.count || (proposed && dvdel.count)) {
2497              *didWrite = true;
2498              SOSManifestRef deleted = SOSManifestCreateWithDigestVector(&dvdel, error);
2499              SOSManifestRef objectsSent = SOSManifestCreateWithDigestVector(&dvadd, error);
2500              SOSManifestRef newProposed = SOSManifestCreateWithPatch(proposed, deleted, objectsSent, error);
2501              CFReleaseSafe(deleted);
2502              CFReleaseSafe(objectsSent);
2503              SOSPeerSetProposedManifest(peer, newProposed);
2504              CFReleaseSafe(newProposed);
2505              proposed = SOSPeerGetProposedManifest(peer);
2506          }
2507          SOSDigestVectorFree(&dvdel);
2508          SOSDigestVectorFree(&dvadd);
2509  
2510          // TODO: If proposed is NULL, and local is empty we should still consider ourselves done.
2511          // It so happens this can't happen in practice today since there is at least a backupbag
2512          // in the backup, but this is a bug waiting to rear its head in the future.
2513          if (ok && CFEqualSafe(local, proposed)) {
2514              CFErrorRef localError = NULL;
2515              if (SOSBackupEventWriteCompleteMarker(journalFile, 899, &localError)) {
2516                  SOSPeerSetSendObjects(peer, true);
2517                  *didWrite = true;
2518                  secnoticeq("backup", "%@:%@ backup done%s", engine->myID, peerID, notify ? " notifying sbd" : "");
2519                  // TODO: Now switch to changes based writing to backup sync.
2520                  // Currently we leave changes enabled but we probably shouldn't
2521              } else {
2522                  secwarning("%@:%@ in SOSBackupPeerWriteCompleteMarker: %@", engine->myID, peerID, localError);
2523                  ok = false;
2524                  *incomplete = true;
2525                  CFErrorPropagate(localError, error);
2526              }
2527          } else {
2528              secnoticeq("backup", "%@:%@ backup incomplete [%zu/%zu]%s", engine->myID, peerID, SOSManifestGetCount(local), SOSManifestGetCount(proposed), notify ? " notifying sbd" : "");
2529              *incomplete = true;
2530          }
2531          CFReleaseNull(objectsSent);
2532      });
2533      if (notify)
2534          SOSBackupPeerPostNotification("writing changes to backup");
2535  
2536  done:
2537      CFReleaseSafe(local);
2538      CFReleaseNull(pendingObjects);
2539      CFReleaseNull(missing);
2540  
2541      return ok;
2542  }
2543  
2544  CF_RETURNS_RETAINED CFSetRef SOSEngineSyncWithBackupPeers(SOSEngineRef engine, CFSetRef /* CFStringRef */ peers, bool forceReset, CFErrorRef *error)
2545  {
2546      __block bool incomplete = false;
2547      CFMutableSetRef handledSet = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
2548  
2549      bool ok = SOSEngineDoTxnOnQueue(engine, error, ^(SOSTransactionRef txn, bool *commit) {
2550          __block bool dirty = false;
2551          CFSetForEach(peers, ^(const void *value) {
2552              bool report_handled = true;
2553              CFErrorRef localError = NULL;
2554              SOSPeerRef peer = NULL;
2555              CFStringRef peerID = asString(value, &localError);
2556              require_action_quiet(peerID, done, report_handled = false);
2557  
2558              peer = SOSEngineCopyPeerWithID_locked(engine, peerID, &localError);
2559              require_quiet(peerID, done);
2560  
2561              if (SOSPeerMapEntryIsBackup(peer)) {
2562                  if(forceReset) {
2563                      SOSPeerSetMustSendMessage(peer, true);
2564                  }
2565  
2566                  report_handled = SOSEngineWriteToBackup_locked(engine, peer, false, &dirty, &incomplete, &localError);
2567              }
2568  
2569          done:
2570              if (localError) {
2571                  secnotice("engine-sync", "Failed to process sync for %@: %@", peerID, localError);
2572              }
2573              if (report_handled) {
2574                  CFSetAddValue(handledSet, peerID);
2575              }
2576              CFReleaseNull(localError);
2577              CFReleaseNull(peer);
2578          });
2579  
2580          if (dirty) {
2581              CFErrorRef saveError = NULL;
2582              if (!SOSEngineSave(engine, txn, &saveError)) {
2583                  secnotice("engine-save", "Failed to save engine: %@", saveError);
2584              }
2585          }
2586      });
2587      if (incomplete) {
2588          // Ensure we get called again in a while (after a backup timeout)
2589          // sbd will do this since we never wrote a complete marker.
2590          // TODO: This relies on us not writing complete marker for update
2591          // event while we havn't finished a full backup, which we currently still do.
2592      }
2593      if (!ok)
2594          CFReleaseNull(handledSet);
2595  
2596      return handledSet;
2597  }
2598  
2599  bool SOSEngineHandleMessage(SOSEngineRef engine, CFStringRef peerID,
2600                              CFDataRef raw_message, CFErrorRef *error)
2601  {
2602      __block bool result = true;
2603      __block bool somethingChanged = false;
2604      SOSMessageRef message = SOSMessageCreateWithData(kCFAllocatorDefault, raw_message, error);
2605      result &= message && SOSEngineDoTxnOnQueue(engine, error, ^(SOSTransactionRef txn, bool *commit) {
2606          result = SOSEngineHandleMessage_locked(engine, peerID, message, txn, commit, &somethingChanged, error);
2607      });
2608      CFReleaseSafe(message);
2609      if (somethingChanged)
2610          SecKeychainChanged();
2611      return result;
2612  }
2613  
2614  void SOSEngineCircleChanged(SOSEngineRef engine, CFStringRef myPeerID, CFArrayRef trustedPeers, CFArrayRef untrustedPeers) {
2615      __block bool peersOrViewsChanged = false;
2616      SOSEngineDoOnQueue(engine, ^{
2617          peersOrViewsChanged = SOSEngineCircleChanged_locked(engine, myPeerID, trustedPeers, untrustedPeers);
2618  
2619          // We should probably get a more precise list of peers that actually need talking to
2620          if (peersOrViewsChanged && engine->myID && CFArrayGetCount(engine->peerIDs) != 0)
2621              SOSCCRequestSyncWithPeersList(engine->peerIDs);
2622      });
2623  
2624      __block bool ok = true;
2625      __block CFErrorRef localError = NULL;
2626      ok &= SOSEngineDoTxnOnQueue(engine, &localError, ^(SOSTransactionRef txn, bool *commit) {
2627          ok = *commit = SOSEngineSave(engine, txn, &localError);
2628      });
2629      if (!ok) {
2630          secerror("failed to save engine state: %@", localError);
2631          CFReleaseSafe(localError);
2632      }
2633  
2634  }
2635  
2636  SOSManifestRef SOSEngineCopyManifest(SOSEngineRef engine, CFErrorRef *error) {
2637      __block SOSManifestRef result = NULL;
2638      SOSEngineDoOnQueue(engine, ^{
2639          result = SOSEngineCopyManifestWithViewNameSet_locked(engine, SOSViewsGetV0ViewSet(), error);
2640      });
2641      return result;
2642  }
2643  
2644  SOSManifestRef SOSEngineCopyLocalPeerManifest(SOSEngineRef engine, SOSPeerRef peer, CFErrorRef *error) {
2645      __block SOSManifestRef result = NULL;
2646      SOSEngineDoOnQueue(engine, ^{
2647          result = SOSEngineCopyLocalPeerManifest_locked(engine, peer, error);
2648      });
2649      return result;
2650  }
2651  
2652  bool SOSEngineUpdateChanges(SOSEngineRef engine, SOSDataSourceTransactionSource source, CFArrayRef changes, CFErrorRef *error) {
2653      __block bool result = true;
2654      SOSEngineDoOnQueue(engine, ^{
2655          result = SOSEngineUpdateChanges_locked(engine, NULL, kSOSDataSourceTransactionDidCommit, source, changes, error);
2656      });
2657      return result;
2658  }
2659  
2660  //
2661  // Peer state layout.  WRONG! It's an array now
2662  // The peer state is an array.
2663  // The first element of the array is a dictionary with any number of keys and
2664  // values in it (for future expansion) such as changing the digest size or type
2665  // or remebering boolean flags for a peers sake.
2666  // The next three are special in that they are manifest digests with special
2667  // meaning and rules as to how they are treated (These are dynamically updated
2668  // based on database activity so they have a fully history of all changes made
2669  // to the local db. The first is the manifest representing the pendingObjects
2670  // to send to the other peer.  This is normally only ever appending to, and in
2671  // particular with transactions originating from the Keychain API that affect
2672  // syncable items will need to add the new objects digests to the pendingObjects list
2673  // while adding the digests of any tombstones encountered to the extra list.
2674  
2675  SOSPeerRef SOSEngineCopyPeerWithID(SOSEngineRef engine, CFStringRef peer_id, CFErrorRef *error) {
2676      __block SOSPeerRef peer = NULL;
2677      SOSEngineDoOnQueue(engine, ^{
2678          peer = SOSEngineCopyPeerWithID_locked(engine, peer_id, error);
2679      });
2680      return peer;
2681  }
2682  
2683  bool SOSEngineForPeerID(SOSEngineRef engine, CFStringRef peerID, CFErrorRef *error, void (^forPeer)(SOSTransactionRef txn, SOSPeerRef peer)) {
2684      __block bool ok = true;
2685      SOSDataSourceReadWithCommitQueue(engine->dataSource, error, ^(SOSTransactionRef txn) {
2686          SOSEngineDoOnQueue(engine, ^{
2687              SOSPeerRef peer = SOSEngineCopyPeerWithID_locked(engine, peerID, error);
2688              if (peer) {
2689                  forPeer(txn, peer);
2690                  CFRelease(peer);
2691              } else {
2692                  ok = false;
2693              }
2694          });
2695      });
2696  
2697      return ok;
2698  }
2699  
2700  bool SOSEngineWithPeerID(SOSEngineRef engine, CFStringRef peerID, CFErrorRef *error, void (^with)(SOSPeerRef peer, SOSCoderRef coder, SOSDataSourceRef dataSource, SOSTransactionRef txn, bool *forceSaveState)) {
2701      __block bool result = true;
2702      result &= SOSEngineDoTxnOnQueue(engine, error, ^(SOSTransactionRef txn, bool *commit) {
2703          SOSPeerRef peer = SOSEngineCopyPeerWithID_locked(engine, peerID, error);
2704          if (!peer) {
2705              result = SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("Engine has no peer for %@"), peerID);
2706          } else {
2707              bool saveState = false;
2708              SOSCoderRef coder = SOSEngineGetCoderInTx_locked(engine, txn, peerID, error);
2709              with(peer, coder, engine->dataSource, txn, &saveState);
2710              CFReleaseSafe(peer);
2711              if (saveState)
2712                  result = SOSEngineSave(engine, txn, error);
2713              // TODO: Don't commit if engineSave fails?
2714          }
2715      });
2716  
2717      return result;
2718  }
2719  
2720  CFDataRef SOSEngineCreateMessageToSyncToPeer(SOSEngineRef engine, CFStringRef peerID, CFMutableArrayRef *attributeList, SOSEnginePeerMessageSentCallback **sentCallback, CFErrorRef *error){
2721  __block CFDataRef message = NULL;
2722      SOSEngineForPeerID(engine, peerID, error, ^(SOSTransactionRef txn, SOSPeerRef peer) {
2723          message = SOSEngineCreateMessage_locked(engine, txn, peer, attributeList, error, sentCallback);
2724      });
2725      return message;
2726  }
2727  
2728  bool SOSEnginePeerDidConnect(SOSEngineRef engine, CFStringRef peerID, CFErrorRef *error) {
2729      return SOSEngineWithPeerID(engine, peerID, error, ^(SOSPeerRef peer, SOSCoderRef coder, SOSDataSourceRef dataSource, SOSTransactionRef txn, bool *saveState) {
2730          *saveState = SOSPeerDidConnect(peer);
2731      });
2732  }
2733  
2734  bool SOSEngineSetPeerConfirmedManifest(SOSEngineRef engine, CFStringRef backupName,
2735                                         CFDataRef keybagDigest, CFDataRef manifestData, CFErrorRef *error) {
2736      __block bool ok = true;
2737  
2738      ok &= SOSEngineForPeerID(engine, backupName, error, ^(SOSTransactionRef txn, SOSPeerRef peer) {
2739          bool dirty = false;
2740          bool incomplete = false;
2741          SOSManifestRef confirmed = NULL;
2742          CFDataRef keybag = SOSPeerGetKeyBag(peer);
2743          CFDataRef computedKeybagDigest = keybag ? CFDataCopySHA1Digest(keybag, NULL) : NULL;
2744          if (CFEqualSafe(keybagDigest, computedKeybagDigest)) {
2745              ok = confirmed = SOSManifestCreateWithData(manifestData, error);
2746              if (ok) {
2747                  // Set both confirmed and proposed (confirmed is just
2748                  // for debug status, proposed is actually what's used
2749                  // by the backup peer).
2750                  SOSPeerSetConfirmedManifest(peer, confirmed);
2751                  SOSPeerSetProposedManifest(peer, confirmed);
2752              }
2753          } else {
2754              // sbd missed a reset event, send it again
2755              // Force SOSEngineWriteToBackup_locked to call SOSPeerWriteReset, which clears
2756              // confirmed and proposed manifests and writes the keybag to the journal.
2757              SOSPeerSetMustSendMessage(peer, true);
2758          }
2759  
2760          // Stop changes from writing complete markers, unless SOSEngineWriteToBackup_locked() detects we are in sync
2761          SOSPeerSetSendObjects(peer, false);
2762          // Write data for this peer if we can, technically not needed for non legacy protocol support all the time.
2763          ok = SOSEngineWriteToBackup_locked(engine, peer, true, &dirty, &incomplete, error);
2764  
2765          if (!ok && error && SecErrorGetOSStatus(*error) == errSecInteractionNotAllowed) {
2766              SOSEnsureBackupWhileUnlocked();
2767          }
2768  
2769          CFReleaseSafe(confirmed);
2770          CFReleaseSafe(computedKeybagDigest);
2771      });
2772      return ok;
2773  }
2774  
2775  CFArrayRef SOSEngineCopyBackupPeerNames(SOSEngineRef engine, CFErrorRef *error) {
2776      __block CFMutableArrayRef backupNames = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
2777      SOSEngineForEachBackupPeer(engine, ^(SOSPeerRef peer) {
2778          CFArrayAppendValue(backupNames, SOSPeerGetID(peer));
2779      });
2780      return backupNames;
2781  }
2782  
2783  CFStringRef SOSEngineEnsureCopyBackupPeerForView(SOSEngineRef engine, CFStringRef backupPeerID, CFErrorRef *error) {
2784      __block CFStringRef backupName = CFSTR("");
2785      SOSEngineForBackupPeer(engine, backupPeerID, ^(SOSPeerRef peer) {
2786          backupName = CFRetainSafe(SOSPeerGetID(peer));
2787      });
2788      return backupName;
2789  }
2790  
2791  static CFMutableDictionaryRef SOSEngineCreateStateDictionary(CFStringRef peerID, SOSManifestRef manifest, CFSetRef vns, CFStringRef coderString) {
2792      CFNumberRef manifestCount = CFNumberCreateWithCFIndex(kCFAllocatorDefault, SOSManifestGetCount(manifest));
2793      CFDataRef manifestHash = SOSManifestGetDigest(manifest, NULL);
2794      CFMutableDictionaryRef result = CFDictionaryCreateMutableForCFTypesWithSafe(kCFAllocatorDefault,
2795                                                                                  kSOSCCEngineStatePeerIDKey, peerID,
2796                                                                                  kSOSCCEngineStateManifestCountKey, manifestCount,
2797                                                                                  kSOSCCEngineStateManifestHashKey, manifestHash,
2798                                                                                  kSOSCCEngineStateSyncSetKey, asSet(vns, NULL),
2799                                                                                  kSOSCCEngineStateCoderKey, coderString,
2800                                                                                  NULL);
2801      CFReleaseNull(manifestCount);
2802      return result;
2803  }
2804  
2805  static void SOSEngineAppendStateDictionary(CFMutableArrayRef stateArray, CFStringRef peerID, SOSManifestRef manifest, CFSetRef vns, CFStringRef coderString) {
2806      CFMutableDictionaryRef newState = SOSEngineCreateStateDictionary(peerID, manifest, vns, coderString);
2807      CFArrayAppendValue(stateArray, newState);
2808      CFReleaseNull(newState);
2809  }
2810  
2811  static CFArrayRef SOSEngineCopyPeerConfirmedDigests_locked(SOSEngineRef engine, CFErrorRef *error) {
2812      CFMutableArrayRef result = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
2813      CFDictionaryForEach(engine->viewNameSet2ChangeTracker, ^(const void *vns, const void *ct) {
2814          SOSManifestRef manifest = SOSEngineCopyManifestWithViewNameSet_locked(engine, vns, error);
2815          SOSEngineAppendStateDictionary(result, NULL, manifest, vns, NULL);
2816          CFReleaseNull(manifest);
2817      });
2818  
2819      // Copy other peers even if we aren't in the circle, since we're observing it.
2820      SOSEngineForEachPeer_locked(engine, ^(SOSPeerRef peer) {
2821          CFTypeRef coderObject = engine->coders ? CFDictionaryGetValue(engine->coders, SOSPeerGetID(peer)) : CFSTR("Coders not loaded.");
2822          CFStringRef coderState = coderObject ? CFCopyDescription(coderObject) : NULL;
2823          SOSEngineAppendStateDictionary(result, SOSPeerGetID(peer), SOSPeerGetConfirmedManifest(peer), SOSPeerGetViewNameSet(peer), coderState);
2824          CFReleaseNull(coderState);
2825      });
2826      return result;
2827  }
2828  
2829  CFArrayRef SOSEngineCopyPeerConfirmedDigests(SOSEngineRef engine, CFErrorRef *error) {
2830      __block CFArrayRef result = NULL;
2831      SOSEngineDoOnQueue(engine, ^{
2832          result = SOSEngineCopyPeerConfirmedDigests_locked(engine, error);
2833      });
2834      return result;
2835  }
2836  
2837  SOSDataSourceRef SOSEngineGetDataSource(SOSEngineRef engine) {
2838      return engine->dataSource;
2839  }
2840  
2841  #define ENGINELOGSTATE "engineLogState"
2842  void SOSEngineLogState(SOSEngineRef engine) {
2843      CFErrorRef error = NULL;
2844      CFArrayRef confirmedDigests = NULL;
2845  
2846      secnotice(ENGINELOGSTATE, "Start");
2847  
2848      require_action_quiet(engine, retOut, secnotice(ENGINELOGSTATE, "No Engine Available"));
2849      confirmedDigests = SOSEngineCopyPeerConfirmedDigests(engine, &error);
2850      require_action_quiet(confirmedDigests, retOut, secnotice(ENGINELOGSTATE, "No engine peers: %@\n", error));
2851  
2852      SOSCCForEachEngineStateAsStringFromArray(confirmedDigests, ^(CFStringRef onePeerDescription) {
2853          secnotice(ENGINELOGSTATE, "%@", onePeerDescription);
2854      });
2855  
2856  retOut:
2857      CFReleaseNull(error);
2858      CFReleaseNull(confirmedDigests);
2859      secnotice(ENGINELOGSTATE, "Finish");
2860  
2861      return;
2862  }
2863  
2864  //For Testing
2865  void TestSOSEngineDoOnQueue(CFTypeRef engine, dispatch_block_t action)
2866  {
2867      dispatch_sync(((SOSEngineRef)engine)->queue, action);
2868  }
2869  CFMutableDictionaryRef TestSOSEngineGetCoders(CFTypeRef engine){
2870      return ((SOSEngineRef)engine)->coders;
2871  }
2872  
2873  bool TestSOSEngineDoTxnOnQueue(CFTypeRef engine, CFErrorRef *error, void(^transaction)(SOSTransactionRef txn, bool *commit))
2874  {
2875      return SOSDataSourceWithCommitQueue(((SOSEngineRef)engine)->dataSource, error, ^(SOSTransactionRef txn, bool *commit) {
2876          TestSOSEngineDoOnQueue((SOSEngineRef)engine, ^{ transaction(txn, commit); });
2877      });
2878  }
2879  bool SOSEngineGetCodersNeedSaving(SOSEngineRef engine){
2880      return engine->codersNeedSaving;
2881  }
2882  
2883  void SOSEngineSetCodersNeedSaving(SOSEngineRef engine, bool saved){
2884      engine->codersNeedSaving = saved;
2885  }
2886