2  * Copyright (c) 2012-2017 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/SOSEnginePriv.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 <Security/SecureObjectSync/SOSPersist.h> 
  37 #include <Security/SecureObjectSync/SOSCloudCircleInternal.h> 
  39 #include <corecrypto/ccder.h> 
  42 #include <utilities/array_size.h> 
  43 #include <utilities/SecCFCCWrappers.h> 
  44 #include <utilities/SecCFError.h> 
  45 #include <utilities/SecCFRelease.h> 
  46 #include <utilities/SecCFWrappers.h> 
  47 #include <utilities/der_plist.h> 
  48 #include <utilities/der_plist_internal.h> 
  49 #include <utilities/debugging.h> 
  50 #include <utilities/iCloudKeychainTrace.h> 
  51 #include <utilities/SecCoreCrypto.h> 
  52 #include <utilities/SecFileLocations.h> 
  53 #include <utilities/SecADWrapper.h> 
  54 #include <utilities/SecTrace.h> 
  57 #include <AssertMacros.h> 
  58 #include <CoreFoundation/CoreFoundation.h> 
  60 #include <securityd/SecItemServer.h>    // TODO: We can't leave this here. 
  61 #include <securityd/SOSCloudCircleServer.h> // TODO: We can't leave this here. 
  62 #include <Security/SecItem.h>           // TODO: We can't leave this here. 
  63 #include <Security/SecItemPriv.h>       // TODO: We can't leave this here. 
  64 #include <securityd/SecItemSchema.h> 
  65 #include <securityd/iCloudTrace.h> 
  67 #include <keychain/ckks/CKKS.h> 
  69 #include <CoreFoundation/CFURL.h> 
  71 #include <Security/SecureObjectSync/SOSEnsureBackup.h> 
  74 // MARK: SOSEngine The Keychain database with syncable keychain support. 
  77 //---------------------------------------------------------------------------------------- 
  78 // MARK: Engine state v0 
  79 //---------------------------------------------------------------------------------------- 
  81 // Key in dataSource for general engine state file. 
  82 // This file only has digest entries in it, no manifests. 
  83 static const CFStringRef kSOSEngineState 
= CFSTR("engine-state"); 
  85 // Keys in state dictionary 
  86 static CFStringRef kSOSEngineManifestCacheKey 
= CFSTR("manifestCache"); 
  87 static CFStringRef kSOSEnginePeerStateKey 
= CFSTR("peerState"); 
  88 static CFStringRef kSOSEnginePeerIDsKey 
= CFSTR("peerIDs"); 
  89 static CFStringRef kSOSEngineIDKey 
= CFSTR("id"); 
  90 static CFStringRef kSOSEngineTraceDateKey 
= CFSTR("traceDate"); 
  92 //---------------------------------------------------------------------------------------- 
  93 // MARK: Engine state v2 
  94 //---------------------------------------------------------------------------------------- 
  96 #if !TARGET_IPHONE_SIMULATOR 
  97 static const CFIndex kCurrentEngineVersion 
= 2; 
  99 // Keychain/datasource items 
 100 // Used for the kSecAttrAccount when saving in the datasource with dsSetStateWithKey 
 101 // Class D [kSecAttrAccessibleAlwaysPrivate/kSecAttrAccessibleAlwaysThisDeviceOnly] 
 102 CFStringRef kSOSEngineStatev2 
= CFSTR("engine-state-v2"); 
 103 CFStringRef kSOSEnginePeerStates 
= CFSTR("engine-peer-states"); 
 104 CFStringRef kSOSEngineManifestCache 
= CFSTR("engine-manifest-cache"); 
 105 CFStringRef kSOSEngineCoders 
= CFSTR("engine-coders"); 
 106 #define kSOSEngineProtectionDomainClassA kSecAttrAccessibleWhenUnlockedThisDeviceOnly 
 108 // Keys for individual dictionaries 
 110 CFStringRef kSOSEngineStateVersionKey 
= CFSTR("engine-stateVersion"); 
 112 // Current save/load routines 
 113 // SOSEngineCreate/SOSEngineLoad/SOSEngineSetState 
 114 // SOSEngineSave/SOSEngineDoSave/SOSEngineCopyState 
 115 // no save/load functions external to this file 
 118     Divide engine state into five pieces: 
 120  - General engine state 
 122     - List of other (trusted) peer IDs 
 124  - Coder data (formerly in peer state) 
 125  - Backup Keybags (backup peers only) 
 126  - Peer state (including manifest hashes -- just keys into ManifestCache) 
 127     [__OpaqueSOSPeer/SOSPeerRef] 
 140     - local manifest hashes (copy of local keychain) 
 141     - peer manifest hashes 
 143  These divisions are based on size, frequency of update, and protection domain 
 145     The Manifest Cache is a dictionary where each key is a hash over its entry, 
 146     which is a concatenation of 20 byte hashes of the keychain items. The local 
 147     keychain is present as one entry. The other entries are subsets of that, one 
 148     for each confirmed/pending/missing/unwanted shared with a peer. The local 
 149     keychain entry can be re-created by iterating over the databse, whereas the 
 150     others are built up through communicating with other peers. 
 152  83:d=2  hl=2 l=  13 prim:   UTF8STRING        :manifestCache 
 153  98:d=2  hl=4 l= 912 cons:   SET 
 154  102:d=3  hl=2 l=  24 cons:    SEQUENCE 
 155  104:d=4  hl=2 l=  20 prim:     OCTET STRING      [HEX DUMP]:DA39A3EE5E6B4B0D3255BFEF95601890AFD80709 
 156  126:d=4  hl=2 l=   0 prim:     OCTET STRING 
 157  128:d=3  hl=2 l= 124 cons:    SEQUENCE 
 158  130:d=4  hl=2 l=  20 prim:     OCTET STRING      [HEX DUMP]:F9B59370A4733F0D174E8D220C5BE3AF062C775B 
 159  152:d=4  hl=2 l= 100 prim:     OCTET STRING      [HEX DUMP]:5A574BB4EC90C3BBCC69EE73CBFE039133AE807265D6A58003B8D205997EAB96390AAB207E63A2E270A476CAB5B2D9D2F7B0E55512AA957B58D5658E7EF907B069B83AA6BA941790A3C3C4A68292D59DABA3CA342966EFF82E1ACAEB691FD6E20772E17E 
 160  254:d=3  hl=4 l= 366 cons:    SEQUENCE 
 161  258:d=4  hl=2 l=  20 prim:     OCTET STRING      [HEX DUMP]:2E69C2F7F3E014075B30004CE0EC6C1AD419EBF5 
 162  280:d=4  hl=4 l= 340 prim:     OCTET STRING      [HEX DUMP]:07571E9678FD7D68812E409CC96C1F54834A099A0C3A2D12CCE2EA95F4505EA52F2C982B2ADEE3DA14D4712C000309BF63D54A98B61AA1D963C40E0E2531C83B28CA5BE6DA0D26400C3C77A618F711DD3CC0BF86CCBAF8AA3332973268B30EEBF21CD8184D9C8427CA13DECCC7BB83C80009A2EF45CCC07F586315C80CEEEEF5D5352FD000AAE6D9CBB4294D5959FD00198225AF9ABD09B341A2FDC278E9FD1465D6A58003B8D205997EAB96390AAB207E63A2E270A476CAB5B2D9D2F7B0E55512AA957B58D5658E7EF907B069B83AA6BA941790A3C3C4A68292D59D95C9D4D8A8BCA2E8242AB0D409F671F298B6DCAE9BC4238C09E07548CEFB300098606F9E4F230C99ABA3CA342966EFF82E1ACAEB691FD6E20772E17EB4FEFB84F8CF75C0C69C59532C354D175A59F961BA4D4DFA017FD8192288F14278AE76712E127D65FE616C7E4FD0713644F7C9A7ABA1CE065694A968 
 163  624:d=3  hl=4 l= 386 cons:    SEQUENCE 
 164  628:d=4  hl=2 l=  20 prim:     OCTET STRING      [HEX DUMP]:CCF179FF718C10F151E7409EDF1A06F0DF10DCAD 
 165  650:d=4  hl=4 l= 360 prim:     OCTET STRING      [HEX DUMP]:07571E9678FD7D68812E409CC96C1F54834A099A0C3A2D12CCE2EA95F4505EA52F2C982B2ADEE3DA14D4712C000309BF63D54A98B61AA1D963C40E0E2531C83B28CA5BE6DA0D26400C3C77A618F711DD3CC0BF86CCBAF8AA3332973268B30EEBF21CD8184D9C8427CA13DECCC7BB83C80009A2EF45CCC07F586315C80CEEEEF5D5352FD000AAE6D9CBB4294D5959FD00198225AF9ABD09B341A2FDC278E9FD145A574BB4EC90C3BBCC69EE73CBFE039133AE807265D6A58003B8D205997EAB96390AAB207E63A2E270A476CAB5B2D9D2F7B0E55512AA957B58D5658E7EF907B069B83AA6BA941790A3C3C4A68292D59D95C9D4D8A8BCA2E8242AB0D409F671F298B6DCAE9BC4238C09E07548CEFB300098606F9E4F230C99ABA3CA342966EFF82E1ACAEB691FD6E20772E17EB4FEFB84F8CF75C0C69C59532C354D175A59F961BA4D4DFA017FD8192288F14278AE76712E127D65FE616C7E4FD0713644F7C9A7ABA1CE065694A968 
 171 static bool SOSEngineLoad(SOSEngineRef engine
, SOSTransactionRef txn
, CFErrorRef 
*error
); 
 172 static bool SOSEngineSetPeers_locked(SOSEngineRef engine
, SOSPeerMetaRef myPeerMeta
, CFArrayRef trustedPeerMetas
, CFArrayRef untrustedPeerMetas
); 
 173 static void SOSEngineApplyPeerState(SOSEngineRef engine
, CFDictionaryRef peerStateMap
); 
 174 static void SOSEngineSynthesizePeerMetas(SOSEngineRef engine
, CFMutableArrayRef trustedPeersMetas
, CFMutableArrayRef untrustedPeers
); 
 175 static bool SOSEngineLoadCoders(SOSEngineRef engine
, SOSTransactionRef txn
, CFErrorRef 
*error
); 
 176 #if !TARGET_IPHONE_SIMULATOR 
 177 static bool SOSEngineDeleteV0State(SOSEngineRef engine
, SOSTransactionRef txn
, CFErrorRef 
*error
); 
 180 static CFStringRef 
