2  * Copyright (c) 2012-2015 Apple Inc. All Rights Reserved. 
   4  * @APPLE_LICENSE_HEADER_START@ 
   6  * This file contains Original Code and/or Modifications of Original Code 
   7  * as defined in and that are subject to the Apple Public Source License 
   8  * Version 2.0 (the 'License'). You may not use this file except in 
   9  * compliance with the License. Please obtain a copy of the License at 
  10  * http://www.opensource.apple.com/apsl/ and read it before using this 
  13  * The Original Code and all software distributed under the License are 
  14  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 
  15  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 
  16  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 
  17  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 
  18  * Please see the License for the specific language governing rights and 
  19  * limitations under the License. 
  21  * @APPLE_LICENSE_HEADER_END@ 
  26  * SOSEngine.c -  Implementation of a secure object syncing engine 
  29 #include <Security/SecureObjectSync/SOSChangeTracker.h> 
  30 #include <Security/SecureObjectSync/SOSEngine.h> 
  31 #include <Security/SecureObjectSync/SOSDigestVector.h> 
  32 #include <Security/SecureObjectSync/SOSInternal.h> 
  33 #include <Security/SecureObjectSync/SOSPeer.h> 
  34 #include <Security/SecureObjectSync/SOSViews.h> 
  35 #include <Security/SecureObjectSync/SOSBackupEvent.h> 
  36 #include <corecrypto/ccder.h> 
  39 #include <utilities/array_size.h> 
  40 #include <utilities/SecCFCCWrappers.h> 
  41 #include <utilities/SecCFError.h> 
  42 #include <utilities/SecCFRelease.h> 
  43 #include <utilities/SecCFWrappers.h> 
  44 #include <utilities/der_plist.h> 
  45 #include <utilities/der_plist_internal.h> 
  46 #include <utilities/debugging.h> 
  47 #include <utilities/iCloudKeychainTrace.h> 
  48 #include <utilities/SecCoreCrypto.h> 
  49 #include <utilities/SecFileLocations.h> 
  50 #include <AssertMacros.h> 
  51 #include <CoreFoundation/CoreFoundation.h> 
  53 #include <securityd/SecItemServer.h>    // TODO: We can't leave this here. 
  54 #include <securityd/SOSCloudCircleServer.h> // TODO: We can't leave this here. 
  55 #include <Security/SecItem.h>           // TODO: We can't leave this here. 
  56 #include <Security/SecItemPriv.h>       // TODO: We can't leave this here. 
  57 #include <securityd/SecItemSchema.h> 
  58 #include <securityd/iCloudTrace.h> 
  60 #include <CoreFoundation/CFURL.h> 
  63 // MARK: SOSEngine The Keychain database with syncable keychain support. 
  66 // Key in dataSource for general engine state file. 
  67 // This file only has digest entries in it, no manifests. 
  68 static const CFStringRef kSOSEngineState 
= CFSTR("engine-state"); 
  70 // Keys in state dictionary 
  71 static CFStringRef kSOSEngineManifestCacheKey 
= CFSTR("manifestCache"); 
  72 static CFStringRef kSOSEnginePeerStateKey 
= CFSTR("peerState"); 
  73 static CFStringRef kSOSEnginePeerIDsKey 
= CFSTR("peerIDs"); 
  74 static CFStringRef kSOSEngineIDKey 
= CFSTR("id"); 
  75 static CFStringRef kSOSEngineTraceDateKey 
