2 * Copyright (c) 2012-2015 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
26 * SOSEngine.c - Implementation of a secure object syncing engine
29 #include <Security/SecureObjectSync/SOSChangeTracker.h>
30 #include <Security/SecureObjectSync/SOSEngine.h>
31 #include <Security/SecureObjectSync/SOSDigestVector.h>
32 #include <Security/SecureObjectSync/SOSInternal.h>
33 #include <Security/SecureObjectSync/SOSPeer.h>
34 #include <Security/SecureObjectSync/SOSViews.h>
35 #include <Security/SecureObjectSync/SOSBackupEvent.h>
36 #include <Security/SecureObjectSync/SOSPersist.h>
37 #include <corecrypto/ccder.h>
40 #include <utilities/array_size.h>
41 #include <utilities/SecCFCCWrappers.h>
42 #include <utilities/SecCFError.h>
43 #include <utilities/SecCFRelease.h>
44 #include <utilities/SecCFWrappers.h>
45 #include <utilities/der_plist.h>
46 #include <utilities/der_plist_internal.h>
47 #include <utilities/debugging.h>
48 #include <utilities/iCloudKeychainTrace.h>
49 #include <utilities/SecCoreCrypto.h>
50 #include <utilities/SecFileLocations.h>
51 #include <AssertMacros.h>
52 #include <CoreFoundation/CoreFoundation.h>
54 #include <securityd/SecItemServer.h> // TODO: We can't leave this here.
55 #include <securityd/SOSCloudCircleServer.h> // TODO: We can't leave this here.
56 #include <Security/SecItem.h> // TODO: We can't leave this here.
57 #include <Security/SecItemPriv.h> // TODO: We can't leave this here.
58 #include <securityd/SecItemSchema.h>
59 #include <securityd/iCloudTrace.h>
61 #include <CoreFoundation/CFURL.h>
64 // MARK: SOSEngine The Keychain database with syncable keychain support.
67 //----------------------------------------------------------------------------------------
68 // MARK: Engine state v0
69 //----------------------------------------------------------------------------------------
71 // Key in dataSource for general engine state file.
72 // This file only has digest entries in it, no manifests.
73 static const CFStringRef kSOSEngineState
= CFSTR("engine-state");
75 // Keys in state dictionary
76 static CFStringRef kSOSEngineManifestCacheKey
= CFSTR("manifestCache");
77 static CFStringRef kSOSEnginePeerStateKey
= CFSTR("peerState");
78 static CFStringRef kSOSEnginePeerIDsKey
= CFSTR("peerIDs");
79 static CFStringRef kSOSEngineIDKey
= CFSTR("id");
80 static CFStringRef kSOSEngineTraceDateKey
= CFSTR("traceDate");
82 //----------------------------------------------------------------------------------------
83 // MARK: Engine state v2
84 //----------------------------------------------------------------------------------------
86 static const CFIndex kCurrentEngineVersion
= 2;
87 // Keychain/datasource items
88 // Used for the kSecAttrAccount when saving in the datasource with dsSetStateWithKey
89 // Class D [kSecAttrAccessibleAlwaysPrivate/kSecAttrAccessibleAlwaysThisDeviceOnly]
90 static CFStringRef kSOSEngineStatev2
= CFSTR("engine-state-v2");
91 static CFStringRef kSOSEnginePeerStates
= CFSTR("engine-peer-states");
92 static CFStringRef kSOSEngineManifestCache
= CFSTR("engine-manifest-cache");
93 #define kSOSEngineProtectionDomainClassD kSecAttrAccessibleAlwaysPrivate // >>>> or kSecAttrAccessibleAlwaysThisDeviceOnly
94 // Class A [kSecAttrAccessibleWhenUnlockedThisDeviceOnly]
95 static CFStringRef kSOSEngineCoders
= CFSTR("engine-coders");
96 #define kSOSEngineProtectionDomainClassA kSecAttrAccessibleWhenUnlockedThisDeviceOnly
98 // Keys for individual dictionaries
100 static CFStringRef kSOSEngineStateVersionKey
= CFSTR("engine-stateVersion");
102 // Current save/load routines
103 // SOSEngineCreate/SOSEngineLoad/SOSEngineSetState
104 // SOSEngineSave/SOSEngineDoSave/SOSEngineCopyState
105 // no save/load functions external to this file
108 Divide engine state into five pieces:
110 - General engine state
112 - List of other (trusted) peer IDs
114 - Coder data (formerly in peer state)
115 - Backup Keybags (backup peers only)
116 - Peer state (including manifest hashes -- just keys into ManifestCache)
117 [__OpaqueSOSPeer/SOSPeerRef]
130 - local manifest hashes (copy of local keychain)
131 - peer manifest hashes
133 These divisions are based on size, frequency of update, and protection domain
135 The Manifest Cache is a dictionary where each key is a hash over its entry,
136 which is a concatenation of 20 byte hashes of the keychain items. The local
137 keychain is present as one entry. The other entries are subsets of that, one
138 for each confirmed/pending/missing/unwanted shared with a peer. The local
139 keychain entry can be re-created by iterating over the databse, whereas the
140 others are built up through communicating with other peers.
142 83:d=2 hl=2 l= 13 prim: UTF8STRING :manifestCache
143 98:d=2 hl=4 l= 912 cons: SET
144 102:d=3 hl=2 l= 24 cons: SEQUENCE
145 104:d=4 hl=2 l= 20 prim: OCTET STRING [HEX DUMP]:DA39A3EE5E6B4B0D3255BFEF95601890AFD80709
146 126:d=4 hl=2 l= 0 prim: OCTET STRING
147 128:d=3 hl=2 l= 124 cons: SEQUENCE
148 130:d=4 hl=2 l= 20 prim: OCTET STRING [HEX DUMP]:F9B59370A4733F0D174E8D220C5BE3AF062C775B
149 152:d=4 hl=2 l= 100 prim: OCTET STRING [HEX DUMP]:5A574BB4EC90C3BBCC69EE73CBFE039133AE807265D6A58003B8D205997EAB96390AAB207E63A2E270A476CAB5B2D9D2F7B0E55512AA957B58D5658E7EF907B069B83AA6BA941790A3C3C4A68292D59DABA3CA342966EFF82E1ACAEB691FD6E20772E17E
150 254:d=3 hl=4 l= 366 cons: SEQUENCE
151 258:d=4 hl=2 l= 20 prim: OCTET STRING [HEX DUMP]:2E69C2F7F3E014075B30004CE0EC6C1AD419EBF5
152 280:d=4 hl=4 l= 340 prim: OCTET STRING [HEX DUMP]:07571E9678FD7D68812E409CC96C1F54834A099A0C3A2D12CCE2EA95F4505EA52F2C982B2ADEE3DA14D4712C000309BF63D54A98B61AA1D963C40E0E2531C83B28CA5BE6DA0D26400C3C77A618F711DD3CC0BF86CCBAF8AA3332973268B30EEBF21CD8184D9C8427CA13DECCC7BB83C80009A2EF45CCC07F586315C80CEEEEF5D5352FD000AAE6D9CBB4294D5959FD00198225AF9ABD09B341A2FDC278E9FD1465D6A58003B8D205997EAB96390AAB207E63A2E270A476CAB5B2D9D2F7B0E55512AA957B58D5658E7EF907B069B83AA6BA941790A3C3C4A68292D59D95C9D4D8A8BCA2E8242AB0D409F671F298B6DCAE9BC4238C09E07548CEFB300098606F9E4F230C99ABA3CA342966EFF82E1ACAEB691FD6E20772E17EB4FEFB84F8CF75C0C69C59532C354D175A59F961BA4D4DFA017FD8192288F14278AE76712E127D65FE616C7E4FD0713644F7C9A7ABA1CE065694A968
153 624:d=3 hl=4 l= 386 cons: SEQUENCE
154 628:d=4 hl=2 l= 20 prim: OCTET STRING [HEX DUMP]:CCF179FF718C10F151E7409EDF1A06F0DF10DCAD
155 650:d=4 hl=4 l= 360 prim: OCTET STRING [HEX DUMP]:07571E9678FD7D68812E409CC96C1F54834A099A0C3A2D12CCE2EA95F4505EA52F2C982B2ADEE3DA14D4712C000309BF63D54A98B61AA1D963C40E0E2531C83B28CA5BE6DA0D26400C3C77A618F711DD3CC0BF86CCBAF8AA3332973268B30EEBF21CD8184D9C8427CA13DECCC7BB83C80009A2EF45CCC07F586315C80CEEEEF5D5352FD000AAE6D9CBB4294D5959FD00198225AF9ABD09B341A2FDC278E9FD145A574BB4EC90C3BBCC69EE73CBFE039133AE807265D6A58003B8D205997EAB96390AAB207E63A2E270A476CAB5B2D9D2F7B0E55512AA957B58D5658E7EF907B069B83AA6BA941790A3C3C4A68292D59D95C9D4D8A8BCA2E8242AB0D409F671F298B6DCAE9BC4238C09E07548CEFB300098606F9E4F230C99ABA3CA342966EFF82E1ACAEB691FD6E20772E17EB4FEFB84F8CF75C0C69C59532C354D175A59F961BA4D4DFA017FD8192288F14278AE76712E127D65FE616C7E4FD0713644F7C9A7ABA1CE065694A968
159 /* SOSEngine implementation. */
160 struct __OpaqueSOSEngine
{
162 SOSDataSourceRef dataSource
;
163 CFStringRef myID
; // My peerID in the circle
164 // We need to address the issues of corrupt keychain items
165 SOSManifestRef unreadable
; // Possibly by having a set of unreadable items, to which we
166 // add any corrupted items in the db that have yet to be deleted.
167 // This happens if we notce corruption during a (read only) query.
168 // We would also perma-subtract unreadable from manifest whenever
169 // anyone asked for manifest. This result would be cached in
170 // The manifestCache below, so we just need a key into the cache
171 CFDataRef localMinusUnreadableDigest
; // or a digest (CFDataRef of the right size).
173 CFMutableDictionaryRef manifestCache
; // digest -> ( refcount, manifest )
174 CFMutableDictionaryRef peerMap
; // peerId -> SOSPeerRef
175 CFDictionaryRef viewNameSet2ChangeTracker
; // CFSetRef of CFStringRef -> SOSChangeTrackerRef
176 CFDictionaryRef viewName2ChangeTracker
; // CFStringRef -> SOSChangeTrackerRef
178 CFDateRef lastTraceDate
; // Last time we did a CloudKeychainTrace
179 CFMutableDictionaryRef coders
;
180 bool haveLoadedCoders
;
183 bool codersNeedSaving
;
185 dispatch_queue_t queue
; // Engine queue
187 dispatch_source_t save_timer
; // Engine state save timer
188 bool save_timer_pending
; // Engine state timer running, read/modify on engine queue
190 dispatch_queue_t syncCompleteQueue
; // Non-retained queue for async notificaion
191 SOSEnginePeerInSyncBlock syncCompleteListener
; // Block to call to notify the listener.
194 static bool SOSEngineLoad(SOSEngineRef engine
, SOSTransactionRef txn
, CFErrorRef
*error
);
195 static bool SOSEngineSetPeers_locked(SOSEngineRef engine
, SOSPeerMetaRef myPeerMeta
, CFArrayRef trustedPeerMetas
, CFArrayRef untrustedPeerMetas
);
196 static void SOSEngineApplyPeerState(SOSEngineRef engine
, CFDictionaryRef peerStateMap
);
197 static void SOSEngineSynthesizePeerMetas(SOSEngineRef engine
, CFMutableArrayRef trustedPeersMetas
, CFMutableArrayRef untrustedPeers
);
198 static bool SOSEngineLoadCoders(SOSEngineRef engine
, SOSTransactionRef txn
, CFErrorRef
*error
);
199 static bool SOSEngineDeleteV0State(SOSEngineRef engine
, SOSTransactionRef txn
, CFErrorRef
*error
);
201 static CFStringRef
SOSPeerIDArrayCreateString(CFArrayRef peerIDs
) {
202 return peerIDs
? CFStringCreateByCombiningStrings(kCFAllocatorDefault
, peerIDs
, CFSTR(" ")) : CFSTR("");
205 static CFStringRef
SOSEngineCopyFormattingDesc(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
206 SOSEngineRef engine
= (SOSEngineRef
)cf
;
207 CFStringRef tpDesc
= SOSPeerIDArrayCreateString(engine
->peerIDs
);
208 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);
209 CFReleaseSafe(tpDesc
);
213 static CFStringRef
SOSEngineCopyDebugDesc(CFTypeRef cf
) {
214 return SOSEngineCopyFormattingDesc(cf
, NULL
);
217 static dispatch_queue_t sEngineQueue
;
218 static CFDictionaryRef sEngineMap
;
220 CFGiblisWithFunctions(SOSEngine
, NULL
, NULL
, NULL
, NULL
, NULL
, SOSEngineCopyFormattingDesc
, SOSEngineCopyDebugDesc
, NULL
, NULL
, ^{
221 sEngineQueue
= dispatch_queue_create("SOSEngine queue", DISPATCH_QUEUE_SERIAL
);
222 sEngineMap
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
225 #define _LOG_RAW_MESSAGES 0
226 void logRawMessage(CFDataRef message
, bool sending
, uint64_t seqno
)
228 #if _LOG_RAW_MESSAGES
229 CFStringRef hexMessage
= NULL
;
231 hexMessage
= CFDataCopyHexString(message
);
233 secnoticeq("engine", "%s RAW%1d %@", sending
? "send" : "recv", seqno
?2:0, hexMessage
);
235 secnoticeq("engine", "%s RAWx %@", sending
? "send" : "recv", hexMessage
); // we don't know vers of received msg here
237 CFReleaseSafe(hexMessage
);
242 // Peer state layout. WRONG! It's an array now
243 // The peer state is an array.
244 // The first element of the array is a dictionary with any number of keys and
245 // values in it (for future expansion) such as changing the digest size or type
246 // or remembering boolean flags for a peers sake.
247 // The next three are special in that they are manifest digests with special
248 // meaning and rules as to how they are treated (These are dynamically updated
249 // based on database activity so they have a fully history of all changes made
250 // to the local db. The first is the manifest representing the pendingObjects
251 // to send to the other peer. This is normally only ever appending to, and in
252 // particular with transactions originating from the Keychain API that affect
253 // syncable items will need to add the new objects digests to the pendingObjects list
254 // while adding the digests of any tombstones encountered to the extra list.
256 CFStringRef
SOSEngineGetMyID(SOSEngineRef engine
) {
257 // TODO: this should not be needed
261 // TEMPORARY: Get the list of IDs for cleanup, this shouldn't be used instead it should iterate KVS.
262 CFArrayRef
SOSEngineGetPeerIDs(SOSEngineRef engine
) {
263 if(!engine
) return NULL
;
264 return engine
->peerIDs
;
267 void SOSEngineClearCache(SOSEngineRef engine
){
268 CFReleaseNull(engine
->manifestCache
);
269 CFReleaseNull(engine
->localMinusUnreadableDigest
);
270 if (engine
->save_timer
)
271 dispatch_source_cancel(engine
->save_timer
);
272 dispatch_release(engine
->queue
);
273 engine
->queue
= NULL
;
276 static SOSPeerRef
SOSEngineCopyPeerWithMapEntry_locked(SOSEngineRef engine
, CFStringRef peerID
, CFTypeRef mapEntry
, CFErrorRef
*error
) {
277 SOSPeerRef peer
= NULL
;
278 if (mapEntry
&& CFGetTypeID(mapEntry
) == SOSPeerGetTypeID()) {
279 // The mapEntry is an SOSPeer, so we're done.
280 peer
= (SOSPeerRef
)CFRetain(mapEntry
);
282 // The mapEntry is a peerState, attempt to initialize a new
283 // peer iff peerID is in the set of trusted peerIDs
284 if (engine
->peerIDs
&& CFArrayContainsValue(engine
->peerIDs
, CFRangeMake(0, CFArrayGetCount(engine
->peerIDs
)), peerID
)) {
285 CFErrorRef localError
= NULL
;
286 peer
= SOSPeerCreateWithState(engine
, peerID
, mapEntry
, &localError
);
288 secerror("error inflating peer: %@: %@ from state: %@", peerID
, localError
, mapEntry
);
289 CFReleaseNull(localError
);
290 peer
= SOSPeerCreateWithState(engine
, peerID
, NULL
, error
);
293 // Replace the map entry with the inflated peer.
294 CFDictionarySetValue(engine
->peerMap
, peerID
, peer
);
297 SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("peer: %@ is untrusted inflating not allowed"), peerID
);
303 static SOSPeerRef
SOSEngineCopyPeerWithID_locked(SOSEngineRef engine
, CFStringRef peerID
, CFErrorRef
*error
) {
304 CFTypeRef mapEntry
= CFDictionaryGetValue(engine
->peerMap
, peerID
);
305 SOSPeerRef peer
= NULL
;
307 peer
= SOSEngineCopyPeerWithMapEntry_locked(engine
, peerID
, mapEntry
, error
);
310 secerror("peer: %@ not found, peerMap: %@, engine: %@", peerID
, engine
->peerMap
, engine
);
311 SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("peer: %@ not found"), peerID
);
316 struct SOSEngineWithPeerContext
{
318 void (^with
)(SOSPeerRef peer
);
321 static void SOSEngineWithPeerMapEntry_locked(const void *peerID
, const void *mapEntry
, void *context
) {
322 struct SOSEngineWithPeerContext
*ewp
= context
;
323 SOSPeerRef peer
= SOSEngineCopyPeerWithMapEntry_locked(ewp
->engine
, peerID
, mapEntry
, NULL
);
330 static void SOSEngineForEachPeer_locked(SOSEngineRef engine
, void (^with
)(SOSPeerRef peer
)) {
331 struct SOSEngineWithPeerContext ewp
= { .engine
= engine
, .with
= with
};
332 CFDictionaryRef peerMapCopy
= CFDictionaryCreateCopy(NULL
, engine
->peerMap
);
333 CFDictionaryApplyFunction(peerMapCopy
, SOSEngineWithPeerMapEntry_locked
, &ewp
);
334 CFRelease(peerMapCopy
);
337 static void SOSEngineWithBackupPeerMapEntry_locked(const void *peerID
, const void *mapEntry
, void *context
) {
338 struct SOSEngineWithPeerContext
*ewp
= context
;
339 // v0 backup peer is always in map but we only consider it a backup peer if it has a keybag.
340 if (SOSPeerMapEntryIsBackup(mapEntry
)) {
341 SOSPeerRef peer
= SOSEngineCopyPeerWithMapEntry_locked(ewp
->engine
, peerID
, mapEntry
, NULL
);
349 static void SOSEngineForEachBackupPeer_locked(SOSEngineRef engine
, void (^with
)(SOSPeerRef peer
)) {
350 struct SOSEngineWithPeerContext ewp
= { .engine
= engine
, .with
= with
};
351 CFDictionaryRef peerMapCopy
= CFDictionaryCreateCopy(NULL
, engine
->peerMap
);
352 CFDictionaryApplyFunction(peerMapCopy
, SOSEngineWithBackupPeerMapEntry_locked
, &ewp
);
353 CFRelease(peerMapCopy
);
359 SOSManifestRef
SOSEngineGetManifestForDigest(SOSEngineRef engine
, CFDataRef digest
) {
360 if (!engine
->manifestCache
|| !digest
) return NULL
;
361 SOSManifestRef manifest
= (SOSManifestRef
)CFDictionaryGetValue(engine
->manifestCache
, digest
);
362 if (!manifest
) return NULL
;
363 if (CFGetTypeID(manifest
) != SOSManifestGetTypeID()) {
364 secerror("dropping corrupt manifest for %@ from cache", digest
);
365 CFDictionaryRemoveValue(engine
->manifestCache
, digest
);
372 void SOSEngineAddManifest(SOSEngineRef engine
, SOSManifestRef manifest
) {
373 CFDataRef digest
= SOSManifestGetDigest(manifest
, NULL
);
375 if (!engine
->manifestCache
)
376 engine
->manifestCache
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
377 CFDictionaryAddValue(engine
->manifestCache
, digest
, manifest
);
381 CFDataRef
SOSEnginePatchRecordAndCopyDigest(SOSEngineRef engine
, SOSManifestRef base
, SOSManifestRef removals
, SOSManifestRef additions
, CFErrorRef
*error
) {
382 CFDataRef digest
= NULL
;
383 SOSManifestRef manifest
= SOSManifestCreateWithPatch(base
, removals
, additions
, error
);
385 SOSEngineAddManifest(engine
, manifest
);
386 digest
= CFRetainSafe(SOSManifestGetDigest(manifest
, NULL
));
388 CFReleaseSafe(manifest
);
392 SOSManifestRef
SOSEngineCopyPersistedManifest(SOSEngineRef engine
, CFDictionaryRef persisted
, CFStringRef key
) {
393 return CFRetainSafe(SOSEngineGetManifestForDigest(engine
, asData(CFDictionaryGetValue(persisted
, key
), NULL
)));
396 CFMutableArrayRef
SOSEngineCopyPersistedManifestArray(SOSEngineRef engine
, CFDictionaryRef persisted
, CFStringRef key
, CFErrorRef
*error
) {
397 CFMutableArrayRef manifests
= NULL
;
398 CFArrayRef digests
= NULL
;
400 if (asArrayOptional(CFDictionaryGetValue(persisted
, key
), &digests
, error
))
401 manifests
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
402 if (digests
) CFArrayForEachC(digests
, digest
) {
403 SOSManifestRef manifest
= SOSEngineGetManifestForDigest(engine
, digest
);
405 CFArrayAppendValue(manifests
, manifest
);
410 static CFDictionaryRef
SOSEngineCopyEncodedManifestCache_locked(SOSEngineRef engine
, CFErrorRef
*error
) {
411 CFMutableDictionaryRef mfc
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
412 SOSEngineForEachPeer_locked(engine
, ^(SOSPeerRef peer
) {
413 SOSPeerAddManifestsInUse(peer
, mfc
);
419 static bool SOSEngineGCManifests_locked(SOSEngineRef engine
, CFErrorRef
*error
) {
420 __block
struct SOSDigestVector mdInCache
= SOSDigestVectorInit
;
421 __block
struct SOSDigestVector mdInUse
= SOSDigestVectorInit
;
422 struct SOSDigestVector mdUnused
= SOSDigestVectorInit
;
423 struct SOSDigestVector mdMissing
= SOSDigestVectorInit
;
426 SOSEngineForEachPeer_locked(engine
, ^(SOSPeerRef peer
) {
427 SOSPeerMarkDigestsInUse(peer
, &mdInUse
);
430 if (engine
->manifestCache
) {
431 CFDictionaryForEach(engine
->manifestCache
, ^(const void *key
, const void *value
) {
432 CFDataRef digest
= (CFDataRef
)key
;
434 SOSDigestVectorAppend(&mdInCache
, CFDataGetBytePtr(digest
));
437 // Delete unused manifests.
438 SOSDigestVectorDiff(&mdInCache
, &mdInUse
, &mdUnused
, &mdMissing
);
439 SOSManifestRef unused
= SOSManifestCreateWithDigestVector(&mdUnused
, NULL
);
440 SOSManifestForEach(unused
, ^(CFDataRef digest
, bool *stop
) {
442 CFDictionaryRemoveValue(engine
->manifestCache
, digest
);
444 CFReleaseSafe(unused
);
447 SOSDigestVectorFree(&mdInCache
);
448 SOSDigestVectorFree(&mdInUse
);
449 SOSDigestVectorFree(&mdUnused
);
450 SOSDigestVectorFree(&mdMissing
);
456 // End of Manifest cache
459 //----------------------------------------------------------------------------------------
461 //----------------------------------------------------------------------------------------
464 Each peer has an associated coder, whcih the engine keeps track of in a
465 CFDictionary indexed by peerID. The coders are read from disk when first needed,
466 then kept in memory as SOSCoders.
468 N.B. Don't rollback coder in memory if a transaction is rolled back, since this
469 might lead to reuse of an IV.
472 static bool SOSEngineCopyCoderData(SOSEngineRef engine
, CFStringRef peerID
, CFDataRef
*coderData
, CFErrorRef
*error
) {
474 SOSCoderRef coder
= (SOSCoderRef
)CFDictionaryGetValue(engine
->coders
, peerID
);
475 if (coder
&& (CFGetTypeID(coder
) == SOSCoderGetTypeID())) {
476 CFErrorRef localError
= NULL
;
477 ok
= *coderData
= SOSCoderCopyDER(coder
, &localError
);
479 secerror("failed to der encode coder for peer %@, dropping it: %@", peerID
, localError
);
480 CFDictionaryRemoveValue(engine
->coders
, peerID
);
481 CFErrorPropagate(localError
, error
);
489 static SOSCoderRef
SOSEngineGetCoderInTx_locked(SOSEngineRef engine
, SOSTransactionRef txn
, CFStringRef peerID
, CFErrorRef
*error
) {
490 if (!engine
->haveLoadedCoders
) {
491 engine
->haveLoadedCoders
= SOSEngineLoadCoders(engine
, txn
, error
);
493 if (!engine
->haveLoadedCoders
) {
498 SOSCoderRef coder
= (SOSCoderRef
)CFDictionaryGetValue(engine
->coders
, peerID
);
499 if (!coder
|| (CFGetTypeID(coder
) != SOSCoderGetTypeID())) {
500 SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("No coder for peer: %@"), peerID
);
505 static SOSCoderRef
SOSEngineGetCoder_locked(SOSEngineRef engine
, CFStringRef peerID
, CFErrorRef
*error
) {
506 return SOSEngineGetCoderInTx_locked(engine
, NULL
, peerID
, error
);
509 static bool SOSEngineEnsureCoder_locked(SOSEngineRef engine
, CFStringRef peerID
, SOSFullPeerInfoRef myPeerInfo
, SOSPeerInfoRef peerInfo
, SOSCoderRef ourCoder
, CFErrorRef
*error
) {
510 if (!ourCoder
|| !SOSCoderIsFor(ourCoder
, peerInfo
, myPeerInfo
)) {
511 secinfo("coder", "New coder for id %@.", peerID
);
512 CFErrorRef localError
= NULL
;
513 SOSCoderRef coder
= SOSCoderCreate(peerInfo
, myPeerInfo
, kCFBooleanFalse
, &localError
);
515 secerror("Failed to create coder for %@: %@", peerID
, localError
);
516 CFErrorPropagate(localError
, error
);
519 CFDictionarySetValue(engine
->coders
, peerID
, coder
);
520 CFReleaseNull(coder
);
525 bool SOSEngineInitializePeerCoder(SOSEngineRef engine
, SOSFullPeerInfoRef myPeerInfo
, SOSPeerInfoRef peerInfo
, CFErrorRef
*error
) {
526 __block
bool ok
= true;
527 CFStringRef peerID
= SOSPeerInfoGetPeerID(peerInfo
);
528 ok
&= SOSEngineForPeerID(engine
, peerID
, error
, ^(SOSTransactionRef txn
, SOSPeerRef peer
, SOSCoderRef coder
) {
529 ok
= SOSEngineEnsureCoder_locked(engine
, peerID
, myPeerInfo
, peerInfo
, coder
, error
);
534 static bool SOSEngineGCPeerState_locked(SOSEngineRef engine
, CFErrorRef
*error
) {
537 //require_quiet(ok = SOSEngineGCManifests_locked(engine, error), exit);
543 static CFMutableDictionaryRef
SOSEngineCopyPeerState_locked(SOSEngineRef engine
, CFErrorRef
*error
) {
544 CFMutableDictionaryRef peerState
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
545 CFDictionaryForEach(engine
->peerMap
, ^(const void *key
, const void *value
) {
546 CFDictionaryRef state
= NULL
;
547 if (value
&& CFGetTypeID(value
) == SOSPeerGetTypeID()) {
548 CFErrorRef localError
= NULL
;
550 state
= SOSPeerCopyState((SOSPeerRef
)value
, &localError
);
552 secnotice("engine", "%@ failed to encode peer: %@", key
, localError
);
553 CFReleaseNull(localError
);
554 // TODO: Potentially replace inflated peer with deflated peer in peerMap
556 // We have a deflated peer.
557 state
= CFRetainSafe(value
);
561 CFDictionarySetValue(peerState
, key
, state
);
562 CFReleaseSafe(state
);
568 static CFMutableDictionaryRef
SOSEngineCopyPeerCoders_locked(SOSEngineRef engine
, CFErrorRef
*error
) {
569 CFMutableDictionaryRef coders
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
570 CFDictionaryForEach(engine
->peerMap
, ^(const void *key
, const void *value
) {
571 CFDataRef coderData
= NULL
;
572 CFErrorRef localError
= NULL
;
573 bool ok
= SOSEngineCopyCoderData(engine
, (CFStringRef
)key
, &coderData
, &localError
);
575 secnotice("engine", "%@ no coder for peer: %@", key
, localError
);
577 if (ok
&& coderData
) {
578 CFDictionarySetValue(coders
, key
, coderData
);
580 CFReleaseNull(coderData
);
581 CFReleaseNull(localError
);
586 //----------------------------------------------------------------------------------------
587 // MARK: Engine state v2 Save
588 //----------------------------------------------------------------------------------------
590 // Coders and keybags
592 static CFDataRef
SOSEngineCopyCoders(SOSEngineRef engine
, CFErrorRef
*error
) {
593 // Copy the CFDataRef version of the coders into a dictionary, which is then DER-encoded for saving
594 CFDictionaryRef coders
= SOSEngineCopyPeerCoders_locked(engine
, error
);
595 CFDataRef der
= CFPropertyListCreateDERData(kCFAllocatorDefault
, coders
, error
);
596 CFReleaseSafe(coders
);
600 static bool SOSEngineSaveCoders(SOSEngineRef engine
, SOSTransactionRef txn
, CFErrorRef
*error
) {
601 // MUST hold engine lock
602 // Device must be unlocked for this to succeed
604 if (engine
->codersNeedSaving
) {
605 CFDataRef derCoders
= SOSEngineCopyCoders(engine
, error
);
606 bool ok
= derCoders
&& SOSDataSourceSetStateWithKey(engine
->dataSource
, txn
, kSOSEngineCoders
,
607 kSOSEngineProtectionDomainClassA
, derCoders
, error
);
609 engine
->codersNeedSaving
= false;
611 CFReleaseSafe(derCoders
);
616 static CFDictionaryRef
SOSEngineCopyBasicState(SOSEngineRef engine
, CFErrorRef
*error
) {
617 // Create a version of the in-memory engine state for saving to disk
618 CFMutableDictionaryRef state
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
620 CFDictionarySetValue(state
, kSOSEngineIDKey
, engine
->myID
);
622 CFDictionarySetValue(state
, kSOSEnginePeerIDsKey
, engine
->peerIDs
);
623 if (engine
->lastTraceDate
)
624 CFDictionarySetValue(state
, kSOSEngineTraceDateKey
, engine
->lastTraceDate
);
626 SOSPersistCFIndex(state
, kSOSEngineStateVersionKey
, kCurrentEngineVersion
);
630 static bool SOSEngineDoSaveOneState(SOSEngineRef engine
, SOSTransactionRef txn
, CFStringRef key
, CFStringRef pdmn
,
631 CFDictionaryRef state
, CFErrorRef
*error
) {
632 CFDataRef derState
= CFPropertyListCreateDERData(kCFAllocatorDefault
, state
, error
);
633 bool ok
= derState
&& SOSDataSourceSetStateWithKey(engine
->dataSource
, txn
, key
, pdmn
, derState
, error
);
634 CFReleaseSafe(derState
);
638 static bool SOSEngineDoSave(SOSEngineRef engine
, SOSTransactionRef txn
, CFErrorRef
*error
) {
641 CFDictionaryRef state
= SOSEngineCopyBasicState(engine
, error
);
642 ok
&= state
&& SOSEngineDoSaveOneState(engine
, txn
, kSOSEngineStatev2
, kSOSEngineProtectionDomainClassD
, state
, error
);
643 CFReleaseNull(state
);
645 state
= SOSEngineCopyPeerState_locked(engine
, error
);
646 ok
&= state
&& SOSEngineDoSaveOneState(engine
, txn
, kSOSEnginePeerStates
, kSOSEngineProtectionDomainClassD
, state
, error
);
647 CFReleaseNull(state
);
649 state
= SOSEngineCopyEncodedManifestCache_locked(engine
, error
);
650 ok
&= state
&& SOSEngineDoSaveOneState(engine
, txn
, kSOSEngineManifestCache
, kSOSEngineProtectionDomainClassD
, state
, error
);
651 CFReleaseNull(state
);
653 ok
&= SOSEngineSaveCoders(engine
, txn
, error
);
655 SOSEngineDeleteV0State(engine
, txn
, NULL
);
660 #if ENGINE_DELAY_SAVE
662 #define SOSENGINE_SAVE_TIMEOUT (NSEC_PER_MSEC * 500ull)
663 #define SOSENGINE_SAVE_LEEWAY (NSEC_PER_MSEC * 500ull)
664 #define SOSENGINE_SAVE_MAX_DELAY (NSEC_PER_MSEC * 500ull)
666 #if !(TARGET_IPHONE_SIMULATOR)
667 static void SOSEngineShouldSave(SOSEngineRef engine
) {
668 bool start_timer
= false;
670 if (engine
->save_timer
== NULL
) {
671 // Schedule the timer to fire on a concurrent queue, so we can follow
672 // the proper procedure of acquiring a dataSource and then engine queues.
673 engine
->save_timer
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
, 0, 0, dispatch_get_global_queue(QOS_CLASS_DEFAULT
, 0));
674 dispatch_source_set_event_handler(engine
->save_timer
, ^{
675 CFErrorRef dsWithError
= NULL
;
677 // Start with clearing the pending state so that any other caller
678 // get their own timer, worse case it that we get a duplicate store.
679 dispatch_sync(engine
->queue
, ^{
680 engine
->save_timer_pending
= false;
683 if (engine
->dataSource
) {
684 if (!SOSDataSourceWith(engine
->dataSource
, &dsWithError
, ^(SOSTransactionRef txn
, bool *commit
) {
685 dispatch_sync(engine
->queue
, ^{
686 CFErrorRef saveError
= NULL
;
687 if (!SOSEngineDoSave(engine
, txn
, &saveError
)) {
688 secerrorq("Failed to save engine state: %@", saveError
);
689 CFReleaseNull(saveError
);
693 secerrorq("Failed to open dataSource to save engine state: %@", dsWithError
);
694 CFReleaseNull(dsWithError
);
698 xpc_transaction_end();
701 assert(engine
->save_timer_pending
== false);
704 if (engine
->save_timer_pending
)
707 engine
->save_timer_pending
= true;
709 // Start a trasaction, then start the timer, the handler for the timer will end
711 xpc_transaction_begin();
713 // Set the timer's fire time to now + SOSENGINE_SAVE_TIMEOUT seconds with a SOSENGINE_SAVE_LEEWAY fuzz factor.
714 dispatch_source_set_timer(engine
->save_timer
,
715 dispatch_time(DISPATCH_TIME_NOW
, SOSENGINE_SAVE_TIMEOUT
),
716 DISPATCH_TIME_FOREVER
, SOSENGINE_SAVE_LEEWAY
);
719 dispatch_resume(engine
->save_timer
);
724 #endif /* ENGINE_DELAY_SAVE */
726 static bool SOSEngineSave(SOSEngineRef engine
, SOSTransactionRef txn
, CFErrorRef
*error
) {
727 // Don't save engine state from tests
728 if (!engine
->dataSource
)
730 #if (TARGET_IPHONE_SIMULATOR) || !ENGINE_DELAY_SAVE
731 return SOSEngineDoSave(engine
, txn
, error
);
733 SOSEngineShouldSave(engine
);
738 //----------------------------------------------------------------------------------------
739 // MARK: Engine state v2 Load/Restore
740 //----------------------------------------------------------------------------------------
742 // Restore the in-memory state of engine from saved state loaded from the db
743 static bool SOSEngineSetManifestCacheWithDictionary(SOSEngineRef engine
, CFDictionaryRef manifestCache
, CFErrorRef
*error
) {
744 __block
bool ok
= true;
745 CFReleaseNull(engine
->manifestCache
);
747 engine
->manifestCache
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
748 CFDictionaryForEach(manifestCache
, ^(const void *key
, const void *value
) {
749 CFDataRef data
= (CFDataRef
)value
;
751 SOSManifestRef mf
= SOSManifestCreateWithData(data
, NULL
);
753 CFDictionarySetValue(engine
->manifestCache
, key
, mf
);
762 static bool SOSEngineUpdateStateWithDictionary(SOSEngineRef engine
, CFDictionaryRef stateDict
, CFErrorRef
*error
) {
766 // If kCurrentEngineVersion > 2, uncomment and fill in code below
767 CFIndex engineVersion
= 0 ;
768 bool versionPresent
= SOSPeerGetOptionalPersistedCFIndex(stateDict
, kSOSEngineStateVersionKey
, &engineVersion
);
769 if (versionPresent
&& (engineVersion
!= kCurrentEngineVersion
)) {
777 static bool SOSEngineSetStateWithDictionary(SOSEngineRef engine
, CFDictionaryRef stateDict
, CFErrorRef
*error
) {
780 SOSEngineUpdateStateWithDictionary(engine
, stateDict
, error
);
781 CFRetainAssign(engine
->myID
, asString(CFDictionaryGetValue(stateDict
, kSOSEngineIDKey
), NULL
));
782 CFRetainAssign(engine
->peerIDs
, asArray(CFDictionaryGetValue(stateDict
, kSOSEnginePeerIDsKey
), NULL
));
783 CFRetainAssign(engine
->lastTraceDate
, asDate(CFDictionaryGetValue(stateDict
, kSOSEngineTraceDateKey
), NULL
));
786 secnotice("engine", "%@", engine
);
790 static bool SOSEngineSetPeerStateWithDictionary(SOSEngineRef engine
, CFDictionaryRef peerStateDict
, CFErrorRef
*error
) {
791 // Set the in-memory peer state using the dictionary version of the DER-encoded version from disk
792 CFMutableArrayRef untrustedPeers
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
793 CFMutableArrayRef trustedPeersMetas
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
794 SOSEngineApplyPeerState(engine
, asDictionary(peerStateDict
, NULL
));
795 SOSEngineSynthesizePeerMetas(engine
, trustedPeersMetas
, untrustedPeers
);
796 SOSEngineSetPeers_locked(engine
, engine
->myID
, trustedPeersMetas
, untrustedPeers
);
797 CFReleaseNull(trustedPeersMetas
);
798 CFReleaseNull(untrustedPeers
);
802 static CFMutableDictionaryRef
derStateToDictionaryCopy(CFDataRef state
, CFErrorRef
*error
) {
804 CFMutableDictionaryRef stateDict
= NULL
;
806 const uint8_t *der
= CFDataGetBytePtr(state
);
807 const uint8_t *der_end
= der
+ CFDataGetLength(state
);
808 ok
= der
= der_decode_dictionary(kCFAllocatorDefault
, kCFPropertyListMutableContainers
, (CFDictionaryRef
*)&stateDict
, error
, der
, der_end
);
809 if (der
&& der
!= der_end
) {
810 ok
= SOSErrorCreate(kSOSErrorDecodeFailure
, error
, NULL
, CFSTR("trailing %td bytes at end of state"), der_end
- der
);
813 CFReleaseNull(stateDict
);
819 static bool SOSEngineLoadCoders(SOSEngineRef engine
, SOSTransactionRef txn
, CFErrorRef
*error
) {
820 // Read the serialized engine state from the datasource (aka keychain) and populate the in-memory engine
822 CFDataRef derCoders
= NULL
;
823 CFMutableDictionaryRef codersDict
= NULL
;
825 derCoders
= SOSDataSourceCopyStateWithKey(engine
->dataSource
, kSOSEngineCoders
, kSOSEngineProtectionDomainClassA
, txn
, error
);
826 require_quiet(derCoders
, xit
);
827 codersDict
= derStateToDictionaryCopy(derCoders
, error
);
828 require_quiet(codersDict
, xit
);
830 CFDictionaryForEach(engine
->peerMap
, ^(const void *peerID
, const void *peerState
) {
832 if (!CFDictionaryContainsKey(engine
->coders
, peerID
)) {
833 CFDataRef coderData
= asData(CFDictionaryGetValue(codersDict
, peerID
), NULL
);
835 CFErrorRef createError
= NULL
;
836 SOSCoderRef coder
= SOSCoderCreateFromData(coderData
, &createError
);
839 CFStringRef coderid
= SOSCoderGetID(coder
);
840 if (!CFEqualSafe(coderid
, (CFStringRef
)peerID
)) {
841 secerror("Coder id %@ on disk does not match: %@", coderid
, peerID
);
843 CFDictionaryAddValue(engine
->coders
, peerID
, coder
);
846 secnotice("coder", "Coder for '%@' failed to create: %@", peerID
, createError
);
848 CFReleaseNull(createError
);
849 CFReleaseNull(coder
);
851 // Needed a coder, didn't find one, notify the account to help us out.
852 // Next attempt to sync will fix this
853 SOSCCEnsurePeerRegistration();
860 CFReleaseNull(derCoders
);
861 CFReleaseNull(codersDict
);
865 static bool SOSEngineDeleteV0State(SOSEngineRef engine
, SOSTransactionRef txn
, CFErrorRef
*error
) {
866 // SOSDataSourceDeleteStateWithKey(engine->dataSource, kSOSEngineState, kSOSEngineProtectionDomainClassD, txn, error);
868 // Create effectively empty state until delete is working
869 CFMutableDictionaryRef state
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
871 CFDictionarySetValue(state
, kSOSEngineIDKey
, engine
->myID
);
872 CFDataRef derState
= CFPropertyListCreateDERData(kCFAllocatorDefault
, state
, error
);
873 CFReleaseNull(state
);
875 bool ok
= derState
&& SOSDataSourceSetStateWithKey(engine
->dataSource
, txn
, kSOSEngineState
, kSOSEngineProtectionDomainClassD
, derState
, error
);
876 CFReleaseSafe(derState
);
880 static bool SOSEngineLoad(SOSEngineRef engine
, SOSTransactionRef txn
, CFErrorRef
*error
) {
881 // Read the serialized engine state from the datasource (aka keychain) and populate the in-memory engine
883 CFDataRef basicEngineState
= NULL
;
884 CFMutableDictionaryRef engineState
= NULL
;
885 CFDictionaryRef manifestCache
= NULL
;
886 CFDictionaryRef peerStateDict
= NULL
;
887 CFMutableDictionaryRef codersDict
= NULL
;
888 // Look for the v2 engine state first
889 basicEngineState
= SOSDataSourceCopyStateWithKey(engine
->dataSource
, kSOSEngineStatev2
, kSOSEngineProtectionDomainClassD
, txn
, error
);
890 if (basicEngineState
) {
891 CFDataRef data
= NULL
;
892 engineState
= derStateToDictionaryCopy(basicEngineState
, error
);
894 data
= SOSDataSourceCopyStateWithKey(engine
->dataSource
, kSOSEngineManifestCache
, kSOSEngineProtectionDomainClassD
, txn
, error
);
895 manifestCache
= derStateToDictionaryCopy(data
, error
);
898 data
= SOSDataSourceCopyStateWithKey(engine
->dataSource
, kSOSEnginePeerStates
, kSOSEngineProtectionDomainClassD
, txn
, error
);
899 peerStateDict
= derStateToDictionaryCopy(data
, error
);
902 // Look for original V0 engine state next
903 CFDataRef v0EngineStateData
= SOSDataSourceCopyStateWithKey(engine
->dataSource
, kSOSEngineState
, kSOSEngineProtectionDomainClassD
, txn
, error
);
904 if (v0EngineStateData
) {
905 engineState
= derStateToDictionaryCopy(v0EngineStateData
, error
);
907 manifestCache
= CFRetainSafe(asDictionary(CFDictionaryGetValue(engineState
, kSOSEngineManifestCacheKey
), NULL
));
908 peerStateDict
= CFRetainSafe(asDictionary(CFDictionaryGetValue(engineState
, kSOSEnginePeerStateKey
), NULL
));
910 CFReleaseNull(v0EngineStateData
);
912 secnotice("coder", "Migrating from v0 engine state; dropping coders and forcing re-negotiation");
913 SOSCCEnsurePeerRegistration();
914 SOSCCSyncWithAllPeers();
917 ok
= engineState
&& SOSEngineSetStateWithDictionary(engine
, engineState
, error
);
919 ok
&= SOSEngineSetManifestCacheWithDictionary(engine
, manifestCache
, error
);
921 ok
&= peerStateDict
&& SOSEngineSetPeerStateWithDictionary(engine
, peerStateDict
, error
);
923 CFReleaseSafe(basicEngineState
);
924 CFReleaseSafe(engineState
);
925 CFReleaseSafe(manifestCache
);
926 CFReleaseSafe(peerStateDict
);
927 CFReleaseSafe(codersDict
);
931 bool SOSTestEngineSaveWithDER(SOSEngineRef engine
, CFDataRef derState
, CFErrorRef
*error
) {
937 bool SOSTestEngineSave(SOSEngineRef engine, CFErrorRef *error) {
943 //----------------------------------------------------------------------------------------
944 // MARK: Change Trackers and Peer Manifests
945 //----------------------------------------------------------------------------------------
947 static SOSManifestRef
SOSEngineCreateManifestWithViewNameSet_locked(SOSEngineRef engine
, CFSetRef viewNameSet
, CFErrorRef
*error
) {
948 // TODO: Potentially tell all changeTrackers to track manifests ( //forall ct do SOSChangeTrackerSetConcrete(ct, true);
949 // and read the entire dataSource and pass all objects though the filter here, instead of
950 // forcing the datasource to be able to do "smart" queries
951 return SOSDataSourceCopyManifestWithViewNameSet(engine
->dataSource
, viewNameSet
, error
);
954 static SOSChangeTrackerRef
SOSEngineCopyChangeTrackerWithViewNameSet_locked(SOSEngineRef engine
, CFSetRef viewNameSet
, CFErrorRef
*error
) {
955 SOSChangeTrackerRef ct
= (SOSChangeTrackerRef
)CFDictionaryGetValue(engine
->viewNameSet2ChangeTracker
, viewNameSet
);
957 SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("no change tracker for view set %@"), viewNameSet
);
958 return CFRetainSafe(ct
);
961 static SOSManifestRef
SOSEngineCopyManifestWithViewNameSet_locked(SOSEngineRef engine
, CFSetRef viewNameSet
, CFErrorRef
*error
) {
962 SOSChangeTrackerRef ct
= SOSEngineCopyChangeTrackerWithViewNameSet_locked(engine
, viewNameSet
, error
);
966 SOSManifestRef manifest
= SOSChangeTrackerCopyManifest(ct
, NULL
);
968 manifest
= SOSEngineCreateManifestWithViewNameSet_locked(engine
, viewNameSet
, error
); // Do the SQL query
969 SOSChangeTrackerSetManifest(ct
, manifest
);
975 SOSManifestRef
SOSEngineCopyLocalPeerManifest_locked(SOSEngineRef engine
, SOSPeerRef peer
, CFErrorRef
*error
) {
976 return SOSEngineCopyManifestWithViewNameSet_locked(engine
, SOSPeerGetViewNameSet(peer
), error
);
979 #define withViewAndBackup(VIEW) do { with(VIEW); if (!isTomb) with(VIEW ## _tomb); } while(0)
982 // Invoke with once for each view an object is in.
983 // TODO: Move this function into the DataSource
984 static void SOSEngineObjectWithView(SOSEngineRef engine
, SOSObjectRef object
, void (^with
)(CFStringRef view
)) {
985 // Filter items into v0 only view here
986 SecDbItemRef item
= (SecDbItemRef
)object
; // TODO: Layer violation, breaks tests
987 if (isDictionary(object
)) {
988 CFTypeRef isTombValue
= CFDictionaryGetValue((CFDictionaryRef
)object
, kSecAttrTombstone
);
989 bool isTomb
= isTombValue
&& CFBooleanGetValue(isTombValue
);
990 // We are in the test just assume v0 and v2 views.
991 withViewAndBackup(kSOSViewKeychainV0
);
992 } else if (SecDbItemIsSyncableOrCorrupted(item
)) {
993 const SecDbClass
*iclass
= SecDbItemGetClass(item
);
994 CFTypeRef pdmn
= SecDbItemGetCachedValueWithName(item
, kSecAttrAccessible
);
995 if ((iclass
== &genp_class
|| iclass
== &inet_class
|| iclass
== &keys_class
|| iclass
== &cert_class
)
997 && (CFEqual(pdmn
, kSecAttrAccessibleWhenUnlocked
)
998 || CFEqual(pdmn
, kSecAttrAccessibleAfterFirstUnlock
)
999 || CFEqual(pdmn
, kSecAttrAccessibleAlwaysPrivate
)
1000 || CFEqual(pdmn
, kSecAttrAccessibleWhenUnlockedThisDeviceOnly
)
1001 || CFEqual(pdmn
, kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
)
1002 || CFEqual(pdmn
, kSecAttrAccessibleAlwaysThisDeviceOnlyPrivate
)))
1004 CFTypeRef tomb
= SecDbItemGetCachedValueWithName(item
, kSecAttrTombstone
);
1006 bool isTomb
= (isNumber(tomb
) && CFNumberGetValue(tomb
, kCFNumberCharType
, &cvalue
) && cvalue
== 1);
1007 CFTypeRef viewHint
= SecDbItemGetCachedValueWithName(item
, kSecAttrSyncViewHint
);
1008 if (viewHint
== NULL
) {
1009 if (iclass
== &cert_class
) {
1010 withViewAndBackup(kSOSViewOtherSyncable
);
1012 if (!SecDbItemGetCachedValueWithName(item
, kSecAttrTokenID
)) {
1013 withViewAndBackup(kSOSViewKeychainV0
);
1015 CFTypeRef agrp
= SecDbItemGetCachedValueWithName(item
, kSecAttrAccessGroup
);
1016 if (iclass
== &keys_class
&& CFEqualSafe(agrp
, CFSTR("com.apple.security.sos"))) {
1017 withViewAndBackup(kSOSViewiCloudIdentity
);
1018 } else if (CFEqualSafe(agrp
, CFSTR("com.apple.cfnetwork"))) {
1019 withViewAndBackup(kSOSViewAutofillPasswords
);
1020 } else if (CFEqualSafe(agrp
, CFSTR("com.apple.safari.credit-cards"))) {
1021 withViewAndBackup(kSOSViewSafariCreditCards
);
1022 } else if (iclass
== &genp_class
) {
1023 if (CFEqualSafe(agrp
, CFSTR("apple")) &&
1024 CFEqualSafe(SecDbItemGetCachedValueWithName(item
, kSecAttrService
), CFSTR("AirPort"))) {
1025 withViewAndBackup(kSOSViewWiFi
);
1026 } else if (CFEqualSafe(agrp
, CFSTR("com.apple.sbd"))) {
1027 withViewAndBackup(kSOSViewBackupBagV0
);
1029 withViewAndBackup(kSOSViewOtherSyncable
); // (genp)
1032 withViewAndBackup(kSOSViewOtherSyncable
); // (inet || keys)
1038 CFStringRef viewHintTomb
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%@-tomb"), viewHint
);
1041 CFRelease(viewHintTomb
);
1047 // TODO: general queries
1050 CFArrayForEachC(engine
->views
, view
) {
1051 bool inView
= SOSViewQueryMatchItem(view
, item
);
1053 CFStringRef viewName
= SOSViewCopyName(view
);
1055 CFReleaseSafe(viewName
);
1063 // Deliver delayed notifiations of changes in keychain
1067 SOSSendViewNotification(CFSetRef viewNotifications
)
1069 CFNotificationCenterRef center
= CFNotificationCenterGetDarwinNotifyCenter();
1071 CFSetForEach(viewNotifications
, ^(const void *value
) {
1072 secinfo("view", "Sending view notification for view %@", value
);
1074 CFStringRef str
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("com.apple.security.view-change.%@"), value
);
1078 CFNotificationCenterPostNotificationWithOptions(center
, str
, NULL
, NULL
, 0);
1085 SOSArmViewNotificationEvents(CFSetRef viewNotifications
)
1087 static CFMutableSetRef pendingViewNotifications
;
1088 static dispatch_once_t onceToken
;
1089 static dispatch_queue_t queue
;
1091 dispatch_once(&onceToken
, ^{
1092 queue
= dispatch_queue_create("ViewNotificationQueue", NULL
);
1094 if (queue
== NULL
|| CFSetGetCount(viewNotifications
) == 0)
1098 * PendingViewNotifications is only modified on queue.
1099 * PendingViewNotifications is used as a signal if a timer is running.
1101 * If a timer is running, new events are just added to the existing
1102 * pendingViewNotifications.
1105 #define DELAY_OF_NOTIFICATION_IN_NS (NSEC_PER_SEC)
1107 CFRetain(viewNotifications
);
1109 dispatch_async(queue
, ^{
1110 if (pendingViewNotifications
== NULL
) {
1111 pendingViewNotifications
= CFSetCreateMutableCopy(NULL
, 0, viewNotifications
);
1113 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
, (int64_t)DELAY_OF_NOTIFICATION_IN_NS
), queue
, ^{
1114 SOSSendViewNotification(pendingViewNotifications
);
1116 // when timer hits, clear out set of modified views
1117 CFRelease(pendingViewNotifications
);
1118 pendingViewNotifications
= NULL
;
1121 CFSetUnion(pendingViewNotifications
, viewNotifications
);
1123 CFRelease(viewNotifications
);
1129 // SOSChangeMapper - Helper for SOSEngineUpdateChanges_locked
1131 struct SOSChangeMapper
{
1132 SOSEngineRef engine
;
1133 SOSTransactionRef txn
;
1134 SOSDataSourceTransactionPhase phase
;
1135 SOSDataSourceTransactionSource source
;
1136 CFMutableDictionaryRef ct2changes
;
1137 CFMutableSetRef viewNotifications
;
1140 static void SOSChangeMapperInit(struct SOSChangeMapper
*cm
, SOSEngineRef engine
, SOSTransactionRef txn
, SOSDataSourceTransactionPhase phase
, SOSDataSourceTransactionSource source
) {
1141 cm
->engine
= engine
;
1144 cm
->source
= source
;
1145 cm
->ct2changes
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1146 cm
->viewNotifications
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
);
1149 static void SOSChangeMapperSendNotifications(struct SOSChangeMapper
*cm
)
1151 SOSArmViewNotificationEvents(cm
->viewNotifications
);
1154 static void SOSChangeMapperFree(struct SOSChangeMapper
*cm
) {
1155 CFReleaseSafe(cm
->ct2changes
);
1156 CFReleaseSafe(cm
->viewNotifications
);
1159 static void SOSChangeMapperAddViewNotification(struct SOSChangeMapper
*cm
, CFStringRef view
)
1161 assert(isString(view
));
1163 // aggregate the PCS view into one notification
1164 if (CFStringHasPrefix(view
, CFSTR("PCS-"))) {
1165 view
= CFSTR("PCS");
1167 CFSetSetValue(cm
->viewNotifications
, view
);
1170 static void SOSChangeMapperAppendObject(struct SOSChangeMapper
*cm
, SOSChangeTrackerRef ct
, bool isAdd
, CFTypeRef object
) {
1171 CFMutableArrayRef changes
= (CFMutableArrayRef
)CFDictionaryGetValue(cm
->ct2changes
, ct
);
1173 changes
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
1174 CFDictionarySetValue(cm
->ct2changes
, ct
, changes
);
1175 CFReleaseSafe(changes
);
1177 isAdd
? SOSChangesAppendAdd(changes
, object
) : SOSChangesAppendDelete(changes
, object
);
1180 static bool SOSChangeMapperIngestChange(struct SOSChangeMapper
*cm
, bool isAdd
, CFTypeRef change
) {
1181 bool someoneCares
= false;
1182 if (isData(change
)) {
1183 // TODO: Reenable assertion once the tests have been updated
1185 // We got a digest for a deleted object. Our dataSource probably couldn't find
1186 // an object with this digest, probably because it went missing, or it was
1187 // discovered to be corrupted.
1188 // Tell all our changeTrackers about this digest since we don't know who might need it.
1189 CFDictionaryForEach(cm
->engine
->viewNameSet2ChangeTracker
, ^(const void *viewNameSet
, const void *ct
) {
1190 SOSChangeMapperAppendObject(cm
, (SOSChangeTrackerRef
)ct
, isAdd
, change
);
1192 someoneCares
= CFDictionaryGetCount(cm
->engine
->viewNameSet2ChangeTracker
);
1194 // We got an object let's figure out which views it's in and schedule it for
1195 // delivery to all changeTrackers interested in any of those views.
1196 SOSObjectRef object
= (SOSObjectRef
)change
;
1197 CFMutableSetRef changeTrackerSet
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
);
1198 // First gather all the changeTrackers interested in this object (eliminating dupes by collecting them in a set)
1199 SOSEngineObjectWithView(cm
->engine
, object
, ^(CFStringRef viewName
) {
1200 const void *ctorset
= CFDictionaryGetValue(cm
->engine
->viewName2ChangeTracker
, viewName
);
1201 if (isSet(ctorset
)) {
1202 CFSetForEach((CFSetRef
)ctorset
, ^(const void *ct
) { CFSetAddValue(changeTrackerSet
, ct
); });
1203 } else if (ctorset
) {
1204 CFSetAddValue(changeTrackerSet
, ctorset
);
1208 SOSChangeMapperAddViewNotification(cm
, viewName
);
1210 // Then append the object to the changes array in the ct2changes dictionary keyed by viewSet
1211 CFSetForEach(changeTrackerSet
, ^(const void *ct
) {
1212 SOSChangeMapperAppendObject(cm
, (SOSChangeTrackerRef
)ct
, isAdd
, object
);
1214 someoneCares
= CFSetGetCount(changeTrackerSet
);
1215 CFReleaseSafe(changeTrackerSet
);
1217 return someoneCares
;
1220 static bool SOSChangeMapperSend(struct SOSChangeMapper
*cm
, CFErrorRef
*error
) {
1221 __block
bool ok
= true;
1222 CFDictionaryForEach(cm
->ct2changes
, ^(const void *ct
, const void *changes
) {
1223 ok
&= SOSChangeTrackerTrackChanges((SOSChangeTrackerRef
)ct
, cm
->engine
, cm
->txn
, cm
->source
, cm
->phase
, (CFArrayRef
)changes
, error
);
1228 static bool SOSEngineUpdateChanges_locked(SOSEngineRef engine
, SOSTransactionRef txn
, SOSDataSourceTransactionPhase phase
, SOSDataSourceTransactionSource source
, CFArrayRef changes
, CFErrorRef
*error
)
1230 secnoticeq("engine", "%@: %s %s %ld changes, txn=%@, %p", engine
->myID
, phase
== kSOSDataSourceTransactionWillCommit
? "will-commit" : phase
== kSOSDataSourceTransactionDidCommit
? "did-commit" : "did-rollback", source
== kSOSDataSourceSOSTransaction
? "sos" : "api", CFArrayGetCount(changes
), txn
, txn
);
1233 case kSOSDataSourceTransactionDidRollback
:
1234 ok
&= SOSEngineLoad(engine
, txn
, error
);
1236 case kSOSDataSourceTransactionDidCommit
: // Corruption causes us to process items at DidCommit
1237 case kSOSDataSourceTransactionWillCommit
:
1239 bool mappedItemChanged
= false;
1241 struct SOSChangeMapper cm
;
1242 SOSChangeMapperInit(&cm
, engine
, txn
, phase
, source
);
1243 SecDbEventRef event
;
1244 CFArrayForEachC(changes
, event
) {
1245 CFTypeRef deleted
= NULL
;
1246 CFTypeRef inserted
= NULL
;
1247 SecDbEventGetComponents(event
, &deleted
, &inserted
, error
);
1249 bool someoneCares
= SOSChangeMapperIngestChange(&cm
, false, deleted
);
1251 mappedItemChanged
= true;
1255 bool someoneCares
= SOSChangeMapperIngestChange(&cm
, true, inserted
);
1257 mappedItemChanged
= true;
1259 if (!someoneCares
&& !isData(inserted
) && SecDbItemIsTombstone((SecDbItemRef
)inserted
) && !CFEqualSafe(SecDbItemGetValue((SecDbItemRef
)inserted
, &v7utomb
, NULL
), kCFBooleanTrue
)) {
1260 CFErrorRef localError
= NULL
;
1261 // A tombstone was inserted but there is no changetracker that
1263 if (!SecDbItemDoDeleteSilently((SecDbItemRef
)inserted
, (SecDbConnectionRef
)txn
, &localError
)) {
1264 secerror("failed to delete tombstone %@ that no one cares about: %@", inserted
, localError
);
1265 CFReleaseNull(localError
);
1271 ok
&= SOSChangeMapperSend(&cm
, error
);
1272 SOSChangeMapperSendNotifications(&cm
); // Trigger notifications for view that changes changed
1273 SOSChangeMapperFree(&cm
);
1275 if (ok
&& phase
== kSOSDataSourceTransactionWillCommit
) {
1276 // Only consider writing if we're in the WillCommit phase.
1277 // DidCommit phases happen outside the database lock and
1278 // writing to the DBConn will cause deadlocks.
1279 if (mappedItemChanged
) {
1280 // Write SOSEngine and SOSPeer state to disk
1281 ok
&= SOSEngineSave(engine
, txn
, error
);
1283 secnotice("engine", "Not saving engine state, nothing changed.");
1292 static void SOSEngineSetNotifyPhaseBlock(SOSEngineRef engine
) {
1293 SOSDataSourceAddNotifyPhaseBlock(engine
->dataSource
, ^(SOSDataSourceRef ds
, SOSTransactionRef txn
, SOSDataSourceTransactionPhase phase
, SOSDataSourceTransactionSource source
, CFArrayRef changes
) {
1294 dispatch_sync(engine
->queue
, ^{
1295 CFErrorRef localError
= NULL
;
1296 if (!SOSEngineUpdateChanges_locked(engine
, txn
, phase
, source
, changes
, &localError
)) {
1297 secerror("updateChanged failed: %@", localError
);
1299 CFReleaseSafe(localError
);
1304 #if 0 // TODO: update these checks
1305 static void SOSEngineCircleChanged_sanitycheck(SOSEngineRef engine
, CFStringRef myPeerID
, CFArrayRef trustedPeers
, CFArrayRef untrustedPeers
) {
1307 CFMutableArrayRef addedPeers
= CFArrayCreateDifference(kCFAllocatorDefault
, trustedPeers
, engine
->peerIDs
);
1308 CFMutableArrayRef deletedPeers
= CFArrayCreateDifference(kCFAllocatorDefault
, engine
->peerIDs
, trustedPeers
);
1309 CFMutableArrayRef addedUntrustedPeers
= CFArrayCreateDifference(kCFAllocatorDefault
, untrustedPeers
, engine
->peerIDs
);
1310 CFMutableArrayRef deletedUntrustedPeers
= CFArrayCreateDifference(kCFAllocatorDefault
, engine
->peerIDs
, untrustedPeers
);
1312 CFStringRef tpDesc
= SOSPeerIDArrayCreateString(trustedPeers
);
1313 CFStringRef apDesc
= SOSPeerIDArrayCreateString(addedPeers
);
1314 CFStringRef dpDesc
= SOSPeerIDArrayCreateString(deletedPeers
);
1315 CFStringRef aupDesc
= SOSPeerIDArrayCreateString(addedUntrustedPeers
);
1316 CFStringRef dupDesc
= SOSPeerIDArrayCreateString(deletedUntrustedPeers
);
1317 secnotice("engine", "trusted %@ added %@ removed %@ add ut: %@ rem ut: %@", tpDesc
, apDesc
, dpDesc
, aupDesc
, dupDesc
);
1318 CFReleaseSafe(dupDesc
);
1319 CFReleaseSafe(aupDesc
);
1320 CFReleaseSafe(dpDesc
);
1321 CFReleaseSafe(apDesc
);
1322 CFReleaseSafe(tpDesc
);
1325 // Ensure SOSAccount isn't giving us the runaround.
1326 // Assert that trustedPeers, untrustedPeers and myPeerId are disjoint sets
1328 CFMutableArrayRef allTrustedPeers
= CFArrayCreateDifference(kCFAllocatorDefault
, trustedPeers
, untrustedPeers
);
1329 assert(CFEqual(trustedPeers
, allTrustedPeers
));
1330 CFReleaseSafe(allTrustedPeers
);
1331 assert(!CFArrayContainsValue(trustedPeers
, CFRangeMake(0, CFArrayGetCount(trustedPeers
)), myPeerID
));
1333 if (untrustedPeers
) {
1334 CFMutableArrayRef allUntrustedPeers
= CFArrayCreateDifference(kCFAllocatorDefault
, untrustedPeers
, trustedPeers
);
1335 assert(CFEqual(untrustedPeers
, allUntrustedPeers
));
1336 CFReleaseSafe(allUntrustedPeers
);
1337 assert(!CFArrayContainsValue(untrustedPeers
, CFRangeMake(0, CFArrayGetCount(trustedPeers
)), myPeerID
));
1340 CFReleaseNull(deletedUntrustedPeers
);
1341 CFReleaseNull(addedUntrustedPeers
);
1342 CFReleaseNull(deletedPeers
);
1343 CFReleaseNull(addedPeers
);
1345 // End of logging and asertions, actual code here.
1349 static SOSChangeTrackerRef
SOSReferenceAndGetChangeTracker(CFDictionaryRef lookup
, CFMutableDictionaryRef referenced
, CFSetRef viewNameSet
) {
1350 SOSChangeTrackerRef ct
= (SOSChangeTrackerRef
)CFDictionaryGetValue(referenced
, viewNameSet
);
1352 ct
= (SOSChangeTrackerRef
)CFDictionaryGetValue(lookup
, viewNameSet
);
1354 SOSChangeTrackerResetRegistration(ct
);
1355 CFDictionarySetValue(referenced
, viewNameSet
, ct
);
1357 ct
= SOSChangeTrackerCreate(kCFAllocatorDefault
, false, NULL
, NULL
);
1358 CFDictionarySetValue(referenced
, viewNameSet
, ct
);
1365 static void CFStringAppendPeerIDAndViews(CFMutableStringRef desc
, CFStringRef peerID
, CFSetRef vns
) {
1366 CFStringSetPerformWithDescription(vns
, ^(CFStringRef description
) {
1367 CFStringAppendFormat(desc
, NULL
, CFSTR(" %@ (%@)"), peerID
, description
);
1371 // Must be called after updating viewNameSet2ChangeTracker
1372 static void SOSEngineUpdateViewName2ChangeTracker(SOSEngineRef engine
) {
1373 // Create the mapping from viewName -> ChangeTracker used for lookup during change notification
1374 CFMutableDictionaryRef newViewName2ChangeTracker
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1375 CFDictionaryForEach(engine
->viewNameSet2ChangeTracker
, ^(const void *viewNameSet
, const void *ct
) {
1376 CFSetForEach(viewNameSet
, ^(const void *viewName
) {
1377 const void *ctorset
= NULL
;
1378 if (CFDictionaryGetValueIfPresent(newViewName2ChangeTracker
, viewName
, &ctorset
)) {
1379 if (isSet(ctorset
)) {
1380 CFSetAddValue((CFMutableSetRef
)ctorset
, ct
);
1381 } else if (!CFEqual(ct
, ctorset
)) {
1382 CFMutableSetRef set
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
);
1383 CFSetAddValue(set
, ctorset
);
1384 CFSetAddValue(set
, ct
);
1385 CFDictionaryReplaceValue(newViewName2ChangeTracker
, viewName
, set
);
1389 CFDictionarySetValue(newViewName2ChangeTracker
, viewName
, ct
);
1393 CFAssignRetained(engine
->viewName2ChangeTracker
, newViewName2ChangeTracker
);
1396 static void SOSEngineSetBackupBag(SOSEngineRef engine
, SOSObjectRef bagItem
);
1398 // This is called only if we are in a circle and we should listen for keybag changes
1399 static void SOSEngineRegisterBackupBagV0Tracker(SOSEngineRef engine
, CFMutableDictionaryRef newViewNameSet2ChangeTracker
, CFMutableStringRef desc
) {
1400 SOSChangeTrackerRef bbct
= SOSReferenceAndGetChangeTracker(engine
->viewNameSet2ChangeTracker
, newViewNameSet2ChangeTracker
, SOSViewsGetV0BackupBagViewSet());
1401 SOSChangeTrackerRegisterChangeUpdate(bbct
, ^bool(SOSChangeTrackerRef ct
, SOSEngineRef engine
, SOSTransactionRef txn
, SOSDataSourceTransactionSource source
, SOSDataSourceTransactionPhase phase
, CFArrayRef changes
, CFErrorRef
*error
) {
1402 SOSChangeRef change
;
1403 CFArrayForEachC(changes
, change
) {
1404 CFTypeRef object
= NULL
;
1405 bool isAdd
= SOSChangeGetObject(change
, &object
);
1406 SecDbItemRef dbi
= (SecDbItemRef
)object
;
1407 if (!isData(object
) &&
1408 CFEqualSafe(SecDbItemGetCachedValueWithName(dbi
, kSecAttrService
), CFSTR("SecureBackupService")) &&
1409 CFEqualSafe(SecDbItemGetCachedValueWithName(dbi
, kSecAttrAccessible
), kSecAttrAccessibleWhenUnlocked
) &&
1410 CFEqualSafe(SecDbItemGetCachedValueWithName(dbi
, kSecAttrAccount
), CFSTR("SecureBackupPublicKeybag"))) {
1411 SOSEngineSetBackupBag(engine
, isAdd
? (SOSObjectRef
)object
: NULL
);
1418 static void SOSEngineReferenceBackupPeer(SOSEngineRef engine
, CFStringRef peerID
, CFSetRef viewNameSet
, CFDataRef keyBag
, CFMutableDictionaryRef newViewNameSet2ChangeTracker
, CFMutableDictionaryRef newPeerMap
) {
1419 CFTypeRef oldEntry
= CFDictionaryGetValue(engine
->peerMap
, peerID
);
1420 CFTypeRef newEntry
= SOSPeerOrStateSetViewsKeyBagAndCreateCopy(oldEntry
, viewNameSet
, keyBag
);
1422 if (isDictionary(newEntry
)) {
1423 // Backup peers, are always inflated
1424 CFAssignRetained(newEntry
, SOSPeerCreateWithState(engine
, peerID
, newEntry
, NULL
));
1425 // If !oldEntry this is an edge (first creation of a peer).
1427 SOSPeerKeyBagDidChange((SOSPeerRef
)newEntry
);
1430 CFDictionarySetValue(newPeerMap
, peerID
, newEntry
);
1431 CFRelease(newEntry
);
1434 SOSChangeTrackerRef ct
= SOSReferenceAndGetChangeTracker(engine
->viewNameSet2ChangeTracker
, newViewNameSet2ChangeTracker
, viewNameSet
);
1436 SOSChangeTrackerUpdatesChanges child
= Block_copy(^bool(SOSChangeTrackerRef ct
, SOSEngineRef engine
, SOSTransactionRef txn
, SOSDataSourceTransactionSource source
, SOSDataSourceTransactionPhase phase
, CFArrayRef changes
, CFErrorRef
*error
) {
1437 return SOSPeerDataSourceWillChange((SOSPeerRef
)newEntry
, SOSEngineGetDataSource(engine
), source
, changes
, error
);
1440 SOSChangeTrackerRegisterChangeUpdate(ct
, child
);
1441 Block_release(child
);
1446 static void SOSEngineReferenceSyncPeer(SOSEngineRef engine
, CFStringRef peerID
, CFSetRef viewNameSet
, CFMutableDictionaryRef newViewNameSet2ChangeTracker
, CFMutableDictionaryRef newPeerMap
) {
1447 CFTypeRef newEntry
= SOSPeerOrStateSetViewsKeyBagAndCreateCopy(CFDictionaryGetValue(engine
->peerMap
, peerID
), viewNameSet
, NULL
);
1449 SOSChangeTrackerRef ct
= SOSReferenceAndGetChangeTracker(engine
->viewNameSet2ChangeTracker
, newViewNameSet2ChangeTracker
, viewNameSet
);
1450 // Standard peer, inflated on demand
1451 SOSChangeTrackerUpdatesManifests trackManifest
;
1452 if (isDictionary(newEntry
)) {
1453 // Uninflated peer, inflate on first notification.
1454 trackManifest
= ^bool(SOSChangeTrackerRef ct
, SOSEngineRef engine
, SOSTransactionRef txn
, SOSDataSourceTransactionSource source
, SOSDataSourceTransactionPhase phase
, SOSManifestRef removals
, SOSManifestRef additions
, CFErrorRef
*error
) {
1455 CFErrorRef localError
= NULL
;
1456 SOSPeerRef peer
= SOSEngineCopyPeerWithID_locked(engine
, peerID
, &localError
);
1459 secerror("%@: peer failed to inflate: %@", peerID
, localError
);
1460 CFReleaseSafe(localError
);
1463 ok
= SOSPeerDataSourceWillCommit(peer
, source
, removals
, additions
, error
);
1465 CFReleaseSafe(peer
);
1469 // Inflated peer, just forward the changes to the peer
1470 trackManifest
= ^bool(SOSChangeTrackerRef ct
, SOSEngineRef engine
, SOSTransactionRef txn
, SOSDataSourceTransactionSource source
, SOSDataSourceTransactionPhase phase
, SOSManifestRef removals
, SOSManifestRef additions
, CFErrorRef
*error
) {
1471 return SOSPeerDataSourceWillCommit((SOSPeerRef
)newEntry
, source
, removals
, additions
, error
);
1474 SOSChangeTrackerUpdatesManifests trackManifestCopy
= Block_copy(trackManifest
);
1475 SOSChangeTrackerRegisterManifestUpdate(ct
, trackManifestCopy
);
1476 Block_release(trackManifestCopy
);
1478 CFDictionarySetValue(newPeerMap
, peerID
, newEntry
);
1479 CFRelease(newEntry
);
1484 static void SOSEngineReferenceTrustedPeer(SOSEngineRef engine
, SOSPeerMetaRef peerMeta
, CFMutableDictionaryRef newViewNameSet2ChangeTracker
, CFMutableDictionaryRef newPeerMap
, CFMutableArrayRef peerIDs
, CFMutableStringRef desc
) {
1485 CFSetRef viewNameSet
= NULL
;
1486 CFDataRef keyBag
= NULL
;
1487 CFStringRef peerID
= SOSPeerMetaGetComponents(peerMeta
, &viewNameSet
, &keyBag
, NULL
);
1488 // We trust peerID so append it to peerIDs
1489 CFArrayAppendValue(peerIDs
, peerID
);
1490 if (desc
) CFStringAppendPeerIDAndViews(desc
, peerID
, viewNameSet
);
1491 // Update the viewNameSet for this peer, to appease tests, default to a viewset of the V0 view.
1493 viewNameSet
= SOSViewsGetV0ViewSet();
1495 // Always inflate backup peers, since they need to register with their changeTrackers right away.
1497 SOSEngineReferenceBackupPeer(engine
, peerID
, viewNameSet
, keyBag
, newViewNameSet2ChangeTracker
, newPeerMap
);
1499 SOSEngineReferenceSyncPeer(engine
, peerID
, viewNameSet
, newViewNameSet2ChangeTracker
, newPeerMap
);
1503 static CFDataRef
SOSEngineLoadV0KeyBag(SOSEngineRef engine
, CFErrorRef
*error
) {
1504 // Return the keybag for the given peerID.
1507 kSecAttrAccessGroup ==> CFSTR("com.apple.sbd")
1508 kSecAttrAccessible ==> kSecAttrAccessibleWhenUnlocked
1509 kSecAttrAccount ==> CFSTR("SecureBackupPublicKeybag")
1510 kSecAttrService ==> CFSTR("SecureBackupService")
1513 CFMutableDictionaryRef keys
= CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault
,
1514 kSecAttrAccessGroup
, CFSTR("com.apple.sbd"),
1515 kSecAttrAccount
, CFSTR("SecureBackupPublicKeybag"),
1516 kSecAttrService
, CFSTR("SecureBackupService"),
1517 kSecAttrAccessible
, kSecAttrAccessibleWhenUnlocked
,
1518 kSecAttrSynchronizable
, kCFBooleanTrue
,
1521 CFDataRef keybag
= engine
->dataSource
->dsCopyItemDataWithKeys(engine
->dataSource
, keys
, error
);
1522 CFReleaseSafe(keys
);
1527 static void SOSEngineReferenceBackupV0Peer(SOSEngineRef engine
, CFMutableDictionaryRef newViewNameSet2ChangeTracker
, CFMutableDictionaryRef newPeerMap
, CFMutableArrayRef newPeerIDs
, CFMutableStringRef desc
) {
1528 SOSPeerRef backupPeer
= (SOSPeerRef
)CFDictionaryGetValue(engine
->peerMap
, kSOSViewKeychainV0_tomb
);
1529 CFDataRef bag
= NULL
;
1530 if (backupPeer
&& CFGetTypeID(backupPeer
) == SOSPeerGetTypeID()) {
1531 bag
= SOSPeerGetKeyBag(backupPeer
);
1533 CFErrorRef localError
= NULL
;
1534 if (!(bag
= SOSEngineLoadV0KeyBag(engine
, &localError
))) {
1535 secnotice("engine", "No keybag found for v0 backup peer: %@", localError
);
1536 CFReleaseSafe(localError
);
1539 SOSEngineReferenceBackupPeer(engine
, kSOSViewKeychainV0_tomb
, SOSViewsGetV0BackupViewSet(), bag
, newViewNameSet2ChangeTracker
, newPeerMap
);
1542 static void SOSEngineReferenceTrustedPeers(SOSEngineRef engine
, CFMutableDictionaryRef newViewNameSet2ChangeTracker
, CFMutableDictionaryRef newPeerMap
, CFMutableArrayRef newPeerIDs
, CFArrayRef trustedPeerMetas
, CFMutableStringRef desc
) {
1543 // Then update the views for all trusted peers and add them to newPeerMap.
1544 if (trustedPeerMetas
!= NULL
&& CFArrayGetCount(trustedPeerMetas
) != 0) {
1545 if (desc
) CFStringAppend(desc
, CFSTR(" trusted"));
1546 // Remake engine->peerIDs
1547 SOSPeerMetaRef peerMeta
;
1548 CFArrayForEachC(trustedPeerMetas
, peerMeta
) {
1549 SOSEngineReferenceTrustedPeer(engine
, peerMeta
, newViewNameSet2ChangeTracker
, newPeerMap
, newPeerIDs
, desc
);
1554 static void SOSEngineReferenceUntrustedPeers(SOSEngineRef engine
, CFMutableDictionaryRef newPeerMap
, CFArrayRef untrustedPeerMetas
, CFMutableStringRef description
) {
1555 // Copy any untrustedPeers to newPeerMap as well if we have a state
1556 // for them, if not no big deal. We also serialize all the untrustedPeers
1557 // since they don't need to be deserializable
1558 if (untrustedPeerMetas
!= NULL
&& CFArrayGetCount(untrustedPeerMetas
) != 0) {
1559 if (description
) CFStringAppend(description
, CFSTR(" untrusted"));
1560 SOSPeerMetaRef peerMeta
;
1561 CFArrayForEachC(untrustedPeerMetas
, peerMeta
) {
1562 CFSetRef views
= NULL
;
1563 CFStringRef peerID
= SOSPeerMetaGetComponents(peerMeta
, &views
, NULL
, NULL
);
1564 if (description
) CFStringAppendPeerIDAndViews(description
, peerID
, views
);
1565 CFSetRef nviews
= NULL
;
1567 views
= nviews
= CFSetCreate(kCFAllocatorDefault
, NULL
, 0, &kCFTypeSetCallBacks
);
1568 CFTypeRef newEntry
= SOSPeerOrStateSetViewsAndCopyState(CFDictionaryGetValue(engine
->peerMap
, peerID
), views
);
1569 CFReleaseSafe(nviews
);
1571 CFDictionarySetValue(newPeerMap
, peerID
, newEntry
);
1572 CFReleaseSafe(newEntry
);
1578 static void SOSEngineReferenceChangeTrackers(SOSEngineRef engine
, CFArrayRef trustedPeerMetas
, CFArrayRef untrustedPeerMetas
, CFMutableStringRef desc
) {
1579 CFMutableArrayRef newPeerIDs
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
1580 CFMutableDictionaryRef newPeerMap
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1581 CFMutableDictionaryRef newViewNameSet2ChangeTracker
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1584 // We have an engineID => in a circle (with 0 or more peers)
1585 // Ensure we have a v0 backup peer and it's listening for backup bag changes
1586 SOSEngineReferenceBackupV0Peer(engine
, newViewNameSet2ChangeTracker
, newPeerMap
, newPeerIDs
, desc
);
1587 SOSEngineRegisterBackupBagV0Tracker(engine
, newViewNameSet2ChangeTracker
, desc
);
1589 SOSEngineReferenceTrustedPeers(engine
, newViewNameSet2ChangeTracker
, newPeerMap
, newPeerIDs
, trustedPeerMetas
, desc
);
1590 SOSEngineReferenceUntrustedPeers(engine
, newPeerMap
, untrustedPeerMetas
, desc
);
1592 CFAssignRetained(engine
->peerIDs
, newPeerIDs
);
1593 CFAssignRetained(engine
->peerMap
, newPeerMap
);
1594 CFAssignRetained(engine
->viewNameSet2ChangeTracker
, newViewNameSet2ChangeTracker
);
1595 SOSEngineUpdateViewName2ChangeTracker(engine
);
1598 // Return true iff peers or views changed
1599 static bool SOSEngineSetPeers_locked(SOSEngineRef engine
, SOSPeerMetaRef myPeerMeta
, CFArrayRef trustedPeerMetas
, CFArrayRef untrustedPeerMetas
) {
1600 CFErrorRef error
= NULL
;
1601 CFSetRef myViews
= NULL
;
1602 CFDataRef myKeyBag
= NULL
;
1603 CFMutableStringRef desc
= CFStringCreateMutableCopy(kCFAllocatorDefault
, 0, CFSTR("me"));
1604 CFStringRef myPeerID
= myPeerMeta
? SOSPeerMetaGetComponents(myPeerMeta
, &myViews
, &myKeyBag
, &error
) : NULL
;
1605 if (desc
) CFStringAppendPeerIDAndViews(desc
, myPeerID
, myViews
);
1607 // Start with no coders
1608 CFMutableDictionaryRef codersToKeep
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1610 // If we're the same peerID we keep known peers (both trusted and untrusted)
1611 if (CFEqualSafe(myPeerID
, engine
->myID
)) {
1612 void (^copyPeerMetasCoder
)(const void *value
) = ^(const void*element
) {
1613 SOSPeerMetaRef peerMeta
= (SOSPeerMetaRef
) element
;
1615 CFStringRef currentID
= SOSPeerMetaGetComponents(peerMeta
, NULL
, NULL
, NULL
);
1617 SOSCoderRef coder
= (SOSCoderRef
) CFDictionaryGetValue(engine
->coders
, currentID
);
1619 CFDictionarySetValue(codersToKeep
, currentID
, coder
);
1624 if (trustedPeerMetas
) {
1625 CFArrayForEach(trustedPeerMetas
, copyPeerMetasCoder
);
1627 if (untrustedPeerMetas
) {
1628 CFArrayForEach(untrustedPeerMetas
, copyPeerMetasCoder
);
1632 CFTransferRetained(engine
->coders
, codersToKeep
);
1633 engine
->codersNeedSaving
= true;
1635 CFRetainAssign(engine
->myID
, myPeerID
);
1637 // Remake engine->peerMap from both trusted and untrusted peers
1638 SOSEngineReferenceChangeTrackers(engine
, trustedPeerMetas
, untrustedPeerMetas
, desc
);
1640 secnotice("engine", "%@", desc
);
1641 CFReleaseSafe(desc
);
1645 static void SOSEngineApplyPeerState(SOSEngineRef engine
, CFDictionaryRef peerStateMap
) {
1646 if (peerStateMap
) CFDictionaryForEach(peerStateMap
, ^(const void *peerID
, const void *peerState
) {
1647 CFTypeRef mapEntry
= CFDictionaryGetValue(engine
->peerMap
, peerID
);
1648 if (mapEntry
&& CFGetTypeID(mapEntry
) == SOSPeerGetTypeID()) {
1649 // Update the state of any already inflated peers
1650 SOSPeerRef peer
= (SOSPeerRef
)mapEntry
;
1651 CFErrorRef localError
= NULL
;
1652 if (!SOSPeerSetState(peer
, engine
, peerState
, &localError
)) {
1653 CFStringRef stateHex
= NULL
;
1654 stateHex
= CFDataCopyHexString(peerState
);
1655 secerror("peer: %@: bad state: %@ in engine state: %@", peerID
, localError
, stateHex
);
1656 CFReleaseSafe(stateHex
);
1657 CFReleaseNull(localError
);
1658 // Possibly ask for an ensurePeerRegistration so we have a good list of peers again.
1661 // Just record the state for non inflated peers for now.
1662 CFDictionarySetValue(engine
->peerMap
, peerID
, peerState
);
1667 static void SOSEngineSynthesizePeerMetas(SOSEngineRef engine
, CFMutableArrayRef trustedPeersMetas
, CFMutableArrayRef untrustedPeers
) {
1668 CFSetRef trustedPeerSet
= engine
->peerIDs
? CFSetCreateCopyOfArrayForCFTypes(engine
->peerIDs
) : NULL
;
1669 CFDictionaryForEach(engine
->peerMap
, ^(const void *peerID
, const void *peerState
) {
1670 SOSPeerMetaRef meta
= NULL
;
1671 if (peerState
&& CFGetTypeID(peerState
) == SOSPeerGetTypeID()) {
1672 SOSPeerRef peer
= (SOSPeerRef
)peerState
;
1673 meta
= SOSPeerMetaCreateWithComponents(peerID
, SOSPeerGetViewNameSet(peer
), SOSPeerGetKeyBag(peer
));
1675 // We don't need to add the meta for the backup case, since
1676 // SOSEngineReferenceBackupV0Peer will do the right thing
1677 if (!CFEqualSafe(peerID
, kSOSViewKeychainV0_tomb
)) {
1678 meta
= SOSPeerMetaCreateWithState(peerID
, peerState
);
1681 // Any peer in peerStateMap that is not in trustedPeers is an untrustedPeer unless it's the v0 backup peer
1682 if ((trustedPeerSet
&& CFSetContainsValue(trustedPeerSet
, peerID
)) || CFEqualSafe(peerID
, kSOSViewKeychainV0_tomb
)) {
1684 CFArrayAppendValue(trustedPeersMetas
, meta
);
1687 CFArrayAppendValue(untrustedPeers
, peerID
);
1689 CFReleaseNull(meta
);
1691 CFReleaseNull(trustedPeerSet
);
1694 static void SOSEngineSetBackupBag(SOSEngineRef engine
, SOSObjectRef bagItem
) {
1695 CFMutableStringRef desc
= NULL
;
1696 SOSPeerRef backupPeer
= SOSEngineCopyPeerWithID_locked(engine
, kSOSViewKeychainV0_tomb
, NULL
);
1697 CFDataRef keybag
= NULL
;
1699 keybag
= SecDbItemGetValue((SecDbItemRef
)bagItem
, &v6v_Data
, NULL
);
1702 // Since SOSPeerSetKeyBag() doesn't notify on the edge from NULL->initial keybag, since
1703 // that is the right behaviour for non v0 backup peers, we need to do it here for the v0 peer.
1704 bool hadBag
= SOSPeerGetKeyBag(backupPeer
);
1705 SOSPeerSetKeyBag(backupPeer
, keybag
);
1707 SOSPeerKeyBagDidChange(backupPeer
);
1709 CFReleaseSafe(backupPeer
);
1711 CFMutableArrayRef untrustedPeerMetas
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
1712 CFMutableArrayRef trustedPeersMetas
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
1713 SOSEngineSynthesizePeerMetas(engine
, trustedPeersMetas
, untrustedPeerMetas
);
1714 SOSEngineReferenceChangeTrackers(engine
, trustedPeersMetas
, untrustedPeerMetas
, desc
);
1715 CFReleaseSafe(trustedPeersMetas
);
1716 CFReleaseSafe(untrustedPeerMetas
);
1719 #define SECONDS_PER_DAY (86400.0)
1721 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
1722 #define TRACE_INTERVAL (7 * SECONDS_PER_DAY)
1723 #elif (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
1724 #define TRACE_INTERVAL (1 * SECONDS_PER_DAY)
1727 #ifdef TRACE_INTERVAL
1728 static void SOSEngineCloudKeychainTrace(SOSEngineRef engine
, CFAbsoluteTime now
) {
1729 CFAssignRetained(engine
->lastTraceDate
, CFDateCreate(kCFAllocatorDefault
, now
));
1730 CFIndex num_peers
= engine
->peerIDs
? 1 + CFArrayGetCount(engine
->peerIDs
) : 1;
1731 SOSManifestRef manifest
= SOSEngineCopyManifestWithViewNameSet_locked(engine
, SOSViewsGetV0ViewSet(), NULL
);
1733 manifest
= SOSDataSourceCopyManifestWithViewNameSet(engine
->dataSource
, SOSViewsGetV0ViewSet(), NULL
);
1734 size_t num_items
= SOSManifestGetCount(manifest
);
1735 CFReleaseSafe(manifest
);
1737 struct _SecServerKeyStats genpStats
= { };
1738 struct _SecServerKeyStats inetStats
= { };
1739 struct _SecServerKeyStats keysStats
= { };
1741 _SecServerGetKeyStats(&genp_class
, &genpStats
);
1742 _SecServerGetKeyStats(&inet_class
, &inetStats
);
1743 _SecServerGetKeyStats(&keys_class
, &keysStats
);
1745 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), ^{
1746 CloudKeychainTrace(num_peers
, num_items
, &genpStats
, &inetStats
, &keysStats
);
1751 static void SOSEngineCloudKeychainTraceIfNeeded(SOSEngineRef engine
) {
1752 #ifdef TRACE_INTERVAL
1755 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
1756 if (engine
->lastTraceDate
) {
1757 CFAbsoluteTime lastTraceTime
= CFDateGetAbsoluteTime(engine
->lastTraceDate
);
1758 if ((now
- lastTraceTime
) >= TRACE_INTERVAL
) {
1759 SOSEngineCloudKeychainTrace(engine
, now
);
1762 SOSEngineCloudKeychainTrace(engine
, now
);
1768 static bool SOSEngineCircleChanged_locked(SOSEngineRef engine
, SOSPeerMetaRef myPeerMeta
, CFArrayRef trustedPeers
, CFArrayRef untrustedPeers
) {
1769 // Sanity check params
1770 // SOSEngineCircleChanged_sanitycheck(engine, myPeerID, trustedPeers, untrustedPeers);
1772 // Transform from SOSPeerInfoRefs to CFDictionaries with the info we want per peer.
1773 // Or, Tell the real SOSPeerRef what the SOSPeerInfoRef is and have it copy out the data it needs.
1774 bool peersOrViewsChanged
= SOSEngineSetPeers_locked(engine
, myPeerMeta
, trustedPeers
, untrustedPeers
);
1776 // Run though all peers and only cache manifests for peers we still have
1777 CFErrorRef localError
= NULL
;
1778 if (!SOSEngineGCPeerState_locked(engine
, &localError
)) {
1779 secerror("SOSEngineGCPeerState_locked failed: %@", localError
);
1780 CFReleaseNull(localError
);
1782 return peersOrViewsChanged
;
1785 // Initialize the engine if a load fails. Basically this is our first time setup
1786 static bool SOSEngineInit(SOSEngineRef engine
, CFErrorRef
*error
) {
1788 secnotice("engine", "new engine for datasource named %@", SOSDataSourceGetName(engine
->dataSource
));
1789 CFAssignRetained(engine
->peerMap
, CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
));
1790 CFAssignRetained(engine
->viewNameSet2ChangeTracker
, CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
));
1791 CFAssignRetained(engine
->viewName2ChangeTracker
, CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
));
1792 CFReleaseNull(engine
->manifestCache
);
1793 CFReleaseNull(engine
->peerIDs
);
1794 // TODO: We shouldn't need to load the backup bag if there was no engine
1795 // state (load failed), since that means there was no circle nor were we an applicant.
1797 // Set up change trackers so we know when a backup peer needs to be created?
1798 // no, since myID is not set, we are not in a circle, so no need to back up
1799 SOSEngineSetPeers_locked(engine
, NULL
, NULL
, NULL
);
1803 // Called by our DataSource in its constructor
1804 SOSEngineRef
SOSEngineCreate(SOSDataSourceRef dataSource
, CFErrorRef
*error
) {
1805 SOSEngineRef engine
= NULL
;
1806 engine
= CFTypeAllocate(SOSEngine
, struct __OpaqueSOSEngine
, kCFAllocatorDefault
);
1807 engine
->dataSource
= dataSource
;
1808 engine
->queue
= dispatch_queue_create("engine", DISPATCH_QUEUE_SERIAL
);
1810 engine
->peerMap
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1811 engine
->viewNameSet2ChangeTracker
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1812 engine
->viewName2ChangeTracker
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1813 //engine->syncCompleteQueue = NULL;
1814 engine
->syncCompleteListener
= NULL
;
1815 engine
->coders
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1816 engine
->haveLoadedCoders
= false;
1817 engine
->codersNeedSaving
= false;
1819 CFErrorRef engineError
= NULL
;
1820 if (!SOSEngineLoad(engine
, NULL
, &engineError
)) {
1821 secwarning("engine failed load state starting with nothing %@", engineError
);
1822 CFReleaseNull(engineError
);
1823 if (!SOSEngineInit(engine
, error
)) {
1824 secerror("engine failed to initialze %@ giving up", error
? *error
: NULL
);
1827 // Successfully loaded engine state, let's trace if we haven't in a while
1828 SOSEngineCloudKeychainTraceIfNeeded(engine
);
1830 SOSEngineSetNotifyPhaseBlock(engine
);
1834 // --- Called from off the queue, need to move to on the queue
1836 static void SOSEngineDoOnQueue(SOSEngineRef engine
, dispatch_block_t action
)
1838 dispatch_sync(engine
->queue
, action
);
1841 static bool SOSEngineDoTxnOnQueue(SOSEngineRef engine
, CFErrorRef
*error
, void(^transaction
)(SOSTransactionRef txn
, bool *commit
))
1843 return SOSDataSourceWithCommitQueue(engine
->dataSource
, error
, ^(SOSTransactionRef txn
, bool *commit
) {
1844 SOSEngineDoOnQueue(engine
, ^{ transaction(txn
, commit
); });
1849 // MARK: SOSEngine API
1852 void SOSEngineDispose(SOSEngineRef engine
) {
1853 // NOOP Engines stick around forever to monitor dataSource changes.
1854 engine
->dataSource
= NULL
;
1855 CFReleaseNull(engine
->coders
);
1858 void SOSEngineForEachPeer(SOSEngineRef engine
, void (^with
)(SOSPeerRef peer
)) {
1859 SOSEngineDoOnQueue(engine
, ^{
1860 SOSEngineForEachPeer_locked(engine
, with
);
1864 static void SOSEngineForEachBackupPeer(SOSEngineRef engine
, void (^with
)(SOSPeerRef peer
)) {
1865 SOSEngineDoOnQueue(engine
, ^{
1866 SOSEngineForEachBackupPeer_locked(engine
, with
);
1871 /* Handle incoming message from peer p. Return false if there was an error, true otherwise. */
1872 bool SOSEngineHandleMessage_locked(SOSEngineRef engine
, CFStringRef peerID
, SOSMessageRef message
,
1873 SOSTransactionRef txn
, bool *commit
, bool *somethingChanged
, CFErrorRef
*error
) {
1874 SOSPeerRef peer
= SOSEngineCopyPeerWithID_locked(engine
, peerID
, error
);
1875 if (!peer
) return false;
1876 CFStringRef peerDesc
= NULL
;
1877 SOSManifestRef localManifest
= NULL
;
1878 SOSManifestRef allAdditions
= NULL
;
1879 SOSManifestRef unwanted
= NULL
;
1880 SOSManifestRef confirmed
= NULL
;
1881 SOSManifestRef base
= NULL
;
1882 SOSManifestRef confirmedRemovals
= NULL
, confirmedAdditions
= NULL
;
1883 __block
struct SOSDigestVector receivedObjects
= SOSDigestVectorInit
;
1884 __block
struct SOSDigestVector unwantedObjects
= SOSDigestVectorInit
;
1886 // Check for unknown criticial extensions in the message, and handle
1887 // any other extensions we support
1888 __block
bool ok
= true;
1889 CFMutableArrayRef changes
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
1891 require_action_quiet(peer
, exit
, ok
= SOSErrorCreate(errSecParam
, error
, NULL
, CFSTR("Couldn't create peer with Engine for %@"), peerID
));
1892 peerDesc
= CFCopyDescription(peer
);
1894 SOSMessageWithExtensions(message
, true, ^(CFDataRef oid
, bool isCritical
, CFDataRef extension
, bool *stop
) {
1895 // OMFG a Critical extension what shall I do!
1896 ok
= SOSErrorCreate(kSOSErrorNotReady
, error
, NULL
, CFSTR("Unknown criticial extension in peer message"));
1899 require_quiet(ok
, exit
);
1901 // Merge Objects from the message into our DataSource.
1902 // Should we move the transaction to the SOSAccount level?
1903 // TODO: Filter incoming objects
1904 //if (!SOSDataSourceForEachObjectInViewSet(engine->dataSource, pendingObjects, SOSPeerGetViewNameSet(peer), error, ^void(CFDataRef key, SOSObjectRef object, bool *stop) {
1905 require_quiet(ok
&= SOSMessageWithSOSObjects(message
, engine
->dataSource
, error
, ^(SOSObjectRef peersObject
, bool *stop
) {
1906 CFDataRef digest
= SOSObjectCopyDigest(engine
->dataSource
, peersObject
, error
);
1910 secerror("%@ peer sent bad object: %@, rolling back changes", SOSPeerGetID(peer
), error
? *error
: NULL
);
1913 SOSDigestVectorAppend(&receivedObjects
, CFDataGetBytePtr(digest
));
1914 SOSObjectRef mergedObject
= NULL
;
1915 SOSMergeResult mr
= SOSDataSourceMergeObject(engine
->dataSource
, txn
, peersObject
, &mergedObject
, error
);
1916 // TODO: If the mr is kSOSMergeLocalObject most of the time (or all of the time),
1917 // consider asking the peer to stop sending us objects, and send it objects instead.
1918 ok
&= (mr
!= kSOSMergeFailure
);
1922 // 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.
1923 secerror("%@ SOSDataSourceMergeObject failed %@ rolling back changes", SOSPeerGetID(peer
), error
? *error
: NULL
);
1924 } else if (mr
==kSOSMergePeersObject
|| mr
==kSOSMergeCreatedObject
) {
1925 *somethingChanged
= true;
1927 // mr == kSOSMergeLocalObject
1928 if (!CFEqual(mergedObject
, peersObject
)) {
1929 // Record this object as something we don't want peer to ever send us again. By adding it to
1930 // unwantedObjects we'll falsely claim to peer we have it until they tell us they don't have it anymore.
1931 SOSDigestVectorAppend(&unwantedObjects
, CFDataGetBytePtr(digest
));
1933 // Ensure localObject is in local manifest (possible corruption) by posting an update when we are done.
1934 SOSChangesAppendAdd(changes
, mergedObject
);
1936 CFReleaseSafe(mergedObject
);
1937 CFReleaseSafe(digest
);
1939 struct SOSDigestVector dvunion
= SOSDigestVectorInit
;
1940 SOSDigestVectorSort(&receivedObjects
);
1941 SOSDigestVectorUnionSorted(SOSManifestGetDigestVector(SOSMessageGetAdditions(message
)), &receivedObjects
, &dvunion
);
1942 allAdditions
= SOSManifestCreateWithDigestVector(&dvunion
, error
);
1943 SOSDigestVectorFree(&receivedObjects
);
1944 SOSDigestVectorFree(&dvunion
);
1946 unwanted
= SOSManifestCreateWithDigestVector(&unwantedObjects
, error
);
1947 SOSDigestVectorFree(&unwantedObjects
);
1949 if (CFArrayGetCount(changes
)) {
1950 // NOTE: This is always notifiying of all additions that end up choosing local, which should be rare, since we shouldn't
1951 // be receiving objects we already have. When we do we tell ourselves to add them all again so our views will properly
1952 // reflect that we actually have these objects if we didn't already.
1954 // Ensure any objects that we received and have locally already are actually in our local manifest
1955 SOSEngineUpdateChanges_locked(engine
, txn
, kSOSDataSourceTransactionDidCommit
, kSOSDataSourceSOSTransaction
, changes
, error
);
1957 CFReleaseSafe(changes
);
1959 // ---- Don't use local or peer manifests from above this line,
1960 // ---- since commiting the SOSDataSourceWith transaction might change them ---
1962 // Take a snapshot of our dataSource's local manifest.
1963 require_quiet(ok
= localManifest
= SOSEngineCopyLocalPeerManifest_locked(engine
, peer
, error
), exit
);
1965 CFDataRef baseDigest
= SOSMessageGetBaseDigest(message
);
1966 CFDataRef proposedDigest
= SOSMessageGetProposedDigest(message
);
1969 // I believe this is no longer needed now that we have eliminated extra,
1970 // since this is handled below once we get a confirmed manifest from our
1973 // If we just received a L00 reset pendingObjects to localManifest
1974 if (!baseDigest
&& !proposedDigest
) {
1975 // TODO: This is definitely busted for v0 peers since v0 peers always send a
1976 // L00 (ManifestDigestMessage as an ack) whereas in v2 this is a protocol restart
1977 // However if we can still find a confirmed manifest below we probably
1978 // don't want to do this even for v2.
1979 // Also I don't think we will ever send a ManifestMessage right now in
1980 // response to a ManifestDigest
1981 SOSPeerSetPendingObjects(peer
, localManifest
);
1982 secnoticeq("engine", "%@:%@ SOSPeerSetPendingObjects: %@", engine
->myID
, peerID
, localManifest
);
1986 base
= SOSPeerCopyManifestForDigest(peer
, baseDigest
);
1987 confirmed
= SOSPeerCopyManifestForDigest(peer
, SOSMessageGetSenderDigest(message
));
1989 if (SOSManifestGetCount(SOSMessageGetRemovals(message
)) || SOSManifestGetCount(allAdditions
)) {
1990 if (base
|| !baseDigest
) {
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
);
2002 secnoticeq("engine", "%@:%@ confirmed: %@ base: %@", engine
->myID
, peerID
, confirmed
, base
);
2004 ok
&= SOSManifestDiff(SOSPeerGetConfirmedManifest(peer
), confirmed
, &confirmedRemovals
, &confirmedAdditions
, error
);
2005 if (SOSManifestGetCount(SOSMessageGetRemovals(message
)))
2006 CFAssignRetained(confirmedRemovals
, SOSManifestCreateUnion(confirmedRemovals
, SOSMessageGetRemovals(message
), error
));
2008 if (SOSManifestGetCount(confirmedRemovals
) || SOSManifestGetCount(confirmedAdditions
) || SOSManifestGetCount(unwanted
))
2009 ok
&= SOSPeerDidReceiveRemovalsAndAdditions(peer
, confirmedRemovals
, confirmedAdditions
, unwanted
, localManifest
, error
);
2010 // TODO: We should probably remove the if below and always call SOSPeerSetConfirmedManifest,
2011 // since having a NULL confirmed will force us to send a manifest message to get in sync again.
2013 SOSPeerSetConfirmedManifest(peer
, confirmed
);
2014 else if (SOSPeerGetConfirmedManifest(peer
)) {
2015 secnoticeq("engine", "%@:%@ unable to find confirmed in %@, sync protocol reset", engine
->myID
, peer
, message
);
2017 SOSPeerSetConfirmedManifest(peer
, NULL
);
2018 //SOSPeerSetSendObjects(peer, true);
2021 // ---- SendObjects and extra->pendingObjects promotion dance ----
2023 // The first block of code below sets peer.sendObjects to true when we receive a L00 and the second block
2024 // moves extra to pendingObjects once we receive a confirmed manifest in or after the L00.
2025 if (!baseDigest
&& !proposedDigest
) {
2026 SOSPeerSetSendObjects(peer
, true);
2029 if (0 /* confirmed && SOSPeerSendObjects(peer) */) {
2030 SOSManifestRef allExtra
= NULL
;
2031 ok
&= SOSManifestDiff(confirmed
, localManifest
, NULL
, &allExtra
, error
);
2032 secnoticeq("engine", "%@:%@ confirmed %@ (re)setting O:%@", engine
->myID
, SOSPeerGetID(peer
), confirmed
, allExtra
);
2033 SOSPeerSetPendingObjects(peer
, allExtra
);
2034 CFReleaseSafe(allExtra
);
2038 secnotice("engine", "recv %@:%@ %@", engine
->myID
, SOSPeerGetID(peer
), message
);
2039 secnotice("peer", "recv %@ -> %@", peerDesc
, peer
);
2041 CFReleaseNull(base
);
2042 CFReleaseSafe(confirmed
);
2043 CFReleaseSafe(localManifest
);
2044 CFReleaseSafe(peerDesc
);
2045 CFReleaseSafe(allAdditions
);
2046 CFReleaseSafe(unwanted
);
2047 CFReleaseSafe(confirmedRemovals
);
2048 CFReleaseSafe(confirmedAdditions
);
2049 CFReleaseSafe(peer
);
2053 static CFDataRef
SOSEngineCopyObjectDER(SOSEngineRef engine
, SOSObjectRef object
, CFErrorRef
*error
) {
2054 CFDataRef der
= NULL
;
2055 CFDictionaryRef plist
= SOSObjectCopyPropertyList(engine
->dataSource
, object
, error
);
2057 der
= CFPropertyListCreateDERData(kCFAllocatorDefault
, plist
, error
);
2066 +-----------------------------+_
2070 _+=============================+ } L
2074 / +==============================
2077 \ | M +------------|
2081 \_+-------------+---------------+
2093 static bool SOSAppendRemoveToPatch(CFTypeRef remove
, CFMutableDictionaryRef patch
, CFErrorRef
*error
) {
2096 static bool SOSAppendAddToPatch(CFTypeRef add
, CFMutableDictionaryRef patch
, CFErrorRef
*error
) {
2099 static bool SOSAppendDiffToPatch(CFTypeRef left
, CFTypeRef right
, CFMutableDictionaryRef patch
, CFErrorRef
*error
) {
2101 if (!left
&& right
) {
2102 SOSAppendAddToPatch(right
, patch
, error
);
2103 } else if (left
&& !right
) {
2104 SOSAppendRemoveToPatch(left
, patch
, error
);
2105 } else if (left
&& right
) {
2106 CFTypeID ltype
= CFGetTypeID(left
);
2107 CFTypeID rtype
= CFGetTypeID(right
);
2108 if (ltype
== rtype
) {
2109 if (CFArrayGetTypeID() == ltype
) {
2110 ok
= SecError(errSecParam
, error
, CFSTR("unsupported type array"), ltype
);
2111 } else if (CFBooleanGetTypeID
== ltype
) {
2112 ok
= SecError(errSecParam
, error
, CFSTR("unsupported type boolean"), ltype
);
2113 } else if (CFDataGetTypeID
== ltype
) {
2114 ok
= SecError(errSecParam
, error
, CFSTR("unsupported type data"), ltype
);
2115 } else if (CFDictionaryGetTypeID
== ltype
) {
2116 __block CFMutableDictionaryRef leftnotright
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
2117 __block CFMutableDictionaryRef rightnotleft
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, right
);
2119 CFDictionaryForEach(left
, ^(const void *key
, const void *lvalue
) {
2120 const void *rvalue
= NULL
;
2121 if (CFDictionaryGetValueIfPresent(right
, key
, &rvalue
)) {
2122 CFDictionaryRemoveValue(rightnotleft
, key
);
2124 CFMutableDictionaryRef subpatch
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
);
2125 CFDictionaryAddValue(patch
, key
, subpatch
);
2126 SOSAppendDiffToPatch(lvalue
, rvalue
, subpatch
, error
);
2127 CFReleaseSafe(subpatch
);
2129 CFDictionaryAddValue(leftnotright
, key
, lvalue
);
2132 // Proccess leftnotright and rightnotleft
2133 CFReleaseSafe(leftnotright
);
2134 CFReleaseSafe(rightnotleft
);
2135 } else if (SOSManifestGetTypeID
== ltype
) {
2136 SOSManifestRef removed
= NULL
, added
= NULL
;
2137 ok
&= SOSManifestDiff(left
, right
, &removed
, &added
, error
);
2138 if (SOSManifestGetCount(removed
) || SOSManifestGetCount(added
)) {
2139 SOSAppendDiffToPatch(lvalue
, rvalue
, subpatch
, error
);
2140 CFStringAppend(, <#CFStringRef appendedString#>)
2142 CFReleaseSafe(removed
);
2143 CFReleaseSafe(added
);
2144 } else if (CFNumberGetTypeID
== ltype
) {
2145 ok
= SecError(errSecParam
, error
, CFSTR("unsupported type number"), ltype
);
2146 } else if (CFSetGetTypeID
== ltype
) {
2147 ok
= SecError(errSecParam
, error
, CFSTR("unsupported type set"), ltype
);
2148 } else if (CFStringGetTypeID
== ltype
) {
2149 ok
= SecError(errSecParam
, error
, CFSTR("unsupported type string"), ltype
);
2151 ok
= SecError(errSecParam
, error
, CFSTR("unknown type %lu"), ltype
);
2154 } else if (!left
&& !right
) {
2160 static __unused
bool SOSEngineCheckPeerIntegrity(SOSEngineRef engine
, SOSPeerRef peer
, CFErrorRef
*error
) {
2162 //static CFMutableDictionaryRef p2amtu;
2163 if (!engine
->p2amtu
)
2164 engine
->p2amtu
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
2165 CFDictionaryRef amtu
= CFDictionaryGetValue(engine
->p2amtu
, SOSPeerGetID(peer
));
2169 SOSManifestRef L
= SOSEngineCopyLocalPeerManifest_locked(engine
, peer
, error
);
2170 SOSManifestRef T
= SOSPeerGetPendingObjects(peer
);
2171 SOSManifestRef C
= SOSPeerGetConfirmedManifest(peer
);
2172 SOSManifestRef U
= SOSPeerGetUnwantedManifest(peer
);
2175 SOSManifestRef CunionU
= SOSManifestCreateUnion(C
, U
, error
);
2176 SOSManifestRef S
= SOSManifestCreateIntersection(L
, CunionU
, error
);
2178 SOSManifestRef AunionT
= NULL
, MunionU
= NULL
;
2179 SOSManifestDiff(L
, C
, &AunionT
, &MunionU
, error
);
2181 SOSManifestRef A
= SOSManifestCreateComplement(T
, AunionT
, error
);
2182 SOSManifestRef M
= SOSManifestCreateComplement(U
, MunionU
, error
);
2184 SOSManifestRef SunionAunionT
= SOSManifestCreateUnion(S
, AunionT
, error
);
2185 SOSManifestRef SunionMunionU
= SOSManifestCreateUnion(S
, MunionU
, error
);
2187 SOSManifestRef AintersectM
= SOSManifestCreateIntersection(A
, M
, error
);
2188 SOSManifestRef AintersectS
= SOSManifestCreateIntersection(A
, S
, error
);
2189 SOSManifestRef AintersectT
= SOSManifestCreateIntersection(A
, T
, error
);
2190 SOSManifestRef AintersectU
= SOSManifestCreateIntersection(A
, U
, error
);
2191 SOSManifestRef MintersectS
= SOSManifestCreateIntersection(M
, S
, error
);
2192 SOSManifestRef MintersectT
= SOSManifestCreateIntersection(M
, T
, error
);
2193 SOSManifestRef MintersectU
= SOSManifestCreateIntersection(M
, U
, error
);
2194 SOSManifestRef SintersectT
= SOSManifestCreateIntersection(S
, T
, error
);
2195 SOSManifestRef SintersectU
= SOSManifestCreateIntersection(S
, U
, error
);
2196 SOSManifestRef TintersectU
= SOSManifestCreateIntersection(T
, U
, error
);
2199 CFDictionaryRef newAmtu
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, CFSTR("A"), A
, CFSTR("M"), M
, CFSTR("T"), T
, CFSTR("U") U
, NULL
);
2200 CFDictionarySetValue(engine
->p2amtu
, SOSPeerGetID(peer
), newAmtu
);
2201 CFMutableStringRef amtuChanges
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
2202 SOSAppendDiffToString(amtu
, newAmtu
, amtuChanges
);
2203 secnotice("engine", "%@: %@", SOSPeerGetID(peer
), amtuChanges
);
2206 #define SOSASSERT(e) (__builtin_expect(!(e), 0) ? secnotice("engine", "state-assertion %s", #e), assert(e) : (void)0)
2208 SOSASSERT(L
? CFEqual(L
, SunionAunionT
) : SOSManifestGetCount(SunionAunionT
) == 0);
2209 SOSASSERT(C
? CFEqual(C
, SunionMunionU
) : SOSManifestGetCount(SunionMunionU
) == 0);
2211 SOSASSERT(SOSManifestGetCount(AintersectM
) == 0);
2212 SOSASSERT(SOSManifestGetCount(AintersectS
) == 0);
2213 SOSASSERT(SOSManifestGetCount(AintersectT
) == 0);
2214 SOSASSERT(SOSManifestGetCount(AintersectU
) == 0);
2215 SOSASSERT(SOSManifestGetCount(MintersectS
) == 0);
2216 SOSASSERT(SOSManifestGetCount(MintersectT
) == 0);
2217 SOSASSERT(SOSManifestGetCount(MintersectU
) == 0);
2218 SOSASSERT(SOSManifestGetCount(SintersectT
) == 0);
2219 SOSASSERT(SOSManifestGetCount(SintersectU
) == 0);
2220 SOSASSERT(SOSManifestGetCount(TintersectU
) == 0);
2222 CFReleaseSafe(AintersectM
);
2223 CFReleaseSafe(AintersectS
);
2224 CFReleaseSafe(AintersectT
);
2225 CFReleaseSafe(AintersectU
);
2226 CFReleaseSafe(MintersectS
);
2227 CFReleaseSafe(MintersectT
);
2228 CFReleaseSafe(MintersectU
);
2229 CFReleaseSafe(SintersectT
);
2230 CFReleaseSafe(SintersectU
);
2231 CFReleaseSafe(TintersectU
);
2233 CFReleaseSafe(AunionT
);
2234 CFReleaseSafe(MunionU
);
2240 //CFReleaseSafe(T); // Get
2241 //CFReleaseSafe(U); // Get
2242 //CFReleaseSafe(C); // Get
2247 void SOSEngineSetSyncCompleteListener(SOSEngineRef engine
, SOSEnginePeerInSyncBlock notify_block
) {
2248 SOSEngineDoOnQueue(engine
, ^{
2249 CFAssignRetained(engine
->syncCompleteListener
, Block_copy(notify_block
));
2253 void SOSEngineSetSyncCompleteListenerQueue(SOSEngineRef engine
, dispatch_queue_t notify_queue
) {
2254 SOSEngineDoOnQueue(engine
, ^{
2255 CFRetainAssign(engine
->syncCompleteQueue
, notify_queue
);
2259 static void SOSEngineCompletedSyncWithPeer(SOSEngineRef engine
, SOSPeerRef peer
) {
2260 SOSEnginePeerInSyncBlock block_to_call
= engine
->syncCompleteListener
;
2262 if (block_to_call
&& engine
->syncCompleteQueue
) {
2263 CFStringRef ID
= CFRetainSafe(SOSPeerGetID(peer
));
2264 CFSetRef views
= CFRetainSafe(SOSPeerGetViewNameSet(peer
));
2265 CFRetainSafe(block_to_call
);
2267 dispatch_async(engine
->syncCompleteQueue
, ^{
2268 block_to_call(ID
, views
);
2270 CFReleaseSafe(views
);
2271 CFReleaseSafe(block_to_call
);
2277 CFDataRef
SOSEngineCreateMessage_locked(SOSEngineRef engine
, SOSTransactionRef txn
, SOSPeerRef peer
,
2278 CFErrorRef
*error
, SOSEnginePeerMessageSentBlock
*sent
) {
2279 SOSManifestRef local
= SOSEngineCopyLocalPeerManifest_locked(engine
, peer
, error
);
2280 __block SOSMessageRef message
= SOSMessageCreate(kCFAllocatorDefault
, SOSPeerGetMessageVersion(peer
), error
);
2281 SOSManifestRef confirmed
= SOSPeerGetConfirmedManifest(peer
);
2282 SOSManifestRef pendingObjects
= SOSPeerGetPendingObjects(peer
);
2283 SOSManifestRef objectsSent
= NULL
;
2284 SOSManifestRef proposed
= NULL
;
2285 SOSManifestRef allMissing
= NULL
;
2286 SOSManifestRef allExtra
= NULL
;
2287 SOSManifestRef extra
= NULL
;
2288 SOSManifestRef excessPending
= NULL
;
2289 SOSManifestRef missing
= NULL
;
2290 SOSManifestRef unwanted
= SOSPeerGetUnwantedManifest(peer
);
2291 SOSManifestRef excessUnwanted
= NULL
;
2292 CFDataRef result
= NULL
;
2294 // Given (C, L, T, U) compute (T, U, M, A)
2298 // U \ (C \ L) => EU
2299 // T \ (L \ C) => ET
2300 // And assert that both EU and ET are empty and if not remove them from U and T respectively
2301 SOSManifestDiff(confirmed
, local
, &allMissing
, &allExtra
, error
);
2302 SOSManifestDiff(allExtra
, pendingObjects
, &extra
, &excessPending
, error
);
2303 if (SOSManifestGetCount(excessPending
)) {
2304 // T \ (L \ C) => excessPending (items both in L and C or in neither that are still pending)
2305 // Can only happen if a member of T was removed from L without us having a chance to update T
2306 secerror("%@ ASSERTION FAILURE purging excess pendingObjects: %@", peer
, excessPending
);
2307 SOSManifestRef newPendingObjects
= SOSManifestCreateComplement(excessPending
, pendingObjects
, error
);
2308 SOSPeerSetPendingObjects(peer
, newPendingObjects
);
2309 CFReleaseSafe(newPendingObjects
);
2310 pendingObjects
= SOSPeerGetPendingObjects(peer
);
2312 SOSManifestDiff(allMissing
, unwanted
, &missing
, &excessUnwanted
, error
);
2313 if (SOSManifestGetCount(excessUnwanted
)) {
2314 // U \ (C \ L) => excessUnwanted (items both in L and C or in neither that are still unwanted)
2315 // Can only happen if a member of U was added to L without us having a chance to update U.
2316 // Since U only contains items the conflict resolver rejected, this implies L somehow got rolled back
2317 // The other option (and more likely) is a member of U was removed from C and not from U.
2318 secerror("%@ ASSERTION FAILURE purging excess unwanted: %@", peer
, excessUnwanted
);
2319 SOSManifestRef newUnwanted
= SOSManifestCreateComplement(excessUnwanted
, unwanted
, error
);
2320 SOSPeerSetUnwantedManifest(peer
, newUnwanted
);
2321 CFReleaseSafe(newUnwanted
);
2322 unwanted
= SOSPeerGetUnwantedManifest(peer
);
2325 CFReleaseNull(allExtra
);
2326 CFReleaseNull(excessPending
);
2327 CFReleaseNull(allMissing
);
2328 CFReleaseNull(excessUnwanted
);
2330 secnoticeq("engine", "%@:%@: send state for peer [%s%s%s][%s%s] P:%zu, E:%zu, M:%zu U:%zu", engine
->myID
, SOSPeerGetID(peer
),
2332 confirmed
? "C":"0",
2333 pendingObjects
? "P":"0",
2334 SOSPeerSendObjects(peer
) ? "O":"o",
2335 SOSPeerMustSendMessage(peer
) ? "S":"s",
2336 SOSManifestGetCount(pendingObjects
),
2337 SOSManifestGetCount(extra
),
2338 SOSManifestGetCount(missing
),
2339 SOSManifestGetCount(unwanted
)
2343 // TODO: Because of not letting things terminate while we have extra left
2344 // we might send objects when we didn't need to, but there is always an
2345 // extra roundtrip required for objects that we assume the other peer
2346 // should have already.
2347 // TODO: If there are extra objects left, calling this function is not
2348 // idempotent we should check if pending is what we are about to send and not send anything in this case.
2349 if (SOSManifestGetCount(pendingObjects
) == 0 && SOSManifestGetCount(extra
) == 0)
2350 SOSPeerSetSendObjects(peer
, false);
2352 // If we aren't missing anything, we've gotten all their data, so we're sync even if they haven't seen ours.
2353 if (missing
&& SOSManifestGetCount(missing
) == 0) {
2354 SOSEngineCompletedSyncWithPeer(engine
, peer
);
2357 if (CFEqualSafe(local
, SOSPeerGetProposedManifest(peer
)) && !SOSPeerMustSendMessage(peer
)) {
2359 if (CFEqual(confirmed
, local
)) {
2360 secnoticeq("engine", "synced <No MSG> %@:%@", engine
->myID
, peer
);
2361 } else if (SOSManifestGetCount(pendingObjects
) == 0 /* TODO: No entries moved from extra to pendingObjects. */
2362 && SOSManifestGetCount(missing
) == 0) {
2363 secnoticeq("engine", "waiting <MSG not resent> %@:%@ extra: %@", engine
->myID
, peer
, extra
);
2368 CFReleaseSafe(local
);
2369 CFReleaseSafe(message
);
2370 CFReleaseNull(extra
);
2371 CFReleaseNull(missing
);
2372 return CFDataCreate(kCFAllocatorDefault
, NULL
, 0);
2376 if (SOSManifestGetCount(pendingObjects
)) {
2377 // If we have additions and we need to send objects, do so.
2378 __block
size_t objectsSize
= 0;
2379 __block
struct SOSDigestVector dv
= SOSDigestVectorInit
;
2380 CFMutableArrayRef changes
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
2381 __block CFErrorRef dsfeError
= NULL
;
2383 if (!SOSDataSourceForEachObject(engine
->dataSource
, txn
, pendingObjects
, &dsfeError
, ^void(CFDataRef key
, SOSObjectRef object
, bool *stop
) {
2384 CFErrorRef localError
= NULL
;
2385 CFDataRef digest
= NULL
;
2386 CFDataRef der
= NULL
;
2387 #if !defined(NDEBUG)
2388 const uint8_t *d
= CFDataGetBytePtr(key
);
2390 secdebug("engine", "%@:%@ object %02X%02X%02X%02X error from SOSDataSourceForEachObject: %@",
2391 engine
->myID
, SOSPeerGetID(peer
), d
[0], d
[1], d
[2], d
[3], dsfeError
);
2393 const uint8_t *d
= CFDataGetBytePtr(key
);
2394 secerror("%@:%@ object %02X%02X%02X%02X dropping from manifest: not found in datasource: %@",
2395 engine
->myID
, SOSPeerGetID(peer
), d
[0], d
[1], d
[2], d
[3], dsfeError
);
2396 SOSChangesAppendDelete(changes
, key
);
2397 } else if (!(der
= SOSEngineCopyObjectDER(engine
, object
, &localError
))
2398 || !(digest
= SOSObjectCopyDigest(engine
->dataSource
, object
, &localError
))) {
2399 if (SecErrorGetOSStatus(localError
) == errSecDecode
) {
2400 // Decode error, we need to drop these objects from our manifests
2401 const uint8_t *d
= CFDataGetBytePtr(key
);
2402 secnoticeq("engine", "%@:%@ object %02X%02X%02X%02X dropping from manifest: %@",
2403 engine
->myID
, SOSPeerGetID(peer
), d
[0], d
[1], d
[2], d
[3], localError
);
2404 SOSChangesAppendDelete(changes
, key
);
2405 CFRelease(localError
);
2407 // Stop iterating and propagate out all other errors.
2408 const uint8_t *d
= CFDataGetBytePtr(key
);
2409 secnoticeq("engine", "%@:%@ object %02X%02X%02X%02X in SOSDataSourceForEachObject: %@",
2410 engine
->myID
, SOSPeerGetID(peer
), d
[0], d
[1], d
[2], d
[3], localError
);
2412 CFErrorPropagate(localError
, error
);
2413 CFReleaseNull(message
);
2416 if (!CFEqual(key
, digest
)) {
2417 const uint8_t *d
= CFDataGetBytePtr(key
);
2418 const uint8_t *e
= CFDataGetBytePtr(digest
);
2419 secnoticeq("engine", "%@:%@ object %02X%02X%02X%02X is really %02X%02X%02X%02X dropping from local manifest",
2420 engine
->myID
, SOSPeerGetID(peer
), d
[0], d
[1], d
[2], d
[3], e
[0], e
[1], e
[2], e
[3]);
2421 SOSChangesAppendDelete(changes
, key
);
2422 SOSChangesAppendAdd(changes
, object
); // This is new behaviour but we think it's more correct
2425 size_t objectLen
= (size_t)CFDataGetLength(der
);
2426 if (SOSMessageAppendObject(message
, der
, &localError
)) {
2427 SOSDigestVectorAppend(&dv
, CFDataGetBytePtr(digest
));
2429 const uint8_t *d
= CFDataGetBytePtr(digest
);
2430 CFStringRef hexder
= CFDataCopyHexString(der
);
2431 secnoticeq("engine", "%@:%@ object %02X%02X%02X%02X der: %@ dropping from manifest: %@",
2432 engine
->myID
, SOSPeerGetID(peer
), d
[0], d
[1], d
[2], d
[3], hexder
, localError
);
2433 CFReleaseNull(hexder
);
2434 CFReleaseNull(message
);
2435 // Since we can't send these objects let's assume they are bad too?
2436 SOSChangesAppendDelete(changes
, digest
);
2438 objectsSize
+= objectLen
;
2439 if (objectsSize
> kSOSMessageMaxObjectsSize
)
2442 CFErrorPropagate(dsfeError
, error
); // this also releases dsfeError
2445 CFReleaseSafe(digest
);
2447 CFReleaseNull(message
);
2450 objectsSent
= SOSManifestCreateWithDigestVector(&dv
, error
);
2452 if (CFArrayGetCount(changes
)) {
2453 CFErrorRef localError
= NULL
;
2454 if (!SOSEngineUpdateChanges_locked(engine
, NULL
, kSOSDataSourceTransactionDidCommit
, kSOSDataSourceSOSTransaction
, changes
, &localError
))
2455 secerror("SOSEngineUpdateChanges_locked: %@ failed: %@", changes
, localError
);
2456 CFReleaseSafe(localError
);
2457 CFAssignRetained(local
, SOSEngineCopyLocalPeerManifest_locked(engine
, peer
, error
));
2459 CFReleaseSafe(changes
);
2460 SOSDigestVectorFree(&dv
);
2461 CFReleaseNull(dsfeError
);
2464 // If we have no confirmed manifest, we want all pendedObjects going out as a manifest
2465 objectsSent
= CFRetainSafe(pendingObjects
);
2468 if (confirmed
|| SOSManifestGetCount(missing
) || SOSManifestGetCount(extra
) || objectsSent
) {
2469 SOSManifestRef allExtra
= SOSManifestCreateUnion(extra
, objectsSent
, error
);
2470 proposed
= SOSManifestCreateWithPatch(confirmed
, missing
, allExtra
, error
);
2471 CFReleaseNull(allExtra
);
2474 SOSManifestRef sender
= local
;
2475 // We actually send the remote peer its own digest.
2476 // Note that both pendingObjects and unwanted may have been changed, so we get them again
2477 if (SOSManifestGetCount(SOSPeerGetPendingObjects(peer
))==0 && SOSManifestGetCount(extra
)==0 &&
2478 SOSManifestGetCount(missing
)==0 && SOSManifestGetCount(SOSPeerGetUnwantedManifest(peer
))!=0) {
2479 secnoticeq("engine", "%@:%@: only have differences in unwanted set; lying to peer to stop sync",engine
->myID
, SOSPeerGetID(peer
));
2483 if (!SOSMessageSetManifests(message
, sender
, confirmed
, proposed
, proposed
, confirmed
? objectsSent
: NULL
, error
)) {
2484 secnoticeq("engine", "%@:%@: failed to set message manifests",engine
->myID
, SOSPeerGetID(peer
));
2485 CFReleaseNull(message
);
2488 CFReleaseNull(objectsSent
);
2491 result
= SOSMessageCreateData(message
, SOSPeerNextSequenceNumber(peer
), error
);
2495 // Capture the peer in our block (SOSEnginePeerMessageSentBlock)
2497 *sent
= Block_copy(^(bool success
) {
2498 dispatch_async(engine
->queue
, ^{
2500 SOSPeerSetMustSendMessage(peer
, false);
2501 if (!confirmed
&& !proposed
) {
2502 SOSPeerSetSendObjects(peer
, true);
2503 secnoticeq("engine", "%@:%@ sendObjects=true L:%@", engine
->myID
, SOSPeerGetID(peer
), local
);
2505 SOSPeerAddLocalManifest(peer
, local
);
2506 SOSPeerAddProposedManifest(peer
, proposed
);
2507 secnoticeq("engine", "send %@:%@ %@", engine
->myID
, SOSPeerGetID(peer
), message
);
2508 //SOSEngineCheckPeerIntegrity(engine, peer, NULL);
2510 secerror("%@:%@ failed to send %@", engine
->myID
, SOSPeerGetID(peer
), message
);
2512 CFReleaseSafe(peer
);
2513 CFReleaseSafe(local
);
2514 CFReleaseSafe(proposed
);
2515 CFReleaseSafe(message
);
2519 CFReleaseSafe(local
);
2520 CFReleaseSafe(proposed
);
2521 CFReleaseSafe(message
);
2523 CFReleaseNull(extra
);
2524 CFReleaseNull(missing
);
2525 if (error
&& *error
)
2526 secerror("%@:%@ error in send: %@", engine
->myID
, SOSPeerGetID(peer
), *error
);
2531 static void SOSEngineLogItemError(SOSEngineRef engine
, CFStringRef peerID
, CFDataRef key
, CFDataRef optionalDigest
, const char *where
, CFErrorRef error
) {
2532 if (!optionalDigest
) {
2533 const uint8_t *d
= CFDataGetBytePtr(key
);
2534 secwarning("%@:%@ object %02X%02X%02X%02X %s: %@", engine
->myID
, peerID
, d
[0], d
[1], d
[2], d
[3], where
, error
? (CFTypeRef
)error
: CFSTR(""));
2536 const uint8_t *d
= CFDataGetBytePtr(key
);
2537 const uint8_t *e
= CFDataGetBytePtr(optionalDigest
);
2538 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]);
2542 static bool SOSEngineWriteToBackup_locked(SOSEngineRef engine
, SOSPeerRef peer
, bool rewriteComplete
, bool *didWrite
, bool *incomplete
, CFErrorRef
*error
) {
2543 __block
bool ok
= SOSPeerWritePendingReset(peer
, error
);
2544 if (!ok
|| !SOSPeerGetKeyBag(peer
))
2546 __block SOSManifestRef local
= SOSEngineCopyLocalPeerManifest_locked(engine
, peer
, error
);
2547 __block SOSManifestRef proposed
= SOSPeerGetProposedManifest(peer
);
2548 __block
bool notify
= true;
2549 SOSManifestRef pendingObjects
= NULL
;
2550 SOSManifestRef missing
= NULL
;
2551 CFStringRef peerID
= SOSPeerGetID(peer
);
2553 ok
&= SOSManifestDiff(proposed
, local
, &missing
, &pendingObjects
, error
);
2555 secnoticeq("engine", "%@:%@: Send state for peer [%s%s%s] O: %zu, M: %zu", engine
->myID
, peerID
,
2558 pendingObjects
? "O":"0",
2559 SOSManifestGetCount(pendingObjects
),
2560 SOSManifestGetCount(missing
));
2562 if (SOSManifestGetCount(missing
) == 0 && SOSManifestGetCount(pendingObjects
) == 0) {
2563 // proposed == local (faster test than CFEqualSafe above), since we
2564 // already did the SOSManifestDiff
2565 if (rewriteComplete
) {
2568 secnoticeq("engine", "%@:%@ backup still done", engine
->myID
, peer
);
2572 ok
&= SOSPeerAppendToJournal(peer
, error
, ^(FILE *journalFile
, keybag_handle_t kbhandle
) {
2573 SOSManifestRef objectsSent
= NULL
;
2574 __block
struct SOSDigestVector dvdel
= SOSDigestVectorInit
;
2575 __block
struct SOSDigestVector dvadd
= SOSDigestVectorInit
;
2576 SOSManifestForEach(missing
, ^(CFDataRef key
, bool *stop
) {
2577 CFErrorRef localError
= NULL
;
2578 if (ftello(journalFile
) > kSOSBackupMaxFileSize
) {
2579 // Highwatermark hit on file.
2581 } else if (SOSBackupEventWriteDelete(journalFile
, key
, &localError
)) {
2582 SOSDigestVectorAppend(&dvdel
, CFDataGetBytePtr(key
));
2584 SOSEngineLogItemError(engine
, peerID
, key
, NULL
, "in SOSPeerWriteDelete", localError
);
2585 CFErrorPropagate(localError
, error
);
2586 // TODO: Update of missing so proposed is updated properly
2587 *stop
= true; // Disk full?
2591 if (ok
&& SOSManifestGetCount(pendingObjects
)) {
2592 CFMutableArrayRef changes
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
2593 ok
&= SOSDataSourceForEachObject(engine
->dataSource
, NULL
, pendingObjects
, error
, ^void(CFDataRef key
, SOSObjectRef object
, bool *stop
) {
2594 CFErrorRef localError
= NULL
;
2595 CFDataRef digest
= NULL
;
2596 CFDictionaryRef backupItem
= NULL
;
2597 if (ftello(journalFile
) > kSOSBackupMaxFileSize
) {
2598 // Highwatermark hit on file.
2600 } else if (!object
) {
2601 SOSEngineLogItemError(engine
, peerID
, key
, NULL
, "dropping from manifest: not found in datasource", localError
);
2602 SOSChangesAppendDelete(changes
, key
);
2603 } else if (!(backupItem
= SOSObjectCopyBackup(engine
->dataSource
, object
, kbhandle
, &localError
))
2604 || !(digest
= SOSObjectCopyDigest(engine
->dataSource
, object
, &localError
))) {
2605 if (SecErrorGetOSStatus(localError
) == errSecDecode
) {
2606 // Decode error, we need to drop these objects from our manifests
2607 SOSEngineLogItemError(engine
, peerID
, key
, NULL
, "dropping from manifest", localError
);
2608 SOSChangesAppendDelete(changes
, key
);
2609 CFRelease(localError
);
2611 // Stop iterating and propagate out all other errors.
2612 SOSEngineLogItemError(engine
, peerID
, key
, NULL
, "in SOSDataSourceForEachObject", localError
);
2614 CFErrorPropagate(localError
, error
);
2618 if (!CFEqual(key
, digest
)) {
2619 SOSEngineLogItemError(engine
, peerID
, key
, digest
, "", NULL
);
2620 SOSChangesAppendDelete(changes
, key
);
2621 SOSChangesAppendAdd(changes
, object
); // This is new behaviour but we think it's more correct
2624 if (SOSBackupEventWriteAdd(journalFile
, backupItem
, &localError
)) {
2625 SOSDigestVectorAppend(&dvadd
, CFDataGetBytePtr(digest
));
2627 SOSEngineLogItemError(engine
, peerID
, key
, NULL
, "in SOSPeerWriteAdd", localError
);
2628 *stop
= true; // Disk full?
2629 CFErrorPropagate(localError
, error
);
2633 CFReleaseSafe(backupItem
);
2634 CFReleaseSafe(digest
);
2636 if (CFArrayGetCount(changes
)) {
2637 CFErrorRef localError
= NULL
;
2638 if (!SOSEngineUpdateChanges_locked(engine
, NULL
, kSOSDataSourceTransactionDidCommit
, kSOSDataSourceSOSTransaction
, changes
, &localError
))
2639 secerror("SOSEngineUpdateChanges_locked: %@ failed: %@", changes
, localError
);
2640 CFReleaseSafe(localError
);
2641 // Since calling SOSEngineUpdateChanges_locked might cause local to change and might cause the backup peer to update proposed, refetch them here.
2642 CFAssignRetained(local
, SOSEngineCopyLocalPeerManifest_locked(engine
, peer
, error
));
2643 proposed
= SOSPeerGetProposedManifest(peer
);
2645 CFReleaseSafe(changes
);
2648 if (dvadd
.count
|| (proposed
&& dvdel
.count
)) {
2650 SOSManifestRef deleted
= SOSManifestCreateWithDigestVector(&dvdel
, error
);
2651 SOSManifestRef objectsSent
= SOSManifestCreateWithDigestVector(&dvadd
, error
);
2652 SOSManifestRef newProposed
= SOSManifestCreateWithPatch(proposed
, deleted
, objectsSent
, error
);
2653 CFReleaseSafe(deleted
);
2654 CFReleaseSafe(objectsSent
);
2655 SOSPeerSetProposedManifest(peer
, newProposed
);
2656 CFReleaseSafe(newProposed
);
2657 proposed
= SOSPeerGetProposedManifest(peer
);
2659 SOSDigestVectorFree(&dvdel
);
2660 SOSDigestVectorFree(&dvadd
);
2662 // TODO: If proposed is NULL, and local is empty we should still consider ourselves done.
2663 // It so happens this can't happen in practice today since there is at least a backupbag
2664 // in the backup, but this is a bug waiting to rear its head in the future.
2665 if (ok
&& CFEqualSafe(local
, proposed
)) {
2666 CFErrorRef localError
= NULL
;
2667 if (SOSBackupEventWriteCompleteMarker(journalFile
, 899, &localError
)) {
2668 SOSPeerSetSendObjects(peer
, true);
2670 secnoticeq("backup", "%@:%@ backup done%s", engine
->myID
, peerID
, notify
? " notifying sbd" : "");
2671 // TODO: Now switch to changes based writing to backup sync.
2672 // Currently we leave changes enabled but we probably shouldn't
2674 secwarning("%@:%@ in SOSBackupPeerWriteCompleteMarker: %@", engine
->myID
, peerID
, localError
);
2677 CFErrorPropagate(localError
, error
);
2680 secnoticeq("backup", "%@:%@ backup incomplete [%zu/%zu]%s", engine
->myID
, peerID
, SOSManifestGetCount(local
), SOSManifestGetCount(proposed
), notify
? " notifying sbd" : "");
2683 CFReleaseNull(objectsSent
);
2686 SOSBackupPeerPostNotification("writing changes to backup");
2689 CFReleaseSafe(local
);
2690 CFReleaseNull(pendingObjects
);
2691 CFReleaseNull(missing
);
2696 bool SOSEngineSyncWithPeers(SOSEngineRef engine
, CFErrorRef
*error
) {
2697 __block
bool ok
= true;
2698 __block
bool incomplete
= false;
2699 ok
&= SOSEngineDoTxnOnQueue(engine
, error
, ^(SOSTransactionRef txn
, bool *commit
) {
2700 __block
bool dirty
= false;
2701 SOSEngineForEachBackupPeer_locked(engine
, ^(SOSPeerRef peer
) {
2702 ok
= SOSEngineWriteToBackup_locked(engine
, peer
, false, &dirty
, &incomplete
, error
);
2706 ok
= SOSEngineSave(engine
, txn
, error
);
2709 // Ensure we get called again in a while (after a backup timeout)
2710 // sbd will do this since we never wrote a complete marker.
2711 // TODO: This relies on us not writing complete marker for update
2712 // event while we havn't finished a full backup, which we currently still do.
2717 bool SOSEngineHandleMessage(SOSEngineRef engine
, CFStringRef peerID
,
2718 CFDataRef raw_message
, CFErrorRef
*error
)
2720 __block
bool result
= true;
2721 __block
bool somethingChanged
= false;
2722 SOSMessageRef message
= SOSMessageCreateWithData(kCFAllocatorDefault
, raw_message
, error
);
2723 result
&= message
&& SOSEngineDoTxnOnQueue(engine
, error
, ^(SOSTransactionRef txn
, bool *commit
) {
2724 result
= SOSEngineHandleMessage_locked(engine
, peerID
, message
, txn
, commit
, &somethingChanged
, error
);
2726 CFReleaseSafe(message
);
2727 if (somethingChanged
)
2728 SecKeychainChanged(false);
2732 void SOSEngineCircleChanged(SOSEngineRef engine
, CFStringRef myPeerID
, CFArrayRef trustedPeers
, CFArrayRef untrustedPeers
) {
2733 __block
bool peersOrViewsChanged
= false;
2734 SOSEngineDoOnQueue(engine
, ^{
2735 peersOrViewsChanged
= SOSEngineCircleChanged_locked(engine
, myPeerID
, trustedPeers
, untrustedPeers
);
2736 engine
->dirty
= peersOrViewsChanged
;
2737 engine
->codersNeedSaving
= peersOrViewsChanged
;
2740 __block
bool ok
= true;
2741 __block CFErrorRef localError
= NULL
;
2742 ok
&= SOSEngineDoTxnOnQueue(engine
, &localError
, ^(SOSTransactionRef txn
, bool *commit
) {
2743 ok
= *commit
= SOSEngineSave(engine
, txn
, &localError
);
2746 secerror("failed to save engine state: %@", localError
);
2747 CFReleaseSafe(localError
);
2750 if (peersOrViewsChanged
)
2751 SOSCCSyncWithAllPeers();
2754 SOSManifestRef
SOSEngineCopyManifest(SOSEngineRef engine
, CFErrorRef
*error
) {
2755 __block SOSManifestRef result
= NULL
;
2756 SOSEngineDoOnQueue(engine
, ^{
2757 result
= SOSEngineCopyManifestWithViewNameSet_locked(engine
, SOSViewsGetV0ViewSet(), error
);
2762 SOSManifestRef
SOSEngineCopyLocalPeerManifest(SOSEngineRef engine
, SOSPeerRef peer
, CFErrorRef
*error
) {
2763 __block SOSManifestRef result
= NULL
;
2764 SOSEngineDoOnQueue(engine
, ^{
2765 result
= SOSEngineCopyLocalPeerManifest_locked(engine
, peer
, error
);
2770 bool SOSEngineUpdateChanges(SOSEngineRef engine
, SOSDataSourceTransactionSource source
, CFArrayRef changes
, CFErrorRef
*error
) {
2771 __block
bool result
= true;
2772 SOSEngineDoOnQueue(engine
, ^{
2773 result
= SOSEngineUpdateChanges_locked(engine
, NULL
, kSOSDataSourceTransactionDidCommit
, source
, changes
, error
);
2779 // Peer state layout. WRONG! It's an array now
2780 // The peer state is an array.
2781 // The first element of the array is a dictionary with any number of keys and
2782 // values in it (for future expansion) such as changing the digest size or type
2783 // or remebering boolean flags for a peers sake.
2784 // The next three are special in that they are manifest digests with special
2785 // meaning and rules as to how they are treated (These are dynamically updated
2786 // based on database activity so they have a fully history of all changes made
2787 // to the local db. The first is the manifest representing the pendingObjects
2788 // to send to the other peer. This is normally only ever appending to, and in
2789 // particular with transactions originating from the Keychain API that affect
2790 // syncable items will need to add the new objects digests to the pendingObjects list
2791 // while adding the digests of any tombstones encountered to the extra list.
2793 SOSPeerRef
SOSEngineCopyPeerWithID(SOSEngineRef engine
, CFStringRef peer_id
, CFErrorRef
*error
) {
2794 __block SOSPeerRef peer
= NULL
;
2795 SOSEngineDoOnQueue(engine
, ^{
2796 peer
= SOSEngineCopyPeerWithID_locked(engine
, peer_id
, error
);
2801 bool SOSEngineForPeerIDNoCoder(SOSEngineRef engine
, CFStringRef peerID
, CFErrorRef
*error
, void (^forPeer
)(SOSTransactionRef txn
, SOSPeerRef peer
)) {
2802 __block
bool ok
= true;
2803 SOSDataSourceReadWithCommitQueue(engine
->dataSource
, error
, ^(SOSTransactionRef txn
) {
2804 SOSEngineDoOnQueue(engine
, ^{
2805 SOSPeerRef peer
= SOSEngineCopyPeerWithID_locked(engine
, peerID
, error
);
2818 bool SOSEngineForPeerID(SOSEngineRef engine
, CFStringRef peerID
, CFErrorRef
*error
, void (^forPeer
)(SOSTransactionRef txn
, SOSPeerRef peer
, SOSCoderRef coder
)) {
2819 __block
bool ok
= true;
2820 SOSDataSourceReadWithCommitQueue(engine
->dataSource
, error
, ^(SOSTransactionRef txn
) {
2821 SOSEngineDoOnQueue(engine
, ^{
2822 SOSPeerRef peer
= SOSEngineCopyPeerWithID_locked(engine
, peerID
, error
);
2824 SOSCoderRef coder
= SOSEngineGetCoder_locked(engine
, peerID
, NULL
);
2825 forPeer(txn
, peer
, coder
);
2836 bool SOSEngineWithPeerID(SOSEngineRef engine
, CFStringRef peerID
, CFErrorRef
*error
, void (^with
)(SOSPeerRef peer
, SOSCoderRef coder
, SOSDataSourceRef dataSource
, SOSTransactionRef txn
, bool *forceSaveState
)) {
2837 __block
bool result
= true;
2838 result
&= SOSEngineDoTxnOnQueue(engine
, error
, ^(SOSTransactionRef txn
, bool *commit
) {
2839 SOSPeerRef peer
= SOSEngineCopyPeerWithID_locked(engine
, peerID
, error
);
2841 result
= SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("Engine has no peer for %@"), peerID
);
2843 bool saveState
= false;
2844 SOSCoderRef coder
= SOSEngineGetCoderInTx_locked(engine
, txn
, peerID
, NULL
);
2845 with(peer
, coder
, engine
->dataSource
, txn
, &saveState
);
2846 CFReleaseSafe(peer
);
2848 result
= SOSEngineSave(engine
, txn
, error
);
2849 // TODO: Don't commit if engineSave fails?
2856 CFDataRef
SOSEngineCreateMessageToSyncToPeer(SOSEngineRef engine
, CFStringRef peerID
, SOSEnginePeerMessageSentBlock
*sentBlock
, CFErrorRef
*error
) {
2857 __block CFDataRef message
= NULL
;
2858 SOSEngineForPeerID(engine
, peerID
, error
, ^(SOSTransactionRef txn
, SOSPeerRef peer
, SOSCoderRef coder
) {
2859 message
= SOSEngineCreateMessage_locked(engine
, txn
, peer
, error
, sentBlock
);
2864 bool SOSEnginePeerDidConnect(SOSEngineRef engine
, CFStringRef peerID
, CFErrorRef
*error
) {
2865 return SOSEngineWithPeerID(engine
, peerID
, error
, ^(SOSPeerRef peer
, SOSCoderRef coder
, SOSDataSourceRef dataSource
, SOSTransactionRef txn
, bool *saveState
) {
2866 *saveState
= SOSPeerDidConnect(peer
);
2870 bool SOSEngineSetPeerConfirmedManifest(SOSEngineRef engine
, CFStringRef backupName
,
2871 CFDataRef keybagDigest
, CFDataRef manifestData
, CFErrorRef
*error
) {
2872 __block
bool ok
= true;
2874 ok
&= SOSEngineForPeerID(engine
, backupName
, error
, ^(SOSTransactionRef txn
, SOSPeerRef peer
, SOSCoderRef coder
) {
2876 bool incomplete
= false;
2877 SOSManifestRef confirmed
= NULL
;
2878 CFDataRef keybag
= SOSPeerGetKeyBag(peer
);
2879 CFDataRef computedKeybagDigest
= keybag
? CFDataCopySHA1Digest(keybag
, NULL
) : NULL
;
2880 if (CFEqualSafe(keybagDigest
, computedKeybagDigest
)) {
2881 ok
= confirmed
= SOSManifestCreateWithData(manifestData
, error
);
2883 // Set both confirmed and proposed (confirmed is just
2884 // for debug status, proposed is actually what's used
2885 // by the backup peer).
2886 SOSPeerSetConfirmedManifest(peer
, confirmed
);
2887 SOSPeerSetProposedManifest(peer
, confirmed
);
2890 // sbd missed a reset event, send it again
2891 // Force SOSEngineWriteToBackup_locked to call SOSPeerWriteReset, which clears
2892 // confirmed and proposed manifests and writes the keybag to the journal.
2893 SOSPeerSetMustSendMessage(peer
, true);
2896 // Stop changes from writing complete markers, unless SOSEngineWriteToBackup_locked() detects we are in sync
2897 SOSPeerSetSendObjects(peer
, false);
2898 // Write data for this peer if we can, technically not needed for non legacy protocol support all the time.
2899 ok
= SOSEngineWriteToBackup_locked(engine
, peer
, true, &dirty
, &incomplete
, error
);
2901 CFReleaseSafe(confirmed
);
2902 CFReleaseSafe(computedKeybagDigest
);
2907 CFArrayRef
SOSEngineCopyBackupPeerNames(SOSEngineRef engine
, CFErrorRef
*error
) {
2908 __block CFMutableArrayRef backupNames
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
2909 SOSEngineForEachBackupPeer(engine
, ^(SOSPeerRef peer
) {
2910 CFArrayAppendValue(backupNames
, SOSPeerGetID(peer
));
2915 static CFStringRef
CFStringCreateWithLabelAndViewNameSetDescription(CFStringRef label
, CFStringRef peerID
, CFSetRef vns
, SOSManifestRef manifest
) {
2916 __block CFStringRef desc
;
2917 CFStringSetPerformWithDescription(vns
, ^(CFStringRef description
) {
2918 desc
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%@ %@ (%@) [%lu]"), label
, peerID
, description
, (manifest
) ? SOSManifestGetCount(manifest
): 0);
2923 static void CFArrayAppendConfirmedDigestsEntry(CFMutableArrayRef array
, CFStringRef label
, CFStringRef peerID
, CFSetRef vns
, SOSManifestRef manifest
) {
2924 CFStringRef desc
= CFStringCreateWithLabelAndViewNameSetDescription(label
, peerID
, vns
, manifest
);
2925 CFArrayAppendValue(array
, desc
);
2926 CFReleaseSafe(desc
);
2927 CFDataRef digest
= SOSManifestGetDigest(manifest
, NULL
);
2929 CFArrayAppendValue(array
, digest
);
2931 CFDataRef nullDigest
= CFDataCreate(kCFAllocatorDefault
, NULL
, 0);
2932 CFArrayAppendValue(array
, nullDigest
);
2933 CFReleaseSafe(nullDigest
);
2937 static CFArrayRef
SOSEngineCopyPeerConfirmedDigests_locked(SOSEngineRef engine
, CFErrorRef
*error
) {
2938 CFMutableArrayRef result
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
2939 CFDictionaryForEach(engine
->viewNameSet2ChangeTracker
, ^(const void *vns
, const void *ct
) {
2940 SOSManifestRef manifest
= SOSEngineCopyManifestWithViewNameSet_locked(engine
, vns
, error
);
2941 CFArrayAppendConfirmedDigestsEntry(result
, CFSTR("local "), engine
->myID
, (CFSetRef
)vns
, manifest
);
2942 CFReleaseSafe(manifest
);
2945 // Copy other peers even if we aren't in the circle, since we're observing it.
2946 SOSEngineForEachPeer_locked(engine
, ^(SOSPeerRef peer
) {
2947 CFArrayAppendConfirmedDigestsEntry(result
, CFSTR("remote"), SOSPeerGetID(peer
), SOSPeerGetViewNameSet(peer
),
2948 SOSPeerGetConfirmedManifest(peer
));
2953 CFArrayRef
SOSEngineCopyPeerConfirmedDigests(SOSEngineRef engine
, CFErrorRef
*error
) {
2954 __block CFArrayRef result
= NULL
;
2955 SOSEngineDoOnQueue(engine
, ^{
2956 result
= SOSEngineCopyPeerConfirmedDigests_locked(engine
, error
);
2961 SOSDataSourceRef
SOSEngineGetDataSource(SOSEngineRef engine
) {
2962 return engine
->dataSource
;
2965 #define ENGINELOGSTATE "engineLogState"
2966 void SOSEngineLogState(SOSEngineRef engine
) {
2967 CFErrorRef error
= NULL
;
2968 CFArrayRef confirmedDigests
= NULL
;
2970 secnotice(ENGINELOGSTATE
, "Start");
2972 require_action_quiet(engine
, retOut
, secnotice(ENGINELOGSTATE
, "No Engine Available"));
2973 confirmedDigests
= SOSEngineCopyPeerConfirmedDigests(engine
, &error
);
2974 require_action_quiet(confirmedDigests
, retOut
, secnotice(ENGINELOGSTATE
, "No engine peers: %@\n", error
));
2975 CFIndex entries
= CFArrayGetCount(confirmedDigests
) / 2;
2976 for(CFIndex i
= 0; i
< entries
; i
++) {
2977 CFStringRef partA
= asString(CFArrayGetValueAtIndex(confirmedDigests
, i
*2), NULL
);
2978 CFDataRef partB
= asData(CFArrayGetValueAtIndex(confirmedDigests
, i
*2+1), NULL
);
2979 if(partA
&& partB
) {
2980 CFStringRef hexDigest
= CFDataCopyHexString(partB
);
2981 secnotice(ENGINELOGSTATE
, "%@ %@", partA
, hexDigest
);
2982 CFReleaseNull(hexDigest
);
2986 CFReleaseNull(error
);
2987 CFReleaseNull(confirmedDigests
);
2988 secnotice(ENGINELOGSTATE
, "Finish");