SOSPeerIDArrayCreateString(CFArrayRef peerIDs
) { 
 181     return peerIDs 
? CFStringCreateByCombiningStrings(kCFAllocatorDefault
, peerIDs
, CFSTR(" ")) : CFSTR(""); 
 184 static CFStringRef 
SOSEngineCopyFormattingDesc(CFTypeRef cf
, CFDictionaryRef formatOptions
) { 
 185     SOSEngineRef engine 
= (SOSEngineRef
)cf
; 
 186     CFStringRef tpDesc 
= SOSPeerIDArrayCreateString(engine
->peerIDs
); 
 187     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); 
 188     CFReleaseSafe(tpDesc
); 
 192 static CFStringRef 
SOSEngineCopyDebugDesc(CFTypeRef cf
) { 
 193     return SOSEngineCopyFormattingDesc(cf
, NULL
); 
 196 static dispatch_queue_t sEngineQueue
; 
 197 static CFDictionaryRef sEngineMap
; 
 199 CFGiblisWithFunctions(SOSEngine
, NULL
, NULL
, NULL
, NULL
, NULL
, SOSEngineCopyFormattingDesc
, SOSEngineCopyDebugDesc
, NULL
, NULL
, ^{ 
 200     sEngineQueue 
= dispatch_queue_create("SOSEngine queue", DISPATCH_QUEUE_SERIAL
); 
 201     sEngineMap 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
 204 #define _LOG_RAW_MESSAGES 0 
 205 void logRawMessage(CFDataRef message
, bool sending
, uint64_t seqno
) 
 207 #if _LOG_RAW_MESSAGES 
 208     CFStringRef hexMessage 
= NULL
; 
 210         hexMessage 
= CFDataCopyHexString(message
); 
 212             secnoticeq("engine", "%s RAW%1d %@", sending 
? "send" : "recv", seqno
?2:0, hexMessage
); 
 214             secnoticeq("engine", "%s RAWx %@", sending 
? "send" : "recv", hexMessage
);  // we don't know vers of received msg here 
 216     CFReleaseSafe(hexMessage
); 
 221 // Peer state layout.  WRONG! It's an array now 
 222 // The peer state is an array. 
 223 // The first element of the array is a dictionary with any number of keys and 
 224 // values in it (for future expansion) such as changing the digest size or type 
 225 // or remembering boolean flags for a peers sake. 
 226 // The next three are special in that they are manifest digests with special 
 227 // meaning and rules as to how they are treated (These are dynamically updated 
 228 // based on database activity so they have a fully history of all changes made 
 229 // to the local db. The first is the manifest representing the pendingObjects 
 230 // to send to the other peer.  This is normally only ever appending to, and in 
 231 // particular with transactions originating from the Keychain API that affect 
 232 // syncable items will need to add the new objects digests to the pendingObjects list 
 233 // while adding the digests of any tombstones encountered to the extra list. 
 235 CFStringRef 
SOSEngineGetMyID(SOSEngineRef engine
) { 
 236     // TODO: this should not be needed 
 240 // TEMPORARY: Get the list of IDs for cleanup, this shouldn't be used instead it should iterate KVS. 
 241 CFArrayRef 
SOSEngineGetPeerIDs(SOSEngineRef engine
) { 
 242     if(!engine
) return NULL
; 
 243     return engine
->peerIDs
; 
 246 void SOSEngineClearCache(SOSEngineRef engine
){ 
 247     CFReleaseNull(engine
->manifestCache
); 
 248     CFReleaseNull(engine
->localMinusUnreadableDigest
); 
 249     if (engine
->save_timer
) 
 250         dispatch_source_cancel(engine
->save_timer
); 
 251     dispatch_release(engine
->queue
); 
 252     engine
->queue 
= NULL
; 
 255 static SOSPeerRef 
SOSEngineCopyPeerWithMapEntry_locked(SOSEngineRef engine
, CFStringRef peerID
, CFTypeRef mapEntry
, CFErrorRef 
*error
) { 
 256     SOSPeerRef peer 
= NULL
; 
 257     if (mapEntry 
&& CFGetTypeID(mapEntry
) == SOSPeerGetTypeID()) { 
 258         // The mapEntry is an SOSPeer, so we're done. 
 259         peer 
= (SOSPeerRef
)CFRetain(mapEntry
); 
 261         // The mapEntry is a peerState, attempt to initialize a new 
 262         // peer iff peerID is in the set of trusted peerIDs 
 263         if (engine
->peerIDs 
&& CFArrayContainsValue(engine
->peerIDs
, CFRangeMake(0, CFArrayGetCount(engine
->peerIDs
)), peerID
)) { 
 264             CFErrorRef localError 
= NULL
; 
 265             peer 
= SOSPeerCreateWithState(engine
, peerID
, mapEntry
, &localError
); 
 267                 secerror("error inflating peer: %@: %@ from state: %@", peerID
, localError
, mapEntry
); 
 268                 CFReleaseNull(localError
); 
 269                 peer 
= SOSPeerCreateWithState(engine
, peerID
, NULL
, error
); 
 272                 // Replace the map entry with the inflated peer. 
 273                 CFDictionarySetValue(engine
->peerMap
, peerID
, peer
); 
 276             SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("peer: %@ is untrusted inflating not allowed"), peerID
); 
 282 static SOSPeerRef 
SOSEngineCopyPeerWithID_locked(SOSEngineRef engine
, CFStringRef peerID
, CFErrorRef 
*error
) { 
 283     CFTypeRef mapEntry 
= CFDictionaryGetValue(engine
->peerMap
, peerID
); 
 284     SOSPeerRef peer 
= NULL
; 
 286         peer 
= SOSEngineCopyPeerWithMapEntry_locked(engine
, peerID
, mapEntry
, error
); 
 289         secerror("peer: %@ not found, peerMap: %@, engine: %@", peerID
, engine
->peerMap
, engine
); 
 290         SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("peer: %@ not found"), peerID
); 
 295 struct SOSEngineWithPeerContext 
{ 
 297     void (^with
)(SOSPeerRef peer
); 
 300 static void SOSEngineWithPeerMapEntry_locked(const void *peerID
, const void *mapEntry
, void *context
) { 
 301     struct SOSEngineWithPeerContext 
*ewp 
= context
; 
 302     SOSPeerRef peer 
= SOSEngineCopyPeerWithMapEntry_locked(ewp
->engine
, peerID
, mapEntry
, NULL
); 
 309 static void SOSEngineForEachPeer_locked(SOSEngineRef engine
, void (^with
)(SOSPeerRef peer
)) { 
 310     struct SOSEngineWithPeerContext ewp 
= { .engine 
= engine
, .with 
= with 
}; 
 311     CFDictionaryRef peerMapCopy 
= CFDictionaryCreateCopy(NULL
, engine
->peerMap
); 
 312     CFDictionaryApplyFunction(peerMapCopy
, SOSEngineWithPeerMapEntry_locked
, &ewp
); 
 313     CFRelease(peerMapCopy
); 
 316 static void SOSEngineWithBackupPeerMapEntry_locked(const void *peerID
, const void *mapEntry
, void *context
) { 
 317     struct SOSEngineWithPeerContext 
*ewp 
= context
; 
 318     // v0 backup peer is always in map but we only consider it a backup peer if it has a keybag. 
 319     if (SOSPeerMapEntryIsBackup(mapEntry
)) { 
 320         SOSPeerRef peer 
= SOSEngineCopyPeerWithMapEntry_locked(ewp
->engine
, peerID
, mapEntry
, NULL
); 
 328 static void SOSEngineForEachBackupPeer_locked(SOSEngineRef engine
, void (^with
)(SOSPeerRef peer
)) { 
 329     struct SOSEngineWithPeerContext ewp 
= { .engine 
= engine
, .with 
= with 
}; 
 330     CFDictionaryRef peerMapCopy 
= CFDictionaryCreateCopy(NULL
, engine
->peerMap
); 
 331     CFDictionaryApplyFunction(peerMapCopy
, SOSEngineWithBackupPeerMapEntry_locked
, &ewp
); 
 332     CFRelease(peerMapCopy
); 
 338 SOSManifestRef 
SOSEngineGetManifestForDigest(SOSEngineRef engine
, CFDataRef digest
) { 
 339     if (!engine
->manifestCache 
|| !digest
) return NULL
; 
 340     SOSManifestRef manifest 
= (SOSManifestRef
)CFDictionaryGetValue(engine
->manifestCache
, digest
); 
 341     if (!manifest
) return NULL
; 
 342     if (CFGetTypeID(manifest
) != SOSManifestGetTypeID()) { 
 343         secerror("dropping corrupt manifest for %@ from cache", digest
); 
 344         CFDictionaryRemoveValue(engine
->manifestCache
, digest
); 
 351 void SOSEngineAddManifest(SOSEngineRef engine
, SOSManifestRef manifest
) { 
 352     CFDataRef digest 
= SOSManifestGetDigest(manifest
, NULL
); 
 354         if (!engine
->manifestCache
) 
 355             engine
->manifestCache 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
 356         CFDictionaryAddValue(engine
->manifestCache
, digest
, manifest
); 
 360 CFDataRef 
SOSEnginePatchRecordAndCopyDigest(SOSEngineRef engine
, SOSManifestRef base
, SOSManifestRef removals
, SOSManifestRef additions
, CFErrorRef 
*error
) { 
 361     CFDataRef digest 
= NULL
; 
 362     SOSManifestRef manifest 
= SOSManifestCreateWithPatch(base
, removals
, additions
, error
); 
 364         SOSEngineAddManifest(engine
, manifest
); 
 365         digest 
= CFRetainSafe(SOSManifestGetDigest(manifest
, NULL
)); 
 367     CFReleaseSafe(manifest
); 
 371 SOSManifestRef 
SOSEngineCopyPersistedManifest(SOSEngineRef engine
, CFDictionaryRef persisted
, CFStringRef key
) { 
 372     return CFRetainSafe(SOSEngineGetManifestForDigest(engine
, asData(CFDictionaryGetValue(persisted
, key
), NULL
))); 
 375 CFMutableArrayRef 
SOSEngineCopyPersistedManifestArray(SOSEngineRef engine
, CFDictionaryRef persisted
, CFStringRef key
, CFErrorRef 
*error
) { 
 376     CFMutableArrayRef manifests 
= NULL
; 
 377     CFArrayRef digests 
= NULL
; 
 379     if (asArrayOptional(CFDictionaryGetValue(persisted
, key
), &digests
, error
)) 
 380         manifests 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
 381     if (digests
) CFArrayForEachC(digests
, digest
) { 
 382         SOSManifestRef manifest 
= SOSEngineGetManifestForDigest(engine
, digest
); 
 384             CFArrayAppendValue(manifests
, manifest
); 
 389 #if !TARGET_IPHONE_SIMULATOR 
 390 static CFDictionaryRef 
SOSEngineCopyEncodedManifestCache_locked(SOSEngineRef engine
, CFErrorRef 
*error
) { 
 391     CFMutableDictionaryRef mfc 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
 392     SOSEngineForEachPeer_locked(engine
, ^(SOSPeerRef peer
) { 
 393         SOSPeerAddManifestsInUse(peer
, mfc
); 
 400 // End of Manifest cache 
 403 //---------------------------------------------------------------------------------------- 
 405 //---------------------------------------------------------------------------------------- 
 408  Each peer has an associated coder, whcih the engine keeps track of in a 
 409  CFDictionary indexed by peerID. The coders are read from disk when first needed, 
 410  then kept in memory as SOSCoders. 
 412  N.B. Don't rollback coder in memory if a transaction is rolled back, since this 
 413  might lead to reuse of an IV. 
 416 static bool SOSEngineCopyCoderData(SOSEngineRef engine
, CFStringRef peerID
, CFDataRef 
*coderData
, CFErrorRef 
*error
) { 
 418     SOSCoderRef coder 
= (SOSCoderRef
)CFDictionaryGetValue(engine
->coders
, peerID
); 
 419     if (coder 
&& (CFGetTypeID(coder
) == SOSCoderGetTypeID())) { 
 420         CFErrorRef localError 
= NULL
; 
 421         ok 
= *coderData 
= SOSCoderCopyDER(coder
, &localError
); 
 423             secerror("failed to der encode coder for peer %@, dropping it: %@", peerID
, localError
); 
 424             CFDictionaryRemoveValue(engine
->coders
, peerID
); 
 425             CFErrorPropagate(localError
, error
); 
 433 static SOSCoderRef 
SOSEngineGetCoderInTx_locked(SOSEngineRef engine
, SOSTransactionRef txn
, CFStringRef peerID
, CFErrorRef 
*error
) { 
 434     if (!engine
->haveLoadedCoders
) { 
 435         engine
->haveLoadedCoders 
= SOSEngineLoadCoders(engine
, txn
, error
); 
 437         if (!engine
->haveLoadedCoders
) { 
 442     SOSCoderRef coder 
= (SOSCoderRef
)CFDictionaryGetValue(engine
->coders
, peerID
); 
 443     if (!coder 
|| (CFGetTypeID(coder
) != SOSCoderGetTypeID())) { 
 444         SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("No coder for peer: %@"), peerID
); 
 449 static bool SOSEngineEnsureCoder_locked(SOSEngineRef engine
, SOSTransactionRef txn
, CFStringRef peerID
, SOSFullPeerInfoRef myPeerInfo
, SOSPeerInfoRef peerInfo
, SOSCoderRef ourCoder
, CFErrorRef 
*error
) { 
 450     //have to have caused coder loading, transactions do this. 
 451     if (!ourCoder 
|| !SOSCoderIsFor(ourCoder
, peerInfo
, myPeerInfo
)) { 
 452         secinfo("coder", "New coder for id %@.", peerID
); 
 453         CFErrorRef localError 
= NULL
; 
 454         SOSCoderRef coder 
= SOSCoderCreate(peerInfo
, myPeerInfo
, kCFBooleanFalse
, &localError
); 
 456             secerror("Failed to create coder for %@: %@", peerID
, localError
); 
 457             CFErrorPropagate(localError
, error
); 
 460         CFDictionarySetValue(engine
->coders
, peerID
, coder
); 
 461         secdebug("coder", "setting coder for peerid: %@, coder: %@", peerID
, coder
); 
 462         CFReleaseNull(coder
); 
 463         engine
->codersNeedSaving 
= true; 
 468 bool SOSEngineInitializePeerCoder(SOSEngineRef engine
, SOSFullPeerInfoRef myPeerInfo
, SOSPeerInfoRef peerInfo
, CFErrorRef 
*error
) { 
 469     __block 
bool ok 
= true; 
 470     CFStringRef peerID 
= SOSPeerInfoGetPeerID(peerInfo
); 
 472     ok 
&= SOSEngineWithPeerID(engine
, peerID
, error
, ^(SOSPeerRef peer
, SOSCoderRef coder
, SOSDataSourceRef dataSource
, SOSTransactionRef txn
, bool *forceSaveState
) { 
 473         ok 
= SOSEngineEnsureCoder_locked(engine
, txn
, peerID
, myPeerInfo
, peerInfo
, coder
, error
); 
 474         *forceSaveState 
= ok
; 
 480 static bool SOSEngineGCPeerState_locked(SOSEngineRef engine
, CFErrorRef 
*error
) { 
 483     //require_quiet(ok = SOSEngineGCManifests_locked(engine, error), exit); 
 488 #if !TARGET_IPHONE_SIMULATOR 
 489 static CFMutableDictionaryRef 
SOSEngineCopyPeerState_locked(SOSEngineRef engine
, CFErrorRef 
*error
) { 
 490     CFMutableDictionaryRef peerState 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
 491     CFDictionaryForEach(engine
->peerMap
, ^(const void *key
, const void *value
) { 
 492         CFDictionaryRef state 
= NULL
; 
 493         if (value 
&& CFGetTypeID(value
) == SOSPeerGetTypeID()) { 
 494             CFErrorRef localError 
= NULL
; 
 496             state 
= SOSPeerCopyState((SOSPeerRef
)value
, &localError
); 
 498                 secnotice("engine", "%@ failed to encode peer: %@", key
, localError
); 
 499             CFReleaseNull(localError
); 
 500             // TODO: Potentially replace inflated peer with deflated peer in peerMap 
 502             // We have a deflated peer. 
 503             state 
= CFRetainSafe(value
); 
 507             CFDictionarySetValue(peerState
, key
, state
); 
 508             CFReleaseSafe(state
); 
 514 static CFMutableDictionaryRef 
SOSEngineCopyPeerCoders_locked(SOSEngineRef engine
, CFErrorRef 
*error
) { 
 515     CFMutableDictionaryRef coders 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
 516     CFDictionaryForEach(engine
->peerMap
, ^(const void *key
, const void *value
) { 
 517         CFDataRef coderData 
= NULL
; 
 518         CFErrorRef localError 
= NULL
; 
 519         bool ok 
= SOSEngineCopyCoderData(engine
, (CFStringRef
)key
, &coderData
, &localError
); 
 522             secnotice("engine", "%@ no coder for peer: %@", key
, localError
); 
 524         if (ok 
&& coderData
) { 
 525             CFDictionarySetValue(coders
, key
, coderData
); 
 527         CFReleaseNull(coderData
); 
 528         CFReleaseNull(localError
); 
 533 //---------------------------------------------------------------------------------------- 
 534 // MARK: Engine state v2 Save 
 535 //---------------------------------------------------------------------------------------- 
 537 // Coders and keybags 
 539 static CFDataRef 
SOSEngineCopyCoders(SOSEngineRef engine
, CFErrorRef 
*error
) { 
 540     // Copy the CFDataRef version of the coders into a dictionary, which is then DER-encoded for saving 
 541     CFDictionaryRef coders 
= SOSEngineCopyPeerCoders_locked(engine
, error
); 
 542     secdebug("coders", "copying coders! %@", coders
); 
 543     CFDataRef der 
= CFPropertyListCreateDERData(kCFAllocatorDefault
, coders
, error
); 
 544     CFReleaseSafe(coders
); 
 548 static bool SOSEngineSaveCoders(SOSEngineRef engine
, SOSTransactionRef txn
, CFErrorRef 
*error
) { 
 549     // MUST hold engine lock 
 550     // Device must be unlocked for this to succeed 
 552     if(!engine
->haveLoadedCoders
){ 
 553         secdebug("coders", "attempting to save coders before we have loaded them!"); 
 557     if (engine
->codersNeedSaving
) { 
 558         CFDataRef derCoders 
= SOSEngineCopyCoders(engine
, error
); 
 559         bool ok 
= derCoders 
&& SOSDataSourceSetStateWithKey(engine
->dataSource
, txn
, kSOSEngineCoders
, 
 560                                                             kSOSEngineProtectionDomainClassA
, derCoders
, error
); 
 562             engine
->codersNeedSaving 
= false; 
 563             secnotice("coder", "saved coders: %@", engine
->coders
); 
 565         CFReleaseSafe(derCoders
); 
 570 bool SOSTestEngineSaveCoders(CFTypeRef engine
, SOSTransactionRef txn
, CFErrorRef 
*error
){ 
 571     return SOSEngineSaveCoders((SOSEngineRef
)engine
, txn
, error
); 
 573 #if !TARGET_IPHONE_SIMULATOR 
 575 static CFDictionaryRef 
SOSEngineCopyBasicState(SOSEngineRef engine
, CFErrorRef 
*error
) { 
 576     // Create a version of the in-memory engine state for saving to disk 
 577     CFMutableDictionaryRef state 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
 579         CFDictionarySetValue(state
, kSOSEngineIDKey
, engine
->myID
); 
 581         CFDictionarySetValue(state
, kSOSEnginePeerIDsKey
, engine
->peerIDs
); 
 582     if (engine
->lastTraceDate
) 
 583         CFDictionarySetValue(state
, kSOSEngineTraceDateKey
, engine
->lastTraceDate
); 
 585     SOSPersistCFIndex(state
, kSOSEngineStateVersionKey
, kCurrentEngineVersion
); 
 589 static bool SOSEngineDoSaveOneState(SOSEngineRef engine
, SOSTransactionRef txn
, CFStringRef key
, CFStringRef pdmn
, 
 590                                     CFDictionaryRef state
, CFErrorRef 
*error
) { 
 591     CFDataRef derState 
= CFPropertyListCreateDERData(kCFAllocatorDefault
, state
, error
); 
 592     bool ok 
= derState 
&& SOSDataSourceSetStateWithKey(engine
->dataSource
, txn
, key
, pdmn
, derState
, error
); 
 593     CFReleaseSafe(derState
); 
 597 static bool SOSEngineDoSave(SOSEngineRef engine
, SOSTransactionRef txn
, CFErrorRef 
*error
) { 
 600     CFDictionaryRef state 
= SOSEngineCopyBasicState(engine
, error
); 
 601     ok 
&= state 
&& SOSEngineDoSaveOneState(engine
, txn
, kSOSEngineStatev2
, kSOSEngineProtectionDomainClassD
, state
, error
); 
 602     CFReleaseNull(state
); 
 604     state 
= SOSEngineCopyPeerState_locked(engine
, error
); 
 605     ok 
&= state 
&& SOSEngineDoSaveOneState(engine
, txn
, kSOSEnginePeerStates
, kSOSEngineProtectionDomainClassD
, state
, error
); 
 606     CFReleaseNull(state
); 
 608     state 
= SOSEngineCopyEncodedManifestCache_locked(engine
, error
); 
 609     ok 
&= state 
&& SOSEngineDoSaveOneState(engine
, txn
, kSOSEngineManifestCache
, kSOSEngineProtectionDomainClassD
, state
, error
); 
 610     CFReleaseNull(state
); 
 612     ok 
&= SOSEngineSaveCoders(engine
, txn
, error
); 
 614     SOSEngineDeleteV0State(engine
, txn
, NULL
); 
 620 static bool SOSEngineSave(SOSEngineRef engine
, SOSTransactionRef txn
, CFErrorRef 
*error
) { 
 621     // Don't save engine state from tests 
 622     if (!engine
->dataSource
) 
 624 #if !TARGET_IPHONE_SIMULATOR 
 625     return SOSEngineDoSave(engine
, txn
, error
); 
 630 //---------------------------------------------------------------------------------------- 
 631 // MARK: Engine state v2 Load/Restore 
 632 //---------------------------------------------------------------------------------------- 
 634 // Restore the in-memory state of engine from saved state loaded from the db 
 635 static bool SOSEngineSetManifestCacheWithDictionary(SOSEngineRef engine
, CFDictionaryRef manifestCache
, CFErrorRef 
*error
) { 
 636     __block 
bool ok 
= true; 
 637     CFReleaseNull(engine
->manifestCache
); 
 639         engine
->manifestCache 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
 640         CFDictionaryForEach(manifestCache
, ^(const void *key
, const void *value
) { 
 641             CFDataRef data 
= (CFDataRef
)value
; 
 643                 SOSManifestRef mf 
= SOSManifestCreateWithData(data
, NULL
); 
 645                     CFDictionarySetValue(engine
->manifestCache
, key
, mf
); 
 654 static bool SOSEngineUpdateStateWithDictionary(SOSEngineRef engine
, CFDictionaryRef stateDict
, CFErrorRef 
*error
) { 
 658         // If kCurrentEngineVersion > 2, uncomment and fill in code below 
 659         CFIndex engineVersion 
= 0 ; 
 660         bool versionPresent 
= SOSPeerGetOptionalPersistedCFIndex(stateDict
, kSOSEngineStateVersionKey
, &engineVersion
); 
 661         if (versionPresent 
&& (engineVersion 
!= kCurrentEngineVersion
)) { 
 669 static bool SOSEngineSetStateWithDictionary(SOSEngineRef engine
, CFDictionaryRef stateDict
, CFErrorRef 
*error
) { 
 672         SOSEngineUpdateStateWithDictionary(engine
, stateDict
, error
); 
 673         CFRetainAssign(engine
->myID
, asString(CFDictionaryGetValue(stateDict
, kSOSEngineIDKey
), NULL
)); 
 674         CFRetainAssign(engine
->peerIDs
, asArray(CFDictionaryGetValue(stateDict
, kSOSEnginePeerIDsKey
), NULL
)); 
 675         CFRetainAssign(engine
->lastTraceDate
, asDate(CFDictionaryGetValue(stateDict
, kSOSEngineTraceDateKey
), NULL
)); 
 678     secnotice("engine", "%@", engine
); 
 682 static bool SOSEngineSetPeerStateWithDictionary(SOSEngineRef engine
, CFDictionaryRef peerStateDict
, CFErrorRef 
*error
) { 
 683     // Set the in-memory peer state using the dictionary version of the DER-encoded version from disk 
 684     CFMutableArrayRef untrustedPeers 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
 685     CFMutableArrayRef trustedPeersMetas 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
 686     SOSEngineApplyPeerState(engine
, asDictionary(peerStateDict
, NULL
)); 
 687     SOSEngineSynthesizePeerMetas(engine
, trustedPeersMetas
, untrustedPeers
); 
 688     SOSEngineSetPeers_locked(engine
, engine
->myID
, trustedPeersMetas
, untrustedPeers
); 
 689     CFReleaseNull(trustedPeersMetas
); 
 690     CFReleaseNull(untrustedPeers
); 
 694 CFMutableDictionaryRef 
derStateToDictionaryCopy(CFDataRef state
, CFErrorRef 
*error
) { 
 696     CFMutableDictionaryRef stateDict 
= NULL
; 
 698         const uint8_t *der 
= CFDataGetBytePtr(state
); 
 699         const uint8_t *der_end 
= der 
+ CFDataGetLength(state
); 
 700         ok 
= der 
= der_decode_dictionary(kCFAllocatorDefault
, kCFPropertyListMutableContainers
, (CFDictionaryRef 
*)&stateDict
, error
, der
, der_end
); 
 701         if (der 
&& der 
!= der_end
) { 
 702             ok 
= SOSErrorCreate(kSOSErrorDecodeFailure
, error
, NULL
, CFSTR("trailing %td bytes at end of state"), der_end 
- der
); 
 705             CFReleaseNull(stateDict
); 
 710 bool TestSOSEngineLoadCoders(CFTypeRef engine
, SOSTransactionRef txn
, CFErrorRef 
*error
) 
 712     return SOSEngineLoadCoders((SOSEngineRef
)engine
, txn
, error
); 
 715 static bool SOSEngineLoadCoders(SOSEngineRef engine
, SOSTransactionRef txn
, CFErrorRef 
*error
) { 
 716     // Read the serialized engine state from the datasource (aka keychain) and populate the in-memory engine 
 717     __block 
bool needPeerRegistration 
= false; 
 719     CFDataRef derCoders 
= NULL
; 
 720     CFMutableDictionaryRef codersDict 
= NULL
; 
 721     derCoders 
= SOSDataSourceCopyStateWithKey(engine
->dataSource
, kSOSEngineCoders
, kSOSEngineProtectionDomainClassA
, txn
, error
); 
 722     require_quiet(derCoders
, xit
); 
 723     codersDict 
= derStateToDictionaryCopy(derCoders
, error
); 
 724     require_quiet(codersDict
, xit
); 
 727      * Make sure all peer have coders 
 729     CFDictionaryForEach(engine
->peerMap
, ^(const void *peerID
, const void *peerState
) { 
 731          * Skip backup peer since they will never have coders 
 733         if (isString(peerID
) && CFStringHasSuffix(peerID
, CFSTR("-tomb"))) { 
 734             secnotice("coder", "Skipping coder check for peer: %@", peerID
); 
 738         CFTypeRef coderRef 
= CFDictionaryGetValue(codersDict
, peerID
); 
 740             CFDataRef coderData 
= asData(coderRef
, NULL
); 
 742                 CFErrorRef createError 
= NULL
; 
 743                 SOSCoderRef coder 
= SOSCoderCreateFromData(coderData
, &createError
); 
 745                     CFDictionaryAddValue(engine
->coders
, peerID
, coder
); 
 746                     secnotice("coder", "adding coder: %@ for peerid: %@", coder
, peerID
); 
 748                     secnotice("coder", "Coder for '%@' failed to create: %@", peerID
, createError
); 
 750                 CFReleaseNull(createError
); 
 751                 CFReleaseNull(coder
); 
 753                 // Needed a coder, didn't find one, notify the account to help us out. 
 754                 // Next attempt to sync will fix this 
 755                 secnotice("coder", "coder for %@ was not cf data: %@", peerID
, coderData
); 
 756                 needPeerRegistration 
= true; 
 759             secnotice("coder", "didn't find coder for peer: %@ engine dictionary: %@", peerID
, codersDict
); 
 760             needPeerRegistration 
= true; 
 764     secnotice("coder", "Will force peer registration: %s",needPeerRegistration 
? "yes" : "no"); 
 766     if (needPeerRegistration
) { 
 767         dispatch_queue_t queue 
= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0); 
 769         dispatch_async(queue
, ^{ 
 770             CFErrorRef eprError 
= NULL
; 
 771             if (!SOSCCProcessEnsurePeerRegistration_Server(&eprError
)) { 
 772                 secnotice("coder", "SOSCCProcessEnsurePeerRegistration failed with: %@", eprError
); 
 774             CFReleaseNull(eprError
); 
 778     engine
->haveLoadedCoders 
= true; 
 781     CFReleaseNull(derCoders
); 
 782     CFReleaseNull(codersDict
); 
 785 #if !TARGET_IPHONE_SIMULATOR 
 786 static bool SOSEngineDeleteV0State(SOSEngineRef engine
, SOSTransactionRef txn
, CFErrorRef 
*error
) { 
 787 //    SOSDataSourceDeleteStateWithKey(engine->dataSource, kSOSEngineState, kSOSEngineProtectionDomainClassD, txn, error); 
 789     // Create effectively empty state until delete is working 
 790     CFMutableDictionaryRef state 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
 792         CFDictionarySetValue(state
, kSOSEngineIDKey
, engine
->myID
); 
 793     CFDataRef derState 
= CFPropertyListCreateDERData(kCFAllocatorDefault
, state
, error
); 
 794     CFReleaseNull(state
); 
 796     bool ok 
= derState 
&& SOSDataSourceSetStateWithKey(engine
->dataSource
, txn
, kSOSEngineState
, kSOSEngineProtectionDomainClassD
, derState
, error
); 
 797     CFReleaseSafe(derState
); 
 801 static bool SOSEngineLoad(SOSEngineRef engine
, SOSTransactionRef txn
, CFErrorRef 
*error
) { 
 802     // Read the serialized engine state from the datasource (aka keychain) and populate the in-memory engine 
 804     CFDataRef basicEngineState 
= NULL
; 
 805     CFMutableDictionaryRef engineState 
= NULL
; 
 806     CFDictionaryRef manifestCache 
= NULL
; 
 807     CFDictionaryRef peerStateDict 
= NULL
; 
 808     CFMutableDictionaryRef codersDict 
= NULL
; 
 809     // Look for the v2 engine state first 
 810     basicEngineState 
= SOSDataSourceCopyStateWithKey(engine
->dataSource
, kSOSEngineStatev2
, kSOSEngineProtectionDomainClassD
, txn
, error
); 
 811     if (basicEngineState
) { 
 812         CFDataRef data 
= NULL
; 
 813         engineState 
= derStateToDictionaryCopy(basicEngineState
, error
); 
 815         data 
= SOSDataSourceCopyStateWithKey(engine
->dataSource
, kSOSEngineManifestCache
, kSOSEngineProtectionDomainClassD
, txn
, error
); 
 816         manifestCache 
= derStateToDictionaryCopy(data
, error
); 
 819         data 
= SOSDataSourceCopyStateWithKey(engine
->dataSource
, kSOSEnginePeerStates
, kSOSEngineProtectionDomainClassD
, txn
, error
); 
 820         peerStateDict 
= derStateToDictionaryCopy(data
, error
); 
 823         // Look for original V0 engine state next 
 824         CFDataRef v0EngineStateData 
= SOSDataSourceCopyStateWithKey(engine
->dataSource
, kSOSEngineState
, kSOSEngineProtectionDomainClassD
, txn
, error
); 
 825         if (v0EngineStateData
) { 
 826             engineState 
= derStateToDictionaryCopy(v0EngineStateData
, error
); 
 828                 manifestCache 
= CFRetainSafe(asDictionary(CFDictionaryGetValue(engineState
, kSOSEngineManifestCacheKey
), NULL
)); 
 829                 peerStateDict 
= CFRetainSafe(asDictionary(CFDictionaryGetValue(engineState
, kSOSEnginePeerStateKey
), NULL
)); 
 831             CFReleaseNull(v0EngineStateData
); 
 833         secnotice("coder", "Migrating from v0 engine state; dropping coders and forcing re-negotiation"); 
 834         SOSCCEnsurePeerRegistration(); 
 836         if (engine
->peerIDs
) { 
 837             SOSCCRequestSyncWithPeersList(engine
->peerIDs
); 
 841     ok 
= engineState 
&& SOSEngineSetStateWithDictionary(engine
, engineState
, error
); 
 843     ok 
&= SOSEngineSetManifestCacheWithDictionary(engine
, manifestCache
, error
); 
 845     ok 
&= peerStateDict 
&& SOSEngineSetPeerStateWithDictionary(engine
, peerStateDict
, error
); 
 847     CFReleaseSafe(basicEngineState
); 
 848     CFReleaseSafe(engineState
); 
 849     CFReleaseSafe(manifestCache
); 
 850     CFReleaseSafe(peerStateDict
); 
 851     CFReleaseSafe(codersDict
); 
 855 bool SOSTestEngineSaveWithDER(SOSEngineRef engine
, CFDataRef derState
, CFErrorRef 
*error
) { 
 860 bool SOSTestEngineSave(SOSEngineRef engine
, SOSTransactionRef txn
, CFErrorRef 
*error
) { 
 861     bool bx 
= SOSEngineSave(engine
, txn
, error
); 
 862     secnotice("test", "saved engine: %@", engine
); 
 866 bool SOSTestEngineLoad(SOSEngineRef engine
, SOSTransactionRef txn
, CFErrorRef 
*error
) { 
 867     bool bx 
= SOSEngineLoad(engine
, txn
, error
); 
 868     secnotice("test", "loaded engine: %@", engine
); 
 872 //---------------------------------------------------------------------------------------- 
 873 // MARK: Change Trackers and Peer Manifests 
 874 //---------------------------------------------------------------------------------------- 
 876 static SOSManifestRef 
SOSEngineCreateManifestWithViewNameSet_locked(SOSEngineRef engine
, CFSetRef viewNameSet
, CFErrorRef 
*error
) { 
 877     // TODO: Potentially tell all changeTrackers to track manifests (    //forall ct do SOSChangeTrackerSetConcrete(ct, true); 
 878     // and read the entire dataSource and pass all objects though the filter here, instead of 
 879     // forcing the datasource to be able to do "smart" queries 
 880     return SOSDataSourceCopyManifestWithViewNameSet(engine
->dataSource
, viewNameSet
, error
); 
 883 static SOSChangeTrackerRef 
SOSEngineCopyChangeTrackerWithViewNameSet_locked(SOSEngineRef engine
, CFSetRef viewNameSet
, CFErrorRef 
*error
) { 
 884     SOSChangeTrackerRef ct 
= (SOSChangeTrackerRef
)CFDictionaryGetValue(engine
->viewNameSet2ChangeTracker
, viewNameSet
); 
 886         SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("no change tracker for view set %@"), viewNameSet
); 
 887     return CFRetainSafe(ct
); 
 890 static SOSManifestRef 
SOSEngineCopyManifestWithViewNameSet_locked(SOSEngineRef engine
, CFSetRef viewNameSet
, CFErrorRef 
*error
) { 
 891     SOSChangeTrackerRef ct 
= SOSEngineCopyChangeTrackerWithViewNameSet_locked(engine
, viewNameSet
, error
); 
 895     SOSManifestRef manifest 
= SOSChangeTrackerCopyManifest(ct
, NULL
); 
 897         manifest 
= SOSEngineCreateManifestWithViewNameSet_locked(engine
, viewNameSet
, error
); // Do the SQL query 
 898         SOSChangeTrackerSetManifest(ct
, manifest
); 
 904 SOSManifestRef 
SOSEngineCopyLocalPeerManifest_locked(SOSEngineRef engine
, SOSPeerRef peer
, CFErrorRef 
*error
) { 
 905     return SOSEngineCopyManifestWithViewNameSet_locked(engine
, SOSPeerGetViewNameSet(peer
), error
); 
 908 #define withViewAndBackup(VIEW)  do { with(VIEW); if (!isTomb) with(VIEW ## _tomb); } while(0) 
 911 // Invoke with once for each view an object is in. 
 912 // TODO: Move this function into the DataSource 
 913 static void SOSEngineObjectWithView(SOSEngineRef engine
, SOSObjectRef object
, void (^with
)(CFStringRef view
)) { 
 914     // Filter items into v0 only view here 
 915     SecDbItemRef item 
= (SecDbItemRef
)object
; // TODO: Layer violation, breaks tests 
 916     if (isDictionary(object
)) { 
 917         CFTypeRef isTombValue 
= CFDictionaryGetValue((CFDictionaryRef
)object
, kSecAttrTombstone
); 
 918         bool isTomb 
= isTombValue 
&& CFBooleanGetValue(isTombValue
); 
 919         // We are in the test just assume v0 and v2 views. 
 920         withViewAndBackup(kSOSViewKeychainV0
); 
 921     } else if (SecDbItemIsSyncableOrCorrupted(item
)) { 
 922         const SecDbClass 
*iclass 
= SecDbItemGetClass(item
); 
 923         CFTypeRef pdmn 
= SecDbItemGetCachedValueWithName(item
, kSecAttrAccessible
); 
 924         if ((iclass 
== genp_class() || iclass 
== inet_class() || iclass 
== keys_class() || iclass 
== cert_class()) 
 926             && (CFEqual(pdmn
, kSecAttrAccessibleWhenUnlocked
) 
 927                 || CFEqual(pdmn
, kSecAttrAccessibleAfterFirstUnlock
) 
 928                 || CFEqual(pdmn
, kSecAttrAccessibleAlwaysPrivate
) 
 929                 || CFEqual(pdmn
, kSecAttrAccessibleWhenUnlockedThisDeviceOnly
) 
 930                 || CFEqual(pdmn
, kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
) 
 931                 || CFEqual(pdmn
, kSecAttrAccessibleAlwaysThisDeviceOnlyPrivate
))) 
 933             CFTypeRef tomb 
= SecDbItemGetCachedValueWithName(item
, kSecAttrTombstone
); 
 935             bool isTomb 
= (isNumber(tomb
) && CFNumberGetValue(tomb
, kCFNumberCharType
, &cvalue
) && cvalue 
== 1); 
 936             CFTypeRef viewHint 
= SecDbItemGetCachedValueWithName(item
, kSecAttrSyncViewHint
); 
 938             // Intecept CKKS-handled items here and short-circuit function 
 939             if(SOSViewHintInCKKSSystem(viewHint
)) { 
 943             if (viewHint 
== NULL
) { 
 944                 if (iclass 
== cert_class()) { 
 945                     withViewAndBackup(kSOSViewOtherSyncable
); 
 947                     if (!SecDbItemGetCachedValueWithName(item
, kSecAttrTokenID
)) { 
 948                         withViewAndBackup(kSOSViewKeychainV0
); 
 950                     CFTypeRef agrp 
= SecDbItemGetCachedValueWithName(item
, kSecAttrAccessGroup
); 
 951                     if (iclass 
== keys_class() && CFEqualSafe(agrp
, CFSTR("com.apple.security.sos"))) { 
 952                         withViewAndBackup(kSOSViewiCloudIdentity
); 
 953                     } else if (CFEqualSafe(agrp
, CFSTR("com.apple.cfnetwork"))) { 
 954                         withViewAndBackup(kSOSViewAutofillPasswords
); 
 955                     } else if (CFEqualSafe(agrp
, CFSTR("com.apple.safari.credit-cards"))) { 
 956                         withViewAndBackup(kSOSViewSafariCreditCards
); 
 957                     } else if (iclass 
== genp_class()) { 
 958                         if (CFEqualSafe(agrp
, CFSTR("apple")) && 
 959                             CFEqualSafe(SecDbItemGetCachedValueWithName(item
, kSecAttrService
), CFSTR("AirPort"))) { 
 960                             withViewAndBackup(kSOSViewWiFi
); 
 961                         } else if (CFEqualSafe(agrp
, CFSTR("com.apple.sbd"))) { 
 962                             withViewAndBackup(kSOSViewBackupBagV0
); 
 964                             withViewAndBackup(kSOSViewOtherSyncable
); // (genp) 
 967                         withViewAndBackup(kSOSViewOtherSyncable
); // (inet || keys) 
 973                     CFStringRef viewHintTomb 
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%@-tomb"), viewHint
); 
 976                         CFRelease(viewHintTomb
); 
 982         // TODO: general queries 
 985         CFArrayForEachC(engine
->views
, view
) { 
 986             bool inView 
= SOSViewQueryMatchItem(view
, item
); 
 988                 CFStringRef viewName 
= SOSViewCopyName(view
); 
 990                 CFReleaseSafe(viewName
); 
 998 // Deliver delayed notifiations of changes in keychain 
1002 SOSSendViewNotification(CFSetRef viewNotifications
) 
1004     CFNotificationCenterRef center 
= CFNotificationCenterGetDarwinNotifyCenter(); 
1006     CFSetForEach(viewNotifications
, ^(const void *value
) { 
1007         secinfo("view", "Sending view notification for view %@", value
); 
1009         CFStringRef str 
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("com.apple.security.view-change.%@"), value
); 
1013         CFNotificationCenterPostNotificationWithOptions(center
, str
, NULL
, NULL
, 0); 
1020 SOSArmViewNotificationEvents(CFSetRef viewNotifications
) 
1022     static CFMutableSetRef pendingViewNotifications
; 
1023     static dispatch_once_t onceToken
; 
1024     static dispatch_queue_t queue
; 
1026     dispatch_once(&onceToken
, ^{ 
1027         queue 
= dispatch_queue_create("ViewNotificationQueue", NULL
); 
1029     if (queue 
== NULL 
|| CFSetGetCount(viewNotifications
) == 0) 
1033      * PendingViewNotifications is only modified on queue. 
1034      * PendingViewNotifications is used as a signal if a timer is running. 
1036      * If a timer is running, new events are just added to the existing 
1037      * pendingViewNotifications. 
1040 #define DELAY_OF_NOTIFICATION_IN_NS    (NSEC_PER_SEC) 
1042     CFRetain(viewNotifications
); 
1044     dispatch_async(queue
, ^{ 
1045         if (pendingViewNotifications 
== NULL
) { 
1046             pendingViewNotifications 
= CFSetCreateMutableCopy(NULL
, 0, viewNotifications
); 
1048             dispatch_after(dispatch_time(DISPATCH_TIME_NOW
, (int64_t)DELAY_OF_NOTIFICATION_IN_NS
), queue
, ^{ 
1049                 SOSSendViewNotification(pendingViewNotifications
); 
1051                 // when timer hits, clear out set of modified views 
1052                 CFRelease(pendingViewNotifications
); 
1053                 pendingViewNotifications 
= NULL
; 
1056             CFSetUnion(pendingViewNotifications
, viewNotifications
); 
1058         CFRelease(viewNotifications
); 
1064 // SOSChangeMapper - Helper for SOSEngineUpdateChanges_locked 
1066 struct SOSChangeMapper 
{ 
1067     SOSEngineRef engine
; 
1068     SOSTransactionRef txn
; 
1069     SOSDataSourceTransactionPhase phase
; 
1070     SOSDataSourceTransactionSource source
; 
1071     CFMutableDictionaryRef ct2changes
; 
1072     CFMutableSetRef viewNotifications
; 
1075 static void SOSChangeMapperInit(struct SOSChangeMapper 
*cm
, SOSEngineRef engine
, SOSTransactionRef txn
, SOSDataSourceTransactionPhase phase
, SOSDataSourceTransactionSource source
) { 
1076     cm
->engine 
= engine
; 
1079     cm
->source 
= source
; 
1080     cm
->ct2changes 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
1081     cm
->viewNotifications 
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
); 
1084 static void SOSChangeMapperSendNotifications(struct SOSChangeMapper 
*cm
) 
1086     SOSArmViewNotificationEvents(cm
->viewNotifications
); 
1089 static void SOSChangeMapperFree(struct SOSChangeMapper 
*cm
) { 
1090     CFReleaseSafe(cm
->ct2changes
); 
1091     CFReleaseSafe(cm
->viewNotifications
); 
1094 static void SOSChangeMapperAddViewNotification(struct SOSChangeMapper 
*cm
, CFStringRef view
) 
1096     assert(isString(view
)); 
1098     // aggregate the PCS view into one notification 
1099     if (CFStringHasPrefix(view
, CFSTR("PCS-"))) { 
1100         view 
= CFSTR("PCS"); 
1102     CFSetSetValue(cm
->viewNotifications
, view
); 
1105 static void SOSChangeMapperAppendObject(struct SOSChangeMapper 
*cm
, SOSChangeTrackerRef ct
, bool isAdd
, CFTypeRef object
) { 
1106     CFMutableArrayRef changes 
= (CFMutableArrayRef
)CFDictionaryGetValue(cm
->ct2changes
, ct
); 
1108         changes 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
1109         CFDictionarySetValue(cm
->ct2changes
, ct
, changes
); 
1110         CFReleaseSafe(changes
); 
1112     isAdd 
? SOSChangesAppendAdd(changes
, object
) : SOSChangesAppendDelete(changes
, object
); 
1115 static bool SOSChangeMapperIngestChange(struct SOSChangeMapper 
*cm
, bool isAdd
, CFTypeRef change
) { 
1116     bool someoneCares 
= false; 
1117     if (isData(change
)) { 
1118         // TODO: Reenable assertion once the tests have been updated 
1120         // We got a digest for a deleted object. Our dataSource probably couldn't find 
1121         // an object with this digest, probably because it went missing, or it was 
1122         // discovered to be corrupted. 
1123         // Tell all our changeTrackers about this digest since we don't know who might need it. 
1124         CFDictionaryForEach(cm
->engine
->viewNameSet2ChangeTracker
, ^(const void *viewNameSet
, const void *ct
) { 
1125             SOSChangeMapperAppendObject(cm
, (SOSChangeTrackerRef
)ct
, isAdd
, change
); 
1127         someoneCares 
= CFDictionaryGetCount(cm
->engine
->viewNameSet2ChangeTracker
); 
1129         // We got an object let's figure out which views it's in and schedule it for 
1130         // delivery to all changeTrackers interested in any of those views. 
1131         SOSObjectRef object 
= (SOSObjectRef
)change
; 
1132         CFMutableSetRef changeTrackerSet 
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
); 
1133         // First gather all the changeTrackers interested in this object (eliminating dupes by collecting them in a set) 
1134         SOSEngineObjectWithView(cm
->engine
, object
, ^(CFStringRef viewName
) { 
1135             const void *ctorset 
= CFDictionaryGetValue(cm
->engine
->viewName2ChangeTracker
, viewName
); 
1136             if (isSet(ctorset
)) { 
1137                 CFSetForEach((CFSetRef
)ctorset
, ^(const void *ct
) { CFSetAddValue(changeTrackerSet
, ct
); }); 
1138             } else if (ctorset
) { 
1139                 CFSetAddValue(changeTrackerSet
, ctorset
); 
1143             SOSChangeMapperAddViewNotification(cm
, viewName
); 
1145         // Then append the object to the changes array in the ct2changes dictionary keyed by viewSet 
1146         CFSetForEach(changeTrackerSet
, ^(const void *ct
) { 
1147             SOSChangeMapperAppendObject(cm
, (SOSChangeTrackerRef
)ct
, isAdd
, object
); 
1149         someoneCares 
= CFSetGetCount(changeTrackerSet
); 
1150         CFReleaseSafe(changeTrackerSet
); 
1152     return someoneCares
; 
1155 static bool SOSChangeMapperSend(struct SOSChangeMapper 
*cm
, CFErrorRef 
*error
) { 
1156     __block 
bool ok 
= true; 
1157     CFDictionaryForEach(cm
->ct2changes
, ^(const void *ct
, const void *changes
) { 
1158         ok 
&= SOSChangeTrackerTrackChanges((SOSChangeTrackerRef
)ct
, cm
->engine
, cm
->txn
, cm
->source
, cm
->phase
, (CFArrayRef
)changes
, error
); 
1163 static bool SOSEngineUpdateChanges_locked(SOSEngineRef engine
, SOSTransactionRef txn
, SOSDataSourceTransactionPhase phase
, SOSDataSourceTransactionSource source
, CFArrayRef changes
, CFErrorRef 
*error
) 
1165     secnoticeq("engine", "%@: %s %s %ld changes, txn=%@, %p", engine
->myID
, phase 
== kSOSDataSourceTransactionWillCommit 
? "will-commit" : phase 
== kSOSDataSourceTransactionDidCommit 
? "did-commit" : "did-rollback", 
1166                source 
== kSOSDataSourceSOSTransaction 
? "sos" : 
1167                source 
== kSOSDataSourceCKKSTransaction 
? "ckks" : 
1168                source 
== kSOSDataSourceAPITransaction 
? "api" : 
1170                CFArrayGetCount(changes
), txn
, txn
); 
1173         case kSOSDataSourceTransactionDidRollback
: 
1174             ok 
&= SOSEngineLoad(engine
, txn
, error
); 
1176         case kSOSDataSourceTransactionDidCommit
: // Corruption causes us to process items at DidCommit 
1177         case kSOSDataSourceTransactionWillCommit
: 
1179             bool mappedItemChanged 
= false; 
1181             struct SOSChangeMapper cm
; 
1182             SOSChangeMapperInit(&cm
, engine
, txn
, phase
, source
); 
1183             SecDbEventRef event
; 
1184             CFArrayForEachC(changes
, event
) { 
1185                 CFTypeRef deleted 
= NULL
; 
1186                 CFTypeRef inserted 
= NULL
; 
1187                 SecDbEventGetComponents(event
, &deleted
, &inserted
, error
); 
1189                     bool someoneCares 
= SOSChangeMapperIngestChange(&cm
, false, deleted
); 
1191 #if TARGET_OS_EMBEDDED 
1192                         SecADAddValueForScalarKey(CFSTR("com.apple.security.sos.delete"), 1); 
1194                         mappedItemChanged 
= true; 
1198                     bool someoneCares 
= SOSChangeMapperIngestChange(&cm
, true, inserted
); 
1200 #if TARGET_OS_EMBEDDED 
1201                         if (deleted 
== NULL
) { 
1202                             SecADAddValueForScalarKey(CFSTR("com.apple.security.sos.add"), 1); 
1204                             SecADAddValueForScalarKey(CFSTR("com.apple.security.sos.update"), 1); 
1207                         mappedItemChanged 
= true; 
1209                     if (!someoneCares 
&& !isData(inserted
) && SecDbItemIsTombstone((SecDbItemRef
)inserted
) && !CFEqualSafe(SecDbItemGetValue((SecDbItemRef
)inserted
, &v7utomb
, NULL
), kCFBooleanTrue
)) { 
1210                         CFErrorRef localError 
= NULL
; 
1211                         // A tombstone was inserted but there is no changetracker that 
1213                         if (!SecDbItemDoDeleteSilently((SecDbItemRef
)inserted
, (SecDbConnectionRef
)txn
, &localError
)) { 
1214                             secerror("failed to delete tombstone %@ that no one cares about: %@", inserted
, localError
); 
1215                             CFReleaseNull(localError
); 
1221             ok 
&= SOSChangeMapperSend(&cm
, error
); 
1222             SOSChangeMapperSendNotifications(&cm
); // Trigger notifications for view that changes changed 
1223             SOSChangeMapperFree(&cm
); 
1225             if (ok 
&& phase 
== kSOSDataSourceTransactionWillCommit
) { 
1226                 // Only consider writing if we're in the WillCommit phase. 
1227                 // DidCommit phases happen outside the database lock and 
1228                 // writing to the DBConn will cause deadlocks. 
1229                 if (mappedItemChanged 
|| source 
== kSOSDataSourceSOSTransaction
) { 
1230                     // Write SOSEngine and SOSPeer state to disk 
1232                     if(!SecCKKSTestDisableSOS()) { 
1234                         secnotice("engine", "saving engine state"); 
1235                         ok 
&= SOSEngineSave(engine
, txn
, error
); 
1237                         if (kSOSDataSourceAPITransaction 
== source 
|| kSOSDataSourceCKKSTransaction 
== source
) 
1238                             SOSCCRequestSyncWithPeersList(engine
->peerIDs
); 
1243                     secinfo("engine", "Not saving engine state, nothing changed."); 
1253 static void SOSEngineSetNotifyPhaseBlock(SOSEngineRef engine
) { 
1254     SOSDataSourceAddNotifyPhaseBlock(engine
->dataSource
, ^(SOSDataSourceRef ds
, SOSTransactionRef txn
, SOSDataSourceTransactionPhase phase
, SOSDataSourceTransactionSource source
, CFArrayRef changes
) { 
1255         dispatch_sync(engine
->queue
, ^{ 
1256             CFErrorRef localError 
= NULL
; 
1257             if (!SOSEngineUpdateChanges_locked(engine
, txn
, phase
, source
, changes
, &localError
)) { 
1258                 secerror("updateChanged failed: %@", localError
); 
1260             CFReleaseSafe(localError
); 
1265 #if 0 // TODO: update these checks 
1266 static void SOSEngineCircleChanged_sanitycheck(SOSEngineRef engine
, CFStringRef myPeerID
, CFArrayRef trustedPeers
, CFArrayRef untrustedPeers
) { 
1268     CFMutableArrayRef addedPeers 
= CFArrayCreateDifference(kCFAllocatorDefault
, trustedPeers
, engine
->peerIDs
); 
1269     CFMutableArrayRef deletedPeers 
= CFArrayCreateDifference(kCFAllocatorDefault
, engine
->peerIDs
, trustedPeers
); 
1270     CFMutableArrayRef addedUntrustedPeers 
= CFArrayCreateDifference(kCFAllocatorDefault
, untrustedPeers
, engine
->peerIDs
); 
1271     CFMutableArrayRef deletedUntrustedPeers 
= CFArrayCreateDifference(kCFAllocatorDefault
, engine
->peerIDs
, untrustedPeers
); 
1273     CFStringRef tpDesc 
= SOSPeerIDArrayCreateString(trustedPeers
); 
1274     CFStringRef apDesc 
= SOSPeerIDArrayCreateString(addedPeers
); 
1275     CFStringRef dpDesc 
= SOSPeerIDArrayCreateString(deletedPeers
); 
1276     CFStringRef aupDesc 
= SOSPeerIDArrayCreateString(addedUntrustedPeers
); 
1277     CFStringRef dupDesc 
= SOSPeerIDArrayCreateString(deletedUntrustedPeers
); 
1278     secnotice("engine", "trusted %@ added %@ removed %@ add ut: %@ rem ut: %@", tpDesc
, apDesc
, dpDesc
, aupDesc
, dupDesc
); 
1279     CFReleaseSafe(dupDesc
); 
1280     CFReleaseSafe(aupDesc
); 
1281     CFReleaseSafe(dpDesc
); 
1282     CFReleaseSafe(apDesc
); 
1283     CFReleaseSafe(tpDesc
); 
1286     // Ensure SOSAccount isn't giving us the runaround. 
1287     // Assert that trustedPeers, untrustedPeers and myPeerId are disjoint sets 
1289         CFMutableArrayRef allTrustedPeers 
= CFArrayCreateDifference(kCFAllocatorDefault
, trustedPeers
, untrustedPeers
); 
1290         assert(CFEqual(trustedPeers
, allTrustedPeers
)); 
1291         CFReleaseSafe(allTrustedPeers
); 
1292         assert(!CFArrayContainsValue(trustedPeers
, CFRangeMake(0, CFArrayGetCount(trustedPeers
)), myPeerID
)); 
1294     if (untrustedPeers
) { 
1295         CFMutableArrayRef allUntrustedPeers 
= CFArrayCreateDifference(kCFAllocatorDefault
, untrustedPeers
, trustedPeers
); 
1296         assert(CFEqual(untrustedPeers
, allUntrustedPeers
)); 
1297         CFReleaseSafe(allUntrustedPeers
); 
1298         assert(!CFArrayContainsValue(untrustedPeers
, CFRangeMake(0, CFArrayGetCount(trustedPeers
)), myPeerID
)); 
1301     CFReleaseNull(deletedUntrustedPeers
); 
1302     CFReleaseNull(addedUntrustedPeers
); 
1303     CFReleaseNull(deletedPeers
); 
1304     CFReleaseNull(addedPeers
); 
1306     // End of logging and asertions, actual code here. 
1310 static SOSChangeTrackerRef 
SOSReferenceAndGetChangeTracker(CFDictionaryRef lookup
, CFMutableDictionaryRef referenced
, CFSetRef viewNameSet
) { 
1311     SOSChangeTrackerRef ct 
= (SOSChangeTrackerRef
)CFDictionaryGetValue(referenced
, viewNameSet
); 
1313         ct 
= (SOSChangeTrackerRef
)CFDictionaryGetValue(lookup
, viewNameSet
); 
1315             SOSChangeTrackerResetRegistration(ct
); 
1316             CFDictionarySetValue(referenced
, viewNameSet
, ct
); 
1318             ct 
= SOSChangeTrackerCreate(kCFAllocatorDefault
, false, NULL
, NULL
); 
1319             CFDictionarySetValue(referenced
, viewNameSet
, ct
); 
1326 static void CFStringAppendPeerIDAndViews(CFMutableStringRef desc
, CFStringRef peerID
, CFSetRef vns
) { 
1327     CFStringSetPerformWithDescription(vns
, ^(CFStringRef description
) { 
1328         CFStringAppendFormat(desc
, NULL
, CFSTR(" %@ (%@)"), peerID
, description
); 
1332 // Must be called after updating viewNameSet2ChangeTracker 
1333 static void SOSEngineUpdateViewName2ChangeTracker(SOSEngineRef engine
) { 
1334     // Create the mapping from viewName -> ChangeTracker used for lookup during change notification 
1335     CFMutableDictionaryRef newViewName2ChangeTracker 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
1336     CFDictionaryForEach(engine
->viewNameSet2ChangeTracker
, ^(const void *viewNameSet
, const void *ct
) { 
1337         CFSetForEach(viewNameSet
, ^(const void *viewName
) { 
1338             const void *ctorset 
= NULL
; 
1339             if (CFDictionaryGetValueIfPresent(newViewName2ChangeTracker
, viewName
, &ctorset
)) { 
1340                 if (isSet(ctorset
)) { 
1341                     CFSetAddValue((CFMutableSetRef
)ctorset
, ct
); 
1342                 } else if (!CFEqual(ct
, ctorset
)) { 
1343                     CFMutableSetRef set 
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
); 
1344                     CFSetAddValue(set
, ctorset
); 
1345                     CFSetAddValue(set
, ct
); 
1346                     CFDictionaryReplaceValue(newViewName2ChangeTracker
, viewName
, set
); 
1350                 CFDictionarySetValue(newViewName2ChangeTracker
, viewName
, ct
); 
1354     CFAssignRetained(engine
->viewName2ChangeTracker
, newViewName2ChangeTracker
); 
1357 static void SOSEngineSetBackupBag(SOSEngineRef engine
, SOSObjectRef bagItem
); 
1359 // This is called only if we are in a circle and we should listen for keybag changes 
1360 static void SOSEngineRegisterBackupBagV0Tracker(SOSEngineRef engine
, CFMutableDictionaryRef newViewNameSet2ChangeTracker
, CFMutableStringRef desc
) { 
1361     SOSChangeTrackerRef bbct 
= SOSReferenceAndGetChangeTracker(engine
->viewNameSet2ChangeTracker
, newViewNameSet2ChangeTracker
, SOSViewsGetV0BackupBagViewSet()); 
1362     SOSChangeTrackerRegisterChangeUpdate(bbct
, ^bool(SOSChangeTrackerRef ct
, SOSEngineRef engine
, SOSTransactionRef txn
, SOSDataSourceTransactionSource source
, SOSDataSourceTransactionPhase phase
, CFArrayRef changes
, CFErrorRef 
*error
) { 
1363         SOSChangeRef change
; 
1364         CFArrayForEachC(changes
, change
) { 
1365             CFTypeRef object 
= NULL
; 
1366             bool isAdd 
= SOSChangeGetObject(change
, &object
); 
1367             SecDbItemRef dbi 
= (SecDbItemRef
)object
; 
1368             if (!isData(object
) && 
1369                 CFEqualSafe(SecDbItemGetCachedValueWithName(dbi
, kSecAttrService
), CFSTR("SecureBackupService")) && 
1370                 CFEqualSafe(SecDbItemGetCachedValueWithName(dbi
, kSecAttrAccessible
), kSecAttrAccessibleWhenUnlocked
) && 
1371                 CFEqualSafe(SecDbItemGetCachedValueWithName(dbi
, kSecAttrAccount
), CFSTR("SecureBackupPublicKeybag"))) { 
1372                 SOSEngineSetBackupBag(engine
, isAdd 
? (SOSObjectRef
)object 
: NULL
); 
1379 static void SOSEngineReferenceBackupPeer(SOSEngineRef engine
, CFStringRef peerID
, CFSetRef viewNameSet
, CFDataRef keyBag
, CFMutableDictionaryRef newViewNameSet2ChangeTracker
, CFMutableDictionaryRef newPeerMap
) { 
1380     CFTypeRef oldEntry 
= CFDictionaryGetValue(engine
->peerMap
, peerID
); 
1381     CFTypeRef newEntry 
= SOSPeerOrStateSetViewsKeyBagAndCreateCopy(oldEntry
, viewNameSet
, keyBag
); 
1383         if (isDictionary(newEntry
)) { 
1384             // Backup peers, are always inflated 
1385             CFAssignRetained(newEntry
, SOSPeerCreateWithState(engine
, peerID
, newEntry
, NULL
)); 
1386             // If !oldEntry this is an edge (first creation of a peer). 
1388                 SOSPeerKeyBagDidChange((SOSPeerRef
)newEntry
); 
1391         CFDictionarySetValue(newPeerMap
, peerID
, newEntry
); 
1392         CFRelease(newEntry
); 
1395             SOSChangeTrackerRef ct 
= SOSReferenceAndGetChangeTracker(engine
->viewNameSet2ChangeTracker
, newViewNameSet2ChangeTracker
, viewNameSet
); 
1397             SOSChangeTrackerUpdatesChanges child 
= Block_copy(^bool(SOSChangeTrackerRef ct
, SOSEngineRef engine
, SOSTransactionRef txn
, SOSDataSourceTransactionSource source
, SOSDataSourceTransactionPhase phase
, CFArrayRef changes
, CFErrorRef 
*error
) { 
1398                 return SOSPeerDataSourceWillChange((SOSPeerRef
)newEntry
, SOSEngineGetDataSource(engine
), source
, changes
, error
); 
1401             SOSChangeTrackerRegisterChangeUpdate(ct
, child
); 
1402             Block_release(child
); 
1407 static void SOSEngineReferenceSyncPeer(SOSEngineRef engine
, CFStringRef peerID
, CFSetRef viewNameSet
, CFMutableDictionaryRef newViewNameSet2ChangeTracker
, CFMutableDictionaryRef newPeerMap
) { 
1408     CFTypeRef newEntry 
= SOSPeerOrStateSetViewsKeyBagAndCreateCopy(CFDictionaryGetValue(engine
->peerMap
, peerID
), viewNameSet
, NULL
); 
1410         SOSChangeTrackerRef ct 
= SOSReferenceAndGetChangeTracker(engine
->viewNameSet2ChangeTracker
, newViewNameSet2ChangeTracker
, viewNameSet
); 
1411         // Standard peer, inflated on demand 
1412         SOSChangeTrackerUpdatesManifests trackManifest
; 
1413         if (isDictionary(newEntry
)) { 
1414             // Uninflated peer, inflate on first notification. 
1415             trackManifest 
= ^bool(SOSChangeTrackerRef ct
, SOSEngineRef engine
, SOSTransactionRef txn
, SOSDataSourceTransactionSource source
, SOSDataSourceTransactionPhase phase
, SOSManifestRef removals
, SOSManifestRef additions
, CFErrorRef 
*error
) { 
1416                 CFErrorRef localError 
= NULL
; 
1417                 SOSPeerRef peer 
= SOSEngineCopyPeerWithID_locked(engine
, peerID
, &localError
); 
1420                     secerror("%@: peer failed to inflate: %@", peerID
, localError
); 
1421                     CFReleaseSafe(localError
); 
1424                     ok 
= SOSPeerDataSourceWillCommit(peer
, source
, removals
, additions
, error
); 
1426                 CFReleaseSafe(peer
); 
1430             // Inflated peer, just forward the changes to the peer 
1431             trackManifest 
= ^bool(SOSChangeTrackerRef ct
, SOSEngineRef engine
, SOSTransactionRef txn
, SOSDataSourceTransactionSource source
, SOSDataSourceTransactionPhase phase
, SOSManifestRef removals
, SOSManifestRef additions
, CFErrorRef 
*error
) { 
1432                 return SOSPeerDataSourceWillCommit((SOSPeerRef
)newEntry
, source
, removals
, additions
, error
); 
1435         SOSChangeTrackerUpdatesManifests trackManifestCopy 
= Block_copy(trackManifest
); 
1436         SOSChangeTrackerRegisterManifestUpdate(ct
, trackManifestCopy
); 
1437         Block_release(trackManifestCopy
); 
1439         CFDictionarySetValue(newPeerMap
, peerID
, newEntry
); 
1440         CFRelease(newEntry
); 
1445 static void SOSEngineReferenceTrustedPeer(SOSEngineRef engine
, SOSPeerMetaRef peerMeta
, CFMutableDictionaryRef newViewNameSet2ChangeTracker
, CFMutableDictionaryRef newPeerMap
, CFMutableArrayRef peerIDs
, CFMutableStringRef desc
) { 
1446     CFSetRef viewNameSet 
= NULL
; 
1447     CFDataRef keyBag 
= NULL
; 
1448     CFStringRef peerID 
= SOSPeerMetaGetComponents(peerMeta
, &viewNameSet
, &keyBag
, NULL
); 
1449     // We trust peerID so append it to peerIDs 
1450     CFArrayAppendValue(peerIDs
, peerID
); 
1451     if (desc
) CFStringAppendPeerIDAndViews(desc
, peerID
, viewNameSet
); 
1452     // Update the viewNameSet for this peer, to appease tests, default to a viewset of the V0 view. 
1454         viewNameSet 
= SOSViewsGetV0ViewSet(); 
1456     // Always inflate backup peers, since they need to register with their changeTrackers right away. 
1458         SOSEngineReferenceBackupPeer(engine
, peerID
, viewNameSet
, keyBag
, newViewNameSet2ChangeTracker
, newPeerMap
); 
1460         SOSEngineReferenceSyncPeer(engine
, peerID
, viewNameSet
, newViewNameSet2ChangeTracker
, newPeerMap
); 
1464 static CFDataRef 
SOSEngineLoadV0KeyBag(SOSEngineRef engine
, CFErrorRef 
*error
) { 
1465     // Return the keybag for the given peerID. 
1468      kSecAttrAccessGroup ==> CFSTR("com.apple.sbd") 
1469      kSecAttrAccessible  ==> kSecAttrAccessibleWhenUnlocked 
1470      kSecAttrAccount     ==> CFSTR("SecureBackupPublicKeybag") 
1471      kSecAttrService     ==> CFSTR("SecureBackupService") 
1474     CFMutableDictionaryRef keys 
= CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault
, 
1475                                             kSecAttrAccessGroup
, CFSTR("com.apple.sbd"), 
1476                                             kSecAttrAccount
, CFSTR("SecureBackupPublicKeybag"), 
1477                                             kSecAttrService
, CFSTR("SecureBackupService"), 
1478                                             kSecAttrAccessible
, kSecAttrAccessibleWhenUnlocked
, 
1479                                             kSecAttrSynchronizable
, kCFBooleanTrue
, 
1482     CFDataRef keybag 
= engine
->dataSource
->dsCopyItemDataWithKeys(engine
->dataSource
, keys
, error
); 
1483     CFReleaseSafe(keys
); 
1488 static void SOSEngineReferenceBackupV0Peer(SOSEngineRef engine
, CFMutableDictionaryRef newViewNameSet2ChangeTracker
, CFMutableDictionaryRef newPeerMap
, CFMutableArrayRef newPeerIDs
, CFMutableStringRef desc
) { 
1489     SOSPeerRef backupPeer 
= (SOSPeerRef
)CFDictionaryGetValue(engine
->peerMap
, kSOSViewKeychainV0_tomb
); 
1490     CFDataRef bag 
= NULL
; 
1491     if (backupPeer 
&& CFGetTypeID(backupPeer
) == SOSPeerGetTypeID()) { 
1492         bag 
= SOSPeerGetKeyBag(backupPeer
); 
1494         CFErrorRef localError 
= NULL
; 
1495         if (!(bag 
= SOSEngineLoadV0KeyBag(engine
, &localError
))) { 
1496             secnotice("engine", "No keybag found for v0 backup peer: %@", localError
); 
1497             CFReleaseSafe(localError
); 
1500     SOSEngineReferenceBackupPeer(engine
, kSOSViewKeychainV0_tomb
, SOSViewsGetV0BackupViewSet(), bag
, newViewNameSet2ChangeTracker
, newPeerMap
); 
1503 static void SOSEngineReferenceTrustedPeers(SOSEngineRef engine
, CFMutableDictionaryRef newViewNameSet2ChangeTracker
, CFMutableDictionaryRef newPeerMap
, CFMutableArrayRef newPeerIDs
, CFArrayRef trustedPeerMetas
, CFMutableStringRef desc
) { 
1504     // Then update the views for all trusted peers and add them to newPeerMap. 
1505     if (trustedPeerMetas 
!= NULL 
&& CFArrayGetCount(trustedPeerMetas
) != 0) { 
1506         if (desc
) CFStringAppend(desc
, CFSTR(" trusted")); 
1507         // Remake engine->peerIDs 
1508         SOSPeerMetaRef peerMeta
; 
1509         CFArrayForEachC(trustedPeerMetas
, peerMeta
) { 
1510             SOSEngineReferenceTrustedPeer(engine
, peerMeta
, newViewNameSet2ChangeTracker
, newPeerMap
, newPeerIDs
, desc
); 
1515 static void SOSEngineReferenceUntrustedPeers(SOSEngineRef engine
, CFMutableDictionaryRef newPeerMap
, CFArrayRef untrustedPeerMetas
, CFMutableStringRef description
) { 
1516     // Copy any untrustedPeers to newPeerMap as well if we have a state 
1517     // for them, if not no big deal.  We also serialize all the untrustedPeers 
1518     // since they don't need to be deserializable 
1519     if (untrustedPeerMetas 
!= NULL 
&& CFArrayGetCount(untrustedPeerMetas
) != 0) { 
1520         if (description
) CFStringAppend(description
, CFSTR(" untrusted")); 
1521         SOSPeerMetaRef peerMeta
; 
1522         CFArrayForEachC(untrustedPeerMetas
, peerMeta
) { 
1523             CFSetRef views 
= NULL
; 
1524             CFStringRef peerID 
= SOSPeerMetaGetComponents(peerMeta
, &views
, NULL
, NULL
); 
1525             if (description
) CFStringAppendPeerIDAndViews(description
, peerID
, views
); 
1526             CFSetRef nviews 
= NULL
; 
1528                 views 
= nviews 
= CFSetCreate(kCFAllocatorDefault
, NULL
, 0, &kCFTypeSetCallBacks
); 
1529             CFTypeRef newEntry 
= SOSPeerOrStateSetViewsAndCopyState(CFDictionaryGetValue(engine
->peerMap
, peerID
), views
); 
1530             CFReleaseSafe(nviews
); 
1532                 CFDictionarySetValue(newPeerMap
, peerID
, newEntry
); 
1533                 CFReleaseSafe(newEntry
); 
1539 static void SOSEngineReferenceChangeTrackers(SOSEngineRef engine
, CFArrayRef trustedPeerMetas
, CFArrayRef untrustedPeerMetas
, CFMutableStringRef desc
) { 
1540     CFMutableArrayRef newPeerIDs 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
1541     CFMutableDictionaryRef newPeerMap 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
1542     CFMutableDictionaryRef newViewNameSet2ChangeTracker 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
1545         // We have an engineID => in a circle (with 0 or more peers) 
1546         // Ensure we have a v0 backup peer and it's listening for backup bag changes 
1547         SOSEngineReferenceBackupV0Peer(engine
, newViewNameSet2ChangeTracker
, newPeerMap
, newPeerIDs
, desc
); 
1548         SOSEngineRegisterBackupBagV0Tracker(engine
, newViewNameSet2ChangeTracker
, desc
); 
1550     SOSEngineReferenceTrustedPeers(engine
, newViewNameSet2ChangeTracker
, newPeerMap
, newPeerIDs
, trustedPeerMetas
, desc
); 
1551     SOSEngineReferenceUntrustedPeers(engine
, newPeerMap
, untrustedPeerMetas
, desc
); 
1553     CFAssignRetained(engine
->peerIDs
, newPeerIDs
); 
1554     CFAssignRetained(engine
->peerMap
, newPeerMap
); 
1555     CFAssignRetained(engine
->viewNameSet2ChangeTracker
, newViewNameSet2ChangeTracker
); 
1556     SOSEngineUpdateViewName2ChangeTracker(engine
); 
1559 // Return true iff peers or views changed 
1560 static bool SOSEngineSetPeers_locked(SOSEngineRef engine
, SOSPeerMetaRef myPeerMeta
, CFArrayRef trustedPeerMetas
, CFArrayRef untrustedPeerMetas
) { 
1561     CFErrorRef error 
= NULL
; 
1562     CFSetRef myViews 
= NULL
; 
1563     CFDataRef myKeyBag 
= NULL
; 
1564     CFMutableStringRef desc 
= CFStringCreateMutableCopy(kCFAllocatorDefault
, 0, CFSTR("me")); 
1565     CFStringRef myPeerID 
= myPeerMeta 
? SOSPeerMetaGetComponents(myPeerMeta
, &myViews
, &myKeyBag
, &error
) : NULL
; 
1566     if (desc
) CFStringAppendPeerIDAndViews(desc
, myPeerID
, myViews
); 
1568     // Start with no coders 
1569     CFMutableDictionaryRef codersToKeep 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
1571     if(engine
->haveLoadedCoders
){ 
1572         // If we're the same peerID we keep known peers (both trusted and untrusted) 
1573         if (CFEqualSafe(myPeerID
, engine
->myID
)) { 
1574             void (^copyPeerMetasCoder
)(const void *value
) = ^(const void*element
) { 
1575                 SOSPeerMetaRef peerMeta 
= (SOSPeerMetaRef
) element
; 
1577                 CFStringRef currentID 
= SOSPeerMetaGetComponents(peerMeta
, NULL
, NULL
, NULL
); 
1579                     SOSCoderRef coder 
= (SOSCoderRef
) CFDictionaryGetValue(engine
->coders
, currentID
); 
1581                         CFDictionarySetValue(codersToKeep
, currentID
, coder
); 
1586             if (trustedPeerMetas
) { 
1587                 CFArrayForEach(trustedPeerMetas
, copyPeerMetasCoder
); 
1589             if (untrustedPeerMetas
) { 
1590                 CFArrayForEach(untrustedPeerMetas
, copyPeerMetasCoder
); 
1594         engine
->codersNeedSaving 
= true; 
1596     CFRetainAssign(engine
->myID
, myPeerID
); 
1597     CFTransferRetained(engine
->coders
, codersToKeep
); 
1599     // Remake engine->peerMap from both trusted and untrusted peers 
1600     SOSEngineReferenceChangeTrackers(engine
, trustedPeerMetas
, untrustedPeerMetas
, desc
); 
1602     secnotice("engine", "%@", desc
); 
1603     CFReleaseSafe(desc
); 
1607 static void SOSEngineApplyPeerState(SOSEngineRef engine
, CFDictionaryRef peerStateMap
) { 
1608     if (peerStateMap
) CFDictionaryForEach(peerStateMap
, ^(const void *peerID
, const void *peerState
) { 
1609         CFTypeRef mapEntry 
= CFDictionaryGetValue(engine
->peerMap
, peerID
); 
1610         if (mapEntry 
&& CFGetTypeID(mapEntry
) == SOSPeerGetTypeID()) { 
1611             // Update the state of any already inflated peers 
1612             SOSPeerRef peer 
= (SOSPeerRef
)mapEntry
; 
1613             CFErrorRef localError 
= NULL
; 
1614             if (!SOSPeerSetState(peer
, engine
, peerState
, &localError
)) { 
1615                 CFStringRef stateHex 
= NULL
; 
1616                 stateHex 
= CFDataCopyHexString(peerState
); 
1617                 secerror("peer: %@: bad state: %@ in engine state: %@", peerID
, localError
, stateHex
); 
1618                 CFReleaseSafe(stateHex
); 
1619                 CFReleaseNull(localError
); 
1620                 // Possibly ask for an ensurePeerRegistration so we have a good list of peers again. 
1623             // Just record the state for non inflated peers for now. 
1624             CFDictionarySetValue(engine
->peerMap
, peerID
, peerState
); 
1629 static void SOSEngineSynthesizePeerMetas(SOSEngineRef engine
, CFMutableArrayRef trustedPeersMetas
, CFMutableArrayRef untrustedPeers
) { 
1630     CFSetRef trustedPeerSet 
= engine
->peerIDs 
? CFSetCreateCopyOfArrayForCFTypes(engine
->peerIDs
) : NULL
; 
1631     CFDictionaryForEach(engine
->peerMap
, ^(const void *peerID
, const void *peerState
) { 
1632         SOSPeerMetaRef meta 
= NULL
; 
1633         if (peerState 
&& CFGetTypeID(peerState
) == SOSPeerGetTypeID()) { 
1634             SOSPeerRef peer 
= (SOSPeerRef
)peerState
; 
1635             meta 
= SOSPeerMetaCreateWithComponents(peerID
, SOSPeerGetViewNameSet(peer
), SOSPeerGetKeyBag(peer
)); 
1637             // We don't need to add the meta for the backup case, since 
1638             //   SOSEngineReferenceBackupV0Peer will do the right thing 
1639             if (!CFEqualSafe(peerID
, kSOSViewKeychainV0_tomb
)) { 
1640                 meta 
= SOSPeerMetaCreateWithState(peerID
, peerState
); 
1643         // Any peer in peerStateMap that is not in trustedPeers is an untrustedPeer unless it's the v0 backup peer 
1644         if ((trustedPeerSet 
&& CFSetContainsValue(trustedPeerSet
, peerID
)) || CFEqualSafe(peerID
, kSOSViewKeychainV0_tomb
)) { 
1646                 CFArrayAppendValue(trustedPeersMetas
, meta
); 
1649             CFArrayAppendValue(untrustedPeers
, peerID
); 
1651         CFReleaseNull(meta
); 
1653     CFReleaseNull(trustedPeerSet
); 
1656 static void SOSEngineSetBackupBag(SOSEngineRef engine
, SOSObjectRef bagItem
) { 
1657     CFMutableStringRef desc 
= NULL
; 
1658     SOSPeerRef backupPeer 
= SOSEngineCopyPeerWithID_locked(engine
, kSOSViewKeychainV0_tomb
, NULL
); 
1659     CFDataRef keybag 
= NULL
; 
1661         keybag 
= SecDbItemGetValue((SecDbItemRef
)bagItem
, &v6v_Data
, NULL
); 
1664     // Since SOSPeerSetKeyBag() doesn't notify on the edge from NULL->initial keybag, since 
1665     // that is the right behaviour for non v0 backup peers, we need to do it here for the v0 peer. 
1666     bool hadBag 
= SOSPeerGetKeyBag(backupPeer
); 
1667     SOSPeerSetKeyBag(backupPeer
, keybag
); 
1669         SOSPeerKeyBagDidChange(backupPeer
); 
1671     CFReleaseSafe(backupPeer
); 
1673     CFMutableArrayRef untrustedPeerMetas 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
1674     CFMutableArrayRef trustedPeersMetas 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
1675     SOSEngineSynthesizePeerMetas(engine
, trustedPeersMetas
, untrustedPeerMetas
); 
1676     SOSEngineReferenceChangeTrackers(engine
, trustedPeersMetas
, untrustedPeerMetas
, desc
); 
1677     CFReleaseSafe(trustedPeersMetas
); 
1678     CFReleaseSafe(untrustedPeerMetas
); 
1681 #define SECONDS_PER_DAY  (86400.0) 
1683 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) 
1684 #define TRACE_INTERVAL (7 * SECONDS_PER_DAY) 
1685 #elif (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR) 
1686 #define TRACE_INTERVAL (1 * SECONDS_PER_DAY) 
1689 #ifdef TRACE_INTERVAL 
1690 static void SOSEngineCloudKeychainTrace(SOSEngineRef engine
, CFAbsoluteTime now
) { 
1691     CFAssignRetained(engine
->lastTraceDate
, CFDateCreate(kCFAllocatorDefault
, now
)); 
1692     CFIndex num_peers 
= engine
->peerIDs 
? 1 + CFArrayGetCount(engine
->peerIDs
) : 1; 
1693     SOSManifestRef manifest 
= SOSEngineCopyManifestWithViewNameSet_locked(engine
, SOSViewsGetV0ViewSet(), NULL
); 
1695         manifest 
= SOSDataSourceCopyManifestWithViewNameSet(engine
->dataSource
, SOSViewsGetV0ViewSet(), NULL
); 
1696     size_t num_items 
= SOSManifestGetCount(manifest
); 
1697     CFReleaseSafe(manifest
); 
1699     struct _SecServerKeyStats genpStats 
= { }; 
1700     struct _SecServerKeyStats inetStats 
= { }; 
1701     struct _SecServerKeyStats keysStats 
= { }; 
1703     _SecServerGetKeyStats(genp_class(), &genpStats
); 
1704     _SecServerGetKeyStats(inet_class(), &inetStats
); 
1705     _SecServerGetKeyStats(keys_class(), &keysStats
); 
1707     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), ^{ 
1708         CloudKeychainTrace(num_peers
, num_items
, &genpStats
, &inetStats
, &keysStats
); 
1713 static void SOSEngineCloudKeychainTraceIfNeeded(SOSEngineRef engine
) { 
1714 #ifdef TRACE_INTERVAL 
1717     CFAbsoluteTime now 
= CFAbsoluteTimeGetCurrent(); 
1718     if (engine
->lastTraceDate
) { 
1719         CFAbsoluteTime lastTraceTime 
= CFDateGetAbsoluteTime(engine
->lastTraceDate
); 
1720         if ((now 
- lastTraceTime
) >= TRACE_INTERVAL
) { 
1721             SOSEngineCloudKeychainTrace(engine
, now
); 
1724         SOSEngineCloudKeychainTrace(engine
, now
); 
1730 static bool SOSEngineCircleChanged_locked(SOSEngineRef engine
, SOSPeerMetaRef myPeerMeta
, CFArrayRef trustedPeers
, CFArrayRef untrustedPeers
) { 
1731     // Sanity check params 
1732 //    SOSEngineCircleChanged_sanitycheck(engine, myPeerID, trustedPeers, untrustedPeers); 
1734     // Transform from SOSPeerInfoRefs to CFDictionaries with the info we want per peer. 
1735     // Or, Tell the real SOSPeerRef what the SOSPeerInfoRef is and have it copy out the data it needs. 
1736     bool peersOrViewsChanged 
= SOSEngineSetPeers_locked(engine
, myPeerMeta
, trustedPeers
, untrustedPeers
); 
1738     // Run though all peers and only cache manifests for peers we still have 
1739     CFErrorRef localError 
= NULL
; 
1740     if (!SOSEngineGCPeerState_locked(engine
, &localError
)) { 
1741         secerror("SOSEngineGCPeerState_locked failed: %@", localError
); 
1742         CFReleaseNull(localError
); 
1744     return peersOrViewsChanged
; 
1747 // Initialize the engine if a load fails.  Basically this is our first time setup 
1748 static bool SOSEngineInit(SOSEngineRef engine
, CFErrorRef 
*error
) { 
1750     secnotice("engine", "new engine for datasource named %@", SOSDataSourceGetName(engine
->dataSource
)); 
1751     CFAssignRetained(engine
->peerMap
, CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
)); 
1752     CFAssignRetained(engine
->viewNameSet2ChangeTracker
, CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
)); 
1753     CFAssignRetained(engine
->viewName2ChangeTracker
, CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
)); 
1754     CFReleaseNull(engine
->manifestCache
); 
1755     CFReleaseNull(engine
->peerIDs
); 
1756     // TODO: We shouldn't need to load the backup bag if there was no engine 
1757     // state (load failed), since that means there was no circle nor were we an applicant. 
1759     // Set up change trackers so we know when a backup peer needs to be created? 
1760     // no, since myID is not set, we are not in a circle, so no need to back up 
1761     SOSEngineSetPeers_locked(engine
, NULL
, NULL
, NULL
); 
1765 // Called by our DataSource in its constructor 
1766 SOSEngineRef 
SOSEngineCreate(SOSDataSourceRef dataSource
, CFErrorRef 
*error
) { 
1767     SOSEngineRef engine 
= NULL
; 
1768     engine 
= CFTypeAllocate(SOSEngine
, struct __OpaqueSOSEngine
, kCFAllocatorDefault
); 
1769     engine
->dataSource 
= dataSource
; 
1770     engine
->queue 
= dispatch_queue_create("engine", DISPATCH_QUEUE_SERIAL
); 
1772     engine
->peerMap 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
1773     engine
->viewNameSet2ChangeTracker 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
1774     engine
->viewName2ChangeTracker 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
1775     //engine->syncCompleteQueue = NULL; 
1776     engine
->syncCompleteListener 
= NULL
; 
1777     engine
->coders 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
1778     engine
->haveLoadedCoders 
= false; 
1779     engine
->codersNeedSaving 
= false; 
1781     CFErrorRef engineError 
= NULL
; 
1782     if (!SOSEngineLoad(engine
, NULL
, &engineError
)) { 
1783         secwarning("engine failed load state starting with nothing %@", engineError
); 
1784         CFReleaseNull(engineError
); 
1785         if (!SOSEngineInit(engine
, error
)) { 
1786             secerror("engine failed to initialze %@ giving up", error 
? *error 
: NULL
); 
1789         // Successfully loaded engine state, let's trace if we haven't in a while 
1790         SOSEngineCloudKeychainTraceIfNeeded(engine
); 
1792     SOSEngineSetNotifyPhaseBlock(engine
); 
1796 // --- Called from off the queue, need to move to on the queue 
1798 static void SOSEngineDoOnQueue(SOSEngineRef engine
, dispatch_block_t action
) 
1800     dispatch_sync(engine
->queue
, action
); 
1803 static bool SOSEngineDoTxnOnQueue(SOSEngineRef engine
, CFErrorRef 
*error
, void(^transaction
)(SOSTransactionRef txn
, bool *commit
)) 
1805     return SOSDataSourceWithCommitQueue(engine
->dataSource
, error
, ^(SOSTransactionRef txn
, bool *commit
) { 
1806         SOSEngineDoOnQueue(engine
, ^{ transaction(txn
, commit
); }); 
1811 // MARK: SOSEngine API 
1814 void SOSEngineDispose(SOSEngineRef engine
) { 
1815     // NOOP Engines stick around forever to monitor dataSource changes. 
1816     engine
->dataSource 
= NULL
; 
1817     CFReleaseNull(engine
->coders
); 
1820 void SOSEngineForEachPeer(SOSEngineRef engine
, void (^with
)(SOSPeerRef peer
)) { 
1821     SOSEngineDoOnQueue(engine
, ^{ 
1822         SOSEngineForEachPeer_locked(engine
, with
); 
1826 static void SOSEngineForEachBackupPeer(SOSEngineRef engine
, void (^with
)(SOSPeerRef peer
)) { 
1827     SOSEngineDoOnQueue(engine
, ^{ 
1828         SOSEngineForEachBackupPeer_locked(engine
, with
); 
1832 static const CFStringRef kSecADSecurityNewItemSyncTimeKey 
= CFSTR("com.apple.security.secureobjectsync.itemtime.new"); 
1833 static const CFStringRef kSecADSecurityKnownItemSyncTimeKey 
= CFSTR("com.apple.security.secureobjectsync.itemtime.known"); 
1836 static void ReportItemSyncTime(SOSDataSourceRef ds
, bool known
, SOSObjectRef object
) 
1838     CFDateRef itemModDate 
= SOSObjectCopyModificationDate(ds
, object
, NULL
); 
1840         CFAbsoluteTime syncTime 
= 0; 
1841         CFAbsoluteTime now 
= CFAbsoluteTimeGetCurrent(); 
1843         CFAbsoluteTime peerModificationAbsoluteTime 
= CFDateGetAbsoluteTime(itemModDate
); 
1844         if (peerModificationAbsoluteTime 
> now
) { 
1845             syncTime 
= now 
- peerModificationAbsoluteTime
; 
1848         SecADClientPushValueForDistributionKey(known 
? kSecADSecurityKnownItemSyncTimeKey 
: kSecADSecurityNewItemSyncTimeKey
, 
1849                                                SecBucket2Significant(syncTime
)); 
1851     CFReleaseNull(itemModDate
); 
1854 /* Handle incoming message from peer p.  Return false if there was an error, true otherwise. */ 
1855 bool SOSEngineHandleMessage_locked(SOSEngineRef engine
, CFStringRef peerID
, SOSMessageRef message
, 
1856                                    SOSTransactionRef txn
, bool *commit
, bool *somethingChanged
, CFErrorRef 
*error
) { 
1857     SOSPeerRef peer 
= SOSEngineCopyPeerWithID_locked(engine
, peerID
, error
); 
1858     if (!peer
) return false; 
1860     CFStringRef peerDesc 
= NULL
; 
1861     SOSManifestRef localManifest 
= NULL
; 
1862     SOSManifestRef allAdditions 
= NULL
; 
1863     SOSManifestRef unwanted 
= NULL
; 
1864     SOSManifestRef confirmed 
= NULL
; 
1865     SOSManifestRef base 
= NULL
; 
1866     SOSManifestRef confirmedRemovals 
= NULL
, confirmedAdditions 
= NULL
; 
1867     __block 
struct SOSDigestVector receivedObjects 
= SOSDigestVectorInit
; 
1868     __block 
struct SOSDigestVector unwantedObjects 
= SOSDigestVectorInit
; 
1870     // Check for unknown criticial extensions in the message, and handle 
1871     // any other extensions we support 
1872     __block 
bool ok 
= true; 
1873     CFMutableArrayRef changes 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
1875     require_action_quiet(peer
, exit
, ok 
= SOSErrorCreate(errSecParam
, error
, NULL
, CFSTR("Couldn't create peer with Engine for %@"), peerID
)); 
1876     peerDesc 
= CFCopyDescription(peer
); 
1878     bool hadBeenInSyncAtStart 
= SOSPeerHasBeenInSync(peer
); 
1880     SOSMessageWithExtensions(message
, true, ^(CFDataRef oid
, bool isCritical
, CFDataRef extension
, bool *stop
) { 
1881         // OMFG a Critical extension what shall I do! 
1882         ok 
= SOSErrorCreate(kSOSErrorNotReady
, error
, NULL
, CFSTR("Unknown criticial extension in peer message")); 
1885     require_quiet(ok
, exit
); 
1887     // Merge Objects from the message into our DataSource. 
1888     // Should we move the transaction to the SOSAccount level? 
1889     // TODO: Filter incoming objects 
1890     //if (!SOSDataSourceForEachObjectInViewSet(engine->dataSource, pendingObjects, SOSPeerGetViewNameSet(peer), error, ^void(CFDataRef key, SOSObjectRef object, bool *stop) { 
1891     require_quiet(ok 
&= SOSMessageWithSOSObjects(message
, engine
->dataSource
, error
, ^(SOSObjectRef peersObject
, bool *stop
) { 
1892         CFDataRef digest 
= SOSObjectCopyDigest(engine
->dataSource
, peersObject
, error
); 
1896             secerror("%@ peer sent bad object: %@, rolling back changes", SOSPeerGetID(peer
), error 
? *error 
: NULL
); 
1899         SOSDigestVectorAppend(&receivedObjects
, CFDataGetBytePtr(digest
)); 
1900         SOSObjectRef mergedObject 
= NULL
; 
1901         SOSMergeResult mr 
= SOSDataSourceMergeObject(engine
->dataSource
, txn
, peersObject
, &mergedObject
, error
); 
1902         // TODO: If the mr is kSOSMergeLocalObject most of the time (or all of the time), 
1903         // consider asking the peer to stop sending us objects, and send it objects instead. 
1904         ok 
&= (mr 
!= kSOSMergeFailure
); 
1908             // 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. 
1909             secerror("%@ SOSDataSourceMergeObject failed %@ rolling back changes", SOSPeerGetID(peer
), error 
? *error 
: NULL
); 
1910         } else if (mr
==kSOSMergePeersObject 
|| mr
==kSOSMergeCreatedObject
) { 
1911             *somethingChanged 
= true; 
1913             // mr == kSOSMergeLocalObject 
1914             if (!CFEqual(mergedObject
, peersObject
)) { 
1915                 // Record this object as something we don't want peer to ever send us again.  By adding it to 
1916                 // unwantedObjects we'll falsely claim to peer we have it until they tell us they don't have it anymore. 
1917                 SOSDigestVectorAppend(&unwantedObjects
, CFDataGetBytePtr(digest
)); 
1919             // Ensure localObject is in local manifest (possible corruption) by posting an update when we are done. 
1920             SOSChangesAppendAdd(changes
, mergedObject
); 
1923         if (ok 
&& hadBeenInSyncAtStart
) { 
1924             ReportItemSyncTime(engine
->dataSource
, 
1925                                mr 
== kSOSMergeLocalObject
, 
1929         CFReleaseSafe(mergedObject
); 
1930         CFReleaseSafe(digest
); 
1932     struct SOSDigestVector dvunion 
= SOSDigestVectorInit
; 
1933     SOSDigestVectorSort(&receivedObjects
); 
1934     SOSDigestVectorUnionSorted(SOSManifestGetDigestVector(SOSMessageGetAdditions(message
)), &receivedObjects
, &dvunion
); 
1935     allAdditions 
= SOSManifestCreateWithDigestVector(&dvunion
, error
); 
1936     SOSDigestVectorFree(&receivedObjects
); 
1937     SOSDigestVectorFree(&dvunion
); 
1939     unwanted 
= SOSManifestCreateWithDigestVector(&unwantedObjects
, error
); 
1940     SOSDigestVectorFree(&unwantedObjects
); 
1942     if (CFArrayGetCount(changes
)) { 
1943         // NOTE: This is always notifiying of all additions that end up choosing local, which should be rare, since we shouldn't 
1944         // be receiving objects we already have.   When we do we tell ourselves to add them all again so our views will properly 
1945         // reflect that we actually have these objects if we didn't already. 
1947         // Ensure any objects that we received and have locally already are actually in our local manifest 
1948         SOSEngineUpdateChanges_locked(engine
, txn
, kSOSDataSourceTransactionDidCommit
, kSOSDataSourceSOSTransaction
, changes
, error
); 
1950     CFReleaseSafe(changes
); 
1952     // ---- Don't use local or peer manifests from above this line, 
1953     // ---- since commiting the SOSDataSourceWith transaction might change them --- 
1955     // Take a snapshot of our dataSource's local manifest. 
1956     require_quiet(ok 
= localManifest 
= SOSEngineCopyLocalPeerManifest_locked(engine
, peer
, error
), exit
); 
1958     CFDataRef baseDigest 
= SOSMessageGetBaseDigest(message
); 
1959     CFDataRef proposedDigest 
= SOSMessageGetProposedDigest(message
); 
1962     // I believe this is no longer needed now that we have eliminated extra, 
1963     // since this is handled below once we get a confirmed manifest from our 
1966     // If we just received a L00 reset pendingObjects to localManifest 
1967     if (!baseDigest 
&& !proposedDigest
) { 
1968         // TODO: This is definitely busted for v0 peers since v0 peers always send a 
1969         // L00 (ManifestDigestMessage as an ack) whereas in v2 this is a protocol restart 
1970         // However if we can still find a confirmed manifest below we probably 
1971         // don't want to do this even for v2. 
1972         // Also I don't think we will ever send a ManifestMessage right now in 
1973         // response to a ManifestDigest 
1974         SOSPeerSetPendingObjects(peer
, localManifest
); 
1975         secnoticeq("engine", "%@:%@ SOSPeerSetPendingObjects: %@", engine
->myID
, peerID
, localManifest
); 
1979     base 
= SOSPeerCopyManifestForDigest(peer
, baseDigest
); 
1981     // Note that the sender digest will only exist if we receive a SOSManifestDigestMessageType (since we never receive v2 messages) 
1982     confirmed 
= SOSPeerCopyManifestForDigest(peer
, SOSMessageGetSenderDigest(message
)); 
1984         if (SOSManifestGetCount(SOSMessageGetRemovals(message
)) || SOSManifestGetCount(allAdditions
)) { 
1985             if (base 
|| !baseDigest
) { 
1987                 secnotice("engine", "SOSEngineHandleMessage_locked (%@): creating a confirmed manifest via a patch (base %zu %@, +%zu, -%zu)", SOSPeerGetID(peer
), 
1988                           SOSManifestGetCount(base
), SOSManifestGetDigest(base
, NULL
), 
1989                           SOSManifestGetCount(allAdditions
), SOSManifestGetCount(SOSMessageGetRemovals(message
))); 
1991                 confirmed 
= SOSManifestCreateWithPatch(base
, SOSMessageGetRemovals(message
), allAdditions
, error
); 
1994                 confirmedRemovals 
= CFRetainSafe(SOSMessageGetRemovals(message
)); 
1995                 confirmedAdditions 
= CFRetainSafe(allAdditions
); 
1997         } else if (baseDigest
) { 
1998             confirmed 
= CFRetainSafe(base
); 
1999             secerror("%@:%@ Protocol error send L00 - figure out later base: %@", engine
->myID
, peerID
, base
); 
2003         secnotice("engine", "SOSEngineHandleMessage_locked (%@): got a confirmed manifest by digest: (%zu, %@)", SOSPeerGetID(peer
), SOSManifestGetCount(confirmed
), SOSMessageGetSenderDigest(message
)); 
2005     secnoticeq("engine", "%@:%@ confirmed: %@ base: %@", engine
->myID
, peerID
, confirmed
, base
); 
2007         ok 
&= SOSManifestDiff(SOSPeerGetConfirmedManifest(peer
), confirmed
, &confirmedRemovals
, &confirmedAdditions
, error
); 
2008         if (SOSManifestGetCount(SOSMessageGetRemovals(message
))) 
2009             CFAssignRetained(confirmedRemovals
, SOSManifestCreateUnion(confirmedRemovals
, SOSMessageGetRemovals(message
), error
)); 
2011     if (SOSManifestGetCount(confirmedRemovals
) || SOSManifestGetCount(confirmedAdditions
) || SOSManifestGetCount(unwanted
)) { 
2012         ok 
&= SOSPeerDidReceiveRemovalsAndAdditions(peer
, confirmedRemovals
, confirmedAdditions
, unwanted
, localManifest
, error
); 
2016     // TODO: We should probably remove the if below and always call SOSPeerSetConfirmedManifest, 
2017     // since having a NULL confirmed will force us to send a manifest message to get in sync again. 
2020         SOSManifestRef previousConfirmedManifest 
= SOSPeerGetConfirmedManifest(peer
); 
2021         if(previousConfirmedManifest
) { 
2022             secnotice("engine", "SOSEngineHandleMessage_locked (%@): new confirmed manifest (%zu, %@) will replace existing confirmed manifest (%zu, %@)", SOSPeerGetID(peer
), 
2023                       SOSManifestGetCount(confirmed
), SOSManifestGetDigest(confirmed
, NULL
), 
2024                       SOSManifestGetCount(previousConfirmedManifest
), SOSManifestGetDigest(previousConfirmedManifest
, NULL
)); 
2026             secnotice("engine", "SOSEngineHandleMessage_locked (%@): new confirmed manifest (%zu, %@) is first manifest for peer", SOSPeerGetID(peer
), 
2027                       SOSManifestGetCount(confirmed
), SOSManifestGetDigest(confirmed
, NULL
)); 
2030         SOSPeerSetConfirmedManifest(peer
, confirmed
); 
2031     } else if (SOSPeerGetConfirmedManifest(peer
)) { 
2032         secnoticeq("engine", "%@:%@ unable to find confirmed in %@, sync protocol reset", engine
->myID
, peer
, message
); 
2034         SOSPeerSetConfirmedManifest(peer
, NULL
); 
2035         //SOSPeerSetSendObjects(peer, true); 
2038     // ---- SendObjects and extra->pendingObjects promotion dance ---- 
2040     // The first block of code below sets peer.sendObjects to true when we receive a L00 and the second block 
2041     // moves extra to pendingObjects once we receive a confirmed manifest in or after the L00. 
2042     if (!baseDigest 
&& !proposedDigest
) { 
2043         SOSPeerSetSendObjects(peer
, true); 
2046     if (0 /* confirmed && SOSPeerSendObjects(peer) */) { 
2047         SOSManifestRef allExtra 
= NULL
; 
2048         ok 
&= SOSManifestDiff(confirmed
, localManifest
, NULL
, &allExtra
, error
); 
2049         secnoticeq("engine", "%@:%@ confirmed %@ (re)setting O:%@", engine
->myID
, SOSPeerGetID(peer
), confirmed
, allExtra
); 
2050         SOSPeerSetPendingObjects(peer
, allExtra
); 
2051         CFReleaseSafe(allExtra
); 
2055     secnotice("engine", "recv %@:%@ %@", engine
->myID
, SOSPeerGetID(peer
), message
); 
2056     secnotice("peer", "recv %@ -> %@", peerDesc
, peer
); 
2058     CFReleaseNull(base
); 
2059     CFReleaseSafe(confirmed
); 
2060     CFReleaseSafe(localManifest
); 
2061     CFReleaseSafe(peerDesc
); 
2062     CFReleaseSafe(allAdditions
); 
2063     CFReleaseSafe(unwanted
); 
2064     CFReleaseSafe(confirmedRemovals
); 
2065     CFReleaseSafe(confirmedAdditions
); 
2066     CFReleaseSafe(peer
); 
2070 static CFDataRef 
SOSEngineCopyObjectDER(SOSEngineRef engine
, SOSObjectRef object
, CFErrorRef 
*error
) { 
2071     CFDataRef der 
= NULL
; 
2072     CFDictionaryRef plist 
= SOSObjectCopyPropertyList(engine
->dataSource
, object
, error
); 
2074         der 
= CFPropertyListCreateDERData(kCFAllocatorDefault
, plist
, error
); 
2083             +-----------------------------+_ 
2087            _+=============================+    } L 
2091        /    +============================== 
2094       \     |        M       +------------| 
2098           \_+-------------+---------------+ 
2110 static bool SOSAppendRemoveToPatch(CFTypeRef remove
, CFMutableDictionaryRef patch
, CFErrorRef 
*error
) { 
2113 static bool SOSAppendAddToPatch(CFTypeRef add
, CFMutableDictionaryRef patch
, CFErrorRef 
*error
) { 
2116 static bool SOSAppendDiffToPatch(CFTypeRef left
, CFTypeRef right
, CFMutableDictionaryRef patch
, CFErrorRef 
*error
) { 
2118     if (!left 
&& right
) { 
2119         SOSAppendAddToPatch(right
, patch
, error
); 
2120     } else if (left 
&& !right
) { 
2121         SOSAppendRemoveToPatch(left
, patch
, error
); 
2122     } else if (left 
&& right
) { 
2123         CFTypeID ltype 
= CFGetTypeID(left
); 
2124         CFTypeID rtype 
= CFGetTypeID(right
); 
2125         if (ltype 
== rtype
) { 
2126             if (CFArrayGetTypeID() == ltype
) { 
2127                 ok 
= SecError(errSecParam
, error
, CFSTR("unsupported type array"), ltype
); 
2128             } else if (CFBooleanGetTypeID 
== ltype
) { 
2129                 ok 
= SecError(errSecParam
, error
, CFSTR("unsupported type boolean"), ltype
); 
2130             } else if (CFDataGetTypeID 
== ltype
) { 
2131                 ok 
= SecError(errSecParam
, error
, CFSTR("unsupported type data"), ltype
); 
2132             } else if (CFDictionaryGetTypeID 
== ltype
) { 
2133                 __block CFMutableDictionaryRef leftnotright 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
2134                 __block CFMutableDictionaryRef rightnotleft 
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, right
); 
2136                 CFDictionaryForEach(left
, ^(const void *key
, const void *lvalue
) { 
2137                     const void *rvalue 
= NULL
; 
2138                     if (CFDictionaryGetValueIfPresent(right
, key
, &rvalue
)) { 
2139                         CFDictionaryRemoveValue(rightnotleft
, key
); 
2141                         CFMutableDictionaryRef subpatch 
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
); 
2142                         CFDictionaryAddValue(patch
, key
, subpatch
); 
2143                         SOSAppendDiffToPatch(lvalue
, rvalue
, subpatch
, error
); 
2144                         CFReleaseSafe(subpatch
); 
2146                         CFDictionaryAddValue(leftnotright
, key
, lvalue
); 
2149                 // Proccess leftnotright and rightnotleft 
2150                 CFReleaseSafe(leftnotright
); 
2151                 CFReleaseSafe(rightnotleft
); 
2152             } else if (SOSManifestGetTypeID 
== ltype
) { 
2153                 SOSManifestRef removed 
= NULL
, added 
= NULL
; 
2154                 ok 
&= SOSManifestDiff(left
, right
, &removed
, &added
, error
); 
2155                 if (SOSManifestGetCount(removed
) || SOSManifestGetCount(added
)) { 
2156                     SOSAppendDiffToPatch(lvalue
, rvalue
, subpatch
, error
); 
2157                     CFStringAppend(, <#CFStringRef appendedString#>) 
2159                 CFReleaseSafe(removed
); 
2160                 CFReleaseSafe(added
); 
2161             } else if (CFNumberGetTypeID 
== ltype
) { 
2162                 ok 
= SecError(errSecParam
, error
, CFSTR("unsupported type number"), ltype
); 
2163             } else if (CFSetGetTypeID 
== ltype
) { 
2164                 ok 
= SecError(errSecParam
, error
, CFSTR("unsupported type set"), ltype
); 
2165             } else if (CFStringGetTypeID 
== ltype
) { 
2166                 ok 
= SecError(errSecParam
, error
, CFSTR("unsupported type string"), ltype
); 
2168                 ok 
= SecError(errSecParam
, error
, CFSTR("unknown type %lu"), ltype
); 
2171     } else if (!left 
&& !right
) { 
2177 static __unused 
bool SOSEngineCheckPeerIntegrity(SOSEngineRef engine
, SOSPeerRef peer
, CFErrorRef 
*error
) { 
2179     //static CFMutableDictionaryRef p2amtu; 
2180     if (!engine
->p2amtu
) 
2181         engine
->p2amtu 
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
); 
2182     CFDictionaryRef amtu 
= CFDictionaryGetValue(engine
->p2amtu
, SOSPeerGetID(peer
)); 
2186     SOSManifestRef L 
= SOSEngineCopyLocalPeerManifest_locked(engine
, peer
, error
); 
2187     SOSManifestRef T 
= SOSPeerGetPendingObjects(peer
); 
2188     SOSManifestRef C 
= SOSPeerGetConfirmedManifest(peer
); 
2189     SOSManifestRef U 
= SOSPeerGetUnwantedManifest(peer
); 
2192     SOSManifestRef CunionU 
= SOSManifestCreateUnion(C
, U
, error
); 
2193     SOSManifestRef S 
= SOSManifestCreateIntersection(L
, CunionU
, error
); 
2195     SOSManifestRef AunionT 
= NULL
, MunionU 
= NULL
; 
2196     SOSManifestDiff(L
, C
, &AunionT
, &MunionU
, error
); 
2198     SOSManifestRef A 
= SOSManifestCreateComplement(T
, AunionT
, error
); 
2199     SOSManifestRef M 
= SOSManifestCreateComplement(U
, MunionU
, error
); 
2201     SOSManifestRef SunionAunionT 
= SOSManifestCreateUnion(S
, AunionT
, error
); 
2202     SOSManifestRef SunionMunionU 
= SOSManifestCreateUnion(S
, MunionU
, error
); 
2204     SOSManifestRef AintersectM 
= SOSManifestCreateIntersection(A
, M
, error
); 
2205     SOSManifestRef AintersectS 
= SOSManifestCreateIntersection(A
, S
, error
); 
2206     SOSManifestRef AintersectT 
= SOSManifestCreateIntersection(A
, T
, error
); 
2207     SOSManifestRef AintersectU 
= SOSManifestCreateIntersection(A
, U
, error
); 
2208     SOSManifestRef MintersectS 
= SOSManifestCreateIntersection(M
, S
, error
); 
2209     SOSManifestRef MintersectT 
= SOSManifestCreateIntersection(M
, T
, error
); 
2210     SOSManifestRef MintersectU 
= SOSManifestCreateIntersection(M
, U
, error
); 
2211     SOSManifestRef SintersectT 
= SOSManifestCreateIntersection(S
, T
, error
); 
2212     SOSManifestRef SintersectU 
= SOSManifestCreateIntersection(S
, U
, error
); 
2213     SOSManifestRef TintersectU 
= SOSManifestCreateIntersection(T
, U
, error
); 
2216     CFDictionaryRef newAmtu 
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, CFSTR("A"), A
, CFSTR("M"), M
, CFSTR("T"), T
, CFSTR("U") U
, NULL
); 
2217     CFDictionarySetValue(engine
->p2amtu
, SOSPeerGetID(peer
), newAmtu
); 
2218     CFMutableStringRef amtuChanges 
= CFStringCreateMutable(kCFAllocatorDefault
, 0); 
2219     SOSAppendDiffToString(amtu
, newAmtu
, amtuChanges
); 
2220     secnotice("engine", "%@: %@", SOSPeerGetID(peer
), amtuChanges
); 
2223 #define SOSASSERT(e)      (__builtin_expect(!(e), 0) ? secnotice("engine", "state-assertion %s", #e), assert(e) : (void)0) 
2225     SOSASSERT(L 
? CFEqual(L
, SunionAunionT
) : SOSManifestGetCount(SunionAunionT
) == 0); 
2226     SOSASSERT(C 
? CFEqual(C
, SunionMunionU
) : SOSManifestGetCount(SunionMunionU
) == 0); 
2228     SOSASSERT(SOSManifestGetCount(AintersectM
) == 0); 
2229     SOSASSERT(SOSManifestGetCount(AintersectS
) == 0); 
2230     SOSASSERT(SOSManifestGetCount(AintersectT
) == 0); 
2231     SOSASSERT(SOSManifestGetCount(AintersectU
) == 0); 
2232     SOSASSERT(SOSManifestGetCount(MintersectS
) == 0); 
2233     SOSASSERT(SOSManifestGetCount(MintersectT
) == 0); 
2234     SOSASSERT(SOSManifestGetCount(MintersectU
) == 0); 
2235     SOSASSERT(SOSManifestGetCount(SintersectT
) == 0); 
2236     SOSASSERT(SOSManifestGetCount(SintersectU
) == 0); 
2237     SOSASSERT(SOSManifestGetCount(TintersectU
) == 0); 
2239     CFReleaseSafe(AintersectM
); 
2240     CFReleaseSafe(AintersectS
); 
2241     CFReleaseSafe(AintersectT
); 
2242     CFReleaseSafe(AintersectU
); 
2243     CFReleaseSafe(MintersectS
); 
2244     CFReleaseSafe(MintersectT
); 
2245     CFReleaseSafe(MintersectU
); 
2246     CFReleaseSafe(SintersectT
); 
2247     CFReleaseSafe(SintersectU
); 
2248     CFReleaseSafe(TintersectU
); 
2250     CFReleaseSafe(AunionT
); 
2251     CFReleaseSafe(MunionU
); 
2252     CFReleaseSafe(CunionU
); 
2254     CFReleaseNull(SunionAunionT
); 
2255     CFReleaseNull(SunionMunionU
); 
2260     //CFReleaseSafe(T); // Get 
2261     //CFReleaseSafe(U); // Get 
2262     //CFReleaseSafe(C); // Get 
2267 void SOSEngineSetSyncCompleteListener(SOSEngineRef engine
, SOSEnginePeerInSyncBlock notify_block
) { 
2268     SOSEngineDoOnQueue(engine
, ^{ 
2269         CFAssignRetained(engine
->syncCompleteListener
, Block_copy(notify_block
)); 
2273 void SOSEngineSetSyncCompleteListenerQueue(SOSEngineRef engine
, dispatch_queue_t notify_queue
) { 
2274     SOSEngineDoOnQueue(engine
, ^{ 
2275         CFRetainAssign(engine
->syncCompleteQueue
, notify_queue
); 
2279 static void SOSEngineCompletedSyncWithPeer(SOSEngineRef engine
, SOSPeerRef peer
) { 
2280     SOSEnginePeerInSyncBlock block_to_call 
= engine
->syncCompleteListener
; 
2282     if (block_to_call 
&& engine
->syncCompleteQueue
) { 
2283         CFStringRef ID 
= CFRetainSafe(SOSPeerGetID(peer
)); 
2284         CFSetRef views 
= CFRetainSafe(SOSPeerGetViewNameSet(peer
)); 
2285         CFRetainSafe(block_to_call
); 
2287         dispatch_async(engine
->syncCompleteQueue
, ^{ 
2288             block_to_call(ID
, views
); 
2290             CFReleaseSafe(views
); 
2291             CFReleaseSafe(block_to_call
); 
2295     SOSPeerSetHasBeenInSync(peer
, true); 
2299 CFDataRef 
SOSEngineCreateMessage_locked(SOSEngineRef engine
, SOSTransactionRef txn
, SOSPeerRef peer
, 
2300                                         CFMutableArrayRef 
*attributeList
, CFErrorRef 
*error
, SOSEnginePeerMessageSentCallback 
**sent
) { 
2301     SOSManifestRef local 
= SOSEngineCopyLocalPeerManifest_locked(engine
, peer
, error
); 
2302     __block SOSMessageRef message 
= SOSMessageCreate(kCFAllocatorDefault
, SOSPeerGetMessageVersion(peer
), error
); 
2303     SOSManifestRef confirmed 
= SOSPeerGetConfirmedManifest(peer
); 
2304     SOSManifestRef pendingObjects 
= SOSPeerGetPendingObjects(peer
); 
2305     SOSManifestRef objectsSent 
= NULL
; 
2306     SOSManifestRef proposed 
= NULL
; 
2307     SOSManifestRef allMissing 
= NULL
; 
2308     SOSManifestRef allExtra 
= NULL
; 
2309     SOSManifestRef extra 
= NULL
; 
2310     SOSManifestRef excessPending 
= NULL
; 
2311     SOSManifestRef missing 
= NULL
; 
2312     SOSManifestRef unwanted 
= SOSPeerGetUnwantedManifest(peer
); 
2313     SOSManifestRef excessUnwanted 
= NULL
; 
2314     CFDataRef result 
= NULL
; 
2316     // Given (C, L, T, U) compute (T, U, M, A) 
2320     // U \ (C \ L) => EU 
2321     // T \ (L \ C) => ET 
2322     // And assert that both EU and ET are empty and if not remove them from U and T respectively 
2323     SOSManifestDiff(confirmed
, local
, &allMissing
, &allExtra
, error
); 
2324     SOSManifestDiff(allExtra
, pendingObjects
, &extra
, &excessPending
, error
); 
2325     if (SOSManifestGetCount(excessPending
)) { 
2326         // T \ (L \ C) => excessPending (items both in L and C or in neither that are still pending) 
2327         // Can only happen if a member of T was removed from L without us having a chance to update T 
2328         secerror("%@ ASSERTION FAILURE purging excess pendingObjects: %@", peer
, excessPending
); 
2329         SOSManifestRef newPendingObjects 
= SOSManifestCreateComplement(excessPending
, pendingObjects
, error
); 
2330         SOSPeerSetPendingObjects(peer
, newPendingObjects
); 
2331         CFReleaseSafe(newPendingObjects
); 
2332         pendingObjects 
= SOSPeerGetPendingObjects(peer
); 
2334     SOSManifestDiff(allMissing
, unwanted
, &missing
, &excessUnwanted
, error
); 
2335     if (SOSManifestGetCount(excessUnwanted
)) { 
2336         // U \ (C \ L) => excessUnwanted (items both in L and C or in neither that are still unwanted) 
2337         // Can only happen if a member of U was added to L without us having a chance to update U. 
2338         // Since U only contains items the conflict resolver rejected, this implies L somehow got rolled back 
2339         // The other option (and more likely) is a member of U was removed from C and not from U. 
2340         secerror("%@ ASSERTION FAILURE purging excess unwanted: %@", peer
, excessUnwanted
); 
2341         SOSManifestRef newUnwanted 
= SOSManifestCreateComplement(excessUnwanted
, unwanted
, error
); 
2342         SOSPeerSetUnwantedManifest(peer
, newUnwanted
); 
2343         CFReleaseSafe(newUnwanted
); 
2344         unwanted 
= SOSPeerGetUnwantedManifest(peer
); 
2347     CFReleaseNull(allExtra
); 
2348     CFReleaseNull(excessPending
); 
2349     CFReleaseNull(allMissing
); 
2350     CFReleaseNull(excessUnwanted
); 
2352     secnoticeq("engine", "%@:%@: send state for peer [%s%s%s][%s%s] local:%zu confirmed:%zu pending:%zu, extra:%zu, missing:%zu unwanted:%zu", engine
->myID
, SOSPeerGetID(peer
), 
2354                confirmed 
? "C":"0", 
2355                pendingObjects 
? "P":"0", 
2356                SOSPeerSendObjects(peer
) ? "O":"o", 
2357                SOSPeerMustSendMessage(peer
) ? "S":"s", 
2358                SOSManifestGetCount(local
), 
2359                SOSManifestGetCount(confirmed
), 
2360                SOSManifestGetCount(pendingObjects
), 
2361                SOSManifestGetCount(extra
), 
2362                SOSManifestGetCount(missing
), 
2363                SOSManifestGetCount(unwanted
) 
2367         // TODO: Because of not letting things terminate while we have extra left 
2368         // we might send objects when we didn't need to, but there is always an 
2369         // extra roundtrip required for objects that we assume the other peer 
2370         // should have already. 
2371         // TODO: If there are extra objects left, calling this function is not 
2372         // idempotent we should check if pending is what we are about to send and not send anything in this case. 
2373         if (SOSManifestGetCount(pendingObjects
) == 0 && SOSManifestGetCount(extra
) == 0) 
2374             SOSPeerSetSendObjects(peer
, false); 
2376         // If we aren't missing anything, we've gotten all their data, so we're sync even if they haven't seen ours. 
2377         if (missing 
&& SOSManifestGetCount(missing
) == 0) { 
2378             SOSEngineCompletedSyncWithPeer(engine
, peer
); 
2381         if (CFEqualSafe(local
, SOSPeerGetProposedManifest(peer
)) && !SOSPeerMustSendMessage(peer
)) { 
2383             if (CFEqual(confirmed
, local
)) { 
2384                 secnoticeq("engine", "synced <No MSG> %@:%@", engine
->myID
,  peer
); 
2385             } else if (SOSManifestGetCount(pendingObjects
) == 0 /* TODO: No entries moved from extra to pendingObjects. */ 
2386                        && SOSManifestGetCount(missing
) == 0) { 
2387                 secnoticeq("engine", "waiting <MSG not resent> %@:%@ extra: %@", engine
->myID
, peer
, extra
); 
2392                 CFReleaseNull(local
); 
2393                 CFReleaseNull(message
); 
2394                 CFReleaseNull(extra
); 
2395                 CFReleaseNull(missing
); 
2396                 return CFDataCreate(kCFAllocatorDefault
, NULL
, 0); 
2400         if (SOSManifestGetCount(pendingObjects
)) { 
2401             // If we have additions and we need to send objects, do so. 
2402             __block 
size_t objectsSize 
= 0; 
2403             __block 
struct SOSDigestVector dv 
= SOSDigestVectorInit
; 
2404             CFMutableArrayRef changes 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
2405             __block CFErrorRef dsfeError 
= NULL
; 
2407             if (!SOSDataSourceForEachObject(engine
->dataSource
, txn
, pendingObjects
, &dsfeError
, ^void(CFDataRef key
, SOSObjectRef object
, bool *stop
) { 
2408                 CFErrorRef localError 
= NULL
; 
2409                 CFDataRef digest 
= NULL
; 
2410                 CFDataRef der 
= NULL
; 
2411 #if !defined(NDEBUG) 
2412                 const uint8_t *d 
= CFDataGetBytePtr(key
); 
2414                 secdebug("engine", "%@:%@ object %02X%02X%02X%02X error from SOSDataSourceForEachObject: %@", 
2415                            engine
->myID
, SOSPeerGetID(peer
), d
[0], d
[1], d
[2], d
[3], dsfeError
); 
2417                     const uint8_t *d 
= CFDataGetBytePtr(key
); 
2418                     secerror("%@:%@ object %02X%02X%02X%02X dropping from manifest: not found in datasource: %@", 
2419                              engine
->myID
, SOSPeerGetID(peer
), d
[0], d
[1], d
[2], d
[3], dsfeError
); 
2420                     SOSChangesAppendDelete(changes
, key
); 
2421                 } else if (!(der 
= SOSEngineCopyObjectDER(engine
, object
, &localError
)) 
2422                            || !(digest 
= SOSObjectCopyDigest(engine
->dataSource
, object
, &localError
))) { 
2423                     if (SecErrorGetOSStatus(localError
) == errSecDecode
) { 
2424                         // Decode error, we need to drop these objects from our manifests 
2425                         const uint8_t *d 
= CFDataGetBytePtr(key
); 
2426                         secnoticeq("engine", "%@:%@ object %02X%02X%02X%02X dropping from manifest: %@", 
2427                                    engine
->myID
, SOSPeerGetID(peer
), d
[0], d
[1], d
[2], d
[3], localError
); 
2428                         SOSChangesAppendDelete(changes
, key
); 
2429                         CFRelease(localError
); 
2431                         // Stop iterating and propagate out all other errors. 
2432                         const uint8_t *d 
= CFDataGetBytePtr(key
); 
2433                         secnoticeq("engine", "%@:%@ object %02X%02X%02X%02X in SOSDataSourceForEachObject: %@", 
2434                                    engine
->myID
, SOSPeerGetID(peer
), d
[0], d
[1], d
[2], d
[3], localError
); 
2436                         CFErrorPropagate(localError
, error
); 
2437                         CFReleaseNull(message
); 
2440                     if (!CFEqual(key
, digest
)) { 
2441                         const uint8_t *d 
= CFDataGetBytePtr(key
); 
2442                         const uint8_t *e 
= CFDataGetBytePtr(digest
); 
2443                         secnoticeq("engine", "%@:%@ object %02X%02X%02X%02X is really %02X%02X%02X%02X dropping from local manifest", 
2444                                    engine
->myID
, SOSPeerGetID(peer
), d
[0], d
[1], d
[2], d
[3], e
[0], e
[1], e
[2], e
[3]); 
2445                         SOSChangesAppendDelete(changes
, key
); 
2446                         SOSChangesAppendAdd(changes
, object
); // This is new behaviour but we think it's more correct 
2449                     size_t objectLen 
= (size_t)CFDataGetLength(der
); 
2450                     if (SOSMessageAppendObject(message
, der
, &localError
)) { 
2451                         SOSDigestVectorAppend(&dv
, CFDataGetBytePtr(digest
)); 
2453                             *attributeList 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
2454                         CFDictionaryRef itemPlist 
= SOSObjectCopyPropertyList(engine
->dataSource
, object
, &localError
); 
2455                         if(itemPlist 
&& !CFArrayContainsValue(*attributeList
, CFRangeMake(0, CFArrayGetCount(*attributeList
)), (CFStringRef
)CFDictionaryGetValue(itemPlist
, kSecAttrAccessGroup
))){ 
2456                             CFArrayAppendValue(*attributeList
, (CFStringRef
)CFDictionaryGetValue(itemPlist
, kSecAttrAccessGroup
)); 
2457                         }//copy access group to array 
2458                         CFReleaseNull(itemPlist
); 
2460                         const uint8_t *d 
= CFDataGetBytePtr(digest
); 
2461                         CFStringRef hexder 
= CFDataCopyHexString(der
); 
2462                         secnoticeq("engine", "%@:%@ object %02X%02X%02X%02X der: %@ dropping from manifest: %@", 
2463                                    engine
->myID
, SOSPeerGetID(peer
), d
[0], d
[1], d
[2], d
[3], hexder
, localError
); 
2464                         CFReleaseNull(hexder
); 
2465                         CFReleaseNull(message
); 
2466                         // Since we can't send these objects let's assume they are bad too? 
2467                         SOSChangesAppendDelete(changes
, digest
); 
2470                     objectsSize 
+= objectLen
; 
2471                     if (objectsSize 
> kSOSMessageMaxObjectsSize
) 
2474                 CFErrorPropagate(dsfeError
, error
); // this also releases dsfeError 
2477                 CFReleaseSafe(digest
); 
2479                 CFReleaseNull(message
); 
2482                 objectsSent 
= SOSManifestCreateWithDigestVector(&dv
, error
); 
2484             if (CFArrayGetCount(changes
)) { 
2485                 CFErrorRef localError 
= NULL
; 
2486                 if (!SOSEngineUpdateChanges_locked(engine
, NULL
, kSOSDataSourceTransactionDidCommit
, kSOSDataSourceSOSTransaction
, changes
, &localError
)) 
2487                     secerror("SOSEngineUpdateChanges_locked: %@ failed: %@", changes
, localError
); 
2488                 CFReleaseSafe(localError
); 
2489                 CFAssignRetained(local
, SOSEngineCopyLocalPeerManifest_locked(engine
, peer
, error
)); 
2491             CFReleaseSafe(changes
); 
2492             SOSDigestVectorFree(&dv
); 
2493             CFReleaseNull(dsfeError
); 
2496         // If we have no confirmed manifest, we want all pendedObjects going out as a manifest 
2497         objectsSent 
= CFRetainSafe(pendingObjects
); 
2500     if (confirmed 
|| SOSManifestGetCount(missing
) || SOSManifestGetCount(extra
) || objectsSent
) { 
2501         SOSManifestRef allExtra 
= SOSManifestCreateUnion(extra
, objectsSent
, error
); 
2502         proposed 
= SOSManifestCreateWithPatch(confirmed
, missing
, allExtra
, error
); 
2503         CFReleaseNull(allExtra
); 
2506     SOSManifestRef sender 
= local
; 
2507     // We actually send the remote peer its own digest. 
2508     // Note that both pendingObjects and unwanted may have been changed, so we get them again 
2509     if (SOSManifestGetCount(SOSPeerGetPendingObjects(peer
))==0 && SOSManifestGetCount(extra
)==0 && 
2510         SOSManifestGetCount(missing
)==0 && SOSManifestGetCount(SOSPeerGetUnwantedManifest(peer
))!=0) { 
2511         secnoticeq("engine", "%@:%@: only have differences in unwanted set; lying to peer to stop sync",engine
->myID
, SOSPeerGetID(peer
)); 
2515     if (!SOSMessageSetManifests(message
, sender
, confirmed
, proposed
, proposed
, confirmed 
? objectsSent 
: NULL
, error
)) { 
2516         secnoticeq("engine", "%@:%@: failed to set message manifests",engine
->myID
, SOSPeerGetID(peer
)); 
2517         CFReleaseNull(message
); 
2520     CFReleaseNull(objectsSent
); 
2523         result 
= SOSMessageCreateData(message
, SOSPeerNextSequenceNumber(peer
), error
); 
2527         SOSEnginePeerMessageSentCallback
* pmsc 
= malloc(sizeof(SOSEnginePeerMessageSentCallback
)); 
2528         memset(pmsc
, 0, sizeof(SOSEnginePeerMessageSentCallback
)); 
2529         pmsc
->engine    
= engine
; CFRetain(pmsc
->engine
); 
2530         pmsc
->peer      
= CFRetainSafe(peer
); 
2531         pmsc
->local     
= CFRetainSafe(local
); 
2532         pmsc
->proposed  
= CFRetainSafe(proposed
); 
2533         pmsc
->message   
= CFRetainSafe(message
); 
2534         pmsc
->confirmed 
= CFRetainSafe(confirmed
); 
2536         SOSEngineMessageCallbackSetCallback(pmsc
, ^(bool success
) { 
2537             // Have to copy pmsc so it'll still be around during the dispatch_async 
2538             SOSEnginePeerMessageSentCallback
* pmsc2 
= malloc(sizeof(SOSEnginePeerMessageSentCallback
)); 
2539             memset(pmsc2
, 0, sizeof(SOSEnginePeerMessageSentCallback
)); 
2540             pmsc2
->engine    
= pmsc
->engine
; CFRetain(pmsc2
->engine
); 
2541             pmsc2
->peer      
= CFRetainSafe(pmsc
->peer
); 
2542             pmsc2
->local     
= CFRetainSafe(pmsc
->local
); 
2543             pmsc2
->proposed  
= CFRetainSafe(pmsc
->proposed
); 
2544             pmsc2
->message   
= CFRetainSafe(pmsc
->message
); 
2545             pmsc2
->confirmed 
= CFRetainSafe(pmsc
->confirmed
); 
2547             dispatch_async(pmsc
->engine
->queue
, ^{ 
2549                     SOSPeerSetMustSendMessage(pmsc2
->peer
, false); 
2550                     if (!pmsc2
->confirmed 
&& !pmsc2
->proposed
) { 
2551                         SOSPeerSetSendObjects(pmsc2
->peer
, true); 
2552                         secnoticeq("engine", "%@:%@ sendObjects=true L:%@", pmsc2
->engine
->myID
, SOSPeerGetID(pmsc2
->peer
), pmsc2
->local
); 
2554                     SOSPeerAddLocalManifest(pmsc2
->peer
, pmsc2
->local
); 
2555                     SOSPeerAddProposedManifest(pmsc2
->peer
, pmsc2
->proposed
); 
2556                     secnoticeq("engine", "send %@:%@ %@", pmsc2
->engine
->myID
, SOSPeerGetID(pmsc2
->peer
), pmsc2
->message
); 
2557                     //SOSEngineCheckPeerIntegrity(engine, peer, NULL); 
2559                     secerror("%@:%@ failed to send %@", pmsc2
->engine
->myID
, SOSPeerGetID(pmsc2
->peer
), pmsc2
->message
); 
2561                 SOSEngineFreeMessageCallback(pmsc2
); 
2568     CFReleaseNull(local
); 
2569     CFReleaseNull(extra
); 
2570     CFReleaseNull(missing
); 
2571     CFReleaseNull(message
); 
2572     CFReleaseNull(proposed
); 
2573     if (error 
&& *error
) 
2574         secerror("%@:%@ error in send: %@", engine
->myID
, SOSPeerGetID(peer
), *error
); 
2579 void SOSEngineMessageCallbackSetCallback(SOSEnginePeerMessageSentCallback 
*sent
, SOSEnginePeerMessageSentBlock block
) { 
2581         sent
->block 
= Block_copy(block
); 
2586 void SOSEngineMessageCallCallback(SOSEnginePeerMessageSentCallback 
*sent
, bool ok
) { 
2587     if (sent 
&& sent
->block
) { 
2592 void SOSEngineFreeMessageCallback(SOSEnginePeerMessageSentCallback
* psmc
) { 
2594         CFReleaseNull(psmc
->engine
); 
2595         CFReleaseNull(psmc
->peer
); 
2596         CFReleaseNull(psmc
->coder
); 
2597         CFReleaseNull(psmc
->local
); 
2598         CFReleaseNull(psmc
->proposed
); 
2599         CFReleaseNull(psmc
->message
); 
2600         CFReleaseNull(psmc
->confirmed
); 
2603             Block_release(psmc
->block
); 
2610 static void SOSEngineLogItemError(SOSEngineRef engine
, CFStringRef peerID
, CFDataRef key
, CFDataRef optionalDigest
, const char *where
, CFErrorRef error
) { 
2611     if (!optionalDigest
) { 
2612         const uint8_t *d 
= CFDataGetBytePtr(key
); 
2613         secwarning("%@:%@ object %02X%02X%02X%02X %s: %@", engine
->myID
, peerID
, d
[0], d
[1], d
[2], d
[3], where
, error 
? (CFTypeRef
)error 
: CFSTR("")); 
2615         const uint8_t *d 
= CFDataGetBytePtr(key
); 
2616         const uint8_t *e 
= CFDataGetBytePtr(optionalDigest
); 
2617         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]); 
2621 static bool SOSEngineWriteToBackup_locked(SOSEngineRef engine
, SOSPeerRef peer
, bool rewriteComplete
, bool *didWrite
, bool *incomplete
, CFErrorRef 
*error
) { 
2622     __block 
bool ok 
= SOSPeerWritePendingReset(peer
, error
); 
2623     if (!ok 
|| !SOSPeerGetKeyBag(peer
)) 
2625     __block SOSManifestRef local 
= SOSEngineCopyLocalPeerManifest_locked(engine
, peer
, error
); 
2626     __block SOSManifestRef proposed 
= SOSPeerGetProposedManifest(peer
); 
2627     __block 
bool notify 
= true; 
2628     SOSManifestRef pendingObjects 
= NULL
; 
2629     SOSManifestRef missing 
= NULL
; 
2630     CFStringRef peerID 
= SOSPeerGetID(peer
); 
2632     ok 
&= SOSManifestDiff(proposed
, local
, &missing
, &pendingObjects
, error
); 
2634     secnoticeq("engine", "%@:%@: Send state for peer [%s%s%s] O: %zu, M: %zu", engine
->myID
, peerID
, 
2637                pendingObjects 
? "O":"0", 
2638                SOSManifestGetCount(pendingObjects
), 
2639                SOSManifestGetCount(missing
)); 
2641     if (SOSManifestGetCount(missing
) == 0 && SOSManifestGetCount(pendingObjects
) == 0) { 
2642         // proposed == local (faster test than CFEqualSafe above), since we 
2643         // already did the SOSManifestDiff 
2644         if (rewriteComplete
) { 
2647             secnoticeq("engine", "%@:%@ backup still done", engine
->myID
, peer
); 
2651     ok 
&= SOSPeerAppendToJournal(peer
, error
, ^(FILE *journalFile
, keybag_handle_t kbhandle
) { 
2652         SOSManifestRef objectsSent 
= NULL
; 
2653         __block 
struct SOSDigestVector dvdel 
= SOSDigestVectorInit
; 
2654         __block 
struct SOSDigestVector dvadd 
= SOSDigestVectorInit
; 
2655         SOSManifestForEach(missing
, ^(CFDataRef key
, bool *stop
) { 
2656             CFErrorRef localError 
= NULL
; 
2657             if (ftello(journalFile
) > kSOSBackupMaxFileSize
) { 
2658                 // Highwatermark hit on file. 
2660             } else if (SOSBackupEventWriteDelete(journalFile
, key
, &localError
)) { 
2661                 SOSDigestVectorAppend(&dvdel
, CFDataGetBytePtr(key
)); 
2663                 SOSEngineLogItemError(engine
, peerID
, key
, NULL
, "in SOSPeerWriteDelete", localError
); 
2664                 CFErrorPropagate(localError
, error
); 
2665                 // TODO: Update of missing so proposed is updated properly 
2666                 *stop 
= true; // Disk full? 
2670         if (ok 
&& SOSManifestGetCount(pendingObjects
)) { 
2671             CFMutableArrayRef changes 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
2672             ok 
&= SOSDataSourceForEachObject(engine
->dataSource
, NULL
, pendingObjects
, error
, ^void(CFDataRef key
, SOSObjectRef object
, bool *stop
) { 
2673                 CFErrorRef localError 
= NULL
; 
2674                 CFDataRef digest 
= NULL
; 
2675                 CFDictionaryRef backupItem 
= NULL
; 
2676                 if (ftello(journalFile
) > kSOSBackupMaxFileSize
) { 
2677                     // Highwatermark hit on file. 
2679                 } else if (!object
) { 
2680                     SOSEngineLogItemError(engine
, peerID
, key
, NULL
, "dropping from manifest: not found in datasource", localError
); 
2681                     SOSChangesAppendDelete(changes
, key
); 
2682                 } else if (!(backupItem 
= SOSObjectCopyBackup(engine
->dataSource
, object
, kbhandle
, &localError
)) 
2683                            || !(digest 
= SOSObjectCopyDigest(engine
->dataSource
, object
, &localError
))) { 
2684                     if (SecErrorGetOSStatus(localError
) == errSecDecode
) { 
2685                         // Decode error, we need to drop these objects from our manifests 
2686                         SOSEngineLogItemError(engine
, peerID
, key
, NULL
, "dropping from manifest", localError
); 
2687                         SOSChangesAppendDelete(changes
, key
); 
2688                         CFRelease(localError
); 
2690                         // Stop iterating and propagate out all other errors. 
2691                         SOSEngineLogItemError(engine
, peerID
, key
, NULL
, "in SOSDataSourceForEachObject", localError
); 
2693                         CFErrorPropagate(localError
, error
); 
2697                     if (!CFEqual(key
, digest
)) { 
2698                         SOSEngineLogItemError(engine
, peerID
, key
, digest
, "", NULL
); 
2699                         SOSChangesAppendDelete(changes
, key
); 
2700                         SOSChangesAppendAdd(changes
, object
); // This is new behaviour but we think it's more correct 
2703                     if (SOSBackupEventWriteAdd(journalFile
, backupItem
, &localError
)) { 
2704                         SOSDigestVectorAppend(&dvadd
, CFDataGetBytePtr(digest
)); 
2706                         SOSEngineLogItemError(engine
, peerID
, key
, NULL
, "in SOSPeerWriteAdd", localError
); 
2707                         *stop 
= true; // Disk full? 
2708                         CFErrorPropagate(localError
, error
); 
2712                 CFReleaseSafe(backupItem
); 
2713                 CFReleaseSafe(digest
); 
2715             if (CFArrayGetCount(changes
)) { 
2716                 CFErrorRef localError 
= NULL
; 
2717                 if (!SOSEngineUpdateChanges_locked(engine
, NULL
, kSOSDataSourceTransactionDidCommit
, kSOSDataSourceSOSTransaction
, changes
, &localError
)) 
2718                     secerror("SOSEngineUpdateChanges_locked: %@ failed: %@", changes
, localError
); 
2719                 CFReleaseSafe(localError
); 
2720                 // Since calling SOSEngineUpdateChanges_locked might cause local to change and might cause the backup peer to update proposed, refetch them here. 
2721                 CFAssignRetained(local
, SOSEngineCopyLocalPeerManifest_locked(engine
, peer
, error
)); 
2722                 proposed 
= SOSPeerGetProposedManifest(peer
); 
2724             CFReleaseSafe(changes
); 
2727         if (dvadd
.count 
|| (proposed 
&& dvdel
.count
)) { 
2729             SOSManifestRef deleted 
= SOSManifestCreateWithDigestVector(&dvdel
, error
); 
2730             SOSManifestRef objectsSent 
= SOSManifestCreateWithDigestVector(&dvadd
, error
); 
2731             SOSManifestRef newProposed 
= SOSManifestCreateWithPatch(proposed
, deleted
, objectsSent
, error
); 
2732             CFReleaseSafe(deleted
); 
2733             CFReleaseSafe(objectsSent
); 
2734             SOSPeerSetProposedManifest(peer
, newProposed
); 
2735             CFReleaseSafe(newProposed
); 
2736             proposed 
= SOSPeerGetProposedManifest(peer
); 
2738         SOSDigestVectorFree(&dvdel
); 
2739         SOSDigestVectorFree(&dvadd
); 
2741         // TODO: If proposed is NULL, and local is empty we should still consider ourselves done. 
2742         // It so happens this can't happen in practice today since there is at least a backupbag 
2743         // in the backup, but this is a bug waiting to rear its head in the future. 
2744         if (ok 
&& CFEqualSafe(local
, proposed
)) { 
2745             CFErrorRef localError 
= NULL
; 
2746             if (SOSBackupEventWriteCompleteMarker(journalFile
, 899, &localError
)) { 
2747                 SOSPeerSetSendObjects(peer
, true); 
2749                 secnoticeq("backup", "%@:%@ backup done%s", engine
->myID
, peerID
, notify 
? " notifying sbd" : ""); 
2750                 // TODO: Now switch to changes based writing to backup sync. 
2751                 // Currently we leave changes enabled but we probably shouldn't 
2753                 secwarning("%@:%@ in SOSBackupPeerWriteCompleteMarker: %@", engine
->myID
, peerID
, localError
); 
2756                 CFErrorPropagate(localError
, error
); 
2759             secnoticeq("backup", "%@:%@ backup incomplete [%zu/%zu]%s", engine
->myID
, peerID
, SOSManifestGetCount(local
), SOSManifestGetCount(proposed
), notify 
? " notifying sbd" : ""); 
2762         CFReleaseNull(objectsSent
); 
2765         SOSBackupPeerPostNotification("writing changes to backup"); 
2768     CFReleaseSafe(local
); 
2769     CFReleaseNull(pendingObjects
); 
2770     CFReleaseNull(missing
); 
2775 CF_RETURNS_RETAINED CFSetRef 
SOSEngineSyncWithBackupPeers(SOSEngineRef engine
, CFSetRef 
/* CFStringRef */ peers
, bool forceReset
, CFErrorRef 
*error
) 
2777     __block 
bool incomplete 
= false; 
2778     CFMutableSetRef handledSet 
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
); 
2780     bool ok 
= SOSEngineDoTxnOnQueue(engine
, error
, ^(SOSTransactionRef txn
, bool *commit
) { 
2781         __block 
bool dirty 
= false; 
2782         CFSetForEach(peers
, ^(const void *value
) { 
2783             bool report_handled 
= true; 
2784             CFErrorRef localError 
= NULL
; 
2785             SOSPeerRef peer 
= NULL
; 
2786             CFStringRef peerID 
= asString(value
, &localError
); 
2787             require_action_quiet(peerID
, done
, report_handled 
= false); 
2789             peer 
= SOSEngineCopyPeerWithID_locked(engine
, peerID
, &localError
); 
2790             require_quiet(peerID
, done
); 
2792             if (SOSPeerMapEntryIsBackup(peer
)) { 
2794                     SOSPeerSetMustSendMessage(peer
, true); 
2797                 report_handled 
= SOSEngineWriteToBackup_locked(engine
, peer
, false, &dirty
, &incomplete
, &localError
); 
2802                 secnotice("engine-sync", "Failed to process sync for %@: %@", peerID
, localError
); 
2804             if (report_handled
) { 
2805                 CFSetAddValue(handledSet
, peerID
); 
2807             CFReleaseNull(localError
); 
2808             CFReleaseNull(peer
); 
2812             CFErrorRef saveError 
= NULL
; 
2813             if (!SOSEngineSave(engine
, txn
, &saveError
)) { 
2814                 secnotice("engine-save", "Failed to save engine: %@", saveError
); 
2819         // Ensure we get called again in a while (after a backup timeout) 
2820         // sbd will do this since we never wrote a complete marker. 
2821         // TODO: This relies on us not writing complete marker for update 
2822         // event while we havn't finished a full backup, which we currently still do. 
2825         CFReleaseNull(handledSet
); 
2830 bool SOSEngineHandleMessage(SOSEngineRef engine
, CFStringRef peerID
, 
2831                             CFDataRef raw_message
, CFErrorRef 
*error
) 
2833     __block 
bool result 
= true; 
2834     __block 
bool somethingChanged 
= false; 
2835     SOSMessageRef message 
= SOSMessageCreateWithData(kCFAllocatorDefault
, raw_message
, error
); 
2836     result 
&= message 
&& SOSEngineDoTxnOnQueue(engine
, error
, ^(SOSTransactionRef txn
, bool *commit
) { 
2837         result 
= SOSEngineHandleMessage_locked(engine
, peerID
, message
, txn
, commit
, &somethingChanged
, error
); 
2839     CFReleaseSafe(message
); 
2840     if (somethingChanged
) 
2841         SecKeychainChanged(); 
2845 void SOSEngineCircleChanged(SOSEngineRef engine
, CFStringRef myPeerID
, CFArrayRef trustedPeers
, CFArrayRef untrustedPeers
) { 
2846     __block 
bool peersOrViewsChanged 
= false; 
2847     SOSEngineDoOnQueue(engine
, ^{ 
2848         peersOrViewsChanged 
= SOSEngineCircleChanged_locked(engine
, myPeerID
, trustedPeers
, untrustedPeers
); 
2850         // We should probably get a more precise list of peers that actually need talking to 
2851         if (peersOrViewsChanged 
&& engine
->myID 
&& CFArrayGetCount(engine
->peerIDs
) != 0) 
2852             SOSCCRequestSyncWithPeersList(engine
->peerIDs
); 
2855     __block 
bool ok 
= true; 
2856     __block CFErrorRef localError 
= NULL
; 
2857     ok 
&= SOSEngineDoTxnOnQueue(engine
, &localError
, ^(SOSTransactionRef txn
, bool *commit
) { 
2858         ok 
= *commit 
= SOSEngineSave(engine
, txn
, &localError
); 
2861         secerror("failed to save engine state: %@", localError
); 
2862         CFReleaseSafe(localError
); 
2867 SOSManifestRef 
SOSEngineCopyManifest(SOSEngineRef engine
, CFErrorRef 
*error
) { 
2868     __block SOSManifestRef result 
= NULL
; 
2869     SOSEngineDoOnQueue(engine
, ^{ 
2870         result 
= SOSEngineCopyManifestWithViewNameSet_locked(engine
, SOSViewsGetV0ViewSet(), error
); 
2875 SOSManifestRef 
SOSEngineCopyLocalPeerManifest(SOSEngineRef engine
, SOSPeerRef peer
, CFErrorRef 
*error
) { 
2876     __block SOSManifestRef result 
= NULL
; 
2877     SOSEngineDoOnQueue(engine
, ^{ 
2878         result 
= SOSEngineCopyLocalPeerManifest_locked(engine
, peer
, error
); 
2883 bool SOSEngineUpdateChanges(SOSEngineRef engine
, SOSDataSourceTransactionSource source
, CFArrayRef changes
, CFErrorRef 
*error
) { 
2884     __block 
bool result 
= true; 
2885     SOSEngineDoOnQueue(engine
, ^{ 
2886         result 
= SOSEngineUpdateChanges_locked(engine
, NULL
, kSOSDataSourceTransactionDidCommit
, source
, changes
, error
); 
2892 // Peer state layout.  WRONG! It's an array now 
2893 // The peer state is an array. 
2894 // The first element of the array is a dictionary with any number of keys and 
2895 // values in it (for future expansion) such as changing the digest size or type 
2896 // or remebering boolean flags for a peers sake. 
2897 // The next three are special in that they are manifest digests with special 
2898 // meaning and rules as to how they are treated (These are dynamically updated 
2899 // based on database activity so they have a fully history of all changes made 
2900 // to the local db. The first is the manifest representing the pendingObjects 
2901 // to send to the other peer.  This is normally only ever appending to, and in 
2902 // particular with transactions originating from the Keychain API that affect 
2903 // syncable items will need to add the new objects digests to the pendingObjects list 
2904 // while adding the digests of any tombstones encountered to the extra list. 
2906 SOSPeerRef 
SOSEngineCopyPeerWithID(SOSEngineRef engine
, CFStringRef peer_id
, CFErrorRef 
*error
) { 
2907     __block SOSPeerRef peer 
= NULL
; 
2908     SOSEngineDoOnQueue(engine
, ^{ 
2909         peer 
= SOSEngineCopyPeerWithID_locked(engine
, peer_id
, error
); 
2914 bool SOSEngineForPeerID(SOSEngineRef engine
, CFStringRef peerID
, CFErrorRef 
*error
, void (^forPeer
)(SOSTransactionRef txn
, SOSPeerRef peer
)) { 
2915     __block 
bool ok 
= true; 
2916     SOSDataSourceReadWithCommitQueue(engine
->dataSource
, error
, ^(SOSTransactionRef txn
) { 
2917         SOSEngineDoOnQueue(engine
, ^{ 
2918             SOSPeerRef peer 
= SOSEngineCopyPeerWithID_locked(engine
, peerID
, error
); 
2931 bool SOSEngineWithPeerID(SOSEngineRef engine
, CFStringRef peerID
, CFErrorRef 
*error
, void (^with
)(SOSPeerRef peer
, SOSCoderRef coder
, SOSDataSourceRef dataSource
, SOSTransactionRef txn
, bool *forceSaveState
)) { 
2932     __block 
bool result 
= true; 
2933     result 
&= SOSEngineDoTxnOnQueue(engine
, error
, ^(SOSTransactionRef txn
, bool *commit
) { 
2934         SOSPeerRef peer 
= SOSEngineCopyPeerWithID_locked(engine
, peerID
, error
); 
2936             result 
= SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("Engine has no peer for %@"), peerID
); 
2938             bool saveState 
= false; 
2939             SOSCoderRef coder 
= SOSEngineGetCoderInTx_locked(engine
, txn
, peerID
, NULL
); 
2940             with(peer
, coder
, engine
->dataSource
, txn
, &saveState
); 
2941             CFReleaseSafe(peer
); 
2943                 result 
= SOSEngineSave(engine
, txn
, error
); 
2944             // TODO: Don't commit if engineSave fails? 
2951 CFDataRef 
SOSEngineCreateMessageToSyncToPeer(SOSEngineRef engine
, CFStringRef peerID
, CFMutableArrayRef 
*attributeList
, SOSEnginePeerMessageSentCallback 
**sentCallback
, CFErrorRef 
*error
){ 
2952 __block CFDataRef message 
= NULL
; 
2953     SOSEngineForPeerID(engine
, peerID
, error
, ^(SOSTransactionRef txn
, SOSPeerRef peer
) { 
2954         message 
= SOSEngineCreateMessage_locked(engine
, txn
, peer
, attributeList
, error
, sentCallback
); 
2959 bool SOSEnginePeerDidConnect(SOSEngineRef engine
, CFStringRef peerID
, CFErrorRef 
*error
) { 
2960     return SOSEngineWithPeerID(engine
, peerID
, error
, ^(SOSPeerRef peer
, SOSCoderRef coder
, SOSDataSourceRef dataSource
, SOSTransactionRef txn
, bool *saveState
) { 
2961         *saveState 
= SOSPeerDidConnect(peer
); 
2965 bool SOSEngineSetPeerConfirmedManifest(SOSEngineRef engine
, CFStringRef backupName
, 
2966                                        CFDataRef keybagDigest
, CFDataRef manifestData
, CFErrorRef 
*error
) { 
2967     __block 
bool ok 
= true; 
2969     ok 
&= SOSEngineForPeerID(engine
, backupName
, error
, ^(SOSTransactionRef txn
, SOSPeerRef peer
) { 
2971         bool incomplete 
= false; 
2972         SOSManifestRef confirmed 
= NULL
; 
2973         CFDataRef keybag 
= SOSPeerGetKeyBag(peer
); 
2974         CFDataRef computedKeybagDigest 
= keybag 
? CFDataCopySHA1Digest(keybag
, NULL
) : NULL
; 
2975         if (CFEqualSafe(keybagDigest
, computedKeybagDigest
)) { 
2976             ok 
= confirmed 
= SOSManifestCreateWithData(manifestData
, error
); 
2978                 // Set both confirmed and proposed (confirmed is just 
2979                 // for debug status, proposed is actually what's used 
2980                 // by the backup peer). 
2981                 SOSPeerSetConfirmedManifest(peer
, confirmed
); 
2982                 SOSPeerSetProposedManifest(peer
, confirmed
); 
2985             // sbd missed a reset event, send it again 
2986             // Force SOSEngineWriteToBackup_locked to call SOSPeerWriteReset, which clears 
2987             // confirmed and proposed manifests and writes the keybag to the journal. 
2988             SOSPeerSetMustSendMessage(peer
, true); 
2991         // Stop changes from writing complete markers, unless SOSEngineWriteToBackup_locked() detects we are in sync 
2992         SOSPeerSetSendObjects(peer
, false); 
2993         // Write data for this peer if we can, technically not needed for non legacy protocol support all the time. 
2994         ok 
= SOSEngineWriteToBackup_locked(engine
, peer
, true, &dirty
, &incomplete
, error
); 
2996         if (!ok 
&& error 
&& SecErrorGetOSStatus(*error
) == errSecInteractionNotAllowed
) { 
2997             SOSEnsureBackupWhileUnlocked(); 
3000         CFReleaseSafe(confirmed
); 
3001         CFReleaseSafe(computedKeybagDigest
); 
3006 CFArrayRef 
SOSEngineCopyBackupPeerNames(SOSEngineRef engine
, CFErrorRef 
*error
) { 
3007     __block CFMutableArrayRef backupNames 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
3008     SOSEngineForEachBackupPeer(engine
, ^(SOSPeerRef peer
) { 
3009         CFArrayAppendValue(backupNames
, SOSPeerGetID(peer
)); 
3014 static CFMutableDictionaryRef 
SOSEngineCreateStateDictionary(CFStringRef peerID
, SOSManifestRef manifest
, CFSetRef vns
, CFStringRef coderString
) { 
3015     CFNumberRef manifestCount 
= CFNumberCreateWithCFIndex(kCFAllocatorDefault
, SOSManifestGetCount(manifest
)); 
3016     CFDataRef manifestHash 
= SOSManifestGetDigest(manifest
, NULL
); 
3017     CFMutableDictionaryRef result 
= CFDictionaryCreateMutableForCFTypesWithSafe(kCFAllocatorDefault
, 
3018                                                                                 kSOSCCEngineStatePeerIDKey
, peerID
, 
3019                                                                                 kSOSCCEngineStateManifestCountKey
, manifestCount
, 
3020                                                                                 kSOSCCEngineStateManifestHashKey
, manifestHash
, 
3021                                                                                 kSOSCCEngineStateSyncSetKey
, asSet(vns
, NULL
), 
3022                                                                                 kSOSCCEngineStateCoderKey
, coderString
, 
3024     CFReleaseNull(manifestCount
); 
3028 static void SOSEngineAppendStateDictionary(CFMutableArrayRef stateArray
, CFStringRef peerID
, SOSManifestRef manifest
, CFSetRef vns
, CFStringRef coderString
) { 
3029     CFMutableDictionaryRef newState 
= SOSEngineCreateStateDictionary(peerID
, manifest
, vns
, coderString
); 
3030     CFArrayAppendValue(stateArray
, newState
); 
3031     CFReleaseNull(newState
); 
3034 static CFArrayRef 
SOSEngineCopyPeerConfirmedDigests_locked(SOSEngineRef engine
, CFErrorRef 
*error
) { 
3035     CFMutableArrayRef result 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
3036     CFDictionaryForEach(engine
->viewNameSet2ChangeTracker
, ^(const void *vns
, const void *ct
) { 
3037         SOSManifestRef manifest 
= SOSEngineCopyManifestWithViewNameSet_locked(engine
, vns
, error
); 
3038         SOSEngineAppendStateDictionary(result
, NULL
, manifest
, vns
, NULL
); 
3039         CFReleaseNull(manifest
); 
3042     // Copy other peers even if we aren't in the circle, since we're observing it. 
3043     SOSEngineForEachPeer_locked(engine
, ^(SOSPeerRef peer
) { 
3044         CFTypeRef coderObject 
= engine
->coders 
? CFDictionaryGetValue(engine
->coders
, SOSPeerGetID(peer
)) : CFSTR("Coders not loaded."); 
3045         CFStringRef coderState 
= coderObject 
? CFCopyDescription(coderObject
) : NULL
; 
3046         SOSEngineAppendStateDictionary(result
, SOSPeerGetID(peer
), SOSPeerGetConfirmedManifest(peer
), SOSPeerGetViewNameSet(peer
), coderState
); 
3047         CFReleaseNull(coderState
); 
3052 CFArrayRef 
SOSEngineCopyPeerConfirmedDigests(SOSEngineRef engine
, CFErrorRef 
*error
) { 
3053     __block CFArrayRef result 
= NULL
; 
3054     SOSEngineDoOnQueue(engine
, ^{ 
3055         result 
= SOSEngineCopyPeerConfirmedDigests_locked(engine
, error
); 
3060 SOSDataSourceRef 
SOSEngineGetDataSource(SOSEngineRef engine
) { 
3061     return engine
->dataSource
; 
3064 #define ENGINELOGSTATE "engineLogState" 
3065 void SOSEngineLogState(SOSEngineRef engine
) { 
3066     CFErrorRef error 
= NULL
; 
3067     CFArrayRef confirmedDigests 
= NULL
; 
3069     secnotice(ENGINELOGSTATE
, "Start"); 
3071     require_action_quiet(engine
, retOut
, secnotice(ENGINELOGSTATE
, "No Engine Available")); 
3072     confirmedDigests 
= SOSEngineCopyPeerConfirmedDigests(engine
, &error
); 
3073     require_action_quiet(confirmedDigests
, retOut
, secnotice(ENGINELOGSTATE
, "No engine peers: %@\n", error
)); 
3075     SOSCCForEachEngineStateAsStringFromArray(confirmedDigests
, ^(CFStringRef onePeerDescription
) { 
3076         secnotice(ENGINELOGSTATE
, "%@", onePeerDescription
); 
3080     CFReleaseNull(error
); 
3081     CFReleaseNull(confirmedDigests
); 
3082     secnotice(ENGINELOGSTATE
, "Finish"); 
3088 void TestSOSEngineDoOnQueue(CFTypeRef engine
, dispatch_block_t action
) 
3090     dispatch_sync(((SOSEngineRef
)engine
)->queue
, action
); 
3092 CFMutableDictionaryRef 
TestSOSEngineGetCoders(CFTypeRef engine
){ 
3093     return ((SOSEngineRef
)engine
)->coders
; 
3096 bool TestSOSEngineDoTxnOnQueue(CFTypeRef engine
, CFErrorRef 
*error
, void(^transaction
)(SOSTransactionRef txn
, bool *commit
)) 
3098     return SOSDataSourceWithCommitQueue(((SOSEngineRef
)engine
)->dataSource
, error
, ^(SOSTransactionRef txn
, bool *commit
) { 
3099         TestSOSEngineDoOnQueue((SOSEngineRef
)engine
, ^{ transaction(txn
, commit
); }); 
3102 bool SOSEngineGetCodersNeedSaving(SOSEngineRef engine
){ 
3103     return engine
->codersNeedSaving
; 
3106 void SOSEngineSetCodersNeedSaving(SOSEngineRef engine
, bool saved
){ 
3107     engine
->codersNeedSaving 
= saved
;