= CFSTR("traceDate"); 
  77 /* SOSEngine implementation. */ 
  78 struct __OpaqueSOSEngine 
{ 
  80     SOSDataSourceRef dataSource
; 
  81     CFStringRef myID
;                       // My peerID in the circle 
  82     // We need to address the issues of corrupt keychain items 
  83     SOSManifestRef unreadable
;              // Possibly by having a set of unreadable items, to which we 
  84     // add any corrupted items in the db that have yet to be deleted. 
  85     // This happens if we notce corruption during a (read only) query. 
  86     // We would also perma-subtract unreadable from manifest whenever 
  87     // anyone asked for manifest.  This result would be cached in 
  88     // The manifestCache below, so we just need a key into the cache 
  89     CFDataRef localMinusUnreadableDigest
;   // or a digest (CFDataRef of the right size). 
  91     CFMutableDictionaryRef manifestCache
;       // digest -> ( refcount, manifest ) 
  92     //CFMutableDictionaryRef peerState;         // peerId -> mutable array of digests 
  93     CFMutableDictionaryRef peerMap
;             // peerId -> SOSPeerRef 
  94     CFDictionaryRef viewNameSet2ChangeTracker
;  // CFSetRef of CFStringRef -> SOSChangeTrackerRef 
  95     CFDictionaryRef viewName2ChangeTracker
;     // CFStringRef -> SOSChangeTrackerRef 
  97     CFDateRef lastTraceDate
;                    // Last time we did a CloudKeychainTrace 
  99     dispatch_queue_t queue
;                     // Engine queue 
 101     dispatch_source_t save_timer
;               // Engine state save timer 
 103     dispatch_queue_t syncCompleteQueue
;             // Non-retained queue for async notificaion 
 104     CFMutableDictionaryRef syncCompleteListeners
;   // Map from PeerID->notification block 
 107 static bool SOSEngineLoad(SOSEngineRef engine
, CFErrorRef 
*error
); 
 110 static CFStringRef 
SOSPeerIDArrayCreateString(CFArrayRef peerIDs
) { 
 111     return peerIDs 
? CFStringCreateByCombiningStrings(kCFAllocatorDefault
, peerIDs
, CFSTR(" ")) : CFSTR(""); 
 114 static CFStringRef 
SOSEngineCopyFormattingDesc(CFTypeRef cf
, CFDictionaryRef formatOptions
) { 
 115     SOSEngineRef engine 
= (SOSEngineRef
)cf
; 
 116     CFStringRef tpDesc 
= SOSPeerIDArrayCreateString(engine
->peerIDs
); 
 117     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); 
 118     CFReleaseSafe(tpDesc
); 
 122 static CFStringRef 
SOSEngineCopyDebugDesc(CFTypeRef cf
) { 
 123     return SOSEngineCopyFormattingDesc(cf
, NULL
); 
 126 static dispatch_queue_t sEngineQueue
; 
 127 static CFDictionaryRef sEngineMap
; 
 129 CFGiblisWithFunctions(SOSEngine
, NULL
, NULL
, NULL
, NULL
, NULL
, SOSEngineCopyFormattingDesc
, SOSEngineCopyDebugDesc
, NULL
, NULL
, ^{ 
 130     sEngineQueue 
= dispatch_queue_create("SOSEngine queue", DISPATCH_QUEUE_SERIAL
); 
 131     sEngineMap 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
 134 #define _LOG_RAW_MESSAGES 0 
 135 void logRawMessage(CFDataRef message
, bool sending
, uint64_t seqno
) 
 137 #if _LOG_RAW_MESSAGES 
 138     CFStringRef hexMessage 
= NULL
; 
 140         hexMessage 
= CFDataCopyHexString(message
); 
 142             secnoticeq("engine", "%s RAW%1d %@", sending 
? "send" : "recv", seqno
?2:0, hexMessage
); 
 144             secnoticeq("engine", "%s RAWx %@", sending 
? "send" : "recv", hexMessage
);  // we don't know vers of received msg here 
 146     CFReleaseSafe(hexMessage
); 
 150 // Peer state layout.  WRONG! It's an array now 
 151 // The peer state is an array. 
 152 // The first element of the array is a dictionary with any number of keys and 
 153 // values in it (for future expansion) such as changing the digest size or type 
 154 // or remembering boolean flags for a peers sake. 
 155 // The next three are special in that they are manifest digests with special 
 156 // meaning and rules as to how they are treated (These are dynamically updated 
 157 // based on database activity so they have a fully history of all changes made 
 158 // to the local db. The first is the manifest representing the pendingObjects 
 159 // to send to the other peer.  This is normally only ever appending to, and in 
 160 // particular with transactions originating from the Keychain API that affect 
 161 // syncable items will need to add the new objects digests to the pendingObjects list 
 162 // while adding the digests of any tombstones encountered to the extra list. 
 164 CFStringRef 
SOSEngineGetMyID(SOSEngineRef engine
) { 
 165     // TODO: this should not be needed 
 169 // TEMPORARY: Get the list of IDs for cleanup, this shouldn't be used instead it should iterate KVS. 
 170 CFArrayRef 
SOSEngineGetPeerIDs(SOSEngineRef engine
) { 
 171     return engine
->peerIDs
; 
 174 void SOSEngineClearCache(SOSEngineRef engine
){ 
 175     CFReleaseNull(engine
->manifestCache
); 
 176     CFReleaseNull(engine
->localMinusUnreadableDigest
); 
 177     dispatch_release(engine
->queue
);                   
 180 static SOSPeerRef 
SOSEngineCopyPeerWithMapEntry_locked(SOSEngineRef engine
, CFStringRef peerID
, CFTypeRef mapEntry
, CFErrorRef 
*error
) { 
 181     SOSPeerRef peer 
= NULL
; 
 182     if (mapEntry 
&& CFGetTypeID(mapEntry
) == SOSPeerGetTypeID()) { 
 183         // The mapEntry is an SOSPeer, so we're done. 
 184         peer 
= (SOSPeerRef
)CFRetain(mapEntry
); 
 186         // The mapEntry is a peerState, attempt to initialize a new 
 187         // peer iff peerID is in the set of trusted peerIDs 
 188         if (engine
->peerIDs 
&& CFArrayContainsValue(engine
->peerIDs
, CFRangeMake(0, CFArrayGetCount(engine
->peerIDs
)), peerID
)) { 
 189             CFErrorRef localError 
= NULL
; 
 190             peer 
= SOSPeerCreateWithState(engine
, peerID
, mapEntry
, &localError
); 
 192                 secerror("error inflating peer: %@: %@ from state: %@", peerID
, localError
, mapEntry
); 
 193                 CFReleaseNull(localError
); 
 194                 peer 
= SOSPeerCreateWithState(engine
, peerID
, NULL
, error
); 
 197                 // Replace the map entry with the inflated peer. 
 198                 CFDictionarySetValue(engine
->peerMap
, peerID
, peer
); 
 201             SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("peer: %@ is untrusted inflating not allowed"), peerID
); 
 207 static SOSPeerRef 
SOSEngineCopyPeerWithID_locked(SOSEngineRef engine
, CFStringRef peerID
, CFErrorRef 
*error
) { 
 208     CFTypeRef mapEntry 
= CFDictionaryGetValue(engine
->peerMap
, peerID
); 
 209     SOSPeerRef peer 
= NULL
; 
 211         peer 
= SOSEngineCopyPeerWithMapEntry_locked(engine
, peerID
, mapEntry
, error
); 
 214         SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("peer: %@ not found"), peerID
); 
 219 struct SOSEngineWithPeerContext 
{ 
 221     void (^with
)(SOSPeerRef peer
); 
 224 static void SOSEngineWithPeerMapEntry_locked(const void *peerID
, const void *mapEntry
, void *context
) { 
 225     struct SOSEngineWithPeerContext 
*ewp 
= context
; 
 226     SOSPeerRef peer 
= SOSEngineCopyPeerWithMapEntry_locked(ewp
->engine
, peerID
, mapEntry
, NULL
); 
 233 static void SOSEngineForEachPeer_locked(SOSEngineRef engine
, void (^with
)(SOSPeerRef peer
)) { 
 234     struct SOSEngineWithPeerContext ewp 
= { .engine 
= engine
, .with 
= with 
}; 
 235     CFDictionaryRef peerMapCopy 
= CFDictionaryCreateCopy(NULL
, engine
->peerMap
); 
 236     CFDictionaryApplyFunction(peerMapCopy
, SOSEngineWithPeerMapEntry_locked
, &ewp
); 
 237     CFRelease(peerMapCopy
); 
 240 static void SOSEngineWithBackupPeerMapEntry_locked(const void *peerID
, const void *mapEntry
, void *context
) { 
 241     struct SOSEngineWithPeerContext 
*ewp 
= context
; 
 242     // v0 backup peer is always in map but we only consider it a backup peer if it has a keybag. 
 243     if (SOSPeerMapEntryIsBackup(mapEntry
)) { 
 244         SOSPeerRef peer 
= SOSEngineCopyPeerWithMapEntry_locked(ewp
->engine
, peerID
, mapEntry
, NULL
); 
 252 static void SOSEngineForEachBackupPeer_locked(SOSEngineRef engine
, void (^with
)(SOSPeerRef peer
)) { 
 253     struct SOSEngineWithPeerContext ewp 
= { .engine 
= engine
, .with 
= with 
}; 
 254     CFDictionaryRef peerMapCopy 
= CFDictionaryCreateCopy(NULL
, engine
->peerMap
); 
 255     CFDictionaryApplyFunction(peerMapCopy
, SOSEngineWithBackupPeerMapEntry_locked
, &ewp
); 
 256     CFRelease(peerMapCopy
); 
 262 SOSManifestRef 
SOSEngineGetManifestForDigest(SOSEngineRef engine
, CFDataRef digest
) { 
 263     if (!engine
->manifestCache 
|| !digest
) return NULL
; 
 264     SOSManifestRef manifest 
= (SOSManifestRef
)CFDictionaryGetValue(engine
->manifestCache
, digest
); 
 265     if (!manifest
) return NULL
; 
 266     if (CFGetTypeID(manifest
) != SOSManifestGetTypeID()) { 
 267         secerror("dropping corrupt manifest for %@ from cache", digest
); 
 268         CFDictionaryRemoveValue(engine
->manifestCache
, digest
); 
 275 void SOSEngineAddManifest(SOSEngineRef engine
, SOSManifestRef manifest
) { 
 276     CFDataRef digest 
= SOSManifestGetDigest(manifest
, NULL
); 
 278         if (!engine
->manifestCache
) 
 279             engine
->manifestCache 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
 280         CFDictionaryAddValue(engine
->manifestCache
, digest
, manifest
); 
 284 CFDataRef 
SOSEnginePatchRecordAndCopyDigest(SOSEngineRef engine
, SOSManifestRef base
, SOSManifestRef removals
, SOSManifestRef additions
, CFErrorRef 
*error
) { 
 285     CFDataRef digest 
= NULL
; 
 286     SOSManifestRef manifest 
= SOSManifestCreateWithPatch(base
, removals
, additions
, error
); 
 288         SOSEngineAddManifest(engine
, manifest
); 
 289         digest 
= CFRetainSafe(SOSManifestGetDigest(manifest
, NULL
)); 
 291     CFReleaseSafe(manifest
); 
 295 SOSManifestRef 
SOSEngineCopyPersistedManifest(SOSEngineRef engine
, CFDictionaryRef persisted
, CFStringRef key
) { 
 296     return CFRetainSafe(SOSEngineGetManifestForDigest(engine
, asData(CFDictionaryGetValue(persisted
, key
), NULL
))); 
 299 CFMutableArrayRef 
SOSEngineCopyPersistedManifestArray(SOSEngineRef engine
, CFDictionaryRef persisted
, CFStringRef key
, CFErrorRef 
*error
) { 
 300     CFMutableArrayRef manifests 
= NULL
; 
 301     CFArrayRef digests 
= NULL
; 
 303     if (asArrayOptional(CFDictionaryGetValue(persisted
, key
), &digests
, error
)) 
 304         manifests 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
 305     if (digests
) CFArrayForEachC(digests
, digest
) { 
 306         SOSManifestRef manifest 
= SOSEngineGetManifestForDigest(engine
, digest
); 
 308             CFArrayAppendValue(manifests
, manifest
); 
 313 static CFDictionaryRef 
SOSEngineCopyEncodedManifestCache_locked(SOSEngineRef engine
, CFErrorRef 
*error
) { 
 314     CFMutableDictionaryRef mfc 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
 315     SOSEngineForEachPeer_locked(engine
, ^(SOSPeerRef peer
) { 
 316         SOSPeerAddManifestsInUse(peer
, mfc
); 
 322 static bool SOSEngineGCManifests_locked(SOSEngineRef engine
, CFErrorRef 
*error
) { 
 323     __block 
struct SOSDigestVector mdInCache 
= SOSDigestVectorInit
; 
 324     __block 
struct SOSDigestVector mdInUse 
= SOSDigestVectorInit
; 
 325     struct SOSDigestVector mdUnused 
= SOSDigestVectorInit
; 
 326     struct SOSDigestVector mdMissing 
= SOSDigestVectorInit
; 
 329     SOSEngineForEachPeer_locked(engine
, ^(SOSPeerRef peer
) { 
 330         SOSPeerMarkDigestsInUse(peer
, &mdInUse
); 
 333     if (engine
->manifestCache
) { 
 334         CFDictionaryForEach(engine
->manifestCache
, ^(const void *key
, const void *value
) { 
 335             CFDataRef digest 
= (CFDataRef
)key
; 
 337                 SOSDigestVectorAppend(&mdInCache
, CFDataGetBytePtr(digest
)); 
 340         // Delete unused manifests. 
 341         SOSDigestVectorDiff(&mdInCache
, &mdInUse
, &mdUnused
, &mdMissing
); 
 342         SOSManifestRef unused 
= SOSManifestCreateWithDigestVector(&mdUnused
, NULL
); 
 343         SOSManifestForEach(unused
, ^(CFDataRef digest
, bool *stop
) { 
 345                 CFDictionaryRemoveValue(engine
->manifestCache
, digest
); 
 347         CFReleaseSafe(unused
); 
 350     SOSDigestVectorFree(&mdInCache
); 
 351     SOSDigestVectorFree(&mdInUse
); 
 352     SOSDigestVectorFree(&mdUnused
); 
 353     SOSDigestVectorFree(&mdMissing
); 
 359 // End of Manifest cache 
 362 static bool SOSEngineGCPeerState_locked(SOSEngineRef engine
, CFErrorRef 
*error
) { 
 365     //require_quiet(ok = SOSEngineGCManifests_locked(engine, error), exit); 
 371 static CFMutableDictionaryRef 
SOSEngineCopyPeerState_locked(SOSEngineRef engine
, CFErrorRef 
*error
) { 
 372     CFMutableDictionaryRef peerState 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
 373     CFDictionaryForEach(engine
->peerMap
, ^(const void *key
, const void *value
) { 
 374         CFDictionaryRef state 
= NULL
; 
 375         if (value 
&& CFGetTypeID(value
) == SOSPeerGetTypeID()) { 
 376             CFErrorRef localError 
= NULL
; 
 378             state 
= SOSPeerCopyState((SOSPeerRef
)value
, &localError
); 
 380                 secnotice("engine", "%@ failed to encode peer: %@", key
, localError
); 
 381             CFReleaseNull(localError
); 
 382             // TODO: Potentially replace inflated peer with deflated peer in peerMap 
 384             // We have a deflated peer. 
 385             state 
= CFRetainSafe(value
); 
 389             CFDictionarySetValue(peerState
, key
, state
); 
 390             CFReleaseSafe(state
); 
 396 static CFDataRef 
SOSEngineCopyState(SOSEngineRef engine
, CFErrorRef 
*error
) { 
 397     CFDataRef der 
= NULL
; 
 398     CFMutableDictionaryRef state 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
 399     if (engine
->myID
) CFDictionarySetValue(state
, kSOSEngineIDKey
, engine
->myID
); 
 400     if (engine
->peerIDs
) CFDictionarySetValue(state
, kSOSEnginePeerIDsKey
, engine
->peerIDs
); 
 401     if (engine
->lastTraceDate
) CFDictionarySetValue(state
, kSOSEngineTraceDateKey
, engine
->lastTraceDate
); 
 402     CFTypeRef peerState 
= SOSEngineCopyPeerState_locked(engine
, error
); 
 403     if (peerState
) CFDictionarySetValue(state
, kSOSEnginePeerStateKey
, peerState
); 
 404     CFReleaseSafe(peerState
); 
 405     CFDictionaryRef mfc 
= SOSEngineCopyEncodedManifestCache_locked(engine
, error
); 
 407         CFDictionarySetValue(state
, kSOSEngineManifestCacheKey
, mfc
); 
 410     der 
= CFPropertyListCreateDERData(kCFAllocatorDefault
, state
, error
); 
 411     CFReleaseSafe(state
); 
 412     secnotice("engine", "%@", engine
); 
 416 static bool SOSEngineDoSave(SOSEngineRef engine
, SOSTransactionRef txn
, CFErrorRef 
*error
) { 
 417     CFDataRef derState 
= SOSEngineCopyState(engine
, error
); 
 418     bool ok 
= derState 
&& SOSDataSourceSetStateWithKey(engine
->dataSource
, txn
, kSOSEngineState
, kSecAttrAccessibleAlways
, derState
, error
); 
 419     CFReleaseSafe(derState
); 
 423 #define SOSENGINE_SAVE_TIMEOUT  (NSEC_PER_MSEC * 500ull) 
 424 #define SOSENGINE_SAVE_LEEWAY  (NSEC_PER_MSEC * 500ull) 
 425 #define SOSENGINE_SAVE_MAX_DELAY  (NSEC_PER_MSEC * 500ull) 
 427 #if !(TARGET_IPHONE_SIMULATOR) 
 428 static void SOSEngineShouldSave(SOSEngineRef engine
) { 
 429     if (engine
->save_timer
) { 
 430         // Possibly defer timer further up to engine->save_deadline 
 434     // Schedule the timer to fire on a concurrent queue, so we can follow 
 435     // the proper procedure of aquiring a dataSource and then engine queues. 
 436     engine
->save_timer 
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
, 0, 0, dispatch_get_global_queue(QOS_CLASS_DEFAULT
, 0)); 
 437     dispatch_source_set_event_handler(engine
->save_timer
, ^{ 
 438         CFErrorRef dsWithError 
= NULL
; 
 439         if (!SOSDataSourceWith(engine
->dataSource
, &dsWithError
, ^(SOSTransactionRef txn
, bool *commit
) { 
 440             dispatch_sync(engine
->queue
, ^{ 
 441                 CFErrorRef saveError 
= NULL
; 
 442                 if (!SOSEngineDoSave(engine
, txn
, &saveError
)) { 
 443                     secerrorq("Failed to save engine state: %@", saveError
); 
 444                     CFReleaseNull(saveError
); 
 448             secerrorq("Failed to open dataSource to save engine state: %@", dsWithError
); 
 449             CFReleaseNull(dsWithError
); 
 451         xpc_transaction_end(); 
 453     // Set the timer's fire time to now + SOSENGINE_SAVE_TIMEOUT seconds with a SOSENGINE_SAVE_LEEWAY fuzz factor. 
 454     dispatch_source_set_timer(engine
->save_timer
, 
 455                               dispatch_time(DISPATCH_TIME_NOW
, SOSENGINE_SAVE_TIMEOUT
), 
 456                               DISPATCH_TIME_FOREVER
, SOSENGINE_SAVE_LEEWAY
); 
 457     // Start a trasaction, then start the timer, the handler for the timer will end 
 459     xpc_transaction_begin(); 
 460     dispatch_resume(engine
->save_timer
); 
 464 static bool SOSEngineSave(SOSEngineRef engine
, SOSTransactionRef txn
, CFErrorRef 
*error
) { 
 465 #if (TARGET_IPHONE_SIMULATOR) 
 466     return SOSEngineDoSave(engine
, txn
, error
); 
 468     SOSEngineShouldSave(engine
); 
 473 static SOSManifestRef 
SOSEngineCreateManifestWithViewNameSet_locked(SOSEngineRef engine
, CFSetRef viewNameSet
, CFErrorRef 
*error
) { 
 474     // TODO: Potentially tell all changeTrackers to track manifests (    //forall ct do SOSChangeTrackerSetConcrete(ct, true); 
 475     // and read the entire dataSource and pass all objects though the filter here, instead of 
 476     // forcing the datasource to be able to do "smart" queries 
 477     return SOSDataSourceCopyManifestWithViewNameSet(engine
->dataSource
, viewNameSet
, error
); 
 480 static SOSChangeTrackerRef 
SOSEngineCopyChangeTrackerWithViewNameSet_locked(SOSEngineRef engine
, CFSetRef viewNameSet
, CFErrorRef 
*error
) { 
 481     SOSChangeTrackerRef ct 
= (SOSChangeTrackerRef
)CFDictionaryGetValue(engine
->viewNameSet2ChangeTracker
, viewNameSet
); 
 483         SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("no change tracker for view set %@"), viewNameSet
); 
 484     return CFRetainSafe(ct
); 
 487 static SOSManifestRef 
SOSEngineCopyManifestWithViewNameSet_locked(SOSEngineRef engine
, CFSetRef viewNameSet
, CFErrorRef 
*error
) { 
 488     SOSChangeTrackerRef ct 
= SOSEngineCopyChangeTrackerWithViewNameSet_locked(engine
, viewNameSet
, error
); 
 492     SOSManifestRef manifest 
= SOSChangeTrackerCopyManifest(ct
, NULL
); 
 494         manifest 
= SOSEngineCreateManifestWithViewNameSet_locked(engine
, viewNameSet
, error
); // Do the SQL query 
 495         SOSChangeTrackerSetManifest(ct
, manifest
); 
 501 SOSManifestRef 
SOSEngineCopyLocalPeerManifest_locked(SOSEngineRef engine
, SOSPeerRef peer
, CFErrorRef 
*error
) { 
 502     return SOSEngineCopyManifestWithViewNameSet_locked(engine
, SOSPeerGetViewNameSet(peer
), error
); 
 505 #define withViewAndBackup(VIEW)  do { with(VIEW); if (!isTomb) with(VIEW ## _tomb); } while(0) 
 508 // Invoke with once for each view an object is in. 
 509 // TODO: Move this function into the DataSource 
 510 static void SOSEngineObjectWithView(SOSEngineRef engine
, SOSObjectRef object
, void (^with
)(CFStringRef view
)) { 
 511     // Filter items into v0 only view here 
 512     SecDbItemRef item 
= (SecDbItemRef
)object
; // TODO: Layer violation, breaks tests 
 513     if (isDictionary(object
)) { 
 514         CFTypeRef isTombValue 
= CFDictionaryGetValue((CFDictionaryRef
)object
, kSecAttrTombstone
); 
 515         bool isTomb 
= isTombValue 
&& CFBooleanGetValue(isTombValue
); 
 516         // We are in the test just assume v0 and v2 views. 
 517         withViewAndBackup(kSOSViewKeychainV0
); 
 518     } else if (SecDbItemIsSyncableOrCorrupted(item
)) { 
 519         const SecDbClass 
*iclass 
= SecDbItemGetClass(item
); 
 520         CFTypeRef pdmn 
= SecDbItemGetCachedValueWithName(item
, kSecAttrAccessible
); 
 521         if ((iclass 
== &genp_class 
|| iclass 
== &inet_class 
|| iclass 
== &keys_class 
|| iclass 
== &cert_class
) 
 523             && (CFEqual(pdmn
, kSecAttrAccessibleWhenUnlocked
) 
 524                 || CFEqual(pdmn
, kSecAttrAccessibleAfterFirstUnlock
) 
 525                 || CFEqual(pdmn
, kSecAttrAccessibleAlways
) 
 526                 || CFEqual(pdmn
, kSecAttrAccessibleWhenUnlockedThisDeviceOnly
) 
 527                 || CFEqual(pdmn
, kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
) 
 528                 || CFEqual(pdmn
, kSecAttrAccessibleAlwaysThisDeviceOnly
))) 
 530             CFTypeRef tomb 
= SecDbItemGetCachedValueWithName(item
, kSecAttrTombstone
); 
 532             bool isTomb 
= (isNumber(tomb
) && CFNumberGetValue(tomb
, kCFNumberCharType
, &cvalue
) && cvalue 
== 1); 
 533             CFTypeRef viewHint 
= SecDbItemGetCachedValueWithName(item
, kSecAttrSyncViewHint
); 
 534             if (viewHint 
== NULL
) { 
 535                 if (iclass 
== &cert_class
) { 
 536                     withViewAndBackup(kSOSViewOtherSyncable
); 
 538                     if (!SecDbItemGetCachedValueWithName(item
, kSecAttrTokenID
)) { 
 539                         withViewAndBackup(kSOSViewKeychainV0
); 
 541                     CFTypeRef agrp 
= SecDbItemGetCachedValueWithName(item
, kSecAttrAccessGroup
); 
 542                     if (iclass 
== &keys_class 
&& CFEqualSafe(agrp
, CFSTR("com.apple.security.sos"))) { 
 543                         withViewAndBackup(kSOSViewiCloudIdentity
); 
 544                     } else if (CFEqualSafe(agrp
, CFSTR("com.apple.cfnetwork"))) { 
 545                         withViewAndBackup(kSOSViewAutofillPasswords
); 
 546                     } else if (CFEqualSafe(agrp
, CFSTR("com.apple.safari.credit-cards"))) { 
 547                         withViewAndBackup(kSOSViewSafariCreditCards
); 
 548                     } else if (iclass 
== &genp_class
) { 
 549                         if (CFEqualSafe(agrp
, CFSTR("apple")) && 
 550                             CFEqualSafe(SecDbItemGetCachedValueWithName(item
, kSecAttrService
), CFSTR("AirPort"))) { 
 551                             withViewAndBackup(kSOSViewWiFi
); 
 552                         } else if (CFEqualSafe(agrp
, CFSTR("com.apple.sbd"))) { 
 553                             withViewAndBackup(kSOSViewBackupBagV0
); 
 555                             withViewAndBackup(kSOSViewOtherSyncable
); // (genp) 
 558                         withViewAndBackup(kSOSViewOtherSyncable
); // (inet || keys) 
 564                     CFStringRef viewHintTomb 
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%@-tomb"), viewHint
); 
 567                         CFRelease(viewHintTomb
); 
 573         // TODO: general queries 
 576         CFArrayForEachC(engine
->views
, view
) { 
 577             bool inView 
= SOSViewQueryMatchItem(view
, item
); 
 579                 CFStringRef viewName 
= SOSViewCopyName(view
); 
 581                 CFReleaseSafe(viewName
); 
 589 // SOSChangeMapper - Helper for SOSEngineUpdateChanges_locked 
 591 struct SOSChangeMapper 
{ 
 593     SOSTransactionRef txn
; 
 594     SOSDataSourceTransactionPhase phase
; 
 595     SOSDataSourceTransactionSource source
; 
 596     CFMutableDictionaryRef ct2changes
; 
 599 static void SOSChangeMapperInit(struct SOSChangeMapper 
*cm
, SOSEngineRef engine
, SOSTransactionRef txn
, SOSDataSourceTransactionPhase phase
, SOSDataSourceTransactionSource source
) { 
 604     cm
->ct2changes 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
 607 static void SOSChangeMapperFree(struct SOSChangeMapper 
*cm
) { 
 608     CFReleaseSafe(cm
->ct2changes
); 
 611 static void SOSChangeMapperAppendObject(struct SOSChangeMapper 
*cm
, SOSChangeTrackerRef ct
, bool isAdd
, CFTypeRef object
) { 
 612     CFMutableArrayRef changes 
= (CFMutableArrayRef
)CFDictionaryGetValue(cm
->ct2changes
, ct
); 
 614         changes 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
 615         CFDictionarySetValue(cm
->ct2changes
, ct
, changes
); 
 616         CFReleaseSafe(changes
); 
 618     isAdd 
? SOSChangesAppendAdd(changes
, object
) : SOSChangesAppendDelete(changes
, object
); 
 621 static bool SOSChangeMapperIngestChange(struct SOSChangeMapper 
*cm
, bool isAdd
, CFTypeRef change
) { 
 622     bool someoneCares 
= false; 
 623     if (isData(change
)) { 
 624         // TODO: Reenable assertion once the tests have been updated 
 626         // We got a digest for a deleted object. Our dataSource probably couldn't find 
 627         // an object with this digest, probably because it went missing, or it was 
 628         // discovered to be corrupted. 
 629         // Tell all our changeTrackers about this digest since we don't know who might need it. 
 630         CFDictionaryForEach(cm
->engine
->viewNameSet2ChangeTracker
, ^(const void *viewNameSet
, const void *ct
) { 
 631             SOSChangeMapperAppendObject(cm
, (SOSChangeTrackerRef
)ct
, isAdd
, change
); 
 633         someoneCares 
= CFDictionaryGetCount(cm
->engine
->viewNameSet2ChangeTracker
); 
 635         // We got an object let's figure out which views it's in and schedule it for 
 636         // delivery to all changeTrackers interested in any of those views. 
 637         SOSObjectRef object 
= (SOSObjectRef
)change
; 
 638         CFMutableSetRef changeTrackerSet 
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
); 
 639         // First gather all the changeTrackers interested in this object (eleminating dupes by collecting them in a set) 
 640         SOSEngineObjectWithView(cm
->engine
, object
, ^(CFStringRef viewName
) { 
 641             const void *ctorset 
= CFDictionaryGetValue(cm
->engine
->viewName2ChangeTracker
, viewName
); 
 642             if (isSet(ctorset
)) { 
 643                 CFSetForEach((CFSetRef
)ctorset
, ^(const void *ct
) { CFSetAddValue(changeTrackerSet
, ct
); }); 
 644             } else if (ctorset
) { 
 645                 CFSetAddValue(changeTrackerSet
, ctorset
); 
 648         // Then append the object to the changes array in the ct2changes dictionary keyed by viewSet 
 649         CFSetForEach(changeTrackerSet
, ^(const void *ct
) { 
 650             SOSChangeMapperAppendObject(cm
, (SOSChangeTrackerRef
)ct
, isAdd
, object
); 
 652         someoneCares 
= CFSetGetCount(changeTrackerSet
); 
 653         CFReleaseSafe(changeTrackerSet
); 
 658 static bool SOSChangeMapperSend(struct SOSChangeMapper 
*cm
, CFErrorRef 
*error
) { 
 659     __block 
bool ok 
= true; 
 660     CFDictionaryForEach(cm
->ct2changes
, ^(const void *ct
, const void *changes
) { 
 661         ok 
&= SOSChangeTrackerTrackChanges((SOSChangeTrackerRef
)ct
, cm
->engine
, cm
->txn
, cm
->source
, cm
->phase
, (CFArrayRef
)changes
, error
); 
 666 static bool SOSEngineUpdateChanges_locked(SOSEngineRef engine
, SOSTransactionRef txn
, SOSDataSourceTransactionPhase phase
, SOSDataSourceTransactionSource source
, CFArrayRef changes
, CFErrorRef 
*error
) 
 668     secnoticeq("engine", "%@: %s %s %ld changes", engine
->myID
, phase 
== kSOSDataSourceTransactionWillCommit 
? "will-commit" : phase 
== kSOSDataSourceTransactionDidCommit 
? "did-commit" : "did-rollback", source 
== kSOSDataSourceSOSTransaction 
? "sos" : "api", CFArrayGetCount(changes
)); 
 671         case kSOSDataSourceTransactionDidRollback
: 
 672             ok 
&= SOSEngineLoad(engine
, error
); 
 674         case kSOSDataSourceTransactionWillCommit
: 
 675         case kSOSDataSourceTransactionDidCommit
: 
 677             struct SOSChangeMapper cm
; 
 678             SOSChangeMapperInit(&cm
, engine
, txn
, phase
, source
); 
 680             CFArrayForEachC(changes
, event
) { 
 681                 CFTypeRef deleted 
= NULL
; 
 682                 CFTypeRef inserted 
= NULL
; 
 683                 SecDbEventGetComponents(event
, &deleted
, &inserted
, error
); 
 685                     SOSChangeMapperIngestChange(&cm
, false, deleted
); 
 687                     bool someoneCares 
= SOSChangeMapperIngestChange(&cm
, true, inserted
); 
 688                     if (!someoneCares 
&& !isData(inserted
) && SecDbItemIsTombstone((SecDbItemRef
)inserted
) && !CFEqualSafe(SecDbItemGetValue((SecDbItemRef
)inserted
, &v7utomb
, NULL
), kCFBooleanTrue
)) { 
 689                         CFErrorRef localError 
= NULL
; 
 690                         // A tombstone was inserted but there is no changetracker that 
 692                         if (!SecDbItemDoDeleteSilently((SecDbItemRef
)inserted
, (SecDbConnectionRef
)txn
, &localError
)) { 
 693                             secerror("failed to delete tombstone %@ that no one cares about: %@", inserted
, localError
); 
 694                             CFReleaseNull(localError
); 
 700             ok 
&= SOSChangeMapperSend(&cm
, error
); 
 701             SOSChangeMapperFree(&cm
); 
 703             if (phase 
== kSOSDataSourceTransactionDidCommit
) { 
 704                 // We are being called outside a transaction, beware, that any 
 705                 // db changes we attempt to make here will cause deadlock! 
 707                 // Write SOSEngine and SOSPeer state to disk 
 708                 // TODO: Only do this if dirty 
 709                 ok 
&= SOSEngineSave(engine
, txn
, error
); 
 717 static void SOSEngineSetNotifyPhaseBlock(SOSEngineRef engine
) { 
 718     SOSDataSourceSetNotifyPhaseBlock(engine
->dataSource
, engine
->queue
, ^(SOSDataSourceRef ds
, SOSTransactionRef txn
, SOSDataSourceTransactionPhase phase
, SOSDataSourceTransactionSource source
, CFArrayRef changes
) { 
 719         CFErrorRef localError 
= NULL
; 
 720         if (!SOSEngineUpdateChanges_locked(engine
, txn
, phase
, source
, changes
, &localError
)) { 
 721             secerror("updateChanged failed: %@", localError
); 
 723         CFReleaseSafe(localError
); 
 727 #if 0 // TODO: update these checks 
 728 static void SOSEngineCircleChanged_sanitycheck(SOSEngineRef engine
, CFStringRef myPeerID
, CFArrayRef trustedPeers
, CFArrayRef untrustedPeers
) { 
 730     CFMutableArrayRef addedPeers 
= CFArrayCreateDifference(kCFAllocatorDefault
, trustedPeers
, engine
->peerIDs
); 
 731     CFMutableArrayRef deletedPeers 
= CFArrayCreateDifference(kCFAllocatorDefault
, engine
->peerIDs
, trustedPeers
); 
 732     CFMutableArrayRef addedUntrustedPeers 
= CFArrayCreateDifference(kCFAllocatorDefault
, untrustedPeers
, engine
->peerIDs
); 
 733     CFMutableArrayRef deletedUntrustedPeers 
= CFArrayCreateDifference(kCFAllocatorDefault
, engine
->peerIDs
, untrustedPeers
); 
 735     CFStringRef tpDesc 
= SOSPeerIDArrayCreateString(trustedPeers
); 
 736     CFStringRef apDesc 
= SOSPeerIDArrayCreateString(addedPeers
); 
 737     CFStringRef dpDesc 
= SOSPeerIDArrayCreateString(deletedPeers
); 
 738     CFStringRef aupDesc 
= SOSPeerIDArrayCreateString(addedUntrustedPeers
); 
 739     CFStringRef dupDesc 
= SOSPeerIDArrayCreateString(deletedUntrustedPeers
); 
 740     secnotice("engine", "trusted %@ added %@ removed %@ add ut: %@ rem ut: %@", tpDesc
, apDesc
, dpDesc
, aupDesc
, dupDesc
); 
 741     CFReleaseSafe(dupDesc
); 
 742     CFReleaseSafe(aupDesc
); 
 743     CFReleaseSafe(dpDesc
); 
 744     CFReleaseSafe(apDesc
); 
 745     CFReleaseSafe(tpDesc
); 
 748     // Ensure SOSAccount isn't giving us the runaround. 
 749     // Assert that trustedPeers, untrustedPeers and myPeerId are disjoint sets 
 751         CFMutableArrayRef allTrustedPeers 
= CFArrayCreateDifference(kCFAllocatorDefault
, trustedPeers
, untrustedPeers
); 
 752         assert(CFEqual(trustedPeers
, allTrustedPeers
)); 
 753         CFReleaseSafe(allTrustedPeers
); 
 754         assert(!CFArrayContainsValue(trustedPeers
, CFRangeMake(0, CFArrayGetCount(trustedPeers
)), myPeerID
)); 
 756     if (untrustedPeers
) { 
 757         CFMutableArrayRef allUntrustedPeers 
= CFArrayCreateDifference(kCFAllocatorDefault
, untrustedPeers
, trustedPeers
); 
 758         assert(CFEqual(untrustedPeers
, allUntrustedPeers
)); 
 759         CFReleaseSafe(allUntrustedPeers
); 
 760         assert(!CFArrayContainsValue(untrustedPeers
, CFRangeMake(0, CFArrayGetCount(trustedPeers
)), myPeerID
)); 
 763     CFReleaseNull(deletedUntrustedPeers
); 
 764     CFReleaseNull(addedUntrustedPeers
); 
 765     CFReleaseNull(deletedPeers
); 
 766     CFReleaseNull(addedPeers
); 
 768     // End of logging and asertions, actual code here. 
 772 static SOSChangeTrackerRef 
SOSReferenceAndGetChangeTracker(CFDictionaryRef lookup
, CFMutableDictionaryRef referenced
, CFSetRef viewNameSet
) { 
 773     SOSChangeTrackerRef ct 
= (SOSChangeTrackerRef
)CFDictionaryGetValue(referenced
, viewNameSet
); 
 775         ct 
= (SOSChangeTrackerRef
)CFDictionaryGetValue(lookup
, viewNameSet
); 
 777             SOSChangeTrackerResetRegistration(ct
); 
 778             CFDictionarySetValue(referenced
, viewNameSet
, ct
); 
 780             ct 
= SOSChangeTrackerCreate(kCFAllocatorDefault
, false, NULL
, NULL
); 
 781             CFDictionarySetValue(referenced
, viewNameSet
, ct
); 
 788 static CFStringRef 
CFStringCreateWithViewNameSet(CFSetRef vns
); 
 790 static void CFStringAppendPeerIDAndViews(CFMutableStringRef desc
, CFStringRef peerID
, CFSetRef vns
) { 
 792         CFStringRef vnsDesc 
= CFStringCreateWithViewNameSet(vns
); 
 793         CFStringAppendFormat(desc
, NULL
, CFSTR(" %@ (%@)"), peerID
, vnsDesc
); 
 794         CFReleaseSafe(vnsDesc
); 
 796         CFStringAppendFormat(desc
, NULL
, CFSTR(" %@ NULL"), peerID
); 
 800 // Must be called after updating viewNameSet2ChangeTracker 
 801 static void SOSEngineUpdateViewName2ChangeTracker(SOSEngineRef engine
) { 
 802     // Create the mapping from viewName -> ChangeTracker used for lookup during change notification 
 803     CFMutableDictionaryRef newViewName2ChangeTracker 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
 804     CFDictionaryForEach(engine
->viewNameSet2ChangeTracker
, ^(const void *viewNameSet
, const void *ct
) { 
 805         CFSetForEach(viewNameSet
, ^(const void *viewName
) { 
 806             const void *ctorset 
= NULL
; 
 807             if (CFDictionaryGetValueIfPresent(newViewName2ChangeTracker
, viewName
, &ctorset
)) { 
 808                 if (isSet(ctorset
)) { 
 809                     CFSetAddValue((CFMutableSetRef
)ctorset
, ct
); 
 810                 } else if (!CFEqual(ct
, ctorset
)) { 
 811                     CFMutableSetRef set 
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
); 
 812                     CFSetAddValue(set
, ctorset
); 
 813                     CFSetAddValue(set
, ct
); 
 814                     CFDictionaryReplaceValue(newViewName2ChangeTracker
, viewName
, set
); 
 818                 CFDictionarySetValue(newViewName2ChangeTracker
, viewName
, ct
); 
 822     CFAssignRetained(engine
->viewName2ChangeTracker
, newViewName2ChangeTracker
); 
 825 static void SOSEngineSetBackupBag(SOSEngineRef engine
, SOSObjectRef bagItem
); 
 827 // This is called only if we are in a circle and we should listen for keybag changes 
 828 static void SOSEngineRegisterBackupBagV0Tracker(SOSEngineRef engine
, CFMutableDictionaryRef newViewNameSet2ChangeTracker
, CFMutableStringRef desc
) { 
 829     SOSChangeTrackerRef bbct 
= SOSReferenceAndGetChangeTracker(engine
->viewNameSet2ChangeTracker
, newViewNameSet2ChangeTracker
, SOSViewsGetV0BackupBagViewSet()); 
 830     SOSChangeTrackerRegisterChangeUpdate(bbct
, ^bool(SOSChangeTrackerRef ct
, SOSEngineRef engine
, SOSTransactionRef txn
, SOSDataSourceTransactionSource source
, SOSDataSourceTransactionPhase phase
, CFArrayRef changes
, CFErrorRef 
*error
) { 
 832         CFArrayForEachC(changes
, change
) { 
 833             CFTypeRef object 
= NULL
; 
 834             bool isAdd 
= SOSChangeGetObject(change
, &object
); 
 835             SecDbItemRef dbi 
= (SecDbItemRef
)object
; 
 836             if (!isData(object
) && 
 837                 CFEqualSafe(SecDbItemGetCachedValueWithName(dbi
, kSecAttrService
), CFSTR("SecureBackupService")) && 
 838                 CFEqualSafe(SecDbItemGetCachedValueWithName(dbi
, kSecAttrAccessible
), kSecAttrAccessibleWhenUnlocked
) && 
 839                 CFEqualSafe(SecDbItemGetCachedValueWithName(dbi
, kSecAttrAccount
), CFSTR("SecureBackupPublicKeybag"))) { 
 840                 SOSEngineSetBackupBag(engine
, isAdd 
? (SOSObjectRef
)object 
: NULL
); 
 847 static void SOSEngineReferenceBackupPeer(SOSEngineRef engine
, CFStringRef peerID
, CFSetRef viewNameSet
, CFDataRef keyBag
, CFMutableDictionaryRef newViewNameSet2ChangeTracker
, CFMutableDictionaryRef newPeerMap
) { 
 848     CFTypeRef oldEntry 
= CFDictionaryGetValue(engine
->peerMap
, peerID
); 
 849     CFTypeRef newEntry 
= SOSPeerOrStateSetViewsKeyBagAndCreateCopy(oldEntry
, viewNameSet
, keyBag
); 
 851         if (isDictionary(newEntry
)) { 
 852             // Backup peers, are always inflated 
 853             CFAssignRetained(newEntry
, SOSPeerCreateWithState(engine
, peerID
, newEntry
, NULL
)); 
 854             // If !oldEntry this is an edge (first creation of a peer). 
 856                 SOSPeerKeyBagDidChange((SOSPeerRef
)newEntry
); 
 859         CFDictionarySetValue(newPeerMap
, peerID
, newEntry
); 
 863             SOSChangeTrackerRef ct 
= SOSReferenceAndGetChangeTracker(engine
->viewNameSet2ChangeTracker
, newViewNameSet2ChangeTracker
, viewNameSet
); 
 865             SOSChangeTrackerUpdatesChanges child 
= Block_copy(^bool(SOSChangeTrackerRef ct
, SOSEngineRef engine
, SOSTransactionRef txn
, SOSDataSourceTransactionSource source
, SOSDataSourceTransactionPhase phase
, CFArrayRef changes
, CFErrorRef 
*error
) { 
 866                 return SOSPeerDataSourceWillChange((SOSPeerRef
)newEntry
, SOSEngineGetDataSource(engine
), source
, changes
, error
); 
 869             SOSChangeTrackerRegisterChangeUpdate(ct
, child
); 
 870             Block_release(child
); 
 875 static void SOSEngineReferenceSyncPeer(SOSEngineRef engine
, CFStringRef peerID
, CFSetRef viewNameSet
, CFMutableDictionaryRef newViewNameSet2ChangeTracker
, CFMutableDictionaryRef newPeerMap
) { 
 876     CFTypeRef newEntry 
= SOSPeerOrStateSetViewsKeyBagAndCreateCopy(CFDictionaryGetValue(engine
->peerMap
, peerID
), viewNameSet
, NULL
); 
 878         SOSChangeTrackerRef ct 
= SOSReferenceAndGetChangeTracker(engine
->viewNameSet2ChangeTracker
, newViewNameSet2ChangeTracker
, viewNameSet
); 
 879         // Standard peer, inflated on demand 
 880         SOSChangeTrackerUpdatesManifests trackManifest
; 
 881         if (isDictionary(newEntry
)) { 
 882             // Uninflated peer, inflate on first notification. 
 883             trackManifest 
= ^bool(SOSChangeTrackerRef ct
, SOSEngineRef engine
, SOSTransactionRef txn
, SOSDataSourceTransactionSource source
, SOSDataSourceTransactionPhase phase
, SOSManifestRef removals
, SOSManifestRef additions
, CFErrorRef 
*error
) { 
 884                 CFErrorRef localError 
= NULL
; 
 885                 SOSPeerRef peer 
= SOSEngineCopyPeerWithID_locked(engine
, peerID
, &localError
); 
 888                     secerror("%@: peer failed to inflate: %@", peerID
, localError
); 
 889                     CFReleaseSafe(localError
); 
 892                     ok 
= SOSPeerDataSourceWillCommit(peer
, source
, removals
, additions
, error
); 
 898             // Inflated peer, just forward the changes to the peer 
 899             trackManifest 
= ^bool(SOSChangeTrackerRef ct
, SOSEngineRef engine
, SOSTransactionRef txn
, SOSDataSourceTransactionSource source
, SOSDataSourceTransactionPhase phase
, SOSManifestRef removals
, SOSManifestRef additions
, CFErrorRef 
*error
) { 
 900                 return SOSPeerDataSourceWillCommit((SOSPeerRef
)newEntry
, source
, removals
, additions
, error
); 
 903         SOSChangeTrackerUpdatesManifests trackManifestCopy 
= Block_copy(trackManifest
); 
 904         SOSChangeTrackerRegisterManifestUpdate(ct
, trackManifestCopy
); 
 905         Block_release(trackManifestCopy
); 
 907         CFDictionarySetValue(newPeerMap
, peerID
, newEntry
); 
 913 static void SOSEngineReferenceTrustedPeer(SOSEngineRef engine
, SOSPeerMetaRef peerMeta
, CFMutableDictionaryRef newViewNameSet2ChangeTracker
, CFMutableDictionaryRef newPeerMap
, CFMutableArrayRef peerIDs
, CFMutableStringRef desc
) { 
 914     CFSetRef viewNameSet 
= NULL
; 
 915     CFDataRef keyBag 
= NULL
; 
 916     CFStringRef peerID 
= SOSPeerMetaGetComponents(peerMeta
, &viewNameSet
, &keyBag
, NULL
); 
 917     // We trust peerID so append it to peerIDs 
 918     CFArrayAppendValue(peerIDs
, peerID
); 
 919     if (desc
) CFStringAppendPeerIDAndViews(desc
, peerID
, viewNameSet
); 
 920     // Update the viewNameSet for this peer, to appease tests, default to a viewset of the V0 view. 
 922         viewNameSet 
= SOSViewsGetV0ViewSet(); 
 924     // Always inflate backup peers, since they need to register with their changeTrackers right away. 
 926         SOSEngineReferenceBackupPeer(engine
, peerID
, viewNameSet
, keyBag
, newViewNameSet2ChangeTracker
, newPeerMap
); 
 928         SOSEngineReferenceSyncPeer(engine
, peerID
, viewNameSet
, newViewNameSet2ChangeTracker
, newPeerMap
); 
 932 static CFDataRef 
SOSEngineLoadV0KeyBag(SOSEngineRef engine
, CFErrorRef 
*error
) { 
 933     // Return the keybag for the given peerID. 
 936      kSecAttrAccessGroup ==> CFSTR("com.apple.sbd") 
 937      kSecAttrAccessible  ==> kSecAttrAccessibleWhenUnlocked 
 938      kSecAttrAccount     ==> CFSTR("SecureBackupPublicKeybag") 
 939      kSecAttrService     ==> CFSTR("SecureBackupService") 
 942     CFMutableDictionaryRef keys 
= CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault
, 
 943                                             kSecAttrAccessGroup
, CFSTR("com.apple.sbd"), 
 944                                             kSecAttrAccount
, CFSTR("SecureBackupPublicKeybag"), 
 945                                             kSecAttrService
, CFSTR("SecureBackupService"), 
 946                                             kSecAttrAccessible
, kSecAttrAccessibleWhenUnlocked
, 
 947                                             kSecAttrSynchronizable
, kCFBooleanTrue
, 
 950     CFDataRef keybag 
= engine
->dataSource
->dsCopyItemDataWithKeys(engine
->dataSource
, keys
, error
); 
 956 static void SOSEngineReferenceBackupV0Peer(SOSEngineRef engine
, CFMutableDictionaryRef newViewNameSet2ChangeTracker
, CFMutableDictionaryRef newPeerMap
, CFMutableArrayRef newPeerIDs
, CFMutableStringRef desc
) { 
 957     SOSPeerRef backupPeer 
= (SOSPeerRef
)CFDictionaryGetValue(engine
->peerMap
, kSOSViewKeychainV0_tomb
); 
 958     CFDataRef bag 
= NULL
; 
 959     if (backupPeer 
&& CFGetTypeID(backupPeer
) == SOSPeerGetTypeID()) { 
 960         bag 
= SOSPeerGetKeyBag(backupPeer
); 
 962         CFErrorRef localError 
= NULL
; 
 963         if (!(bag 
= SOSEngineLoadV0KeyBag(engine
, &localError
))) { 
 964             secnotice("engine", "No keybag found for v0 backup peer: %@", localError
); 
 965             CFReleaseSafe(localError
); 
 968     SOSEngineReferenceBackupPeer(engine
, kSOSViewKeychainV0_tomb
, SOSViewsGetV0BackupViewSet(), bag
, newViewNameSet2ChangeTracker
, newPeerMap
); 
 971 static void SOSEngineReferenceTrustedPeers(SOSEngineRef engine
, CFMutableDictionaryRef newViewNameSet2ChangeTracker
, CFMutableDictionaryRef newPeerMap
, CFMutableArrayRef newPeerIDs
, CFArrayRef trustedPeerMetas
, CFMutableStringRef desc
) { 
 972     // Then update the views for all trusted peers and add them to newPeerMap. 
 973     if (trustedPeerMetas 
!= NULL 
&& CFArrayGetCount(trustedPeerMetas
) != 0) { 
 974         if (desc
) CFStringAppend(desc
, CFSTR(" trusted")); 
 975         // Remake engine->peerIDs 
 976         SOSPeerMetaRef peerMeta
; 
 977         CFArrayForEachC(trustedPeerMetas
, peerMeta
) { 
 978             SOSEngineReferenceTrustedPeer(engine
, peerMeta
, newViewNameSet2ChangeTracker
, newPeerMap
, newPeerIDs
, desc
); 
 983 static void SOSEngineReferenceUntrustedPeers(SOSEngineRef engine
, CFMutableDictionaryRef newPeerMap
, CFArrayRef untrustedPeerMetas
, CFMutableStringRef description
) { 
 984     // Copy any untrustedPeers to newPeerMap as well if we have a state 
 985     // for them, if not no big deal.  We also serialize all the untrustedPeers 
 986     // since they don't need to be deserializable 
 987     if (untrustedPeerMetas 
!= NULL 
&& CFArrayGetCount(untrustedPeerMetas
) != 0) { 
 988         if (description
) CFStringAppend(description
, CFSTR(" untrusted")); 
 989         SOSPeerMetaRef peerMeta
; 
 990         CFArrayForEachC(untrustedPeerMetas
, peerMeta
) { 
 991             CFSetRef views 
= NULL
; 
 992             CFStringRef peerID 
= SOSPeerMetaGetComponents(peerMeta
, &views
, NULL
, NULL
); 
 993             if (description
) CFStringAppendPeerIDAndViews(description
, peerID
, views
); 
 994             CFSetRef nviews 
= NULL
; 
 996                 views 
= nviews 
= CFSetCreate(kCFAllocatorDefault
, NULL
, 0, &kCFTypeSetCallBacks
); 
 997             CFTypeRef newEntry 
= SOSPeerOrStateSetViewsAndCopyState(CFDictionaryGetValue(engine
->peerMap
, peerID
), views
); 
 998             CFReleaseSafe(nviews
); 
1000                 CFDictionarySetValue(newPeerMap
, peerID
, newEntry
); 
1001                 CFReleaseSafe(newEntry
); 
1007 static void SOSEngineReferenceChangeTrackers(SOSEngineRef engine
, CFArrayRef trustedPeerMetas
, CFArrayRef untrustedPeerMetas
, CFMutableStringRef desc
) { 
1008     CFMutableArrayRef newPeerIDs 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
1009     CFMutableDictionaryRef newPeerMap 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
1010     CFMutableDictionaryRef newViewNameSet2ChangeTracker 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
1013         // We have an engineID => in a circle (with 0 or more peers) 
1014         // Ensure we have a v0 backup peer and it's listening for backup bag changes 
1015         SOSEngineReferenceBackupV0Peer(engine
, newViewNameSet2ChangeTracker
, newPeerMap
, newPeerIDs
, desc
); 
1016         SOSEngineRegisterBackupBagV0Tracker(engine
, newViewNameSet2ChangeTracker
, desc
); 
1018     SOSEngineReferenceTrustedPeers(engine
, newViewNameSet2ChangeTracker
, newPeerMap
, newPeerIDs
, trustedPeerMetas
, desc
); 
1019     SOSEngineReferenceUntrustedPeers(engine
, newPeerMap
, untrustedPeerMetas
, desc
); 
1021     CFAssignRetained(engine
->peerIDs
, newPeerIDs
); 
1022     CFAssignRetained(engine
->peerMap
, newPeerMap
); 
1023     CFAssignRetained(engine
->viewNameSet2ChangeTracker
, newViewNameSet2ChangeTracker
); 
1024     SOSEngineUpdateViewName2ChangeTracker(engine
); 
1027 // Return true iff peers or views changed 
1028 static bool SOSEngineSetPeers_locked(SOSEngineRef engine
, SOSPeerMetaRef myPeerMeta
, CFArrayRef trustedPeerMetas
, CFArrayRef untrustedPeerMetas
) { 
1029     CFErrorRef error 
= NULL
; 
1030     CFSetRef myViews 
= NULL
; 
1031     CFDataRef myKeyBag 
= NULL
; 
1032     CFMutableStringRef desc 
= CFStringCreateMutableCopy(kCFAllocatorDefault
, 0, CFSTR("me")); 
1033     CFStringRef myPeerID 
= myPeerMeta 
? SOSPeerMetaGetComponents(myPeerMeta
, &myViews
, &myKeyBag
, &error
) : NULL
; 
1034     if (desc
) CFStringAppendPeerIDAndViews(desc
, myPeerID
, myViews
); 
1036     CFRetainAssign(engine
->myID
, myPeerID
); 
1038     // Remake engine->peerMap from both trusted and untrusted peers 
1039     SOSEngineReferenceChangeTrackers(engine
, trustedPeerMetas
, untrustedPeerMetas
, desc
); 
1041     secnotice("engine", "%@", desc
); 
1042     CFReleaseSafe(desc
); 
1046 static void SOSEngineApplyPeerState(SOSEngineRef engine
, CFDictionaryRef peerStateMap
) { 
1047     if (peerStateMap
) CFDictionaryForEach(peerStateMap
, ^(const void *peerID
, const void *peerState
) { 
1048         CFTypeRef mapEntry 
= CFDictionaryGetValue(engine
->peerMap
, peerID
); 
1049         if (mapEntry 
&& CFGetTypeID(mapEntry
) == SOSPeerGetTypeID()) { 
1050             // Update the state of any already inflated peers 
1051             SOSPeerRef peer 
= (SOSPeerRef
)mapEntry
; 
1052             CFErrorRef localError 
= NULL
; 
1053             if (!SOSPeerSetState(peer
, engine
, peerState
, &localError
)) { 
1054                 CFStringRef stateHex 
= NULL
; 
1055                 stateHex 
= CFDataCopyHexString(peerState
); 
1056                 secerror("peer: %@: bad state: %@ in engine state: %@", peerID
, localError
, stateHex
); 
1057                 CFReleaseSafe(stateHex
); 
1058                 CFReleaseNull(localError
); 
1059                 // Possibly ask for an ensurePeerRegistration so we have a good list or peers again. 
1062             // Just record the state for non inflated peers for now. 
1063             CFDictionarySetValue(engine
->peerMap
, peerID
, peerState
); 
1068 static void SOSEngineSynthesizePeerMetas(SOSEngineRef engine
, CFMutableArrayRef trustedPeersMetas
, CFMutableArrayRef untrustedPeers
) { 
1069     CFSetRef trustedPeerSet 
= engine
->peerIDs 
? CFSetCreateCopyOfArrayForCFTypes(engine
->peerIDs
) : NULL
; 
1070     CFDictionaryForEach(engine
->peerMap
, ^(const void *peerID
, const void *peerState
) { 
1071         SOSPeerMetaRef meta 
= NULL
; 
1072         if (peerState 
&& CFGetTypeID(peerState
) == SOSPeerGetTypeID()) { 
1073             SOSPeerRef peer 
= (SOSPeerRef
)peerState
; 
1074             meta 
= SOSPeerMetaCreateWithComponents(peerID
, SOSPeerGetViewNameSet(peer
), SOSPeerGetKeyBag(peer
)); 
1076             // We don't need to add the meta for the backup case, since 
1077             //   SOSEngineReferenceBackupV0Peer will do the right thing 
1078             if (!CFEqualSafe(peerID
, kSOSViewKeychainV0_tomb
)) { 
1079                 meta 
= SOSPeerMetaCreateWithState(peerID
, peerState
); 
1082         // Any peer in peerStateMap that is not in trustedPeers is an untrustedPeer unless it's the v0 backup peer 
1083         if ((trustedPeerSet 
&& CFSetContainsValue(trustedPeerSet
, peerID
)) || CFEqualSafe(peerID
, kSOSViewKeychainV0_tomb
)) { 
1085                 CFArrayAppendValue(trustedPeersMetas
, meta
); 
1088             CFArrayAppendValue(untrustedPeers
, peerID
); 
1090         CFReleaseNull(meta
); 
1092     CFReleaseNull(trustedPeerSet
); 
1095 static void SOSEngineSetBackupBag(SOSEngineRef engine
, SOSObjectRef bagItem
) { 
1096     CFMutableStringRef desc 
= NULL
; 
1097     SOSPeerRef backupPeer 
= SOSEngineCopyPeerWithID_locked(engine
, kSOSViewKeychainV0_tomb
, NULL
); 
1098     CFDataRef keybag 
= NULL
; 
1100         keybag 
= SecDbItemGetValue((SecDbItemRef
)bagItem
, &v6v_Data
, NULL
); 
1103     // Since SOSPeerSetKeyBag() doesn't notify on the edge from NULL->initial keybag, since 
1104     // that is the right behaviour for non v0 backup peers, we need to do it here for the v0 peer. 
1105     bool hadBag 
= SOSPeerGetKeyBag(backupPeer
); 
1106     SOSPeerSetKeyBag(backupPeer
, keybag
); 
1108         SOSPeerKeyBagDidChange(backupPeer
); 
1110     CFReleaseSafe(backupPeer
); 
1112     CFMutableArrayRef untrustedPeerMetas 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
1113     CFMutableArrayRef trustedPeersMetas 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
1114     SOSEngineSynthesizePeerMetas(engine
, trustedPeersMetas
, untrustedPeerMetas
); 
1115     SOSEngineReferenceChangeTrackers(engine
, trustedPeersMetas
, untrustedPeerMetas
, desc
); 
1116     CFReleaseSafe(trustedPeersMetas
); 
1117     CFReleaseSafe(untrustedPeerMetas
); 
1120 #define SECONDS_PER_DAY  (86400.0) 
1122 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) 
1123 #define TRACE_INTERVAL (7 * SECONDS_PER_DAY) 
1124 #elif (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR) 
1125 #define TRACE_INTERVAL (1 * SECONDS_PER_DAY) 
1128 #ifdef TRACE_INTERVAL 
1129 static void SOSEngineCloudKeychainTrace(SOSEngineRef engine
, CFAbsoluteTime now
) { 
1130     CFAssignRetained(engine
->lastTraceDate
, CFDateCreate(kCFAllocatorDefault
, now
)); 
1131     CFIndex num_peers 
= engine
->peerIDs 
? 1 + CFArrayGetCount(engine
->peerIDs
) : 1; 
1132     SOSManifestRef manifest 
= SOSEngineCopyManifestWithViewNameSet_locked(engine
, SOSViewsGetV0ViewSet(), NULL
); 
1134         manifest 
= SOSDataSourceCopyManifestWithViewNameSet(engine
->dataSource
, SOSViewsGetV0ViewSet(), NULL
); 
1135     size_t num_items 
= SOSManifestGetCount(manifest
); 
1136     CFReleaseSafe(manifest
); 
1138     struct _SecServerKeyStats genpStats 
= { }; 
1139     struct _SecServerKeyStats inetStats 
= { }; 
1140     struct _SecServerKeyStats keysStats 
= { }; 
1142     _SecServerGetKeyStats(&genp_class
, &genpStats
); 
1143     _SecServerGetKeyStats(&inet_class
, &inetStats
); 
1144     _SecServerGetKeyStats(&keys_class
, &keysStats
); 
1146     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), ^{ 
1147         CloudKeychainTrace(num_peers
, num_items
, &genpStats
, &inetStats
, &keysStats
); 
1152 static void SOSEngineCloudKeychainTraceIfNeeded(SOSEngineRef engine
) { 
1153 #ifdef TRACE_INTERVAL 
1156     CFAbsoluteTime now 
= CFAbsoluteTimeGetCurrent(); 
1157     if (engine
->lastTraceDate
) { 
1158         CFAbsoluteTime lastTraceTime 
= CFDateGetAbsoluteTime(engine
->lastTraceDate
); 
1159         if ((now 
- lastTraceTime
) >= TRACE_INTERVAL
) { 
1160             SOSEngineCloudKeychainTrace(engine
, now
); 
1163         SOSEngineCloudKeychainTrace(engine
, now
); 
1168 // Restore the in-memory state of engine from saved state loaded from the db 
1169 static bool SOSEngineSetState(SOSEngineRef engine
, CFDataRef state
, CFErrorRef 
*error
) { 
1170     __block 
bool ok 
= true; 
1172         CFMutableDictionaryRef dict 
= NULL
; 
1173         const uint8_t *der 
= CFDataGetBytePtr(state
); 
1174         const uint8_t *der_end 
= der 
+ CFDataGetLength(state
); 
1175         ok 
= der 
= der_decode_dictionary(kCFAllocatorDefault
, kCFPropertyListMutableContainers
, (CFDictionaryRef 
*)&dict
, error
, der
, der_end
); 
1176         if (der 
&& der 
!= der_end
) { 
1177             ok 
= SOSErrorCreate(kSOSErrorDecodeFailure
, error
, NULL
, CFSTR("trailing %td bytes at end of state"), der_end 
- der
); 
1179             CFReleaseNull(engine
->manifestCache
); 
1180             CFMutableDictionaryRef mfc 
= (CFMutableDictionaryRef
)CFDictionaryGetValue(dict
, kSOSEngineManifestCacheKey
); 
1182                 engine
->manifestCache 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
1183                 CFDictionaryForEach(mfc
, ^(const void *key
, const void *value
) { 
1184                     CFDataRef data 
= (CFDataRef
)value
; 
1186                         SOSManifestRef mf 
= SOSManifestCreateWithData(data
, NULL
); 
1188                             CFDictionarySetValue(engine
->manifestCache
, key
, mf
); 
1193             CFMutableArrayRef untrustedPeers 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
1194             CFMutableArrayRef trustedPeersMetas 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
1195             CFRetainAssign(engine
->peerIDs
, asArray(CFDictionaryGetValue(dict
, kSOSEnginePeerIDsKey
), NULL
)); 
1196             CFRetainAssign(engine
->lastTraceDate
, asDate(CFDictionaryGetValue(dict
, kSOSEngineTraceDateKey
), NULL
)); 
1197             SOSEngineApplyPeerState(engine
, asDictionary(CFDictionaryGetValue(dict
, kSOSEnginePeerStateKey
), NULL
)); 
1198             SOSEngineSynthesizePeerMetas(engine
, trustedPeersMetas
, untrustedPeers
); 
1199             SOSEngineSetPeers_locked(engine
, (CFStringRef
)CFDictionaryGetValue(dict
, kSOSEngineIDKey
), 
1200                                      trustedPeersMetas
, untrustedPeers
); 
1201             CFReleaseNull(trustedPeersMetas
); 
1202             CFReleaseNull(untrustedPeers
); 
1204         CFReleaseNull(dict
); 
1206     secnotice("engine", "%@", engine
); 
1210 static bool SOSEngineLoad(SOSEngineRef engine
, CFErrorRef 
*error
) { 
1211     CFDataRef state 
= SOSDataSourceCopyStateWithKey(engine
->dataSource
, kSOSEngineState
, kSecAttrAccessibleAlways
, error
); 
1212     bool ok 
= state 
&& SOSEngineSetState(engine
, state
, error
); 
1213     CFReleaseSafe(state
); 
1217 static CFStringRef accountStatusFileName 
= CFSTR("accountStatus.plist"); 
1219 static bool SOSEngineCircleChanged_locked(SOSEngineRef engine
, SOSPeerMetaRef myPeerMeta
, CFArrayRef trustedPeers
, CFArrayRef untrustedPeers
) { 
1220     // Sanity check params 
1221 //    SOSEngineCircleChanged_sanitycheck(engine, myPeerID, trustedPeers, untrustedPeers); 
1223     // Transform from SOSPeerInfoRefs to CFDictionaries with the info we want per peer. 
1224     // Or, Tell the real SOSPeerRef what the SOSPeerInfoRef is and have it copy out the data it needs. 
1225     bool peersOrViewsChanged 
= SOSEngineSetPeers_locked(engine
, myPeerMeta
, trustedPeers
, untrustedPeers
); 
1227     // Run though all peers and only cache manifests for peers we still have 
1228     CFErrorRef localError 
= NULL
; 
1229     if (!SOSEngineGCPeerState_locked(engine
, &localError
)) { 
1230         secerror("SOSEngineGCPeerState_locked failed: %@", localError
); 
1231         CFReleaseNull(localError
); 
1233     return peersOrViewsChanged
; 
1236 // Initialize the engine if a load fails.  Basically this is our first time setup 
1237 static bool SOSEngineInit(SOSEngineRef engine
, CFErrorRef 
*error
) { 
1239     secnotice("engine", "new engine for datasource named %@", SOSDataSourceGetName(engine
->dataSource
)); 
1240     CFAssignRetained(engine
->peerMap
, CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
)); 
1241     CFAssignRetained(engine
->viewNameSet2ChangeTracker
, CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
)); 
1242     CFAssignRetained(engine
->viewName2ChangeTracker
, CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
)); 
1243     CFReleaseNull(engine
->manifestCache
); 
1244     CFReleaseNull(engine
->peerIDs
); 
1245     // TODO: We shouldn't need to load the backup bag if there was no engine 
1246     // state (load failed), since that means there was no circle nor were we an applicant. 
1248     // Set up change trackers so we know when a backup peer needs to be created? 
1249     // no, since myID is not set, we are not in a circle, so no need to back up 
1250     SOSEngineSetPeers_locked(engine
, NULL
, NULL
, NULL
); 
1254 // Called by our DataSource in its constructor 
1255 SOSEngineRef 
SOSEngineCreate(SOSDataSourceRef dataSource
, CFErrorRef 
*error
) { 
1256     SOSEngineRef engine 
= NULL
; 
1257     engine 
= CFTypeAllocate(SOSEngine
, struct __OpaqueSOSEngine
, kCFAllocatorDefault
); 
1258     engine
->dataSource 
= dataSource
; 
1259     engine
->queue 
= dispatch_queue_create("engine", DISPATCH_QUEUE_SERIAL
); 
1261     engine
->peerMap 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
1262     engine
->viewNameSet2ChangeTracker 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
1263     engine
->viewName2ChangeTracker 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
1264     //engine->syncCompleteQueue = NULL; 
1265     engine
->syncCompleteListeners 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
1266     CFErrorRef engineError 
= NULL
; 
1267     if (!SOSEngineLoad(engine
, &engineError
)) { 
1268         secwarning("engine failed load state starting with nothing %@", engineError
); 
1269         CFReleaseNull(engineError
); 
1270         if (!SOSEngineInit(engine
, error
)) { 
1271             secerror("engine failed to initialze %@ giving up", error 
? *error 
: NULL
); 
1274         // Successfully loaded engine state, let's trace if we haven't in a while 
1275         SOSEngineCloudKeychainTraceIfNeeded(engine
); 
1277     SOSEngineSetNotifyPhaseBlock(engine
); 
1281 // --- Called from off the queue, need to move to on the queue 
1283 static void SOSEngineDoOnQueue(SOSEngineRef engine
, dispatch_block_t action
) 
1285     dispatch_sync(engine
->queue
, action
); 
1288 static bool SOSEngineDoTxnOnQueue(SOSEngineRef engine
, CFErrorRef 
*error
, void(^transaction
)(SOSTransactionRef txn
, bool *commit
)) 
1290     return SOSDataSourceWith(engine
->dataSource
, error
, ^(SOSTransactionRef txn
, bool *commit
) { 
1291         SOSEngineDoOnQueue(engine
, ^{ transaction(txn
, commit
); }); 
1296 // MARK: SOSEngine API 
1299 void SOSEngineDispose(SOSEngineRef engine
) { 
1300     // NOOP Engines stick around forever to monitor dataSource changes. 
1303 void SOSEngineForEachPeer(SOSEngineRef engine
, void (^with
)(SOSPeerRef peer
)) { 
1304     SOSEngineDoOnQueue(engine
, ^{ 
1305         SOSEngineForEachPeer_locked(engine
, with
); 
1309 static void SOSEngineForEachBackupPeer(SOSEngineRef engine
, void (^with
)(SOSPeerRef peer
)) { 
1310     SOSEngineDoOnQueue(engine
, ^{ 
1311         SOSEngineForEachBackupPeer_locked(engine
, with
); 
1316 /* Handle incoming message from peer p.  Return false if there was an error, true otherwise. */ 
1317 bool SOSEngineHandleMessage_locked(SOSEngineRef engine
, CFStringRef peerID
, SOSMessageRef message
, 
1318                                    SOSTransactionRef txn
, bool *commit
, bool *somethingChanged
, CFErrorRef 
*error
) { 
1319     SOSPeerRef peer 
= SOSEngineCopyPeerWithID_locked(engine
, peerID
, error
); 
1320     if (!peer
) return false; 
1321     CFStringRef peerDesc 
= NULL
; 
1322     SOSManifestRef localManifest 
= NULL
; 
1323     SOSManifestRef allAdditions 
= NULL
; 
1324     SOSManifestRef unwanted 
= NULL
; 
1325     SOSManifestRef confirmed 
= NULL
; 
1326     SOSManifestRef base 
= NULL
; 
1327     SOSManifestRef confirmedRemovals 
= NULL
, confirmedAdditions 
= NULL
; 
1328     __block 
struct SOSDigestVector receivedObjects 
= SOSDigestVectorInit
; 
1329     __block 
struct SOSDigestVector unwantedObjects 
= SOSDigestVectorInit
; 
1331     // Check for unknown criticial extensions in the message, and handle 
1332     // any other extensions we support 
1333     __block 
bool ok 
= true; 
1334     CFMutableArrayRef changes 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
1336     require_action_quiet(peer
, exit
, ok 
= SOSErrorCreate(errSecParam
, error
, NULL
, CFSTR("Couldn't create peer with Engine for %@"), peerID
)); 
1337     peerDesc 
= CFCopyDescription(peer
); 
1339     SOSMessageWithExtensions(message
, true, ^(CFDataRef oid
, bool isCritical
, CFDataRef extension
, bool *stop
) { 
1340         // OMFG a Critical extension what shall I do! 
1341         ok 
= SOSErrorCreate(kSOSErrorNotReady
, error
, NULL
, CFSTR("Unknown criticial extension in peer message")); 
1344     require_quiet(ok
, exit
); 
1346     // Merge Objects from the message into our DataSource. 
1347     // Should we move the transaction to the SOSAccount level? 
1348     // TODO: Filter incoming objects 
1349     //if (!SOSDataSourceForEachObjectInViewSet(engine->dataSource, pendingObjects, SOSPeerGetViewNameSet(peer), error, ^void(CFDataRef key, SOSObjectRef object, bool *stop) { 
1350     require_quiet(ok 
&= SOSMessageWithSOSObjects(message
, engine
->dataSource
, error
, ^(SOSObjectRef peersObject
, bool *stop
) { 
1351         CFDataRef digest 
= SOSObjectCopyDigest(engine
->dataSource
, peersObject
, error
); 
1355             secerror("%@ peer sent bad object: %@, rolling back changes", SOSPeerGetID(peer
), error 
? *error 
: NULL
); 
1358         SOSDigestVectorAppend(&receivedObjects
, CFDataGetBytePtr(digest
)); 
1359         SOSObjectRef mergedObject 
= NULL
; 
1360         SOSMergeResult mr 
= SOSDataSourceMergeObject(engine
->dataSource
, txn
, peersObject
, &mergedObject
, error
); 
1361         // TODO: If the mr is kSOSMergeLocalObject most of the time (or all of the time), 
1362         // consider asking the peer to stop sending us objects, and send it objects instead. 
1363         ok 
&= (mr 
!= kSOSMergeFailure
); 
1367             // 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. 
1368             secerror("%@ SOSDataSourceMergeObject failed %@ rolling back changes", SOSPeerGetID(peer
), error 
? *error 
: NULL
); 
1369         } else if (mr
==kSOSMergePeersObject 
|| mr
==kSOSMergeCreatedObject
) { 
1370             *somethingChanged 
= true; 
1372             // mr == kSOSMergeLocalObject 
1373             if (!CFEqual(mergedObject
, peersObject
)) { 
1374                 // Record this object as something we don't want peer to ever send us again.  By adding it to 
1375                 // unwantedObjects we'll falsely claim to peer we have it until they tell us they don't have it anymore. 
1376                 SOSDigestVectorAppend(&unwantedObjects
, CFDataGetBytePtr(digest
)); 
1378             // Ensure localObject is in local manifest (possible corruption) by posting an update when we are done. 
1379             SOSChangesAppendAdd(changes
, mergedObject
); 
1381         CFReleaseSafe(mergedObject
); 
1382         CFReleaseSafe(digest
); 
1384     struct SOSDigestVector dvunion 
= SOSDigestVectorInit
; 
1385     SOSDigestVectorSort(&receivedObjects
); 
1386     SOSDigestVectorUnionSorted(SOSManifestGetDigestVector(SOSMessageGetAdditions(message
)), &receivedObjects
, &dvunion
); 
1387     allAdditions 
= SOSManifestCreateWithDigestVector(&dvunion
, error
); 
1388     SOSDigestVectorFree(&receivedObjects
); 
1389     SOSDigestVectorFree(&dvunion
); 
1391     unwanted 
= SOSManifestCreateWithDigestVector(&unwantedObjects
, error
); 
1392     SOSDigestVectorFree(&unwantedObjects
); 
1394     if (CFArrayGetCount(changes
)) { 
1395         // NOTE: This is always notifiying of all additions that end up choosing local, which should be rare, since we shouldn't 
1396         // be receiving objects we already have.   When we do we tell ourselves to add them all again so our views will properly 
1397         // reflect that we actually have these objects if we didn't already. 
1399         // Ensure any objects that we received and have locally already are actually in our local manifest 
1400         SOSEngineUpdateChanges_locked(engine
, txn
, kSOSDataSourceTransactionDidCommit
, kSOSDataSourceSOSTransaction
, changes
, error
); 
1402     CFReleaseSafe(changes
); 
1404     // ---- Don't use local or peer manifests from above this line, 
1405     // ---- since commiting the SOSDataSourceWith transaction might change them --- 
1407     // Take a snapshot of our dataSource's local manifest. 
1408     require_quiet(ok 
= localManifest 
= SOSEngineCopyLocalPeerManifest_locked(engine
, peer
, error
), exit
); 
1410     CFDataRef baseDigest 
= SOSMessageGetBaseDigest(message
); 
1411     CFDataRef proposedDigest 
= SOSMessageGetProposedDigest(message
); 
1414     // I believe this is no longer needed now that we have eliminated extra, 
1415     // since this is handled below once we get a confirmed manifest from our 
1418     // If we just received a L00 reset pendingObjects to localManifest 
1419     if (!baseDigest 
&& !proposedDigest
) { 
1420         // TODO: This is definitely busted for v0 peers since v0 peers always send a 
1421         // L00 (ManifestDigestMessage as an ack) whereas in v2 this is a protocol restart 
1422         // However if we can still find a confirmed manifest below we probably 
1423         // don't want to do this even for v2. 
1424         // Also I don't think we will ever send a ManifestMessage right now in 
1425         // response to a ManifestDigest 
1426         SOSPeerSetPendingObjects(peer
, localManifest
); 
1427         secnoticeq("engine", "%@:%@ SOSPeerSetPendingObjects: %@", engine
->myID
, peerID
, localManifest
); 
1431     base 
= SOSPeerCopyManifestForDigest(peer
, baseDigest
); 
1432     confirmed 
= SOSPeerCopyManifestForDigest(peer
, SOSMessageGetSenderDigest(message
)); 
1434         if (SOSManifestGetCount(SOSMessageGetRemovals(message
)) || SOSManifestGetCount(allAdditions
)) { 
1435             if (base 
|| !baseDigest
) { 
1436                 confirmed 
= SOSManifestCreateWithPatch(base
, SOSMessageGetRemovals(message
), allAdditions
, error
); 
1439                 confirmedRemovals 
= CFRetainSafe(SOSMessageGetRemovals(message
)); 
1440                 confirmedAdditions 
= CFRetainSafe(allAdditions
); 
1442         } else if (baseDigest
) { 
1443             confirmed 
= CFRetainSafe(base
); 
1444             secerror("%@:%@ Protocol error send L00 - figure out later base: %@", engine
->myID
, peerID
, base
); 
1447     secnoticeq("engine", "%@:%@ confirmed: %@ base: %@", engine
->myID
, peerID
, confirmed
, base
); 
1449         ok 
&= SOSManifestDiff(SOSPeerGetConfirmedManifest(peer
), confirmed
, &confirmedRemovals
, &confirmedAdditions
, error
); 
1450         if (SOSManifestGetCount(SOSMessageGetRemovals(message
))) 
1451             CFAssignRetained(confirmedRemovals
, SOSManifestCreateUnion(confirmedRemovals
, SOSMessageGetRemovals(message
), error
)); 
1453     if (SOSManifestGetCount(confirmedRemovals
) || SOSManifestGetCount(confirmedAdditions
) || SOSManifestGetCount(unwanted
)) 
1454         ok 
&= SOSPeerDidReceiveRemovalsAndAdditions(peer
, confirmedRemovals
, confirmedAdditions
, unwanted
, localManifest
, error
); 
1455     // TODO: We should probably remove the if below and always call SOSPeerSetConfirmedManifest, 
1456     // since having a NULL confirmed will force us to send a manifest message to get in sync again. 
1458         SOSPeerSetConfirmedManifest(peer
, confirmed
); 
1459     else if (SOSPeerGetConfirmedManifest(peer
)) { 
1460         secnoticeq("engine", "%@:%@ unable to find confirmed in %@, sync protocol reset", engine
->myID
, peer
, message
); 
1462         SOSPeerSetConfirmedManifest(peer
, NULL
); 
1463         //SOSPeerSetSendObjects(peer, true); 
1466     // ---- SendObjects and extra->pendingObjects promotion dance ---- 
1468     // The first block of code below sets peer.sendObjects to true when we receive a L00 and the second block 
1469     // moves extra to pendingObjects once we receive a confirmed manifest in or after the L00. 
1470     if (!baseDigest 
&& !proposedDigest
) { 
1471         SOSPeerSetSendObjects(peer
, true); 
1474     if (0 /* confirmed && SOSPeerSendObjects(peer) */) { 
1475         SOSManifestRef allExtra 
= NULL
; 
1476         ok 
&= SOSManifestDiff(confirmed
, localManifest
, NULL
, &allExtra
, error
); 
1477         secnoticeq("engine", "%@:%@ confirmed %@ (re)setting O:%@", engine
->myID
, SOSPeerGetID(peer
), confirmed
, allExtra
); 
1478         SOSPeerSetPendingObjects(peer
, allExtra
); 
1479         CFReleaseSafe(allExtra
); 
1483     secnotice("engine", "recv %@:%@ %@", engine
->myID
, SOSPeerGetID(peer
), message
); 
1484     secnotice("peer", "recv %@ -> %@", peerDesc
, peer
); 
1486     CFReleaseNull(base
); 
1487     CFReleaseSafe(confirmed
); 
1488     CFReleaseSafe(localManifest
); 
1489     CFReleaseSafe(peerDesc
); 
1490     CFReleaseSafe(allAdditions
); 
1491     CFReleaseSafe(unwanted
); 
1492     CFReleaseSafe(confirmedRemovals
); 
1493     CFReleaseSafe(confirmedAdditions
); 
1494     CFReleaseSafe(peer
); 
1498 static CFDataRef 
SOSEngineCopyObjectDER(SOSEngineRef engine
, SOSObjectRef object
, CFErrorRef 
*error
) { 
1499     CFDataRef der 
= NULL
; 
1500     CFDictionaryRef plist 
= SOSObjectCopyPropertyList(engine
->dataSource
, object
, error
); 
1502         der 
= CFPropertyListCreateDERData(kCFAllocatorDefault
, plist
, error
); 
1511             +-----------------------------+_ 
1515            _+=============================+    } L 
1519        /    +============================== 
1522       \     |        M       +------------| 
1526           \_+-------------+---------------+ 
1538 static bool SOSAppendRemoveToPatch(CFTypeRef remove
, CFMutableDictionaryRef patch
, CFErrorRef 
*error
) { 
1541 static bool SOSAppendAddToPatch(CFTypeRef add
, CFMutableDictionaryRef patch
, CFErrorRef 
*error
) { 
1544 static bool SOSAppendDiffToPatch(CFTypeRef left
, CFTypeRef right
, CFMutableDictionaryRef patch
, CFErrorRef 
*error
) { 
1546     if (!left 
&& right
) { 
1547         SOSAppendAddToPatch(right
, patch
, error
); 
1548     } else if (left 
&& !right
) { 
1549         SOSAppendRemoveToPatch(left
, patch
, error
); 
1550     } else if (left 
&& right
) { 
1551         CFTypeID ltype 
= CFGetTypeID(left
); 
1552         CFTypeID rtype 
= CFGetTypeID(right
); 
1553         if (ltype 
== rtype
) { 
1554             if (CFArrayGetTypeID() == ltype
) { 
1555                 ok 
= SecError(errSecParam
, error
, CFSTR("unsupported type array"), ltype
); 
1556             } else if (CFBooleanGetTypeID 
== ltype
) { 
1557                 ok 
= SecError(errSecParam
, error
, CFSTR("unsupported type boolean"), ltype
); 
1558             } else if (CFDataGetTypeID 
== ltype
) { 
1559                 ok 
= SecError(errSecParam
, error
, CFSTR("unsupported type data"), ltype
); 
1560             } else if (CFDictionaryGetTypeID 
== ltype
) { 
1561                 __block CFMutableDictionaryRef leftnotright 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
1562                 __block CFMutableDictionaryRef rightnotleft 
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, right
); 
1564                 CFDictionaryForEach(left
, ^(const void *key
, const void *lvalue
) { 
1565                     const void *rvalue 
= NULL
; 
1566                     if (CFDictionaryGetValueIfPresent(right
, key
, &rvalue
)) { 
1567                         CFDictionaryRemoveValue(rightnotleft
, key
); 
1569                         CFMutableDictionaryRef subpatch 
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
); 
1570                         CFDictionaryAddValue(patch
, key
, subpatch
); 
1571                         SOSAppendDiffToPatch(lvalue
, rvalue
, subpatch
, error
); 
1572                         CFReleaseSafe(subpatch
); 
1574                         CFDictionaryAddValue(leftnotright
, key
, lvalue
); 
1577                 // Proccess leftnotright and rightnotleft 
1578                 CFReleaseSafe(leftnotright
); 
1579                 CFReleaseSafe(rightnotleft
); 
1580             } else if (SOSManifestGetTypeID 
== ltype
) { 
1581                 SOSManifestRef removed 
= NULL
, added 
= NULL
; 
1582                 ok 
&= SOSManifestDiff(left
, right
, &removed
, &added
, error
); 
1583                 if (SOSManifestGetCount(removed
) || SOSManifestGetCount(added
)) { 
1584                     SOSAppendDiffToPatch(lvalue
, rvalue
, subpatch
, error
); 
1585                     CFStringAppend(, <#CFStringRef appendedString#>) 
1587                 CFReleaseSafe(removed
); 
1588                 CFReleaseSafe(added
); 
1589             } else if (CFNumberGetTypeID 
== ltype
) { 
1590                 ok 
= SecError(errSecParam
, error
, CFSTR("unsupported type number"), ltype
); 
1591             } else if (CFSetGetTypeID 
== ltype
) { 
1592                 ok 
= SecError(errSecParam
, error
, CFSTR("unsupported type set"), ltype
); 
1593             } else if (CFStringGetTypeID 
== ltype
) { 
1594                 ok 
= SecError(errSecParam
, error
, CFSTR("unsupported type string"), ltype
); 
1596                 ok 
= SecError(errSecParam
, error
, CFSTR("unknown type %lu"), ltype
); 
1599     } else if (!left 
&& !right
) { 
1605 static __unused 
bool SOSEngineCheckPeerIntegrity(SOSEngineRef engine
, SOSPeerRef peer
, CFErrorRef 
*error
) { 
1607     //static CFMutableDictionaryRef p2amtu; 
1608     if (!engine
->p2amtu
) 
1609         engine
->p2amtu 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
1610     CFDictionaryRef amtu 
= CFDictionaryGetValue(engine
->p2amtu
, SOSPeerGetID(peer
)); 
1614     SOSManifestRef L 
= SOSEngineCopyLocalPeerManifest_locked(engine
, peer
, error
); 
1615     SOSManifestRef T 
= SOSPeerGetPendingObjects(peer
); 
1616     SOSManifestRef C 
= SOSPeerGetConfirmedManifest(peer
); 
1617     SOSManifestRef U 
= SOSPeerGetUnwantedManifest(peer
); 
1620     SOSManifestRef CunionU 
= SOSManifestCreateUnion(C
, U
, error
); 
1621     SOSManifestRef S 
= SOSManifestCreateIntersection(L
, CunionU
, error
); 
1623     SOSManifestRef AunionT 
= NULL
, MunionU 
= NULL
; 
1624     SOSManifestDiff(L
, C
, &AunionT
, &MunionU
, error
); 
1626     SOSManifestRef A 
= SOSManifestCreateComplement(T
, AunionT
, error
); 
1627     SOSManifestRef M 
= SOSManifestCreateComplement(U
, MunionU
, error
); 
1629     SOSManifestRef SunionAunionT 
= SOSManifestCreateUnion(S
, AunionT
, error
); 
1630     SOSManifestRef SunionMunionU 
= SOSManifestCreateUnion(S
, MunionU
, error
); 
1632     SOSManifestRef AintersectM 
= SOSManifestCreateIntersection(A
, M
, error
); 
1633     SOSManifestRef AintersectS 
= SOSManifestCreateIntersection(A
, S
, error
); 
1634     SOSManifestRef AintersectT 
= SOSManifestCreateIntersection(A
, T
, error
); 
1635     SOSManifestRef AintersectU 
= SOSManifestCreateIntersection(A
, U
, error
); 
1636     SOSManifestRef MintersectS 
= SOSManifestCreateIntersection(M
, S
, error
); 
1637     SOSManifestRef MintersectT 
= SOSManifestCreateIntersection(M
, T
, error
); 
1638     SOSManifestRef MintersectU 
= SOSManifestCreateIntersection(M
, U
, error
); 
1639     SOSManifestRef SintersectT 
= SOSManifestCreateIntersection(S
, T
, error
); 
1640     SOSManifestRef SintersectU 
= SOSManifestCreateIntersection(S
, U
, error
); 
1641     SOSManifestRef TintersectU 
= SOSManifestCreateIntersection(T
, U
, error
); 
1644     CFDictionaryRef newAmtu 
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, CFSTR("A"), A
, CFSTR("M"), M
, CFSTR("T"), T
, CFSTR("U") U
, NULL
); 
1645     CFDictionarySetValue(engine
->p2amtu
, SOSPeerGetID(peer
), newAmtu
); 
1646     CFMutableStringRef amtuChanges 
= CFStringCreateMutable(kCFAllocatorDefault
, 0); 
1647     SOSAppendDiffToString(amtu
, newAmtu
, amtuChanges
); 
1648     secnotice("engine", "%@: %@", SOSPeerGetID(peer
), amtuChanges
); 
1651 #define SOSASSERT(e)      (__builtin_expect(!(e), 0) ? secnotice("engine", "state-assertion %s", #e), assert(e) : (void)0) 
1653     SOSASSERT(L 
? CFEqual(L
, SunionAunionT
) : SOSManifestGetCount(SunionAunionT
) == 0); 
1654     SOSASSERT(C 
? CFEqual(C
, SunionMunionU
) : SOSManifestGetCount(SunionMunionU
) == 0); 
1656     SOSASSERT(SOSManifestGetCount(AintersectM
) == 0); 
1657     SOSASSERT(SOSManifestGetCount(AintersectS
) == 0); 
1658     SOSASSERT(SOSManifestGetCount(AintersectT
) == 0); 
1659     SOSASSERT(SOSManifestGetCount(AintersectU
) == 0); 
1660     SOSASSERT(SOSManifestGetCount(MintersectS
) == 0); 
1661     SOSASSERT(SOSManifestGetCount(MintersectT
) == 0); 
1662     SOSASSERT(SOSManifestGetCount(MintersectU
) == 0); 
1663     SOSASSERT(SOSManifestGetCount(SintersectT
) == 0); 
1664     SOSASSERT(SOSManifestGetCount(SintersectU
) == 0); 
1665     SOSASSERT(SOSManifestGetCount(TintersectU
) == 0); 
1667     CFReleaseSafe(AintersectM
); 
1668     CFReleaseSafe(AintersectS
); 
1669     CFReleaseSafe(AintersectT
); 
1670     CFReleaseSafe(AintersectU
); 
1671     CFReleaseSafe(MintersectS
); 
1672     CFReleaseSafe(MintersectT
); 
1673     CFReleaseSafe(MintersectU
); 
1674     CFReleaseSafe(SintersectT
); 
1675     CFReleaseSafe(SintersectU
); 
1676     CFReleaseSafe(TintersectU
); 
1678     CFReleaseSafe(AunionT
); 
1679     CFReleaseSafe(MunionU
); 
1685     //CFReleaseSafe(T); // Get 
1686     //CFReleaseSafe(U); // Get 
1687     //CFReleaseSafe(C); // Get 
1692 void SOSEngineSetSyncCompleteListener(SOSEngineRef engine
, CFStringRef peerID
, dispatch_block_t notify_block
) { 
1693     dispatch_block_t copy 
= Block_copy(notify_block
); 
1694     SOSEngineDoOnQueue(engine
, ^{ 
1696             CFDictionarySetValue(engine
->syncCompleteListeners
, peerID
, copy
); 
1698             CFDictionaryRemoveValue(engine
->syncCompleteListeners
, peerID
); 
1701     CFReleaseNull(copy
); 
1704 void SOSEngineSetSyncCompleteListenerQueue(SOSEngineRef engine
, dispatch_queue_t notify_queue
) { 
1705     SOSEngineDoOnQueue(engine
, ^{ 
1706         engine
->syncCompleteQueue 
= notify_queue
; 
1710 static void SOSEngineCompletedSyncWithPeer(SOSEngineRef engine
, SOSPeerRef peer
) { 
1711     dispatch_block_t notify 
= CFDictionaryGetValue(engine
->syncCompleteListeners
, SOSPeerGetID(peer
)); 
1712     if (notify 
&& engine
->syncCompleteQueue
) 
1713         dispatch_async(engine
->syncCompleteQueue
, notify
); 
1715     // Delete dictionary entry? 
1719 CFDataRef 
SOSEngineCreateMessage_locked(SOSEngineRef engine
, SOSPeerRef peer
, 
1720                                         CFErrorRef 
*error
, SOSEnginePeerMessageSentBlock 
*sent
) { 
1721     SOSManifestRef local 
= SOSEngineCopyLocalPeerManifest_locked(engine
, peer
, error
); 
1722     __block SOSMessageRef message 
= SOSMessageCreate(kCFAllocatorDefault
, SOSPeerGetMessageVersion(peer
), error
); 
1723     SOSManifestRef confirmed 
= SOSPeerGetConfirmedManifest(peer
); 
1724     SOSManifestRef pendingObjects 
= SOSPeerGetPendingObjects(peer
); 
1725     SOSManifestRef objectsSent 
= NULL
; 
1726     SOSManifestRef proposed 
= NULL
; 
1727     SOSManifestRef allMissing 
= NULL
; 
1728     SOSManifestRef allExtra 
= NULL
; 
1729     SOSManifestRef extra 
= NULL
; 
1730     SOSManifestRef excessPending 
= NULL
; 
1731     SOSManifestRef missing 
= NULL
; 
1732     SOSManifestRef unwanted 
= SOSPeerGetUnwantedManifest(peer
); 
1733     SOSManifestRef excessUnwanted 
= NULL
; 
1734     CFDataRef result 
= NULL
; 
1736     // Given (C, L, T, U) compute (T, U, M, A) 
1740     // U \ (C \ L) => EU 
1741     // T \ (L \ C) => ET 
1742     // And assert that both EU and ET are empty and if not remove them from U and T respectively 
1743     SOSManifestDiff(confirmed
, local
, &allMissing
, &allExtra
, error
); 
1744     SOSManifestDiff(allExtra
, pendingObjects
, &extra
, &excessPending
, error
); 
1745     if (SOSManifestGetCount(excessPending
)) { 
1746         // T \ (L \ C) => excessPending (items both in L and C or in neither that are still pending) 
1747         // Can only happen if a member of T was removed from L without us having a chance to update T 
1748         secerror("%@ ASSERTION FAILURE purging excess pendingObjects: %@", peer
, excessPending
); 
1749         SOSManifestRef newPendingObjects 
= SOSManifestCreateComplement(excessPending
, pendingObjects
, error
); 
1750         SOSPeerSetPendingObjects(peer
, newPendingObjects
); 
1751         CFReleaseSafe(newPendingObjects
); 
1752         pendingObjects 
= SOSPeerGetPendingObjects(peer
); 
1754     SOSManifestDiff(allMissing
, unwanted
, &missing
, &excessUnwanted
, error
); 
1755     if (SOSManifestGetCount(excessUnwanted
)) { 
1756         // U \ (C \ L) => excessUnwanted (items both in L and C or in neither that are still unwanted) 
1757         // Can only happen if a member of U was added to L without us having a chance to update U. 
1758         // Since U only contains items the conflict resolver rejected, this implies L somehow got rolled back 
1759         // The other option (and more likely) is a member of U was removed from C and not from U. 
1760         secerror("%@ ASSERTION FAILURE purging excess unwanted: %@", peer
, excessUnwanted
); 
1761         SOSManifestRef newUnwanted 
= SOSManifestCreateComplement(excessUnwanted
, unwanted
, error
); 
1762         SOSPeerSetUnwantedManifest(peer
, newUnwanted
); 
1763         CFReleaseSafe(newUnwanted
); 
1764         unwanted 
= SOSPeerGetUnwantedManifest(peer
); 
1767     CFReleaseNull(allExtra
); 
1768     CFReleaseNull(excessPending
); 
1769     CFReleaseNull(allMissing
); 
1770     CFReleaseNull(excessUnwanted
); 
1772     secnoticeq("engine", "%@:%@: send state for peer [%s%s%s][%s%s] P:%zu, E:%zu, M:%zu U:%zu", engine
->myID
, SOSPeerGetID(peer
), 
1774                confirmed 
? "C":"0", 
1775                pendingObjects 
? "P":"0", 
1776                SOSPeerSendObjects(peer
) ? "O":"o", 
1777                SOSPeerMustSendMessage(peer
) ? "S":"s", 
1778                SOSManifestGetCount(pendingObjects
), 
1779                SOSManifestGetCount(extra
), 
1780                SOSManifestGetCount(missing
), 
1781                SOSManifestGetCount(unwanted
) 
1785         // TODO: Because of not letting things terminate while we have extra left 
1786         // we might send objects when we didn't need to, but there is always an 
1787         // extra roundtrip required for objects that we assume the other peer 
1788         // should have already. 
1789         // TODO: If there are extra objects left, calling this function is not 
1790         // idempotent we should check if pending is what we are about to send and not send anything in this case. 
1791         if (SOSManifestGetCount(pendingObjects
) == 0 && SOSManifestGetCount(extra
) == 0) 
1792             SOSPeerSetSendObjects(peer
, false); 
1794         // If we aren't missing anything, we've gotten all their data, so we're sync even if they haven't seen ours. 
1795         if (missing 
&& SOSManifestGetCount(missing
) == 0) { 
1796             SOSEngineCompletedSyncWithPeer(engine
, peer
); 
1799         if (CFEqualSafe(local
, SOSPeerGetProposedManifest(peer
)) && !SOSPeerMustSendMessage(peer
)) { 
1801             if (CFEqual(confirmed
, local
)) { 
1802                 secnoticeq("engine", "synced <No MSG> %@:%@", engine
->myID
,  peer
); 
1803             } else if (SOSManifestGetCount(pendingObjects
) == 0 /* TODO: No entries moved from extra to pendingObjects. */ 
1804                        && SOSManifestGetCount(missing
) == 0) { 
1805                 secnoticeq("engine", "waiting <MSG not resent> %@:%@ extra: %@", engine
->myID
, peer
, extra
); 
1810                 CFReleaseSafe(local
); 
1811                 CFReleaseSafe(message
); 
1812                 CFReleaseNull(extra
); 
1813                 CFReleaseNull(missing
); 
1814                 return CFDataCreate(kCFAllocatorDefault
, NULL
, 0); 
1818         if (SOSManifestGetCount(pendingObjects
)) { 
1819             // If we have additions and we need to send objects, do so. 
1820             __block 
size_t objectsSize 
= 0; 
1821             __block 
struct SOSDigestVector dv 
= SOSDigestVectorInit
; 
1822             CFMutableArrayRef changes 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
1823             if (!SOSDataSourceForEachObject(engine
->dataSource
, pendingObjects
, error
, ^void(CFDataRef key
, SOSObjectRef object
, bool *stop
) { 
1824                 CFErrorRef localError 
= NULL
; 
1825                 CFDataRef digest 
= NULL
; 
1826                 CFDataRef der 
= NULL
; 
1828                     const uint8_t *d 
= CFDataGetBytePtr(key
); 
1829                     secnoticeq("engine", "%@:%@ object %02X%02X%02X%02X dropping from manifest: not found in datasource", 
1830                                engine
->myID
, SOSPeerGetID(peer
), d
[0], d
[1], d
[2], d
[3]); 
1831                     SOSChangesAppendDelete(changes
, key
); 
1832                 } else if (!(der 
= SOSEngineCopyObjectDER(engine
, object
, &localError
)) 
1833                            || !(digest 
= SOSObjectCopyDigest(engine
->dataSource
, object
, &localError
))) { 
1834                     if (SecErrorGetOSStatus(localError
) == errSecDecode
) { 
1835                         // Decode error, we need to drop these objects from our manifests 
1836                         const uint8_t *d 
= CFDataGetBytePtr(key
); 
1837                         secnoticeq("engine", "%@:%@ object %02X%02X%02X%02X dropping from manifest: %@", 
1838                             engine
->myID
, SOSPeerGetID(peer
), d
[0], d
[1], d
[2], d
[3], localError
); 
1839                         SOSChangesAppendDelete(changes
, key
); 
1840                         CFRelease(localError
); 
1842                         // Stop iterating and propagate out all other errors. 
1843                         const uint8_t *d 
= CFDataGetBytePtr(key
); 
1844                         secnoticeq("engine", "%@:%@ object %02X%02X%02X%02X in SOSDataSourceForEachObject: %@", 
1845                             engine
->myID
, SOSPeerGetID(peer
), d
[0], d
[1], d
[2], d
[3], localError
); 
1847                         CFErrorPropagate(localError
, error
); 
1848                         CFReleaseNull(message
); 
1851                     if (!CFEqual(key
, digest
)) { 
1852                         const uint8_t *d 
= CFDataGetBytePtr(key
); 
1853                         const uint8_t *e 
= CFDataGetBytePtr(digest
); 
1854                         secnoticeq("engine", "%@:%@ object %02X%02X%02X%02X is really %02X%02X%02X%02X dropping from local manifest", 
1855                                    engine
->myID
, SOSPeerGetID(peer
), d
[0], d
[1], d
[2], d
[3], e
[0], e
[1], e
[2], e
[3]); 
1856                         SOSChangesAppendDelete(changes
, key
); 
1857                         SOSChangesAppendAdd(changes
, object
); // This is new behaviour but we think it's more correct 
1860                     size_t objectLen 
= (size_t)CFDataGetLength(der
); 
1861                     if (SOSMessageAppendObject(message
, der
, &localError
)) { 
1862                         SOSDigestVectorAppend(&dv
, CFDataGetBytePtr(digest
)); 
1864                         const uint8_t *d 
= CFDataGetBytePtr(digest
); 
1865                         CFStringRef hexder 
= CFDataCopyHexString(der
); 
1866                         secnoticeq("engine", "%@:%@ object %02X%02X%02X%02X der: %@ dropping from manifest: %@", 
1867                                   engine
->myID
, SOSPeerGetID(peer
), d
[0], d
[1], d
[2], d
[3], hexder
, localError
); 
1868                         CFReleaseNull(hexder
); 
1869                         CFReleaseNull(message
); 
1870                         // Since we can't send these objects let's assume they are bad too? 
1871                         SOSChangesAppendDelete(changes
, digest
); 
1873                     objectsSize 
+= objectLen
; 
1874                     if (objectsSize 
> kSOSMessageMaxObjectsSize
) 
1878                 CFReleaseSafe(digest
); 
1880                 CFReleaseNull(message
); 
1883                 objectsSent 
= SOSManifestCreateWithDigestVector(&dv
, error
); 
1884             if (CFArrayGetCount(changes
)) { 
1885                 CFErrorRef localError 
= NULL
; 
1886                 if (!SOSEngineUpdateChanges_locked(engine
, NULL
, kSOSDataSourceTransactionDidCommit
, kSOSDataSourceSOSTransaction
, changes
, &localError
)) 
1887                     secerror("SOSEngineUpdateChanges_locked: %@ failed: %@", changes
, localError
); 
1888                 CFReleaseSafe(localError
); 
1889                 CFAssignRetained(local
, SOSEngineCopyLocalPeerManifest_locked(engine
, peer
, error
)); 
1891             CFReleaseSafe(changes
); 
1894         // If we have no confirmed manifest, we want all pendedObjects going out as a manifest 
1895         objectsSent 
= CFRetainSafe(pendingObjects
); 
1898     if (confirmed 
|| SOSManifestGetCount(missing
) || SOSManifestGetCount(extra
) || objectsSent
) { 
1899         SOSManifestRef allExtra 
= SOSManifestCreateUnion(extra
, objectsSent
, error
); 
1900         proposed 
= SOSManifestCreateWithPatch(confirmed
, missing
, allExtra
, error
); 
1901         CFReleaseNull(allExtra
); 
1904     if (!SOSMessageSetManifests(message
, local
, confirmed
, proposed
, proposed
, confirmed 
? objectsSent 
: NULL
, error
)) 
1905         CFReleaseNull(message
); 
1907     CFReleaseNull(objectsSent
); 
1910         result 
= SOSMessageCreateData(message
, SOSPeerNextSequenceNumber(peer
), error
); 
1914         // Capture the peer in our block (SOSEnginePeerMessageSentBlock) 
1916         *sent 
= Block_copy(^(bool success
) { 
1917             dispatch_async(engine
->queue
, ^{ 
1919                 SOSPeerSetMustSendMessage(peer
, false); 
1920                 if (!confirmed 
&& !proposed
) { 
1921                     SOSPeerSetSendObjects(peer
, true); 
1922                     secnoticeq("engine", "%@:%@ sendObjects=true L:%@", engine
->myID
, SOSPeerGetID(peer
), local
); 
1924                 SOSPeerAddLocalManifest(peer
, local
); 
1925                 SOSPeerAddProposedManifest(peer
, proposed
); 
1926                 secnoticeq("engine", "send %@:%@ %@", engine
->myID
, SOSPeerGetID(peer
), message
); 
1927                 //SOSEngineCheckPeerIntegrity(engine, peer, NULL); 
1929                 secerror("%@:%@ failed to send %@", engine
->myID
, SOSPeerGetID(peer
), message
); 
1931             CFReleaseSafe(peer
); 
1932             CFReleaseSafe(local
); 
1933             CFReleaseSafe(proposed
); 
1934             CFReleaseSafe(message
); 
1938         CFReleaseSafe(local
); 
1939         CFReleaseSafe(proposed
); 
1940         CFReleaseSafe(message
); 
1942     CFReleaseNull(extra
); 
1943     CFReleaseNull(missing
); 
1944     if (error 
&& *error
) 
1945         secerror("%@:%@ error in send: %@", engine
->myID
, SOSPeerGetID(peer
), *error
); 
1950 static void SOSEngineLogItemError(SOSEngineRef engine
, CFStringRef peerID
, CFDataRef key
, CFDataRef optionalDigest
, const char *where
, CFErrorRef error
) { 
1951     if (!optionalDigest
) { 
1952         const uint8_t *d 
= CFDataGetBytePtr(key
); 
1953         secwarning("%@:%@ object %02X%02X%02X%02X %s: %@", engine
->myID
, peerID
, d
[0], d
[1], d
[2], d
[3], where
, error 
? (CFTypeRef
)error 
: CFSTR("")); 
1955         const uint8_t *d 
= CFDataGetBytePtr(key
); 
1956         const uint8_t *e 
= CFDataGetBytePtr(optionalDigest
); 
1957         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]); 
1961 static bool SOSEngineWriteToBackup_locked(SOSEngineRef engine
, SOSPeerRef peer
, bool rewriteComplete
, bool *didWrite
, bool *incomplete
, CFErrorRef 
*error
) { 
1962     __block 
bool ok 
= SOSPeerWritePendingReset(peer
, error
); 
1963     if (!ok 
|| !SOSPeerGetKeyBag(peer
)) 
1965     __block SOSManifestRef local 
= SOSEngineCopyLocalPeerManifest_locked(engine
, peer
, error
); 
1966     __block SOSManifestRef proposed 
= SOSPeerGetProposedManifest(peer
); 
1967     __block 
bool notify 
= true; 
1968     SOSManifestRef pendingObjects 
= NULL
; 
1969     SOSManifestRef missing 
= NULL
; 
1970     CFStringRef peerID 
= SOSPeerGetID(peer
); 
1972     ok 
&= SOSManifestDiff(proposed
, local
, &missing
, &pendingObjects
, error
); 
1974     secnoticeq("engine", "%@:%@: Send state for peer [%s%s%s] O: %zu, M: %zu", engine
->myID
, peerID
, 
1977                pendingObjects 
? "O":"0", 
1978                SOSManifestGetCount(pendingObjects
), 
1979                SOSManifestGetCount(missing
)); 
1981     if (SOSManifestGetCount(missing
) == 0 && SOSManifestGetCount(pendingObjects
) == 0) { 
1982         // proposed == local (faster test than CFEqualSafe above), since we 
1983         // already did the SOSManifestDiff 
1984         if (rewriteComplete
) { 
1987             secnoticeq("engine", "%@:%@ backup still done", engine
->myID
, peer
); 
1991     ok 
&= SOSPeerAppendToJournal(peer
, error
, ^(FILE *journalFile
, keybag_handle_t kbhandle
) { 
1992         SOSManifestRef objectsSent 
= NULL
; 
1993         __block 
struct SOSDigestVector dvdel 
= SOSDigestVectorInit
; 
1994         __block 
struct SOSDigestVector dvadd 
= SOSDigestVectorInit
; 
1995         SOSManifestForEach(missing
, ^(CFDataRef key
, bool *stop
) { 
1996             CFErrorRef localError 
= NULL
; 
1997             if (ftello(journalFile
) > kSOSBackupMaxFileSize
) { 
1998                 // Highwatermark hit on file. 
2000             } else if (SOSBackupEventWriteDelete(journalFile
, key
, &localError
)) { 
2001                 SOSDigestVectorAppend(&dvdel
, CFDataGetBytePtr(key
)); 
2003                 SOSEngineLogItemError(engine
, peerID
, key
, NULL
, "in SOSPeerWriteDelete", localError
); 
2004                 CFErrorPropagate(localError
, error
); 
2005                 // TODO: Update of missing so proposed is updated properly 
2006                 *stop 
= true; // Disk full? 
2010         if (ok 
&& SOSManifestGetCount(pendingObjects
)) { 
2011             CFMutableArrayRef changes 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
2012             ok 
&= SOSDataSourceForEachObject(engine
->dataSource
, pendingObjects
, error
, ^void(CFDataRef key
, SOSObjectRef object
, bool *stop
) { 
2013                 CFErrorRef localError 
= NULL
; 
2014                 CFDataRef digest 
= NULL
; 
2015                 CFDictionaryRef backupItem 
= NULL
; 
2016                 if (ftello(journalFile
) > kSOSBackupMaxFileSize
) { 
2017                     // Highwatermark hit on file. 
2019                 } else if (!object
) { 
2020                     SOSEngineLogItemError(engine
, peerID
, key
, NULL
, "dropping from manifest: not found in datasource", localError
); 
2021                     SOSChangesAppendDelete(changes
, key
); 
2022                 } else if (!(backupItem 
= SOSObjectCopyBackup(engine
->dataSource
, object
, kbhandle
, &localError
)) 
2023                            || !(digest 
= SOSObjectCopyDigest(engine
->dataSource
, object
, &localError
))) { 
2024                     if (SecErrorGetOSStatus(localError
) == errSecDecode
) { 
2025                         // Decode error, we need to drop these objects from our manifests 
2026                         SOSEngineLogItemError(engine
, peerID
, key
, NULL
, "dropping from manifest", localError
); 
2027                         SOSChangesAppendDelete(changes
, key
); 
2028                         CFRelease(localError
); 
2030                         // Stop iterating and propagate out all other errors. 
2031                         SOSEngineLogItemError(engine
, peerID
, key
, NULL
, "in SOSDataSourceForEachObject", localError
); 
2033                         CFErrorPropagate(localError
, error
); 
2037                     if (!CFEqual(key
, digest
)) { 
2038                         SOSEngineLogItemError(engine
, peerID
, key
, digest
, "", NULL
); 
2039                         SOSChangesAppendDelete(changes
, key
); 
2040                         SOSChangesAppendAdd(changes
, object
); // This is new behaviour but we think it's more correct 
2043                     if (SOSBackupEventWriteAdd(journalFile
, backupItem
, &localError
)) { 
2044                         SOSDigestVectorAppend(&dvadd
, CFDataGetBytePtr(digest
)); 
2046                         SOSEngineLogItemError(engine
, peerID
, key
, NULL
, "in SOSPeerWriteAdd", localError
); 
2047                         *stop 
= true; // Disk full? 
2048                         CFErrorPropagate(localError
, error
); 
2052                 CFReleaseSafe(backupItem
); 
2053                 CFReleaseSafe(digest
); 
2055             if (CFArrayGetCount(changes
)) { 
2056                 CFErrorRef localError 
= NULL
; 
2057                 if (!SOSEngineUpdateChanges_locked(engine
, NULL
, kSOSDataSourceTransactionDidCommit
, kSOSDataSourceSOSTransaction
, changes
, &localError
)) 
2058                     secerror("SOSEngineUpdateChanges_locked: %@ failed: %@", changes
, localError
); 
2059                 CFReleaseSafe(localError
); 
2060                 // Since calling SOSEngineUpdateChanges_locked might cause local to change and might cause the backup peer to update proposed, refetch them here. 
2061                 CFAssignRetained(local
, SOSEngineCopyLocalPeerManifest_locked(engine
, peer
, error
)); 
2062                 proposed 
= SOSPeerGetProposedManifest(peer
); 
2064             CFReleaseSafe(changes
); 
2067         if (dvadd
.count 
|| (proposed 
&& dvdel
.count
)) { 
2069             SOSManifestRef deleted 
= SOSManifestCreateWithDigestVector(&dvdel
, error
); 
2070             SOSManifestRef objectsSent 
= SOSManifestCreateWithDigestVector(&dvadd
, error
); 
2071             SOSManifestRef newProposed 
= SOSManifestCreateWithPatch(proposed
, deleted
, objectsSent
, error
); 
2072             CFReleaseSafe(deleted
); 
2073             CFReleaseSafe(objectsSent
); 
2074             SOSPeerSetProposedManifest(peer
, newProposed
); 
2075             CFReleaseSafe(newProposed
); 
2076             proposed 
= SOSPeerGetProposedManifest(peer
); 
2078         SOSDigestVectorFree(&dvdel
); 
2079         SOSDigestVectorFree(&dvadd
); 
2081         // TODO: If proposed is NULL, and local is empty we should still consider ourselves done. 
2082         // It so happens this can't happen in practice today since there is at least a backupbag 
2083         // in the backup, but this is a bug waiting to rear its head in the future. 
2084         if (ok 
&& CFEqualSafe(local
, proposed
)) { 
2085             CFErrorRef localError 
= NULL
; 
2086             if (SOSBackupEventWriteCompleteMarker(journalFile
, 899, &localError
)) { 
2087                 SOSPeerSetSendObjects(peer
, true); 
2089                 secnoticeq("backup", "%@:%@ backup done%s", engine
->myID
, peerID
, notify 
? " notifying sbd" : ""); 
2090                 // TODO: Now switch to changes based writing to backup sync. 
2091                 // Currently we leave changes enabled but we probably shouldn't 
2093                 secwarning("%@:%@ in SOSBackupPeerWriteCompleteMarker: %@", engine
->myID
, peerID
, localError
); 
2096                 CFErrorPropagate(localError
, error
); 
2099             secnoticeq("backup", "%@:%@ backup incomplete [%zu/%zu]%s", engine
->myID
, peerID
, SOSManifestGetCount(local
), SOSManifestGetCount(proposed
), notify 
? " notifying sbd" : ""); 
2102         CFReleaseNull(objectsSent
); 
2105         SOSBackupPeerPostNotification("writing changes to backup"); 
2108     CFReleaseSafe(local
); 
2109     CFReleaseNull(pendingObjects
); 
2110     CFReleaseNull(missing
); 
2115 bool SOSEngineSyncWithPeers(SOSEngineRef engine
, CFTypeRef idsTransport
, CFTypeRef kvsTransport
, CFErrorRef 
*error
) { 
2116     __block 
bool ok 
= true; 
2117     __block 
bool incomplete 
= false; 
2118     ok 
&= SOSEngineDoTxnOnQueue(engine
, error
, ^(SOSTransactionRef txn
, bool *commit
) { 
2119         __block 
bool dirty 
= false; 
2120         SOSEngineForEachBackupPeer_locked(engine
, ^(SOSPeerRef peer
) { 
2121             ok 
= SOSEngineWriteToBackup_locked(engine
, peer
, false, &dirty
, &incomplete
, error
); 
2125             ok 
= SOSEngineSave(engine
, txn
, error
); 
2128         // Ensure we get called again in a while (after a backup timeout) 
2129         // sbd will do this since we never wrote a complete marker. 
2130         // TODO: This relies on us not writing complete marker for update 
2131         // event while we havn't finished a full backup, which we currently still do. 
2136 bool SOSEngineHandleMessage(SOSEngineRef engine
, CFStringRef peerID
, 
2137                             CFDataRef raw_message
, CFErrorRef 
*error
) 
2139     __block 
bool result 
= true; 
2140     __block 
bool somethingChanged 
= false; 
2141     SOSMessageRef message 
= SOSMessageCreateWithData(kCFAllocatorDefault
, raw_message
, error
); 
2142     result 
&= message 
&& SOSEngineDoTxnOnQueue(engine
, error
, ^(SOSTransactionRef txn
, bool *commit
) { 
2143         result 
= SOSEngineHandleMessage_locked(engine
, peerID
, message
, txn
, commit
, &somethingChanged
, error
); 
2145     CFReleaseSafe(message
); 
2146     if (somethingChanged
) 
2147         SecKeychainChanged(false); 
2151 void SOSEngineCircleChanged(SOSEngineRef engine
, CFStringRef myPeerID
, CFArrayRef trustedPeers
, CFArrayRef untrustedPeers
) { 
2152     __block 
bool peersOrViewsChanged 
= false; 
2153     SOSEngineDoOnQueue(engine
, ^{ 
2154         peersOrViewsChanged 
= SOSEngineCircleChanged_locked(engine
, myPeerID
, trustedPeers
, untrustedPeers
); 
2157     __block 
bool ok 
= true; 
2158     __block CFErrorRef localError 
= NULL
; 
2159     ok 
&= SOSEngineDoTxnOnQueue(engine
, &localError
, ^(SOSTransactionRef txn
, bool *commit
) { 
2160         ok 
= *commit 
= SOSEngineSave(engine
, txn
, &localError
); 
2163         secerror("failed to save engine state: %@", localError
); 
2164         CFReleaseSafe(localError
); 
2167     if (peersOrViewsChanged
) 
2168         SOSCCSyncWithAllPeers(); 
2171 SOSManifestRef 
SOSEngineCopyManifest(SOSEngineRef engine
, CFErrorRef 
*error
) { 
2172     __block SOSManifestRef result 
= NULL
; 
2173     SOSEngineDoOnQueue(engine
, ^{ 
2174         result 
= SOSEngineCopyManifestWithViewNameSet_locked(engine
, SOSViewsGetV0ViewSet(), error
); 
2179 SOSManifestRef 
SOSEngineCopyLocalPeerManifest(SOSEngineRef engine
, SOSPeerRef peer
, CFErrorRef 
*error
) { 
2180     __block SOSManifestRef result 
= NULL
; 
2181     SOSEngineDoOnQueue(engine
, ^{ 
2182         result 
= SOSEngineCopyLocalPeerManifest_locked(engine
, peer
, error
); 
2187 bool SOSEngineUpdateChanges(SOSEngineRef engine
, SOSDataSourceTransactionSource source
, CFArrayRef changes
, CFErrorRef 
*error
) { 
2188     __block 
bool result 
= true; 
2189     SOSEngineDoOnQueue(engine
, ^{ 
2190         result 
= SOSEngineUpdateChanges_locked(engine
, NULL
, kSOSDataSourceTransactionDidCommit
, source
, changes
, error
); 
2196 // Peer state layout.  WRONG! It's an array now 
2197 // The peer state is an array. 
2198 // The first element of the array is a dictionary with any number of keys and 
2199 // values in it (for future expansion) such as changing the digest size or type 
2200 // or remebering boolean flags for a peers sake. 
2201 // The next three are special in that they are manifest digests with special 
2202 // meaning and rules as to how they are treated (These are dynamically updated 
2203 // based on database activity so they have a fully history of all changes made 
2204 // to the local db. The first is the manifest representing the pendingObjects 
2205 // to send to the other peer.  This is normally only ever appending to, and in 
2206 // particular with transactions originating from the Keychain API that affect 
2207 // syncable items will need to add the new objects digests to the pendingObjects list 
2208 // while adding the digests of any tombstones encountered to the extra list. 
2210 SOSPeerRef 
SOSEngineCopyPeerWithID(SOSEngineRef engine
, CFStringRef peer_id
, CFErrorRef 
*error
) { 
2211     __block SOSPeerRef peer 
= NULL
; 
2212     SOSEngineDoOnQueue(engine
, ^{ 
2213         peer 
= SOSEngineCopyPeerWithID_locked(engine
, peer_id
, error
); 
2218 bool SOSEngineForPeerID(SOSEngineRef engine
, CFStringRef peerID
, CFErrorRef 
*error
, void (^forPeer
)(SOSPeerRef peer
)) { 
2219     __block 
bool ok 
= true; 
2220     SOSEngineDoOnQueue(engine
, ^{ 
2221         SOSPeerRef peer 
= SOSEngineCopyPeerWithID_locked(engine
, peerID
, error
); 
2232 bool SOSEngineWithPeerID(SOSEngineRef engine
, CFStringRef peerID
, CFErrorRef 
*error
, void (^with
)(SOSPeerRef peer
, SOSDataSourceRef dataSource
, SOSTransactionRef txn
, bool *forceSaveState
)) { 
2233     __block 
bool result 
= true; 
2234     result 
&= SOSEngineDoTxnOnQueue(engine
, error
, ^(SOSTransactionRef txn
, bool *commit
) { 
2235         SOSPeerRef peer 
= SOSEngineCopyPeerWithID_locked(engine
, peerID
, error
); 
2237             result 
= SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("Engine has no peer for %@"), peerID
); 
2239             bool saveState 
= false; 
2240             with(peer
, engine
->dataSource
, txn
, &saveState
); 
2241             CFReleaseSafe(peer
); 
2243                 result 
= SOSEngineSave(engine
, txn
, error
); 
2244             // TODO: Don't commit if engineSave fails? 
2251 CFDataRef 
SOSEngineCreateMessageToSyncToPeer(SOSEngineRef engine
, CFStringRef peerID
, SOSEnginePeerMessageSentBlock 
*sentBlock
, CFErrorRef 
*error
) { 
2252     __block CFDataRef message 
= NULL
; 
2253     SOSEngineForPeerID(engine
, peerID
, error
, ^(SOSPeerRef peer
) { 
2254         message 
= SOSEngineCreateMessage_locked(engine
, peer
, error
, sentBlock
); 
2259 bool SOSEnginePeerDidConnect(SOSEngineRef engine
, CFStringRef peerID
, CFErrorRef 
*error
) { 
2260     return SOSEngineWithPeerID(engine
, peerID
, error
, ^(SOSPeerRef peer
, SOSDataSourceRef dataSource
, SOSTransactionRef txn
, bool *saveState
) { 
2261         *saveState 
= SOSPeerDidConnect(peer
); 
2265 bool SOSEngineSetPeerConfirmedManifest(SOSEngineRef engine
, CFStringRef backupName
, 
2266                                        CFDataRef keybagDigest
, CFDataRef manifestData
, CFErrorRef 
*error
) { 
2267     __block 
bool ok 
= true; 
2269     ok 
&= SOSEngineForPeerID(engine
, backupName
, error
, ^(SOSPeerRef peer
) { 
2271         bool incomplete 
= false; 
2272         SOSManifestRef confirmed 
= NULL
; 
2273         CFDataRef keybag 
= SOSPeerGetKeyBag(peer
); 
2274         CFDataRef computedKeybagDigest 
= keybag 
? CFDataCopySHA1Digest(keybag
, NULL
) : NULL
; 
2275         if (CFEqualSafe(keybagDigest
, computedKeybagDigest
)) { 
2276             ok 
= confirmed 
= SOSManifestCreateWithData(manifestData
, error
); 
2278                 // Set both confirmed and proposed (confirmed is just 
2279                 // for debug status, proposed is actually what's used 
2280                 // by the backup peer). 
2281                 SOSPeerSetConfirmedManifest(peer
, confirmed
); 
2282                 SOSPeerSetProposedManifest(peer
, confirmed
); 
2285             // sbd missed a reset event, send it again 
2286             // Force SOSEngineWriteToBackup_locked to call SOSPeerWriteReset, which clears 
2287             // confirmed and proposed manifests and writes the keybag to the journal. 
2288             SOSPeerSetMustSendMessage(peer
, true); 
2291         // Stop changes from writing complete markers, unless SOSEngineWriteToBackup_locked() detects we are in sync 
2292         SOSPeerSetSendObjects(peer
, false); 
2293         // Write data for this peer if we can, technically not needed for non legacy protocol support all the time. 
2294         ok 
= SOSEngineWriteToBackup_locked(engine
, peer
, true, &dirty
, &incomplete
, error
); 
2296         CFReleaseSafe(confirmed
); 
2297         CFReleaseSafe(computedKeybagDigest
); 
2302 CFArrayRef 
SOSEngineCopyBackupPeerNames(SOSEngineRef engine
, CFErrorRef 
*error
) { 
2303     __block CFMutableArrayRef backupNames 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
2304     SOSEngineForEachBackupPeer(engine
, ^(SOSPeerRef peer
) { 
2305         CFArrayAppendValue(backupNames
, SOSPeerGetID(peer
)); 
2310 static CFStringRef 
CFStringCreateWithViewNameSet(CFSetRef vns
) { 
2311     CFIndex count 
= CFSetGetCount(vns
); 
2312     CFMutableArrayRef mvn 
= CFArrayCreateMutableForCFTypesWithCapacity(kCFAllocatorDefault
, count
); 
2313     CFSetForEach(vns
, ^(const void *value
) { 
2314         CFArrayAppendValue(mvn
, value
); 
2316     CFArraySortValues(mvn
, CFRangeMake(0, count
), (CFComparatorFunction
)CFStringCompare
, 0); 
2317     CFStringRef string 
= CFStringCreateByCombiningStrings(kCFAllocatorDefault
, mvn
, CFSTR(":")); 
2322 static CFStringRef 
CFStringCreateWithLabelAndViewNameSetDescription(CFStringRef label
, CFStringRef peerID
, CFSetRef vns
, SOSManifestRef manifest
) { 
2323     CFStringRef vnsDesc 
= CFStringCreateWithViewNameSet(vns
); 
2326         desc 
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%@ %@ (%@) [%lu]"), label
, peerID
, vnsDesc
, SOSManifestGetCount(manifest
)); 
2328         desc 
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%@ %@ (%@)"), label
, peerID
, vnsDesc
); 
2329     CFReleaseSafe(vnsDesc
); 
2333 static void CFArrayAppendConfirmedDigestsEntry(CFMutableArrayRef array
, CFStringRef label
, CFStringRef peerID
, CFSetRef vns
, SOSManifestRef manifest
) { 
2334     CFStringRef desc 
= CFStringCreateWithLabelAndViewNameSetDescription(label
, peerID
, vns
, manifest
); 
2335     CFArrayAppendValue(array
, desc
); 
2336     CFReleaseSafe(desc
); 
2337     CFDataRef digest 
= SOSManifestGetDigest(manifest
, NULL
); 
2339         CFArrayAppendValue(array
, digest
); 
2341         CFDataRef nullDigest 
= CFDataCreate(kCFAllocatorDefault
, NULL
, 0); 
2342         CFArrayAppendValue(array
, nullDigest
); 
2343         CFReleaseSafe(nullDigest
); 
2347 static CFArrayRef 
SOSEngineCopyPeerConfirmedDigests_locked(SOSEngineRef engine
, CFErrorRef 
*error
) { 
2348     CFMutableArrayRef result 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
2349     CFDictionaryForEach(engine
->viewNameSet2ChangeTracker
, ^(const void *vns
, const void *ct
) { 
2350         SOSManifestRef manifest 
= SOSEngineCopyManifestWithViewNameSet_locked(engine
, vns
, error
); 
2351         CFArrayAppendConfirmedDigestsEntry(result
, CFSTR("local "), engine
->myID
, (CFSetRef
)vns
, manifest
); 
2352         CFReleaseSafe(manifest
); 
2355     // Copy other peers even if we aren't in the circle, since we're observing it. 
2356     SOSEngineForEachPeer_locked(engine
, ^(SOSPeerRef peer
) { 
2357         CFArrayAppendConfirmedDigestsEntry(result
, CFSTR("remote"), SOSPeerGetID(peer
), SOSPeerGetViewNameSet(peer
), 
2358                                            SOSPeerGetConfirmedManifest(peer
)); 
2363 CFArrayRef 
SOSEngineCopyPeerConfirmedDigests(SOSEngineRef engine
, CFErrorRef 
*error
) { 
2364     __block CFArrayRef result 
= NULL
; 
2365     SOSEngineDoOnQueue(engine
, ^{ 
2366         result 
= SOSEngineCopyPeerConfirmedDigests_locked(engine
, error
); 
2371 SOSDataSourceRef 
SOSEngineGetDataSource(SOSEngineRef engine
) { 
2372     return engine
->dataSource
;