2 * Copyright (c) 2012-2015 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
26 * SOSEngine.c - Implementation of a secure object syncing engine
29 #include <Security/SecureObjectSync/SOSChangeTracker.h>
30 #include <Security/SecureObjectSync/SOSEngine.h>
31 #include <Security/SecureObjectSync/SOSDigestVector.h>
32 #include <Security/SecureObjectSync/SOSInternal.h>
33 #include <Security/SecureObjectSync/SOSPeer.h>
34 #include <Security/SecureObjectSync/SOSViews.h>
35 #include <Security/SecureObjectSync/SOSBackupEvent.h>
36 #include <corecrypto/ccder.h>
39 #include <utilities/array_size.h>
40 #include <utilities/SecCFCCWrappers.h>
41 #include <utilities/SecCFError.h>
42 #include <utilities/SecCFRelease.h>
43 #include <utilities/SecCFWrappers.h>
44 #include <utilities/der_plist.h>
45 #include <utilities/der_plist_internal.h>
46 #include <utilities/debugging.h>
47 #include <utilities/iCloudKeychainTrace.h>
48 #include <utilities/SecCoreCrypto.h>
49 #include <utilities/SecFileLocations.h>
50 #include <AssertMacros.h>
51 #include <CoreFoundation/CoreFoundation.h>
53 #include <securityd/SecItemServer.h> // TODO: We can't leave this here.
54 #include <securityd/SOSCloudCircleServer.h> // TODO: We can't leave this here.
55 #include <Security/SecItem.h> // TODO: We can't leave this here.
56 #include <Security/SecItemPriv.h> // TODO: We can't leave this here.
57 #include <securityd/SecItemSchema.h>
58 #include <securityd/iCloudTrace.h>
60 #include <CoreFoundation/CFURL.h>
63 // MARK: SOSEngine The Keychain database with syncable keychain support.
66 // Key in dataSource for general engine state file.
67 // This file only has digest entries in it, no manifests.
68 static const CFStringRef kSOSEngineState
= CFSTR("engine-state");
70 // Keys in state dictionary
71 static CFStringRef kSOSEngineManifestCacheKey
= CFSTR("manifestCache");
72 static CFStringRef kSOSEnginePeerStateKey
= CFSTR("peerState");
73 static CFStringRef kSOSEnginePeerIDsKey
= CFSTR("peerIDs");
74 static CFStringRef kSOSEngineIDKey
= CFSTR("id");
75 static CFStringRef kSOSEngineTraceDateKey
= CFSTR("traceDate");
77 /* SOSEngine implementation. */
78 struct __OpaqueSOSEngine
{
80 SOSDataSourceRef dataSource
;
81 CFStringRef myID
; // My peerID in the circle
82 // We need to address the issues of corrupt keychain items
83 SOSManifestRef unreadable
; // Possibly by having a set of unreadable items, to which we
84 // add any corrupted items in the db that have yet to be deleted.
85 // This happens if we notce corruption during a (read only) query.
86 // We would also perma-subtract unreadable from manifest whenever
87 // anyone asked for manifest. This result would be cached in
88 // The manifestCache below, so we just need a key into the cache
89 CFDataRef localMinusUnreadableDigest
; // or a digest (CFDataRef of the right size).
91 CFMutableDictionaryRef manifestCache
; // digest -> ( refcount, manifest )
92 CFMutableDictionaryRef peerMap
; // peerId -> SOSPeerRef
93 CFDictionaryRef viewNameSet2ChangeTracker
; // CFSetRef of CFStringRef -> SOSChangeTrackerRef
94 CFDictionaryRef viewName2ChangeTracker
; // CFStringRef -> SOSChangeTrackerRef
96 CFDateRef lastTraceDate
; // Last time we did a CloudKeychainTrace
98 dispatch_queue_t queue
; // Engine queue
100 dispatch_source_t save_timer
; // Engine state save timer
102 dispatch_queue_t syncCompleteQueue
; // Non-retained queue for async notificaion
103 CFMutableDictionaryRef syncCompleteListeners
; // Map from PeerID->notification block
106 static bool SOSEngineLoad(SOSEngineRef engine
, CFErrorRef
*error
);
109 static CFStringRef
SOSPeerIDArrayCreateString(CFArrayRef peerIDs
) {
110 return peerIDs
? CFStringCreateByCombiningStrings(kCFAllocatorDefault
, peerIDs
, CFSTR(" ")) : CFSTR("");
113 static CFStringRef
SOSEngineCopyFormattingDesc(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
114 SOSEngineRef engine
= (SOSEngineRef
)cf
;
115 CFStringRef tpDesc
= SOSPeerIDArrayCreateString(engine
->peerIDs
);
116 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);
117 CFReleaseSafe(tpDesc
);
121 static CFStringRef
SOSEngineCopyDebugDesc(CFTypeRef cf
) {
122 return SOSEngineCopyFormattingDesc(cf
, NULL
);
125 static dispatch_queue_t sEngineQueue
;
126 static CFDictionaryRef sEngineMap
;
128 CFGiblisWithFunctions(SOSEngine
, NULL
, NULL
, NULL
, NULL
, NULL
, SOSEngineCopyFormattingDesc
, SOSEngineCopyDebugDesc
, NULL
, NULL
, ^{
129 sEngineQueue
= dispatch_queue_create("SOSEngine queue", DISPATCH_QUEUE_SERIAL
);
130 sEngineMap
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
133 #define _LOG_RAW_MESSAGES 0
134 void logRawMessage(CFDataRef message
, bool sending
, uint64_t seqno
)
136 #if _LOG_RAW_MESSAGES
137 CFStringRef hexMessage
= NULL
;
139 hexMessage
= CFDataCopyHexString(message
);
141 secnoticeq("engine", "%s RAW%1d %@", sending
? "send" : "recv", seqno
?2:0, hexMessage
);
143 secnoticeq("engine", "%s RAWx %@", sending
? "send" : "recv", hexMessage
); // we don't know vers of received msg here
145 CFReleaseSafe(hexMessage
);
149 // Peer state layout. WRONG! It's an array now
150 // The peer state is an array.
151 // The first element of the array is a dictionary with any number of keys and
152 // values in it (for future expansion) such as changing the digest size or type
153 // or remembering boolean flags for a peers sake.
154 // The next three are special in that they are manifest digests with special
155 // meaning and rules as to how they are treated (These are dynamically updated
156 // based on database activity so they have a fully history of all changes made
157 // to the local db. The first is the manifest representing the pendingObjects
158 // to send to the other peer. This is normally only ever appending to, and in
159 // particular with transactions originating from the Keychain API that affect
160 // syncable items will need to add the new objects digests to the pendingObjects list
161 // while adding the digests of any tombstones encountered to the extra list.
163 CFStringRef
SOSEngineGetMyID(SOSEngineRef engine
) {
164 // TODO: this should not be needed
168 // TEMPORARY: Get the list of IDs for cleanup, this shouldn't be used instead it should iterate KVS.
169 CFArrayRef
SOSEngineGetPeerIDs(SOSEngineRef engine
) {
170 return engine
->peerIDs
;
173 void SOSEngineClearCache(SOSEngineRef engine
){
174 CFReleaseNull(engine
->manifestCache
);
175 CFReleaseNull(engine
->localMinusUnreadableDigest
);
176 dispatch_release(engine
->queue
);
179 static SOSPeerRef
SOSEngineCopyPeerWithMapEntry_locked(SOSEngineRef engine
, CFStringRef peerID
, CFTypeRef mapEntry
, CFErrorRef
*error
) {
180 SOSPeerRef peer
= NULL
;
181 if (mapEntry
&& CFGetTypeID(mapEntry
) == SOSPeerGetTypeID()) {
182 // The mapEntry is an SOSPeer, so we're done.
183 peer
= (SOSPeerRef
)CFRetain(mapEntry
);
185 // The mapEntry is a peerState, attempt to initialize a new
186 // peer iff peerID is in the set of trusted peerIDs
187 if (engine
->peerIDs
&& CFArrayContainsValue(engine
->peerIDs
, CFRangeMake(0, CFArrayGetCount(engine
->peerIDs
)), peerID
)) {
188 CFErrorRef localError
= NULL
;
189 peer
= SOSPeerCreateWithState(engine
, peerID
, mapEntry
, &localError
);
191 secerror("error inflating peer: %@: %@ from state: %@", peerID
, localError
, mapEntry
);
192 CFReleaseNull(localError
);
193 peer
= SOSPeerCreateWithState(engine
, peerID
, NULL
, error
);
196 // Replace the map entry with the inflated peer.
197 CFDictionarySetValue(engine
->peerMap
, peerID
, peer
);
200 SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("peer: %@ is untrusted inflating not allowed"), peerID
);
206 static SOSPeerRef
SOSEngineCopyPeerWithID_locked(SOSEngineRef engine
, CFStringRef peerID
, CFErrorRef
*error
) {
207 CFTypeRef mapEntry
= CFDictionaryGetValue(engine
->peerMap
, peerID
);
208 SOSPeerRef peer
= NULL
;
210 peer
= SOSEngineCopyPeerWithMapEntry_locked(engine
, peerID
, mapEntry
, error
);
213 secerror("peer: %@ not found, peerMap: %@, engine: %@", peerID
, engine
->peerMap
, engine
);
214 SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("peer: %@ not found"), peerID
);
219 struct SOSEngineWithPeerContext
{
221 void (^with
)(SOSPeerRef peer
);
224 static void SOSEngineWithPeerMapEntry_locked(const void *peerID
, const void *mapEntry
, void *context
) {
225 struct SOSEngineWithPeerContext
*ewp
= context
;
226 SOSPeerRef peer
= SOSEngineCopyPeerWithMapEntry_locked(ewp
->engine
, peerID
, mapEntry
, NULL
);
233 static void SOSEngineForEachPeer_locked(SOSEngineRef engine
, void (^with
)(SOSPeerRef peer
)) {
234 struct SOSEngineWithPeerContext ewp
= { .engine
= engine
, .with
= with
};
235 CFDictionaryRef peerMapCopy
= CFDictionaryCreateCopy(NULL
, engine
->peerMap
);
236 CFDictionaryApplyFunction(peerMapCopy
, SOSEngineWithPeerMapEntry_locked
, &ewp
);
237 CFRelease(peerMapCopy
);
240 static void SOSEngineWithBackupPeerMapEntry_locked(const void *peerID
, const void *mapEntry
, void *context
) {
241 struct SOSEngineWithPeerContext
*ewp
= context
;
242 // v0 backup peer is always in map but we only consider it a backup peer if it has a keybag.
243 if (SOSPeerMapEntryIsBackup(mapEntry
)) {
244 SOSPeerRef peer
= SOSEngineCopyPeerWithMapEntry_locked(ewp
->engine
, peerID
, mapEntry
, NULL
);
252 static void SOSEngineForEachBackupPeer_locked(SOSEngineRef engine
, void (^with
)(SOSPeerRef peer
)) {
253 struct SOSEngineWithPeerContext ewp
= { .engine
= engine
, .with
= with
};
254 CFDictionaryRef peerMapCopy
= CFDictionaryCreateCopy(NULL
, engine
->peerMap
);
255 CFDictionaryApplyFunction(peerMapCopy
, SOSEngineWithBackupPeerMapEntry_locked
, &ewp
);
256 CFRelease(peerMapCopy
);
262 SOSManifestRef
SOSEngineGetManifestForDigest(SOSEngineRef engine
, CFDataRef digest
) {
263 if (!engine
->manifestCache
|| !digest
) return NULL
;
264 SOSManifestRef manifest
= (SOSManifestRef
)CFDictionaryGetValue(engine
->manifestCache
, digest
);
265 if (!manifest
) return NULL
;
266 if (CFGetTypeID(manifest
) != SOSManifestGetTypeID()) {
267 secerror("dropping corrupt manifest for %@ from cache", digest
);
268 CFDictionaryRemoveValue(engine
->manifestCache
, digest
);
275 void SOSEngineAddManifest(SOSEngineRef engine
, SOSManifestRef manifest
) {
276 CFDataRef digest
= SOSManifestGetDigest(manifest
, NULL
);
278 if (!engine
->manifestCache
)
279 engine
->manifestCache
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
280 CFDictionaryAddValue(engine
->manifestCache
, digest
, manifest
);
284 CFDataRef
SOSEnginePatchRecordAndCopyDigest(SOSEngineRef engine
, SOSManifestRef base
, SOSManifestRef removals
, SOSManifestRef additions
, CFErrorRef
*error
) {
285 CFDataRef digest
= NULL
;
286 SOSManifestRef manifest
= SOSManifestCreateWithPatch(base
, removals
, additions
, error
);
288 SOSEngineAddManifest(engine
, manifest
);
289 digest
= CFRetainSafe(SOSManifestGetDigest(manifest
, NULL
));
291 CFReleaseSafe(manifest
);
295 SOSManifestRef
SOSEngineCopyPersistedManifest(SOSEngineRef engine
, CFDictionaryRef persisted
, CFStringRef key
) {
296 return CFRetainSafe(SOSEngineGetManifestForDigest(engine
, asData(CFDictionaryGetValue(persisted
, key
), NULL
)));
299 CFMutableArrayRef
SOSEngineCopyPersistedManifestArray(SOSEngineRef engine
, CFDictionaryRef persisted
, CFStringRef key
, CFErrorRef
*error
) {
300 CFMutableArrayRef manifests
= NULL
;
301 CFArrayRef digests
= NULL
;
303 if (asArrayOptional(CFDictionaryGetValue(persisted
, key
), &digests
, error
))
304 manifests
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
305 if (digests
) CFArrayForEachC(digests
, digest
) {
306 SOSManifestRef manifest
= SOSEngineGetManifestForDigest(engine
, digest
);
308 CFArrayAppendValue(manifests
, manifest
);
313 static CFDictionaryRef
SOSEngineCopyEncodedManifestCache_locked(SOSEngineRef engine
, CFErrorRef
*error
) {
314 CFMutableDictionaryRef mfc
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
315 SOSEngineForEachPeer_locked(engine
, ^(SOSPeerRef peer
) {
316 SOSPeerAddManifestsInUse(peer
, mfc
);
322 static bool SOSEngineGCManifests_locked(SOSEngineRef engine
, CFErrorRef
*error
) {
323 __block
struct SOSDigestVector mdInCache
= SOSDigestVectorInit
;
324 __block
struct SOSDigestVector mdInUse
= SOSDigestVectorInit
;
325 struct SOSDigestVector mdUnused
= SOSDigestVectorInit
;
326 struct SOSDigestVector mdMissing
= SOSDigestVectorInit
;
329 SOSEngineForEachPeer_locked(engine
, ^(SOSPeerRef peer
) {
330 SOSPeerMarkDigestsInUse(peer
, &mdInUse
);
333 if (engine
->manifestCache
) {
334 CFDictionaryForEach(engine
->manifestCache
, ^(const void *key
, const void *value
) {
335 CFDataRef digest
= (CFDataRef
)key
;
337 SOSDigestVectorAppend(&mdInCache
, CFDataGetBytePtr(digest
));
340 // Delete unused manifests.
341 SOSDigestVectorDiff(&mdInCache
, &mdInUse
, &mdUnused
, &mdMissing
);
342 SOSManifestRef unused
= SOSManifestCreateWithDigestVector(&mdUnused
, NULL
);
343 SOSManifestForEach(unused
, ^(CFDataRef digest
, bool *stop
) {
345 CFDictionaryRemoveValue(engine
->manifestCache
, digest
);
347 CFReleaseSafe(unused
);
350 SOSDigestVectorFree(&mdInCache
);
351 SOSDigestVectorFree(&mdInUse
);
352 SOSDigestVectorFree(&mdUnused
);
353 SOSDigestVectorFree(&mdMissing
);
359 // End of Manifest cache
362 static bool SOSEngineGCPeerState_locked(SOSEngineRef engine
, CFErrorRef
*error
) {
365 //require_quiet(ok = SOSEngineGCManifests_locked(engine, error), exit);
371 static CFMutableDictionaryRef
SOSEngineCopyPeerState_locked(SOSEngineRef engine
, CFErrorRef
*error
) {
372 CFMutableDictionaryRef peerState
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
373 CFDictionaryForEach(engine
->peerMap
, ^(const void *key
, const void *value
) {
374 CFDictionaryRef state
= NULL
;
375 if (value
&& CFGetTypeID(value
) == SOSPeerGetTypeID()) {
376 CFErrorRef localError
= NULL
;
378 state
= SOSPeerCopyState((SOSPeerRef
)value
, &localError
);
380 secnotice("engine", "%@ failed to encode peer: %@", key
, localError
);
381 CFReleaseNull(localError
);
382 // TODO: Potentially replace inflated peer with deflated peer in peerMap
384 // We have a deflated peer.
385 state
= CFRetainSafe(value
);
389 CFDictionarySetValue(peerState
, key
, state
);
390 CFReleaseSafe(state
);
396 static CFDataRef
SOSEngineCopyState(SOSEngineRef engine
, CFErrorRef
*error
) {
397 CFDataRef der
= NULL
;
398 CFMutableDictionaryRef state
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
399 if (engine
->myID
) CFDictionarySetValue(state
, kSOSEngineIDKey
, engine
->myID
);
400 if (engine
->peerIDs
) CFDictionarySetValue(state
, kSOSEnginePeerIDsKey
, engine
->peerIDs
);
401 if (engine
->lastTraceDate
) CFDictionarySetValue(state
, kSOSEngineTraceDateKey
, engine
->lastTraceDate
);
402 CFTypeRef peerState
= SOSEngineCopyPeerState_locked(engine
, error
);
403 if (peerState
) CFDictionarySetValue(state
, kSOSEnginePeerStateKey
, peerState
);
404 CFReleaseSafe(peerState
);
405 CFDictionaryRef mfc
= SOSEngineCopyEncodedManifestCache_locked(engine
, error
);
407 CFDictionarySetValue(state
, kSOSEngineManifestCacheKey
, mfc
);
410 der
= CFPropertyListCreateDERData(kCFAllocatorDefault
, state
, error
);
411 CFReleaseSafe(state
);
412 secnotice("engine", "%@", engine
);
416 static bool SOSEngineDoSave(SOSEngineRef engine
, SOSTransactionRef txn
, CFErrorRef
*error
) {
417 CFDataRef derState
= SOSEngineCopyState(engine
, error
);
418 bool ok
= derState
&& SOSDataSourceSetStateWithKey(engine
->dataSource
, txn
, kSOSEngineState
, kSecAttrAccessibleAlways
, derState
, error
);
419 CFReleaseSafe(derState
);
423 #define SOSENGINE_SAVE_TIMEOUT (NSEC_PER_MSEC * 500ull)
424 #define SOSENGINE_SAVE_LEEWAY (NSEC_PER_MSEC * 500ull)
425 #define SOSENGINE_SAVE_MAX_DELAY (NSEC_PER_MSEC * 500ull)
427 #if !(TARGET_IPHONE_SIMULATOR)
428 static void SOSEngineShouldSave(SOSEngineRef engine
) {
429 if (engine
->save_timer
) {
430 // Possibly defer timer further up to engine->save_deadline
434 // Schedule the timer to fire on a concurrent queue, so we can follow
435 // the proper procedure of acquiring a dataSource and then engine queues.
436 engine
->save_timer
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
, 0, 0, dispatch_get_global_queue(QOS_CLASS_DEFAULT
, 0));
437 dispatch_source_set_event_handler(engine
->save_timer
, ^{
438 CFErrorRef dsWithError
= NULL
;
439 if (engine
->dataSource
) {
440 if (!SOSDataSourceWith(engine
->dataSource
, &dsWithError
, ^(SOSTransactionRef txn
, bool *commit
) {
441 dispatch_sync(engine
->queue
, ^{
442 CFErrorRef saveError
= NULL
;
443 if (!SOSEngineDoSave(engine
, txn
, &saveError
)) {
444 secerrorq("Failed to save engine state: %@", saveError
);
445 CFReleaseNull(saveError
);
449 secerrorq("Failed to open dataSource to save engine state: %@", dsWithError
);
450 CFReleaseNull(dsWithError
);
453 xpc_transaction_end();
455 // Set the timer's fire time to now + SOSENGINE_SAVE_TIMEOUT seconds with a SOSENGINE_SAVE_LEEWAY fuzz factor.
456 dispatch_source_set_timer(engine
->save_timer
,
457 dispatch_time(DISPATCH_TIME_NOW
, SOSENGINE_SAVE_TIMEOUT
),
458 DISPATCH_TIME_FOREVER
, SOSENGINE_SAVE_LEEWAY
);
459 // Start a trasaction, then start the timer, the handler for the timer will end
461 xpc_transaction_begin();
462 dispatch_resume(engine
->save_timer
);
466 static bool SOSEngineSave(SOSEngineRef engine
, SOSTransactionRef txn
, CFErrorRef
*error
) {
467 // Don't save engine state from tests
468 if (!engine
->dataSource
)
470 #if (TARGET_IPHONE_SIMULATOR)
471 return SOSEngineDoSave(engine
, txn
, error
);
473 SOSEngineShouldSave(engine
);
478 static SOSManifestRef
SOSEngineCreateManifestWithViewNameSet_locked(SOSEngineRef engine
, CFSetRef viewNameSet
, CFErrorRef
*error
) {
479 // TODO: Potentially tell all changeTrackers to track manifests ( //forall ct do SOSChangeTrackerSetConcrete(ct, true);
480 // and read the entire dataSource and pass all objects though the filter here, instead of
481 // forcing the datasource to be able to do "smart" queries
482 return SOSDataSourceCopyManifestWithViewNameSet(engine
->dataSource
, viewNameSet
, error
);
485 static SOSChangeTrackerRef
SOSEngineCopyChangeTrackerWithViewNameSet_locked(SOSEngineRef engine
, CFSetRef viewNameSet
, CFErrorRef
*error
) {
486 SOSChangeTrackerRef ct
= (SOSChangeTrackerRef
)CFDictionaryGetValue(engine
->viewNameSet2ChangeTracker
, viewNameSet
);
488 SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("no change tracker for view set %@"), viewNameSet
);
489 return CFRetainSafe(ct
);
492 static SOSManifestRef
SOSEngineCopyManifestWithViewNameSet_locked(SOSEngineRef engine
, CFSetRef viewNameSet
, CFErrorRef
*error
) {
493 SOSChangeTrackerRef ct
= SOSEngineCopyChangeTrackerWithViewNameSet_locked(engine
, viewNameSet
, error
);
497 SOSManifestRef manifest
= SOSChangeTrackerCopyManifest(ct
, NULL
);
499 manifest
= SOSEngineCreateManifestWithViewNameSet_locked(engine
, viewNameSet
, error
); // Do the SQL query
500 SOSChangeTrackerSetManifest(ct
, manifest
);
506 SOSManifestRef
SOSEngineCopyLocalPeerManifest_locked(SOSEngineRef engine
, SOSPeerRef peer
, CFErrorRef
*error
) {
507 return SOSEngineCopyManifestWithViewNameSet_locked(engine
, SOSPeerGetViewNameSet(peer
), error
);
510 #define withViewAndBackup(VIEW) do { with(VIEW); if (!isTomb) with(VIEW ## _tomb); } while(0)
513 // Invoke with once for each view an object is in.
514 // TODO: Move this function into the DataSource
515 static void SOSEngineObjectWithView(SOSEngineRef engine
, SOSObjectRef object
, void (^with
)(CFStringRef view
)) {
516 // Filter items into v0 only view here
517 SecDbItemRef item
= (SecDbItemRef
)object
; // TODO: Layer violation, breaks tests
518 if (isDictionary(object
)) {
519 CFTypeRef isTombValue
= CFDictionaryGetValue((CFDictionaryRef
)object
, kSecAttrTombstone
);
520 bool isTomb
= isTombValue
&& CFBooleanGetValue(isTombValue
);
521 // We are in the test just assume v0 and v2 views.
522 withViewAndBackup(kSOSViewKeychainV0
);
523 } else if (SecDbItemIsSyncableOrCorrupted(item
)) {
524 const SecDbClass
*iclass
= SecDbItemGetClass(item
);
525 CFTypeRef pdmn
= SecDbItemGetCachedValueWithName(item
, kSecAttrAccessible
);
526 if ((iclass
== &genp_class
|| iclass
== &inet_class
|| iclass
== &keys_class
|| iclass
== &cert_class
)
528 && (CFEqual(pdmn
, kSecAttrAccessibleWhenUnlocked
)
529 || CFEqual(pdmn
, kSecAttrAccessibleAfterFirstUnlock
)
530 || CFEqual(pdmn
, kSecAttrAccessibleAlways
)
531 || CFEqual(pdmn
, kSecAttrAccessibleWhenUnlockedThisDeviceOnly
)
532 || CFEqual(pdmn
, kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
)
533 || CFEqual(pdmn
, kSecAttrAccessibleAlwaysThisDeviceOnly
)))
535 CFTypeRef tomb
= SecDbItemGetCachedValueWithName(item
, kSecAttrTombstone
);
537 bool isTomb
= (isNumber(tomb
) && CFNumberGetValue(tomb
, kCFNumberCharType
, &cvalue
) && cvalue
== 1);
538 CFTypeRef viewHint
= SecDbItemGetCachedValueWithName(item
, kSecAttrSyncViewHint
);
539 if (viewHint
== NULL
) {
540 if (iclass
== &cert_class
) {
541 withViewAndBackup(kSOSViewOtherSyncable
);
543 if (!SecDbItemGetCachedValueWithName(item
, kSecAttrTokenID
)) {
544 withViewAndBackup(kSOSViewKeychainV0
);
546 CFTypeRef agrp
= SecDbItemGetCachedValueWithName(item
, kSecAttrAccessGroup
);
547 if (iclass
== &keys_class
&& CFEqualSafe(agrp
, CFSTR("com.apple.security.sos"))) {
548 withViewAndBackup(kSOSViewiCloudIdentity
);
549 } else if (CFEqualSafe(agrp
, CFSTR("com.apple.cfnetwork"))) {
550 withViewAndBackup(kSOSViewAutofillPasswords
);
551 } else if (CFEqualSafe(agrp
, CFSTR("com.apple.safari.credit-cards"))) {
552 withViewAndBackup(kSOSViewSafariCreditCards
);
553 } else if (iclass
== &genp_class
) {
554 if (CFEqualSafe(agrp
, CFSTR("apple")) &&
555 CFEqualSafe(SecDbItemGetCachedValueWithName(item
, kSecAttrService
), CFSTR("AirPort"))) {
556 withViewAndBackup(kSOSViewWiFi
);
557 } else if (CFEqualSafe(agrp
, CFSTR("com.apple.sbd"))) {
558 withViewAndBackup(kSOSViewBackupBagV0
);
560 withViewAndBackup(kSOSViewOtherSyncable
); // (genp)
563 withViewAndBackup(kSOSViewOtherSyncable
); // (inet || keys)
569 CFStringRef viewHintTomb
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%@-tomb"), viewHint
);
572 CFRelease(viewHintTomb
);
578 // TODO: general queries
581 CFArrayForEachC(engine
->views
, view
) {
582 bool inView
= SOSViewQueryMatchItem(view
, item
);
584 CFStringRef viewName
= SOSViewCopyName(view
);
586 CFReleaseSafe(viewName
);
594 // SOSChangeMapper - Helper for SOSEngineUpdateChanges_locked
596 struct SOSChangeMapper
{
598 SOSTransactionRef txn
;
599 SOSDataSourceTransactionPhase phase
;
600 SOSDataSourceTransactionSource source
;
601 CFMutableDictionaryRef ct2changes
;
604 static void SOSChangeMapperInit(struct SOSChangeMapper
*cm
, SOSEngineRef engine
, SOSTransactionRef txn
, SOSDataSourceTransactionPhase phase
, SOSDataSourceTransactionSource source
) {
609 cm
->ct2changes
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
612 static void SOSChangeMapperFree(struct SOSChangeMapper
*cm
) {
613 CFReleaseSafe(cm
->ct2changes
);
616 static void SOSChangeMapperAppendObject(struct SOSChangeMapper
*cm
, SOSChangeTrackerRef ct
, bool isAdd
, CFTypeRef object
) {
617 CFMutableArrayRef changes
= (CFMutableArrayRef
)CFDictionaryGetValue(cm
->ct2changes
, ct
);
619 changes
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
620 CFDictionarySetValue(cm
->ct2changes
, ct
, changes
);
621 CFReleaseSafe(changes
);
623 isAdd
? SOSChangesAppendAdd(changes
, object
) : SOSChangesAppendDelete(changes
, object
);
626 static bool SOSChangeMapperIngestChange(struct SOSChangeMapper
*cm
, bool isAdd
, CFTypeRef change
) {
627 bool someoneCares
= false;
628 if (isData(change
)) {
629 // TODO: Reenable assertion once the tests have been updated
631 // We got a digest for a deleted object. Our dataSource probably couldn't find
632 // an object with this digest, probably because it went missing, or it was
633 // discovered to be corrupted.
634 // Tell all our changeTrackers about this digest since we don't know who might need it.
635 CFDictionaryForEach(cm
->engine
->viewNameSet2ChangeTracker
, ^(const void *viewNameSet
, const void *ct
) {
636 SOSChangeMapperAppendObject(cm
, (SOSChangeTrackerRef
)ct
, isAdd
, change
);
638 someoneCares
= CFDictionaryGetCount(cm
->engine
->viewNameSet2ChangeTracker
);
640 // We got an object let's figure out which views it's in and schedule it for
641 // delivery to all changeTrackers interested in any of those views.
642 SOSObjectRef object
= (SOSObjectRef
)change
;
643 CFMutableSetRef changeTrackerSet
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
);
644 // First gather all the changeTrackers interested in this object (eleminating dupes by collecting them in a set)
645 SOSEngineObjectWithView(cm
->engine
, object
, ^(CFStringRef viewName
) {
646 const void *ctorset
= CFDictionaryGetValue(cm
->engine
->viewName2ChangeTracker
, viewName
);
647 if (isSet(ctorset
)) {
648 CFSetForEach((CFSetRef
)ctorset
, ^(const void *ct
) { CFSetAddValue(changeTrackerSet
, ct
); });
649 } else if (ctorset
) {
650 CFSetAddValue(changeTrackerSet
, ctorset
);
653 // Then append the object to the changes array in the ct2changes dictionary keyed by viewSet
654 CFSetForEach(changeTrackerSet
, ^(const void *ct
) {
655 SOSChangeMapperAppendObject(cm
, (SOSChangeTrackerRef
)ct
, isAdd
, object
);
657 someoneCares
= CFSetGetCount(changeTrackerSet
);
658 CFReleaseSafe(changeTrackerSet
);
663 static bool SOSChangeMapperSend(struct SOSChangeMapper
*cm
, CFErrorRef
*error
) {
664 __block
bool ok
= true;
665 CFDictionaryForEach(cm
->ct2changes
, ^(const void *ct
, const void *changes
) {
666 ok
&= SOSChangeTrackerTrackChanges((SOSChangeTrackerRef
)ct
, cm
->engine
, cm
->txn
, cm
->source
, cm
->phase
, (CFArrayRef
)changes
, error
);
671 static bool SOSEngineUpdateChanges_locked(SOSEngineRef engine
, SOSTransactionRef txn
, SOSDataSourceTransactionPhase phase
, SOSDataSourceTransactionSource source
, CFArrayRef changes
, CFErrorRef
*error
)
673 secnoticeq("engine", "%@: %s %s %ld changes", engine
->myID
, phase
== kSOSDataSourceTransactionWillCommit
? "will-commit" : phase
== kSOSDataSourceTransactionDidCommit
? "did-commit" : "did-rollback", source
== kSOSDataSourceSOSTransaction
? "sos" : "api", CFArrayGetCount(changes
));
676 case kSOSDataSourceTransactionDidRollback
:
677 ok
&= SOSEngineLoad(engine
, error
);
679 case kSOSDataSourceTransactionWillCommit
:
680 case kSOSDataSourceTransactionDidCommit
:
682 struct SOSChangeMapper cm
;
683 SOSChangeMapperInit(&cm
, engine
, txn
, phase
, source
);
685 CFArrayForEachC(changes
, event
) {
686 CFTypeRef deleted
= NULL
;
687 CFTypeRef inserted
= NULL
;
688 SecDbEventGetComponents(event
, &deleted
, &inserted
, error
);
690 SOSChangeMapperIngestChange(&cm
, false, deleted
);
692 bool someoneCares
= SOSChangeMapperIngestChange(&cm
, true, inserted
);
693 if (!someoneCares
&& !isData(inserted
) && SecDbItemIsTombstone((SecDbItemRef
)inserted
) && !CFEqualSafe(SecDbItemGetValue((SecDbItemRef
)inserted
, &v7utomb
, NULL
), kCFBooleanTrue
)) {
694 CFErrorRef localError
= NULL
;
695 // A tombstone was inserted but there is no changetracker that
697 if (!SecDbItemDoDeleteSilently((SecDbItemRef
)inserted
, (SecDbConnectionRef
)txn
, &localError
)) {
698 secerror("failed to delete tombstone %@ that no one cares about: %@", inserted
, localError
);
699 CFReleaseNull(localError
);
705 ok
&= SOSChangeMapperSend(&cm
, error
);
706 SOSChangeMapperFree(&cm
);
708 if (phase
== kSOSDataSourceTransactionDidCommit
) {
709 // We are being called outside a transaction, beware, that any
710 // db changes we attempt to make here will cause deadlock!
712 // Write SOSEngine and SOSPeer state to disk
713 // TODO: Only do this if dirty
714 ok
&= SOSEngineSave(engine
, txn
, error
);
722 static void SOSEngineSetNotifyPhaseBlock(SOSEngineRef engine
) {
723 SOSDataSourceSetNotifyPhaseBlock(engine
->dataSource
, engine
->queue
, ^(SOSDataSourceRef ds
, SOSTransactionRef txn
, SOSDataSourceTransactionPhase phase
, SOSDataSourceTransactionSource source
, CFArrayRef changes
) {
724 CFErrorRef localError
= NULL
;
725 if (!SOSEngineUpdateChanges_locked(engine
, txn
, phase
, source
, changes
, &localError
)) {
726 secerror("updateChanged failed: %@", localError
);
728 CFReleaseSafe(localError
);
732 #if 0 // TODO: update these checks
733 static void SOSEngineCircleChanged_sanitycheck(SOSEngineRef engine
, CFStringRef myPeerID
, CFArrayRef trustedPeers
, CFArrayRef untrustedPeers
) {
735 CFMutableArrayRef addedPeers
= CFArrayCreateDifference(kCFAllocatorDefault
, trustedPeers
, engine
->peerIDs
);
736 CFMutableArrayRef deletedPeers
= CFArrayCreateDifference(kCFAllocatorDefault
, engine
->peerIDs
, trustedPeers
);
737 CFMutableArrayRef addedUntrustedPeers
= CFArrayCreateDifference(kCFAllocatorDefault
, untrustedPeers
, engine
->peerIDs
);
738 CFMutableArrayRef deletedUntrustedPeers
= CFArrayCreateDifference(kCFAllocatorDefault
, engine
->peerIDs
, untrustedPeers
);
740 CFStringRef tpDesc
= SOSPeerIDArrayCreateString(trustedPeers
);
741 CFStringRef apDesc
= SOSPeerIDArrayCreateString(addedPeers
);
742 CFStringRef dpDesc
= SOSPeerIDArrayCreateString(deletedPeers
);
743 CFStringRef aupDesc
= SOSPeerIDArrayCreateString(addedUntrustedPeers
);
744 CFStringRef dupDesc
= SOSPeerIDArrayCreateString(deletedUntrustedPeers
);
745 secnotice("engine", "trusted %@ added %@ removed %@ add ut: %@ rem ut: %@", tpDesc
, apDesc
, dpDesc
, aupDesc
, dupDesc
);
746 CFReleaseSafe(dupDesc
);
747 CFReleaseSafe(aupDesc
);
748 CFReleaseSafe(dpDesc
);
749 CFReleaseSafe(apDesc
);
750 CFReleaseSafe(tpDesc
);
753 // Ensure SOSAccount isn't giving us the runaround.
754 // Assert that trustedPeers, untrustedPeers and myPeerId are disjoint sets
756 CFMutableArrayRef allTrustedPeers
= CFArrayCreateDifference(kCFAllocatorDefault
, trustedPeers
, untrustedPeers
);
757 assert(CFEqual(trustedPeers
, allTrustedPeers
));
758 CFReleaseSafe(allTrustedPeers
);
759 assert(!CFArrayContainsValue(trustedPeers
, CFRangeMake(0, CFArrayGetCount(trustedPeers
)), myPeerID
));
761 if (untrustedPeers
) {
762 CFMutableArrayRef allUntrustedPeers
= CFArrayCreateDifference(kCFAllocatorDefault
, untrustedPeers
, trustedPeers
);
763 assert(CFEqual(untrustedPeers
, allUntrustedPeers
));
764 CFReleaseSafe(allUntrustedPeers
);
765 assert(!CFArrayContainsValue(untrustedPeers
, CFRangeMake(0, CFArrayGetCount(trustedPeers
)), myPeerID
));
768 CFReleaseNull(deletedUntrustedPeers
);
769 CFReleaseNull(addedUntrustedPeers
);
770 CFReleaseNull(deletedPeers
);
771 CFReleaseNull(addedPeers
);
773 // End of logging and asertions, actual code here.
777 static SOSChangeTrackerRef
SOSReferenceAndGetChangeTracker(CFDictionaryRef lookup
, CFMutableDictionaryRef referenced
, CFSetRef viewNameSet
) {
778 SOSChangeTrackerRef ct
= (SOSChangeTrackerRef
)CFDictionaryGetValue(referenced
, viewNameSet
);
780 ct
= (SOSChangeTrackerRef
)CFDictionaryGetValue(lookup
, viewNameSet
);
782 SOSChangeTrackerResetRegistration(ct
);
783 CFDictionarySetValue(referenced
, viewNameSet
, ct
);
785 ct
= SOSChangeTrackerCreate(kCFAllocatorDefault
, false, NULL
, NULL
);
786 CFDictionarySetValue(referenced
, viewNameSet
, ct
);
793 static CFStringRef
CFStringCreateWithViewNameSet(CFSetRef vns
);
795 static void CFStringAppendPeerIDAndViews(CFMutableStringRef desc
, CFStringRef peerID
, CFSetRef vns
) {
797 CFStringRef vnsDesc
= CFStringCreateWithViewNameSet(vns
);
798 CFStringAppendFormat(desc
, NULL
, CFSTR(" %@ (%@)"), peerID
, vnsDesc
);
799 CFReleaseSafe(vnsDesc
);
801 CFStringAppendFormat(desc
, NULL
, CFSTR(" %@ NULL"), peerID
);
805 // Must be called after updating viewNameSet2ChangeTracker
806 static void SOSEngineUpdateViewName2ChangeTracker(SOSEngineRef engine
) {
807 // Create the mapping from viewName -> ChangeTracker used for lookup during change notification
808 CFMutableDictionaryRef newViewName2ChangeTracker
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
809 CFDictionaryForEach(engine
->viewNameSet2ChangeTracker
, ^(const void *viewNameSet
, const void *ct
) {
810 CFSetForEach(viewNameSet
, ^(const void *viewName
) {
811 const void *ctorset
= NULL
;
812 if (CFDictionaryGetValueIfPresent(newViewName2ChangeTracker
, viewName
, &ctorset
)) {
813 if (isSet(ctorset
)) {
814 CFSetAddValue((CFMutableSetRef
)ctorset
, ct
);
815 } else if (!CFEqual(ct
, ctorset
)) {
816 CFMutableSetRef set
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
);
817 CFSetAddValue(set
, ctorset
);
818 CFSetAddValue(set
, ct
);
819 CFDictionaryReplaceValue(newViewName2ChangeTracker
, viewName
, set
);
823 CFDictionarySetValue(newViewName2ChangeTracker
, viewName
, ct
);
827 CFAssignRetained(engine
->viewName2ChangeTracker
, newViewName2ChangeTracker
);
830 static void SOSEngineSetBackupBag(SOSEngineRef engine
, SOSObjectRef bagItem
);
832 // This is called only if we are in a circle and we should listen for keybag changes
833 static void SOSEngineRegisterBackupBagV0Tracker(SOSEngineRef engine
, CFMutableDictionaryRef newViewNameSet2ChangeTracker
, CFMutableStringRef desc
) {
834 SOSChangeTrackerRef bbct
= SOSReferenceAndGetChangeTracker(engine
->viewNameSet2ChangeTracker
, newViewNameSet2ChangeTracker
, SOSViewsGetV0BackupBagViewSet());
835 SOSChangeTrackerRegisterChangeUpdate(bbct
, ^bool(SOSChangeTrackerRef ct
, SOSEngineRef engine
, SOSTransactionRef txn
, SOSDataSourceTransactionSource source
, SOSDataSourceTransactionPhase phase
, CFArrayRef changes
, CFErrorRef
*error
) {
837 CFArrayForEachC(changes
, change
) {
838 CFTypeRef object
= NULL
;
839 bool isAdd
= SOSChangeGetObject(change
, &object
);
840 SecDbItemRef dbi
= (SecDbItemRef
)object
;
841 if (!isData(object
) &&
842 CFEqualSafe(SecDbItemGetCachedValueWithName(dbi
, kSecAttrService
), CFSTR("SecureBackupService")) &&
843 CFEqualSafe(SecDbItemGetCachedValueWithName(dbi
, kSecAttrAccessible
), kSecAttrAccessibleWhenUnlocked
) &&
844 CFEqualSafe(SecDbItemGetCachedValueWithName(dbi
, kSecAttrAccount
), CFSTR("SecureBackupPublicKeybag"))) {
845 SOSEngineSetBackupBag(engine
, isAdd
? (SOSObjectRef
)object
: NULL
);
852 static void SOSEngineReferenceBackupPeer(SOSEngineRef engine
, CFStringRef peerID
, CFSetRef viewNameSet
, CFDataRef keyBag
, CFMutableDictionaryRef newViewNameSet2ChangeTracker
, CFMutableDictionaryRef newPeerMap
) {
853 CFTypeRef oldEntry
= CFDictionaryGetValue(engine
->peerMap
, peerID
);
854 CFTypeRef newEntry
= SOSPeerOrStateSetViewsKeyBagAndCreateCopy(oldEntry
, viewNameSet
, keyBag
);
856 if (isDictionary(newEntry
)) {
857 // Backup peers, are always inflated
858 CFAssignRetained(newEntry
, SOSPeerCreateWithState(engine
, peerID
, newEntry
, NULL
));
859 // If !oldEntry this is an edge (first creation of a peer).
861 SOSPeerKeyBagDidChange((SOSPeerRef
)newEntry
);
864 CFDictionarySetValue(newPeerMap
, peerID
, newEntry
);
868 SOSChangeTrackerRef ct
= SOSReferenceAndGetChangeTracker(engine
->viewNameSet2ChangeTracker
, newViewNameSet2ChangeTracker
, viewNameSet
);
870 SOSChangeTrackerUpdatesChanges child
= Block_copy(^bool(SOSChangeTrackerRef ct
, SOSEngineRef engine
, SOSTransactionRef txn
, SOSDataSourceTransactionSource source
, SOSDataSourceTransactionPhase phase
, CFArrayRef changes
, CFErrorRef
*error
) {
871 return SOSPeerDataSourceWillChange((SOSPeerRef
)newEntry
, SOSEngineGetDataSource(engine
), source
, changes
, error
);
874 SOSChangeTrackerRegisterChangeUpdate(ct
, child
);
875 Block_release(child
);
880 static void SOSEngineReferenceSyncPeer(SOSEngineRef engine
, CFStringRef peerID
, CFSetRef viewNameSet
, CFMutableDictionaryRef newViewNameSet2ChangeTracker
, CFMutableDictionaryRef newPeerMap
) {
881 CFTypeRef newEntry
= SOSPeerOrStateSetViewsKeyBagAndCreateCopy(CFDictionaryGetValue(engine
->peerMap
, peerID
), viewNameSet
, NULL
);
883 SOSChangeTrackerRef ct
= SOSReferenceAndGetChangeTracker(engine
->viewNameSet2ChangeTracker
, newViewNameSet2ChangeTracker
, viewNameSet
);
884 // Standard peer, inflated on demand
885 SOSChangeTrackerUpdatesManifests trackManifest
;
886 if (isDictionary(newEntry
)) {
887 // Uninflated peer, inflate on first notification.
888 trackManifest
= ^bool(SOSChangeTrackerRef ct
, SOSEngineRef engine
, SOSTransactionRef txn
, SOSDataSourceTransactionSource source
, SOSDataSourceTransactionPhase phase
, SOSManifestRef removals
, SOSManifestRef additions
, CFErrorRef
*error
) {
889 CFErrorRef localError
= NULL
;
890 SOSPeerRef peer
= SOSEngineCopyPeerWithID_locked(engine
, peerID
, &localError
);
893 secerror("%@: peer failed to inflate: %@", peerID
, localError
);
894 CFReleaseSafe(localError
);
897 ok
= SOSPeerDataSourceWillCommit(peer
, source
, removals
, additions
, error
);
903 // Inflated peer, just forward the changes to the peer
904 trackManifest
= ^bool(SOSChangeTrackerRef ct
, SOSEngineRef engine
, SOSTransactionRef txn
, SOSDataSourceTransactionSource source
, SOSDataSourceTransactionPhase phase
, SOSManifestRef removals
, SOSManifestRef additions
, CFErrorRef
*error
) {
905 return SOSPeerDataSourceWillCommit((SOSPeerRef
)newEntry
, source
, removals
, additions
, error
);
908 SOSChangeTrackerUpdatesManifests trackManifestCopy
= Block_copy(trackManifest
);
909 SOSChangeTrackerRegisterManifestUpdate(ct
, trackManifestCopy
);
910 Block_release(trackManifestCopy
);
912 CFDictionarySetValue(newPeerMap
, peerID
, newEntry
);
918 static void SOSEngineReferenceTrustedPeer(SOSEngineRef engine
, SOSPeerMetaRef peerMeta
, CFMutableDictionaryRef newViewNameSet2ChangeTracker
, CFMutableDictionaryRef newPeerMap
, CFMutableArrayRef peerIDs
, CFMutableStringRef desc
) {
919 CFSetRef viewNameSet
= NULL
;
920 CFDataRef keyBag
= NULL
;
921 CFStringRef peerID
= SOSPeerMetaGetComponents(peerMeta
, &viewNameSet
, &keyBag
, NULL
);
922 // We trust peerID so append it to peerIDs
923 CFArrayAppendValue(peerIDs
, peerID
);
924 if (desc
) CFStringAppendPeerIDAndViews(desc
, peerID
, viewNameSet
);
925 // Update the viewNameSet for this peer, to appease tests, default to a viewset of the V0 view.
927 viewNameSet
= SOSViewsGetV0ViewSet();
929 // Always inflate backup peers, since they need to register with their changeTrackers right away.
931 SOSEngineReferenceBackupPeer(engine
, peerID
, viewNameSet
, keyBag
, newViewNameSet2ChangeTracker
, newPeerMap
);
933 SOSEngineReferenceSyncPeer(engine
, peerID
, viewNameSet
, newViewNameSet2ChangeTracker
, newPeerMap
);
937 static CFDataRef
SOSEngineLoadV0KeyBag(SOSEngineRef engine
, CFErrorRef
*error
) {
938 // Return the keybag for the given peerID.
941 kSecAttrAccessGroup ==> CFSTR("com.apple.sbd")
942 kSecAttrAccessible ==> kSecAttrAccessibleWhenUnlocked
943 kSecAttrAccount ==> CFSTR("SecureBackupPublicKeybag")
944 kSecAttrService ==> CFSTR("SecureBackupService")
947 CFMutableDictionaryRef keys
= CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault
,
948 kSecAttrAccessGroup
, CFSTR("com.apple.sbd"),
949 kSecAttrAccount
, CFSTR("SecureBackupPublicKeybag"),
950 kSecAttrService
, CFSTR("SecureBackupService"),
951 kSecAttrAccessible
, kSecAttrAccessibleWhenUnlocked
,
952 kSecAttrSynchronizable
, kCFBooleanTrue
,
955 CFDataRef keybag
= engine
->dataSource
->dsCopyItemDataWithKeys(engine
->dataSource
, keys
, error
);
961 static void SOSEngineReferenceBackupV0Peer(SOSEngineRef engine
, CFMutableDictionaryRef newViewNameSet2ChangeTracker
, CFMutableDictionaryRef newPeerMap
, CFMutableArrayRef newPeerIDs
, CFMutableStringRef desc
) {
962 SOSPeerRef backupPeer
= (SOSPeerRef
)CFDictionaryGetValue(engine
->peerMap
, kSOSViewKeychainV0_tomb
);
963 CFDataRef bag
= NULL
;
964 if (backupPeer
&& CFGetTypeID(backupPeer
) == SOSPeerGetTypeID()) {
965 bag
= SOSPeerGetKeyBag(backupPeer
);
967 CFErrorRef localError
= NULL
;
968 if (!(bag
= SOSEngineLoadV0KeyBag(engine
, &localError
))) {
969 secnotice("engine", "No keybag found for v0 backup peer: %@", localError
);
970 CFReleaseSafe(localError
);
973 SOSEngineReferenceBackupPeer(engine
, kSOSViewKeychainV0_tomb
, SOSViewsGetV0BackupViewSet(), bag
, newViewNameSet2ChangeTracker
, newPeerMap
);
976 static void SOSEngineReferenceTrustedPeers(SOSEngineRef engine
, CFMutableDictionaryRef newViewNameSet2ChangeTracker
, CFMutableDictionaryRef newPeerMap
, CFMutableArrayRef newPeerIDs
, CFArrayRef trustedPeerMetas
, CFMutableStringRef desc
) {
977 // Then update the views for all trusted peers and add them to newPeerMap.
978 if (trustedPeerMetas
!= NULL
&& CFArrayGetCount(trustedPeerMetas
) != 0) {
979 if (desc
) CFStringAppend(desc
, CFSTR(" trusted"));
980 // Remake engine->peerIDs
981 SOSPeerMetaRef peerMeta
;
982 CFArrayForEachC(trustedPeerMetas
, peerMeta
) {
983 SOSEngineReferenceTrustedPeer(engine
, peerMeta
, newViewNameSet2ChangeTracker
, newPeerMap
, newPeerIDs
, desc
);
988 static void SOSEngineReferenceUntrustedPeers(SOSEngineRef engine
, CFMutableDictionaryRef newPeerMap
, CFArrayRef untrustedPeerMetas
, CFMutableStringRef description
) {
989 // Copy any untrustedPeers to newPeerMap as well if we have a state
990 // for them, if not no big deal. We also serialize all the untrustedPeers
991 // since they don't need to be deserializable
992 if (untrustedPeerMetas
!= NULL
&& CFArrayGetCount(untrustedPeerMetas
) != 0) {
993 if (description
) CFStringAppend(description
, CFSTR(" untrusted"));
994 SOSPeerMetaRef peerMeta
;
995 CFArrayForEachC(untrustedPeerMetas
, peerMeta
) {
996 CFSetRef views
= NULL
;
997 CFStringRef peerID
= SOSPeerMetaGetComponents(peerMeta
, &views
, NULL
, NULL
);
998 if (description
) CFStringAppendPeerIDAndViews(description
, peerID
, views
);
999 CFSetRef nviews
= NULL
;
1001 views
= nviews
= CFSetCreate(kCFAllocatorDefault
, NULL
, 0, &kCFTypeSetCallBacks
);
1002 CFTypeRef newEntry
= SOSPeerOrStateSetViewsAndCopyState(CFDictionaryGetValue(engine
->peerMap
, peerID
), views
);
1003 CFReleaseSafe(nviews
);
1005 CFDictionarySetValue(newPeerMap
, peerID
, newEntry
);
1006 CFReleaseSafe(newEntry
);
1012 static void SOSEngineReferenceChangeTrackers(SOSEngineRef engine
, CFArrayRef trustedPeerMetas
, CFArrayRef untrustedPeerMetas
, CFMutableStringRef desc
) {
1013 CFMutableArrayRef newPeerIDs
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
1014 CFMutableDictionaryRef newPeerMap
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1015 CFMutableDictionaryRef newViewNameSet2ChangeTracker
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1018 // We have an engineID => in a circle (with 0 or more peers)
1019 // Ensure we have a v0 backup peer and it's listening for backup bag changes
1020 SOSEngineReferenceBackupV0Peer(engine
, newViewNameSet2ChangeTracker
, newPeerMap
, newPeerIDs
, desc
);
1021 SOSEngineRegisterBackupBagV0Tracker(engine
, newViewNameSet2ChangeTracker
, desc
);
1023 SOSEngineReferenceTrustedPeers(engine
, newViewNameSet2ChangeTracker
, newPeerMap
, newPeerIDs
, trustedPeerMetas
, desc
);
1024 SOSEngineReferenceUntrustedPeers(engine
, newPeerMap
, untrustedPeerMetas
, desc
);
1026 CFAssignRetained(engine
->peerIDs
, newPeerIDs
);
1027 CFAssignRetained(engine
->peerMap
, newPeerMap
);
1028 CFAssignRetained(engine
->viewNameSet2ChangeTracker
, newViewNameSet2ChangeTracker
);
1029 SOSEngineUpdateViewName2ChangeTracker(engine
);
1032 // Return true iff peers or views changed
1033 static bool SOSEngineSetPeers_locked(SOSEngineRef engine
, SOSPeerMetaRef myPeerMeta
, CFArrayRef trustedPeerMetas
, CFArrayRef untrustedPeerMetas
) {
1034 CFErrorRef error
= NULL
;
1035 CFSetRef myViews
= NULL
;
1036 CFDataRef myKeyBag
= NULL
;
1037 CFMutableStringRef desc
= CFStringCreateMutableCopy(kCFAllocatorDefault
, 0, CFSTR("me"));
1038 CFStringRef myPeerID
= myPeerMeta
? SOSPeerMetaGetComponents(myPeerMeta
, &myViews
, &myKeyBag
, &error
) : NULL
;
1039 if (desc
) CFStringAppendPeerIDAndViews(desc
, myPeerID
, myViews
);
1041 CFRetainAssign(engine
->myID
, myPeerID
);
1043 // Remake engine->peerMap from both trusted and untrusted peers
1044 SOSEngineReferenceChangeTrackers(engine
, trustedPeerMetas
, untrustedPeerMetas
, desc
);
1046 secnotice("engine", "%@", desc
);
1047 CFReleaseSafe(desc
);
1051 static void SOSEngineApplyPeerState(SOSEngineRef engine
, CFDictionaryRef peerStateMap
) {
1052 if (peerStateMap
) CFDictionaryForEach(peerStateMap
, ^(const void *peerID
, const void *peerState
) {
1053 CFTypeRef mapEntry
= CFDictionaryGetValue(engine
->peerMap
, peerID
);
1054 if (mapEntry
&& CFGetTypeID(mapEntry
) == SOSPeerGetTypeID()) {
1055 // Update the state of any already inflated peers
1056 SOSPeerRef peer
= (SOSPeerRef
)mapEntry
;
1057 CFErrorRef localError
= NULL
;
1058 if (!SOSPeerSetState(peer
, engine
, peerState
, &localError
)) {
1059 CFStringRef stateHex
= NULL
;
1060 stateHex
= CFDataCopyHexString(peerState
);
1061 secerror("peer: %@: bad state: %@ in engine state: %@", peerID
, localError
, stateHex
);
1062 CFReleaseSafe(stateHex
);
1063 CFReleaseNull(localError
);
1064 // Possibly ask for an ensurePeerRegistration so we have a good list of peers again.
1067 // Just record the state for non inflated peers for now.
1068 CFDictionarySetValue(engine
->peerMap
, peerID
, peerState
);
1073 static void SOSEngineSynthesizePeerMetas(SOSEngineRef engine
, CFMutableArrayRef trustedPeersMetas
, CFMutableArrayRef untrustedPeers
) {
1074 CFSetRef trustedPeerSet
= engine
->peerIDs
? CFSetCreateCopyOfArrayForCFTypes(engine
->peerIDs
) : NULL
;
1075 CFDictionaryForEach(engine
->peerMap
, ^(const void *peerID
, const void *peerState
) {
1076 SOSPeerMetaRef meta
= NULL
;
1077 if (peerState
&& CFGetTypeID(peerState
) == SOSPeerGetTypeID()) {
1078 SOSPeerRef peer
= (SOSPeerRef
)peerState
;
1079 meta
= SOSPeerMetaCreateWithComponents(peerID
, SOSPeerGetViewNameSet(peer
), SOSPeerGetKeyBag(peer
));
1081 // We don't need to add the meta for the backup case, since
1082 // SOSEngineReferenceBackupV0Peer will do the right thing
1083 if (!CFEqualSafe(peerID
, kSOSViewKeychainV0_tomb
)) {
1084 meta
= SOSPeerMetaCreateWithState(peerID
, peerState
);
1087 // Any peer in peerStateMap that is not in trustedPeers is an untrustedPeer unless it's the v0 backup peer
1088 if ((trustedPeerSet
&& CFSetContainsValue(trustedPeerSet
, peerID
)) || CFEqualSafe(peerID
, kSOSViewKeychainV0_tomb
)) {
1090 CFArrayAppendValue(trustedPeersMetas
, meta
);
1093 CFArrayAppendValue(untrustedPeers
, peerID
);
1095 CFReleaseNull(meta
);
1097 CFReleaseNull(trustedPeerSet
);
1100 static void SOSEngineSetBackupBag(SOSEngineRef engine
, SOSObjectRef bagItem
) {
1101 CFMutableStringRef desc
= NULL
;
1102 SOSPeerRef backupPeer
= SOSEngineCopyPeerWithID_locked(engine
, kSOSViewKeychainV0_tomb
, NULL
);
1103 CFDataRef keybag
= NULL
;
1105 keybag
= SecDbItemGetValue((SecDbItemRef
)bagItem
, &v6v_Data
, NULL
);
1108 // Since SOSPeerSetKeyBag() doesn't notify on the edge from NULL->initial keybag, since
1109 // that is the right behaviour for non v0 backup peers, we need to do it here for the v0 peer.
1110 bool hadBag
= SOSPeerGetKeyBag(backupPeer
);
1111 SOSPeerSetKeyBag(backupPeer
, keybag
);
1113 SOSPeerKeyBagDidChange(backupPeer
);
1115 CFReleaseSafe(backupPeer
);
1117 CFMutableArrayRef untrustedPeerMetas
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
1118 CFMutableArrayRef trustedPeersMetas
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
1119 SOSEngineSynthesizePeerMetas(engine
, trustedPeersMetas
, untrustedPeerMetas
);
1120 SOSEngineReferenceChangeTrackers(engine
, trustedPeersMetas
, untrustedPeerMetas
, desc
);
1121 CFReleaseSafe(trustedPeersMetas
);
1122 CFReleaseSafe(untrustedPeerMetas
);
1125 #define SECONDS_PER_DAY (86400.0)
1127 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
1128 #define TRACE_INTERVAL (7 * SECONDS_PER_DAY)
1129 #elif (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
1130 #define TRACE_INTERVAL (1 * SECONDS_PER_DAY)
1133 #ifdef TRACE_INTERVAL
1134 static void SOSEngineCloudKeychainTrace(SOSEngineRef engine
, CFAbsoluteTime now
) {
1135 CFAssignRetained(engine
->lastTraceDate
, CFDateCreate(kCFAllocatorDefault
, now
));
1136 CFIndex num_peers
= engine
->peerIDs
? 1 + CFArrayGetCount(engine
->peerIDs
) : 1;
1137 SOSManifestRef manifest
= SOSEngineCopyManifestWithViewNameSet_locked(engine
, SOSViewsGetV0ViewSet(), NULL
);
1139 manifest
= SOSDataSourceCopyManifestWithViewNameSet(engine
->dataSource
, SOSViewsGetV0ViewSet(), NULL
);
1140 size_t num_items
= SOSManifestGetCount(manifest
);
1141 CFReleaseSafe(manifest
);
1143 struct _SecServerKeyStats genpStats
= { };
1144 struct _SecServerKeyStats inetStats
= { };
1145 struct _SecServerKeyStats keysStats
= { };
1147 _SecServerGetKeyStats(&genp_class
, &genpStats
);
1148 _SecServerGetKeyStats(&inet_class
, &inetStats
);
1149 _SecServerGetKeyStats(&keys_class
, &keysStats
);
1151 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), ^{
1152 CloudKeychainTrace(num_peers
, num_items
, &genpStats
, &inetStats
, &keysStats
);
1157 static void SOSEngineCloudKeychainTraceIfNeeded(SOSEngineRef engine
) {
1158 #ifdef TRACE_INTERVAL
1161 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
1162 if (engine
->lastTraceDate
) {
1163 CFAbsoluteTime lastTraceTime
= CFDateGetAbsoluteTime(engine
->lastTraceDate
);
1164 if ((now
- lastTraceTime
) >= TRACE_INTERVAL
) {
1165 SOSEngineCloudKeychainTrace(engine
, now
);
1168 SOSEngineCloudKeychainTrace(engine
, now
);
1173 // Restore the in-memory state of engine from saved state loaded from the db
1174 static bool SOSEngineSetState(SOSEngineRef engine
, CFDataRef state
, CFErrorRef
*error
) {
1175 __block
bool ok
= true;
1177 CFMutableDictionaryRef dict
= NULL
;
1178 const uint8_t *der
= CFDataGetBytePtr(state
);
1179 const uint8_t *der_end
= der
+ CFDataGetLength(state
);
1180 ok
= der
= der_decode_dictionary(kCFAllocatorDefault
, kCFPropertyListMutableContainers
, (CFDictionaryRef
*)&dict
, error
, der
, der_end
);
1181 if (der
&& der
!= der_end
) {
1182 ok
= SOSErrorCreate(kSOSErrorDecodeFailure
, error
, NULL
, CFSTR("trailing %td bytes at end of state"), der_end
- der
);
1184 CFReleaseNull(engine
->manifestCache
);
1185 CFMutableDictionaryRef mfc
= (CFMutableDictionaryRef
)CFDictionaryGetValue(dict
, kSOSEngineManifestCacheKey
);
1187 engine
->manifestCache
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1188 CFDictionaryForEach(mfc
, ^(const void *key
, const void *value
) {
1189 CFDataRef data
= (CFDataRef
)value
;
1191 SOSManifestRef mf
= SOSManifestCreateWithData(data
, NULL
);
1193 CFDictionarySetValue(engine
->manifestCache
, key
, mf
);
1198 CFMutableArrayRef untrustedPeers
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
1199 CFMutableArrayRef trustedPeersMetas
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
1200 CFRetainAssign(engine
->peerIDs
, asArray(CFDictionaryGetValue(dict
, kSOSEnginePeerIDsKey
), NULL
));
1201 CFRetainAssign(engine
->lastTraceDate
, asDate(CFDictionaryGetValue(dict
, kSOSEngineTraceDateKey
), NULL
));
1202 SOSEngineApplyPeerState(engine
, asDictionary(CFDictionaryGetValue(dict
, kSOSEnginePeerStateKey
), NULL
));
1203 SOSEngineSynthesizePeerMetas(engine
, trustedPeersMetas
, untrustedPeers
);
1204 SOSEngineSetPeers_locked(engine
, (CFStringRef
)CFDictionaryGetValue(dict
, kSOSEngineIDKey
),
1205 trustedPeersMetas
, untrustedPeers
);
1206 CFReleaseNull(trustedPeersMetas
);
1207 CFReleaseNull(untrustedPeers
);
1209 CFReleaseNull(dict
);
1211 secnotice("engine", "%@", engine
);
1215 static bool SOSEngineLoad(SOSEngineRef engine
, CFErrorRef
*error
) {
1216 CFDataRef state
= SOSDataSourceCopyStateWithKey(engine
->dataSource
, kSOSEngineState
, kSecAttrAccessibleAlways
, error
);
1217 bool ok
= state
&& SOSEngineSetState(engine
, state
, error
);
1218 CFReleaseSafe(state
);
1222 static bool SOSEngineCircleChanged_locked(SOSEngineRef engine
, SOSPeerMetaRef myPeerMeta
, CFArrayRef trustedPeers
, CFArrayRef untrustedPeers
) {
1223 // Sanity check params
1224 // SOSEngineCircleChanged_sanitycheck(engine, myPeerID, trustedPeers, untrustedPeers);
1226 // Transform from SOSPeerInfoRefs to CFDictionaries with the info we want per peer.
1227 // Or, Tell the real SOSPeerRef what the SOSPeerInfoRef is and have it copy out the data it needs.
1228 bool peersOrViewsChanged
= SOSEngineSetPeers_locked(engine
, myPeerMeta
, trustedPeers
, untrustedPeers
);
1230 // Run though all peers and only cache manifests for peers we still have
1231 CFErrorRef localError
= NULL
;
1232 if (!SOSEngineGCPeerState_locked(engine
, &localError
)) {
1233 secerror("SOSEngineGCPeerState_locked failed: %@", localError
);
1234 CFReleaseNull(localError
);
1236 return peersOrViewsChanged
;
1239 // Initialize the engine if a load fails. Basically this is our first time setup
1240 static bool SOSEngineInit(SOSEngineRef engine
, CFErrorRef
*error
) {
1242 secnotice("engine", "new engine for datasource named %@", SOSDataSourceGetName(engine
->dataSource
));
1243 CFAssignRetained(engine
->peerMap
, CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
));
1244 CFAssignRetained(engine
->viewNameSet2ChangeTracker
, CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
));
1245 CFAssignRetained(engine
->viewName2ChangeTracker
, CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
));
1246 CFReleaseNull(engine
->manifestCache
);
1247 CFReleaseNull(engine
->peerIDs
);
1248 // TODO: We shouldn't need to load the backup bag if there was no engine
1249 // state (load failed), since that means there was no circle nor were we an applicant.
1251 // Set up change trackers so we know when a backup peer needs to be created?
1252 // no, since myID is not set, we are not in a circle, so no need to back up
1253 SOSEngineSetPeers_locked(engine
, NULL
, NULL
, NULL
);
1257 // Called by our DataSource in its constructor
1258 SOSEngineRef
SOSEngineCreate(SOSDataSourceRef dataSource
, CFErrorRef
*error
) {
1259 SOSEngineRef engine
= NULL
;
1260 engine
= CFTypeAllocate(SOSEngine
, struct __OpaqueSOSEngine
, kCFAllocatorDefault
);
1261 engine
->dataSource
= dataSource
;
1262 engine
->queue
= dispatch_queue_create("engine", DISPATCH_QUEUE_SERIAL
);
1264 engine
->peerMap
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1265 engine
->viewNameSet2ChangeTracker
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1266 engine
->viewName2ChangeTracker
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1267 //engine->syncCompleteQueue = NULL;
1268 engine
->syncCompleteListeners
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1269 CFErrorRef engineError
= NULL
;
1270 if (!SOSEngineLoad(engine
, &engineError
)) {
1271 secwarning("engine failed load state starting with nothing %@", engineError
);
1272 CFReleaseNull(engineError
);
1273 if (!SOSEngineInit(engine
, error
)) {
1274 secerror("engine failed to initialze %@ giving up", error
? *error
: NULL
);
1277 // Successfully loaded engine state, let's trace if we haven't in a while
1278 SOSEngineCloudKeychainTraceIfNeeded(engine
);
1280 SOSEngineSetNotifyPhaseBlock(engine
);
1284 // --- Called from off the queue, need to move to on the queue
1286 static void SOSEngineDoOnQueue(SOSEngineRef engine
, dispatch_block_t action
)
1288 dispatch_sync(engine
->queue
, action
);
1291 static bool SOSEngineDoTxnOnQueue(SOSEngineRef engine
, CFErrorRef
*error
, void(^transaction
)(SOSTransactionRef txn
, bool *commit
))
1293 return SOSDataSourceWith(engine
->dataSource
, error
, ^(SOSTransactionRef txn
, bool *commit
) {
1294 SOSEngineDoOnQueue(engine
, ^{ transaction(txn
, commit
); });
1299 // MARK: SOSEngine API
1302 void SOSEngineDispose(SOSEngineRef engine
) {
1303 // NOOP Engines stick around forever to monitor dataSource changes.
1304 engine
->dataSource
= NULL
;
1307 void SOSEngineForEachPeer(SOSEngineRef engine
, void (^with
)(SOSPeerRef peer
)) {
1308 SOSEngineDoOnQueue(engine
, ^{
1309 SOSEngineForEachPeer_locked(engine
, with
);
1313 static void SOSEngineForEachBackupPeer(SOSEngineRef engine
, void (^with
)(SOSPeerRef peer
)) {
1314 SOSEngineDoOnQueue(engine
, ^{
1315 SOSEngineForEachBackupPeer_locked(engine
, with
);
1320 /* Handle incoming message from peer p. Return false if there was an error, true otherwise. */
1321 bool SOSEngineHandleMessage_locked(SOSEngineRef engine
, CFStringRef peerID
, SOSMessageRef message
,
1322 SOSTransactionRef txn
, bool *commit
, bool *somethingChanged
, CFErrorRef
*error
) {
1323 SOSPeerRef peer
= SOSEngineCopyPeerWithID_locked(engine
, peerID
, error
);
1324 if (!peer
) return false;
1325 CFStringRef peerDesc
= NULL
;
1326 SOSManifestRef localManifest
= NULL
;
1327 SOSManifestRef allAdditions
= NULL
;
1328 SOSManifestRef unwanted
= NULL
;
1329 SOSManifestRef confirmed
= NULL
;
1330 SOSManifestRef base
= NULL
;
1331 SOSManifestRef confirmedRemovals
= NULL
, confirmedAdditions
= NULL
;
1332 __block
struct SOSDigestVector receivedObjects
= SOSDigestVectorInit
;
1333 __block
struct SOSDigestVector unwantedObjects
= SOSDigestVectorInit
;
1335 // Check for unknown criticial extensions in the message, and handle
1336 // any other extensions we support
1337 __block
bool ok
= true;
1338 CFMutableArrayRef changes
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
1340 require_action_quiet(peer
, exit
, ok
= SOSErrorCreate(errSecParam
, error
, NULL
, CFSTR("Couldn't create peer with Engine for %@"), peerID
));
1341 peerDesc
= CFCopyDescription(peer
);
1343 SOSMessageWithExtensions(message
, true, ^(CFDataRef oid
, bool isCritical
, CFDataRef extension
, bool *stop
) {
1344 // OMFG a Critical extension what shall I do!
1345 ok
= SOSErrorCreate(kSOSErrorNotReady
, error
, NULL
, CFSTR("Unknown criticial extension in peer message"));
1348 require_quiet(ok
, exit
);
1350 // Merge Objects from the message into our DataSource.
1351 // Should we move the transaction to the SOSAccount level?
1352 // TODO: Filter incoming objects
1353 //if (!SOSDataSourceForEachObjectInViewSet(engine->dataSource, pendingObjects, SOSPeerGetViewNameSet(peer), error, ^void(CFDataRef key, SOSObjectRef object, bool *stop) {
1354 require_quiet(ok
&= SOSMessageWithSOSObjects(message
, engine
->dataSource
, error
, ^(SOSObjectRef peersObject
, bool *stop
) {
1355 CFDataRef digest
= SOSObjectCopyDigest(engine
->dataSource
, peersObject
, error
);
1359 secerror("%@ peer sent bad object: %@, rolling back changes", SOSPeerGetID(peer
), error
? *error
: NULL
);
1362 SOSDigestVectorAppend(&receivedObjects
, CFDataGetBytePtr(digest
));
1363 SOSObjectRef mergedObject
= NULL
;
1364 SOSMergeResult mr
= SOSDataSourceMergeObject(engine
->dataSource
, txn
, peersObject
, &mergedObject
, error
);
1365 // TODO: If the mr is kSOSMergeLocalObject most of the time (or all of the time),
1366 // consider asking the peer to stop sending us objects, and send it objects instead.
1367 ok
&= (mr
!= kSOSMergeFailure
);
1371 // 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.
1372 secerror("%@ SOSDataSourceMergeObject failed %@ rolling back changes", SOSPeerGetID(peer
), error
? *error
: NULL
);
1373 } else if (mr
==kSOSMergePeersObject
|| mr
==kSOSMergeCreatedObject
) {
1374 *somethingChanged
= true;
1376 // mr == kSOSMergeLocalObject
1377 if (!CFEqual(mergedObject
, peersObject
)) {
1378 // Record this object as something we don't want peer to ever send us again. By adding it to
1379 // unwantedObjects we'll falsely claim to peer we have it until they tell us they don't have it anymore.
1380 SOSDigestVectorAppend(&unwantedObjects
, CFDataGetBytePtr(digest
));
1382 // Ensure localObject is in local manifest (possible corruption) by posting an update when we are done.
1383 SOSChangesAppendAdd(changes
, mergedObject
);
1385 CFReleaseSafe(mergedObject
);
1386 CFReleaseSafe(digest
);
1388 struct SOSDigestVector dvunion
= SOSDigestVectorInit
;
1389 SOSDigestVectorSort(&receivedObjects
);
1390 SOSDigestVectorUnionSorted(SOSManifestGetDigestVector(SOSMessageGetAdditions(message
)), &receivedObjects
, &dvunion
);
1391 allAdditions
= SOSManifestCreateWithDigestVector(&dvunion
, error
);
1392 SOSDigestVectorFree(&receivedObjects
);
1393 SOSDigestVectorFree(&dvunion
);
1395 unwanted
= SOSManifestCreateWithDigestVector(&unwantedObjects
, error
);
1396 SOSDigestVectorFree(&unwantedObjects
);
1398 if (CFArrayGetCount(changes
)) {
1399 // NOTE: This is always notifiying of all additions that end up choosing local, which should be rare, since we shouldn't
1400 // be receiving objects we already have. When we do we tell ourselves to add them all again so our views will properly
1401 // reflect that we actually have these objects if we didn't already.
1403 // Ensure any objects that we received and have locally already are actually in our local manifest
1404 SOSEngineUpdateChanges_locked(engine
, txn
, kSOSDataSourceTransactionDidCommit
, kSOSDataSourceSOSTransaction
, changes
, error
);
1406 CFReleaseSafe(changes
);
1408 // ---- Don't use local or peer manifests from above this line,
1409 // ---- since commiting the SOSDataSourceWith transaction might change them ---
1411 // Take a snapshot of our dataSource's local manifest.
1412 require_quiet(ok
= localManifest
= SOSEngineCopyLocalPeerManifest_locked(engine
, peer
, error
), exit
);
1414 CFDataRef baseDigest
= SOSMessageGetBaseDigest(message
);
1415 CFDataRef proposedDigest
= SOSMessageGetProposedDigest(message
);
1418 // I believe this is no longer needed now that we have eliminated extra,
1419 // since this is handled below once we get a confirmed manifest from our
1422 // If we just received a L00 reset pendingObjects to localManifest
1423 if (!baseDigest
&& !proposedDigest
) {
1424 // TODO: This is definitely busted for v0 peers since v0 peers always send a
1425 // L00 (ManifestDigestMessage as an ack) whereas in v2 this is a protocol restart
1426 // However if we can still find a confirmed manifest below we probably
1427 // don't want to do this even for v2.
1428 // Also I don't think we will ever send a ManifestMessage right now in
1429 // response to a ManifestDigest
1430 SOSPeerSetPendingObjects(peer
, localManifest
);
1431 secnoticeq("engine", "%@:%@ SOSPeerSetPendingObjects: %@", engine
->myID
, peerID
, localManifest
);
1435 base
= SOSPeerCopyManifestForDigest(peer
, baseDigest
);
1436 confirmed
= SOSPeerCopyManifestForDigest(peer
, SOSMessageGetSenderDigest(message
));
1438 if (SOSManifestGetCount(SOSMessageGetRemovals(message
)) || SOSManifestGetCount(allAdditions
)) {
1439 if (base
|| !baseDigest
) {
1440 confirmed
= SOSManifestCreateWithPatch(base
, SOSMessageGetRemovals(message
), allAdditions
, error
);
1443 confirmedRemovals
= CFRetainSafe(SOSMessageGetRemovals(message
));
1444 confirmedAdditions
= CFRetainSafe(allAdditions
);
1446 } else if (baseDigest
) {
1447 confirmed
= CFRetainSafe(base
);
1448 secerror("%@:%@ Protocol error send L00 - figure out later base: %@", engine
->myID
, peerID
, base
);
1451 secnoticeq("engine", "%@:%@ confirmed: %@ base: %@", engine
->myID
, peerID
, confirmed
, base
);
1453 ok
&= SOSManifestDiff(SOSPeerGetConfirmedManifest(peer
), confirmed
, &confirmedRemovals
, &confirmedAdditions
, error
);
1454 if (SOSManifestGetCount(SOSMessageGetRemovals(message
)))
1455 CFAssignRetained(confirmedRemovals
, SOSManifestCreateUnion(confirmedRemovals
, SOSMessageGetRemovals(message
), error
));
1457 if (SOSManifestGetCount(confirmedRemovals
) || SOSManifestGetCount(confirmedAdditions
) || SOSManifestGetCount(unwanted
))
1458 ok
&= SOSPeerDidReceiveRemovalsAndAdditions(peer
, confirmedRemovals
, confirmedAdditions
, unwanted
, localManifest
, error
);
1459 // TODO: We should probably remove the if below and always call SOSPeerSetConfirmedManifest,
1460 // since having a NULL confirmed will force us to send a manifest message to get in sync again.
1462 SOSPeerSetConfirmedManifest(peer
, confirmed
);
1463 else if (SOSPeerGetConfirmedManifest(peer
)) {
1464 secnoticeq("engine", "%@:%@ unable to find confirmed in %@, sync protocol reset", engine
->myID
, peer
, message
);
1466 SOSPeerSetConfirmedManifest(peer
, NULL
);
1467 //SOSPeerSetSendObjects(peer, true);
1470 // ---- SendObjects and extra->pendingObjects promotion dance ----
1472 // The first block of code below sets peer.sendObjects to true when we receive a L00 and the second block
1473 // moves extra to pendingObjects once we receive a confirmed manifest in or after the L00.
1474 if (!baseDigest
&& !proposedDigest
) {
1475 SOSPeerSetSendObjects(peer
, true);
1478 if (0 /* confirmed && SOSPeerSendObjects(peer) */) {
1479 SOSManifestRef allExtra
= NULL
;
1480 ok
&= SOSManifestDiff(confirmed
, localManifest
, NULL
, &allExtra
, error
);
1481 secnoticeq("engine", "%@:%@ confirmed %@ (re)setting O:%@", engine
->myID
, SOSPeerGetID(peer
), confirmed
, allExtra
);
1482 SOSPeerSetPendingObjects(peer
, allExtra
);
1483 CFReleaseSafe(allExtra
);
1487 secnotice("engine", "recv %@:%@ %@", engine
->myID
, SOSPeerGetID(peer
), message
);
1488 secnotice("peer", "recv %@ -> %@", peerDesc
, peer
);
1490 CFReleaseNull(base
);
1491 CFReleaseSafe(confirmed
);
1492 CFReleaseSafe(localManifest
);
1493 CFReleaseSafe(peerDesc
);
1494 CFReleaseSafe(allAdditions
);
1495 CFReleaseSafe(unwanted
);
1496 CFReleaseSafe(confirmedRemovals
);
1497 CFReleaseSafe(confirmedAdditions
);
1498 CFReleaseSafe(peer
);
1502 static CFDataRef
SOSEngineCopyObjectDER(SOSEngineRef engine
, SOSObjectRef object
, CFErrorRef
*error
) {
1503 CFDataRef der
= NULL
;
1504 CFDictionaryRef plist
= SOSObjectCopyPropertyList(engine
->dataSource
, object
, error
);
1506 der
= CFPropertyListCreateDERData(kCFAllocatorDefault
, plist
, error
);
1515 +-----------------------------+_
1519 _+=============================+ } L
1523 / +==============================
1526 \ | M +------------|
1530 \_+-------------+---------------+
1542 static bool SOSAppendRemoveToPatch(CFTypeRef remove
, CFMutableDictionaryRef patch
, CFErrorRef
*error
) {
1545 static bool SOSAppendAddToPatch(CFTypeRef add
, CFMutableDictionaryRef patch
, CFErrorRef
*error
) {
1548 static bool SOSAppendDiffToPatch(CFTypeRef left
, CFTypeRef right
, CFMutableDictionaryRef patch
, CFErrorRef
*error
) {
1550 if (!left
&& right
) {
1551 SOSAppendAddToPatch(right
, patch
, error
);
1552 } else if (left
&& !right
) {
1553 SOSAppendRemoveToPatch(left
, patch
, error
);
1554 } else if (left
&& right
) {
1555 CFTypeID ltype
= CFGetTypeID(left
);
1556 CFTypeID rtype
= CFGetTypeID(right
);
1557 if (ltype
== rtype
) {
1558 if (CFArrayGetTypeID() == ltype
) {
1559 ok
= SecError(errSecParam
, error
, CFSTR("unsupported type array"), ltype
);
1560 } else if (CFBooleanGetTypeID
== ltype
) {
1561 ok
= SecError(errSecParam
, error
, CFSTR("unsupported type boolean"), ltype
);
1562 } else if (CFDataGetTypeID
== ltype
) {
1563 ok
= SecError(errSecParam
, error
, CFSTR("unsupported type data"), ltype
);
1564 } else if (CFDictionaryGetTypeID
== ltype
) {
1565 __block CFMutableDictionaryRef leftnotright
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1566 __block CFMutableDictionaryRef rightnotleft
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, right
);
1568 CFDictionaryForEach(left
, ^(const void *key
, const void *lvalue
) {
1569 const void *rvalue
= NULL
;
1570 if (CFDictionaryGetValueIfPresent(right
, key
, &rvalue
)) {
1571 CFDictionaryRemoveValue(rightnotleft
, key
);
1573 CFMutableDictionaryRef subpatch
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
);
1574 CFDictionaryAddValue(patch
, key
, subpatch
);
1575 SOSAppendDiffToPatch(lvalue
, rvalue
, subpatch
, error
);
1576 CFReleaseSafe(subpatch
);
1578 CFDictionaryAddValue(leftnotright
, key
, lvalue
);
1581 // Proccess leftnotright and rightnotleft
1582 CFReleaseSafe(leftnotright
);
1583 CFReleaseSafe(rightnotleft
);
1584 } else if (SOSManifestGetTypeID
== ltype
) {
1585 SOSManifestRef removed
= NULL
, added
= NULL
;
1586 ok
&= SOSManifestDiff(left
, right
, &removed
, &added
, error
);
1587 if (SOSManifestGetCount(removed
) || SOSManifestGetCount(added
)) {
1588 SOSAppendDiffToPatch(lvalue
, rvalue
, subpatch
, error
);
1589 CFStringAppend(, <#CFStringRef appendedString#>)
1591 CFReleaseSafe(removed
);
1592 CFReleaseSafe(added
);
1593 } else if (CFNumberGetTypeID
== ltype
) {
1594 ok
= SecError(errSecParam
, error
, CFSTR("unsupported type number"), ltype
);
1595 } else if (CFSetGetTypeID
== ltype
) {
1596 ok
= SecError(errSecParam
, error
, CFSTR("unsupported type set"), ltype
);
1597 } else if (CFStringGetTypeID
== ltype
) {
1598 ok
= SecError(errSecParam
, error
, CFSTR("unsupported type string"), ltype
);
1600 ok
= SecError(errSecParam
, error
, CFSTR("unknown type %lu"), ltype
);
1603 } else if (!left
&& !right
) {
1609 static __unused
bool SOSEngineCheckPeerIntegrity(SOSEngineRef engine
, SOSPeerRef peer
, CFErrorRef
*error
) {
1611 //static CFMutableDictionaryRef p2amtu;
1612 if (!engine
->p2amtu
)
1613 engine
->p2amtu
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1614 CFDictionaryRef amtu
= CFDictionaryGetValue(engine
->p2amtu
, SOSPeerGetID(peer
));
1618 SOSManifestRef L
= SOSEngineCopyLocalPeerManifest_locked(engine
, peer
, error
);
1619 SOSManifestRef T
= SOSPeerGetPendingObjects(peer
);
1620 SOSManifestRef C
= SOSPeerGetConfirmedManifest(peer
);
1621 SOSManifestRef U
= SOSPeerGetUnwantedManifest(peer
);
1624 SOSManifestRef CunionU
= SOSManifestCreateUnion(C
, U
, error
);
1625 SOSManifestRef S
= SOSManifestCreateIntersection(L
, CunionU
, error
);
1627 SOSManifestRef AunionT
= NULL
, MunionU
= NULL
;
1628 SOSManifestDiff(L
, C
, &AunionT
, &MunionU
, error
);
1630 SOSManifestRef A
= SOSManifestCreateComplement(T
, AunionT
, error
);
1631 SOSManifestRef M
= SOSManifestCreateComplement(U
, MunionU
, error
);
1633 SOSManifestRef SunionAunionT
= SOSManifestCreateUnion(S
, AunionT
, error
);
1634 SOSManifestRef SunionMunionU
= SOSManifestCreateUnion(S
, MunionU
, error
);
1636 SOSManifestRef AintersectM
= SOSManifestCreateIntersection(A
, M
, error
);
1637 SOSManifestRef AintersectS
= SOSManifestCreateIntersection(A
, S
, error
);
1638 SOSManifestRef AintersectT
= SOSManifestCreateIntersection(A
, T
, error
);
1639 SOSManifestRef AintersectU
= SOSManifestCreateIntersection(A
, U
, error
);
1640 SOSManifestRef MintersectS
= SOSManifestCreateIntersection(M
, S
, error
);
1641 SOSManifestRef MintersectT
= SOSManifestCreateIntersection(M
, T
, error
);
1642 SOSManifestRef MintersectU
= SOSManifestCreateIntersection(M
, U
, error
);
1643 SOSManifestRef SintersectT
= SOSManifestCreateIntersection(S
, T
, error
);
1644 SOSManifestRef SintersectU
= SOSManifestCreateIntersection(S
, U
, error
);
1645 SOSManifestRef TintersectU
= SOSManifestCreateIntersection(T
, U
, error
);
1648 CFDictionaryRef newAmtu
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, CFSTR("A"), A
, CFSTR("M"), M
, CFSTR("T"), T
, CFSTR("U") U
, NULL
);
1649 CFDictionarySetValue(engine
->p2amtu
, SOSPeerGetID(peer
), newAmtu
);
1650 CFMutableStringRef amtuChanges
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
1651 SOSAppendDiffToString(amtu
, newAmtu
, amtuChanges
);
1652 secnotice("engine", "%@: %@", SOSPeerGetID(peer
), amtuChanges
);
1655 #define SOSASSERT(e) (__builtin_expect(!(e), 0) ? secnotice("engine", "state-assertion %s", #e), assert(e) : (void)0)
1657 SOSASSERT(L
? CFEqual(L
, SunionAunionT
) : SOSManifestGetCount(SunionAunionT
) == 0);
1658 SOSASSERT(C
? CFEqual(C
, SunionMunionU
) : SOSManifestGetCount(SunionMunionU
) == 0);
1660 SOSASSERT(SOSManifestGetCount(AintersectM
) == 0);
1661 SOSASSERT(SOSManifestGetCount(AintersectS
) == 0);
1662 SOSASSERT(SOSManifestGetCount(AintersectT
) == 0);
1663 SOSASSERT(SOSManifestGetCount(AintersectU
) == 0);
1664 SOSASSERT(SOSManifestGetCount(MintersectS
) == 0);
1665 SOSASSERT(SOSManifestGetCount(MintersectT
) == 0);
1666 SOSASSERT(SOSManifestGetCount(MintersectU
) == 0);
1667 SOSASSERT(SOSManifestGetCount(SintersectT
) == 0);
1668 SOSASSERT(SOSManifestGetCount(SintersectU
) == 0);
1669 SOSASSERT(SOSManifestGetCount(TintersectU
) == 0);
1671 CFReleaseSafe(AintersectM
);
1672 CFReleaseSafe(AintersectS
);
1673 CFReleaseSafe(AintersectT
);
1674 CFReleaseSafe(AintersectU
);
1675 CFReleaseSafe(MintersectS
);
1676 CFReleaseSafe(MintersectT
);
1677 CFReleaseSafe(MintersectU
);
1678 CFReleaseSafe(SintersectT
);
1679 CFReleaseSafe(SintersectU
);
1680 CFReleaseSafe(TintersectU
);
1682 CFReleaseSafe(AunionT
);
1683 CFReleaseSafe(MunionU
);
1689 //CFReleaseSafe(T); // Get
1690 //CFReleaseSafe(U); // Get
1691 //CFReleaseSafe(C); // Get
1696 void SOSEngineSetSyncCompleteListener(SOSEngineRef engine
, CFStringRef peerID
, dispatch_block_t notify_block
) {
1697 dispatch_block_t copy
= Block_copy(notify_block
);
1698 SOSEngineDoOnQueue(engine
, ^{
1700 CFDictionarySetValue(engine
->syncCompleteListeners
, peerID
, copy
);
1702 CFDictionaryRemoveValue(engine
->syncCompleteListeners
, peerID
);
1705 CFReleaseNull(copy
);
1708 void SOSEngineSetSyncCompleteListenerQueue(SOSEngineRef engine
, dispatch_queue_t notify_queue
) {
1709 SOSEngineDoOnQueue(engine
, ^{
1710 engine
->syncCompleteQueue
= notify_queue
;
1714 static void SOSEngineCompletedSyncWithPeer(SOSEngineRef engine
, SOSPeerRef peer
) {
1715 dispatch_block_t notify
= CFDictionaryGetValue(engine
->syncCompleteListeners
, SOSPeerGetID(peer
));
1716 if (notify
&& engine
->syncCompleteQueue
)
1717 dispatch_async(engine
->syncCompleteQueue
, notify
);
1719 // Delete dictionary entry?
1723 CFDataRef
SOSEngineCreateMessage_locked(SOSEngineRef engine
, SOSPeerRef peer
,
1724 CFErrorRef
*error
, SOSEnginePeerMessageSentBlock
*sent
) {
1725 SOSManifestRef local
= SOSEngineCopyLocalPeerManifest_locked(engine
, peer
, error
);
1726 __block SOSMessageRef message
= SOSMessageCreate(kCFAllocatorDefault
, SOSPeerGetMessageVersion(peer
), error
);
1727 SOSManifestRef confirmed
= SOSPeerGetConfirmedManifest(peer
);
1728 SOSManifestRef pendingObjects
= SOSPeerGetPendingObjects(peer
);
1729 SOSManifestRef objectsSent
= NULL
;
1730 SOSManifestRef proposed
= NULL
;
1731 SOSManifestRef allMissing
= NULL
;
1732 SOSManifestRef allExtra
= NULL
;
1733 SOSManifestRef extra
= NULL
;
1734 SOSManifestRef excessPending
= NULL
;
1735 SOSManifestRef missing
= NULL
;
1736 SOSManifestRef unwanted
= SOSPeerGetUnwantedManifest(peer
);
1737 SOSManifestRef excessUnwanted
= NULL
;
1738 CFDataRef result
= NULL
;
1740 // Given (C, L, T, U) compute (T, U, M, A)
1744 // U \ (C \ L) => EU
1745 // T \ (L \ C) => ET
1746 // And assert that both EU and ET are empty and if not remove them from U and T respectively
1747 SOSManifestDiff(confirmed
, local
, &allMissing
, &allExtra
, error
);
1748 SOSManifestDiff(allExtra
, pendingObjects
, &extra
, &excessPending
, error
);
1749 if (SOSManifestGetCount(excessPending
)) {
1750 // T \ (L \ C) => excessPending (items both in L and C or in neither that are still pending)
1751 // Can only happen if a member of T was removed from L without us having a chance to update T
1752 secerror("%@ ASSERTION FAILURE purging excess pendingObjects: %@", peer
, excessPending
);
1753 SOSManifestRef newPendingObjects
= SOSManifestCreateComplement(excessPending
, pendingObjects
, error
);
1754 SOSPeerSetPendingObjects(peer
, newPendingObjects
);
1755 CFReleaseSafe(newPendingObjects
);
1756 pendingObjects
= SOSPeerGetPendingObjects(peer
);
1758 SOSManifestDiff(allMissing
, unwanted
, &missing
, &excessUnwanted
, error
);
1759 if (SOSManifestGetCount(excessUnwanted
)) {
1760 // U \ (C \ L) => excessUnwanted (items both in L and C or in neither that are still unwanted)
1761 // Can only happen if a member of U was added to L without us having a chance to update U.
1762 // Since U only contains items the conflict resolver rejected, this implies L somehow got rolled back
1763 // The other option (and more likely) is a member of U was removed from C and not from U.
1764 secerror("%@ ASSERTION FAILURE purging excess unwanted: %@", peer
, excessUnwanted
);
1765 SOSManifestRef newUnwanted
= SOSManifestCreateComplement(excessUnwanted
, unwanted
, error
);
1766 SOSPeerSetUnwantedManifest(peer
, newUnwanted
);
1767 CFReleaseSafe(newUnwanted
);
1768 unwanted
= SOSPeerGetUnwantedManifest(peer
);
1771 CFReleaseNull(allExtra
);
1772 CFReleaseNull(excessPending
);
1773 CFReleaseNull(allMissing
);
1774 CFReleaseNull(excessUnwanted
);
1776 secnoticeq("engine", "%@:%@: send state for peer [%s%s%s][%s%s] P:%zu, E:%zu, M:%zu U:%zu", engine
->myID
, SOSPeerGetID(peer
),
1778 confirmed
? "C":"0",
1779 pendingObjects
? "P":"0",
1780 SOSPeerSendObjects(peer
) ? "O":"o",
1781 SOSPeerMustSendMessage(peer
) ? "S":"s",
1782 SOSManifestGetCount(pendingObjects
),
1783 SOSManifestGetCount(extra
),
1784 SOSManifestGetCount(missing
),
1785 SOSManifestGetCount(unwanted
)
1789 // TODO: Because of not letting things terminate while we have extra left
1790 // we might send objects when we didn't need to, but there is always an
1791 // extra roundtrip required for objects that we assume the other peer
1792 // should have already.
1793 // TODO: If there are extra objects left, calling this function is not
1794 // idempotent we should check if pending is what we are about to send and not send anything in this case.
1795 if (SOSManifestGetCount(pendingObjects
) == 0 && SOSManifestGetCount(extra
) == 0)
1796 SOSPeerSetSendObjects(peer
, false);
1798 // If we aren't missing anything, we've gotten all their data, so we're sync even if they haven't seen ours.
1799 if (missing
&& SOSManifestGetCount(missing
) == 0) {
1800 SOSEngineCompletedSyncWithPeer(engine
, peer
);
1803 if (CFEqualSafe(local
, SOSPeerGetProposedManifest(peer
)) && !SOSPeerMustSendMessage(peer
)) {
1805 if (CFEqual(confirmed
, local
)) {
1806 secnoticeq("engine", "synced <No MSG> %@:%@", engine
->myID
, peer
);
1807 } else if (SOSManifestGetCount(pendingObjects
) == 0 /* TODO: No entries moved from extra to pendingObjects. */
1808 && SOSManifestGetCount(missing
) == 0) {
1809 secnoticeq("engine", "waiting <MSG not resent> %@:%@ extra: %@", engine
->myID
, peer
, extra
);
1814 CFReleaseSafe(local
);
1815 CFReleaseSafe(message
);
1816 CFReleaseNull(extra
);
1817 CFReleaseNull(missing
);
1818 return CFDataCreate(kCFAllocatorDefault
, NULL
, 0);
1822 if (SOSManifestGetCount(pendingObjects
)) {
1823 // If we have additions and we need to send objects, do so.
1824 __block
size_t objectsSize
= 0;
1825 __block
struct SOSDigestVector dv
= SOSDigestVectorInit
;
1826 CFMutableArrayRef changes
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
1827 if (!SOSDataSourceForEachObject(engine
->dataSource
, pendingObjects
, error
, ^void(CFDataRef key
, SOSObjectRef object
, bool *stop
) {
1828 CFErrorRef localError
= NULL
;
1829 CFDataRef digest
= NULL
;
1830 CFDataRef der
= NULL
;
1832 const uint8_t *d
= CFDataGetBytePtr(key
);
1833 secnoticeq("engine", "%@:%@ object %02X%02X%02X%02X dropping from manifest: not found in datasource",
1834 engine
->myID
, SOSPeerGetID(peer
), d
[0], d
[1], d
[2], d
[3]);
1835 SOSChangesAppendDelete(changes
, key
);
1836 } else if (!(der
= SOSEngineCopyObjectDER(engine
, object
, &localError
))
1837 || !(digest
= SOSObjectCopyDigest(engine
->dataSource
, object
, &localError
))) {
1838 if (SecErrorGetOSStatus(localError
) == errSecDecode
) {
1839 // Decode error, we need to drop these objects from our manifests
1840 const uint8_t *d
= CFDataGetBytePtr(key
);
1841 secnoticeq("engine", "%@:%@ object %02X%02X%02X%02X dropping from manifest: %@",
1842 engine
->myID
, SOSPeerGetID(peer
), d
[0], d
[1], d
[2], d
[3], localError
);
1843 SOSChangesAppendDelete(changes
, key
);
1844 CFRelease(localError
);
1846 // Stop iterating and propagate out all other errors.
1847 const uint8_t *d
= CFDataGetBytePtr(key
);
1848 secnoticeq("engine", "%@:%@ object %02X%02X%02X%02X in SOSDataSourceForEachObject: %@",
1849 engine
->myID
, SOSPeerGetID(peer
), d
[0], d
[1], d
[2], d
[3], localError
);
1851 CFErrorPropagate(localError
, error
);
1852 CFReleaseNull(message
);
1855 if (!CFEqual(key
, digest
)) {
1856 const uint8_t *d
= CFDataGetBytePtr(key
);
1857 const uint8_t *e
= CFDataGetBytePtr(digest
);
1858 secnoticeq("engine", "%@:%@ object %02X%02X%02X%02X is really %02X%02X%02X%02X dropping from local manifest",
1859 engine
->myID
, SOSPeerGetID(peer
), d
[0], d
[1], d
[2], d
[3], e
[0], e
[1], e
[2], e
[3]);
1860 SOSChangesAppendDelete(changes
, key
);
1861 SOSChangesAppendAdd(changes
, object
); // This is new behaviour but we think it's more correct
1864 size_t objectLen
= (size_t)CFDataGetLength(der
);
1865 if (SOSMessageAppendObject(message
, der
, &localError
)) {
1866 SOSDigestVectorAppend(&dv
, CFDataGetBytePtr(digest
));
1868 const uint8_t *d
= CFDataGetBytePtr(digest
);
1869 CFStringRef hexder
= CFDataCopyHexString(der
);
1870 secnoticeq("engine", "%@:%@ object %02X%02X%02X%02X der: %@ dropping from manifest: %@",
1871 engine
->myID
, SOSPeerGetID(peer
), d
[0], d
[1], d
[2], d
[3], hexder
, localError
);
1872 CFReleaseNull(hexder
);
1873 CFReleaseNull(message
);
1874 // Since we can't send these objects let's assume they are bad too?
1875 SOSChangesAppendDelete(changes
, digest
);
1877 objectsSize
+= objectLen
;
1878 if (objectsSize
> kSOSMessageMaxObjectsSize
)
1882 CFReleaseSafe(digest
);
1884 CFReleaseNull(message
);
1887 objectsSent
= SOSManifestCreateWithDigestVector(&dv
, error
);
1888 if (CFArrayGetCount(changes
)) {
1889 CFErrorRef localError
= NULL
;
1890 if (!SOSEngineUpdateChanges_locked(engine
, NULL
, kSOSDataSourceTransactionDidCommit
, kSOSDataSourceSOSTransaction
, changes
, &localError
))
1891 secerror("SOSEngineUpdateChanges_locked: %@ failed: %@", changes
, localError
);
1892 CFReleaseSafe(localError
);
1893 CFAssignRetained(local
, SOSEngineCopyLocalPeerManifest_locked(engine
, peer
, error
));
1895 CFReleaseSafe(changes
);
1898 // If we have no confirmed manifest, we want all pendedObjects going out as a manifest
1899 objectsSent
= CFRetainSafe(pendingObjects
);
1902 if (confirmed
|| SOSManifestGetCount(missing
) || SOSManifestGetCount(extra
) || objectsSent
) {
1903 SOSManifestRef allExtra
= SOSManifestCreateUnion(extra
, objectsSent
, error
);
1904 proposed
= SOSManifestCreateWithPatch(confirmed
, missing
, allExtra
, error
);
1905 CFReleaseNull(allExtra
);
1908 SOSManifestRef sender
= local
;
1909 // We actually send the remote peer its own digest.
1910 // Note that both pendingObjects and unwanted may have been changed, so we get them again
1911 if (SOSManifestGetCount(SOSPeerGetPendingObjects(peer
))==0 && SOSManifestGetCount(extra
)==0 &&
1912 SOSManifestGetCount(missing
)==0 && SOSManifestGetCount(SOSPeerGetUnwantedManifest(peer
))!=0) {
1913 secnoticeq("engine", "%@:%@: only have differences in unwanted set; lying to peer to stop sync",engine
->myID
, SOSPeerGetID(peer
));
1917 if (!SOSMessageSetManifests(message
, sender
, confirmed
, proposed
, proposed
, confirmed
? objectsSent
: NULL
, error
)) {
1918 secnoticeq("engine", "%@:%@: failed to set message manifests",engine
->myID
, SOSPeerGetID(peer
));
1919 CFReleaseNull(message
);
1922 CFReleaseNull(objectsSent
);
1925 result
= SOSMessageCreateData(message
, SOSPeerNextSequenceNumber(peer
), error
);
1929 // Capture the peer in our block (SOSEnginePeerMessageSentBlock)
1931 *sent
= Block_copy(^(bool success
) {
1932 dispatch_async(engine
->queue
, ^{
1934 SOSPeerSetMustSendMessage(peer
, false);
1935 if (!confirmed
&& !proposed
) {
1936 SOSPeerSetSendObjects(peer
, true);
1937 secnoticeq("engine", "%@:%@ sendObjects=true L:%@", engine
->myID
, SOSPeerGetID(peer
), local
);
1939 SOSPeerAddLocalManifest(peer
, local
);
1940 SOSPeerAddProposedManifest(peer
, proposed
);
1941 secnoticeq("engine", "send %@:%@ %@", engine
->myID
, SOSPeerGetID(peer
), message
);
1942 //SOSEngineCheckPeerIntegrity(engine, peer, NULL);
1944 secerror("%@:%@ failed to send %@", engine
->myID
, SOSPeerGetID(peer
), message
);
1946 CFReleaseSafe(peer
);
1947 CFReleaseSafe(local
);
1948 CFReleaseSafe(proposed
);
1949 CFReleaseSafe(message
);
1953 CFReleaseSafe(local
);
1954 CFReleaseSafe(proposed
);
1955 CFReleaseSafe(message
);
1957 CFReleaseNull(extra
);
1958 CFReleaseNull(missing
);
1959 if (error
&& *error
)
1960 secerror("%@:%@ error in send: %@", engine
->myID
, SOSPeerGetID(peer
), *error
);
1965 static void SOSEngineLogItemError(SOSEngineRef engine
, CFStringRef peerID
, CFDataRef key
, CFDataRef optionalDigest
, const char *where
, CFErrorRef error
) {
1966 if (!optionalDigest
) {
1967 const uint8_t *d
= CFDataGetBytePtr(key
);
1968 secwarning("%@:%@ object %02X%02X%02X%02X %s: %@", engine
->myID
, peerID
, d
[0], d
[1], d
[2], d
[3], where
, error
? (CFTypeRef
)error
: CFSTR(""));
1970 const uint8_t *d
= CFDataGetBytePtr(key
);
1971 const uint8_t *e
= CFDataGetBytePtr(optionalDigest
);
1972 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]);
1976 static bool SOSEngineWriteToBackup_locked(SOSEngineRef engine
, SOSPeerRef peer
, bool rewriteComplete
, bool *didWrite
, bool *incomplete
, CFErrorRef
*error
) {
1977 __block
bool ok
= SOSPeerWritePendingReset(peer
, error
);
1978 if (!ok
|| !SOSPeerGetKeyBag(peer
))
1980 __block SOSManifestRef local
= SOSEngineCopyLocalPeerManifest_locked(engine
, peer
, error
);
1981 __block SOSManifestRef proposed
= SOSPeerGetProposedManifest(peer
);
1982 __block
bool notify
= true;
1983 SOSManifestRef pendingObjects
= NULL
;
1984 SOSManifestRef missing
= NULL
;
1985 CFStringRef peerID
= SOSPeerGetID(peer
);
1987 ok
&= SOSManifestDiff(proposed
, local
, &missing
, &pendingObjects
, error
);
1989 secnoticeq("engine", "%@:%@: Send state for peer [%s%s%s] O: %zu, M: %zu", engine
->myID
, peerID
,
1992 pendingObjects
? "O":"0",
1993 SOSManifestGetCount(pendingObjects
),
1994 SOSManifestGetCount(missing
));
1996 if (SOSManifestGetCount(missing
) == 0 && SOSManifestGetCount(pendingObjects
) == 0) {
1997 // proposed == local (faster test than CFEqualSafe above), since we
1998 // already did the SOSManifestDiff
1999 if (rewriteComplete
) {
2002 secnoticeq("engine", "%@:%@ backup still done", engine
->myID
, peer
);
2006 ok
&= SOSPeerAppendToJournal(peer
, error
, ^(FILE *journalFile
, keybag_handle_t kbhandle
) {
2007 SOSManifestRef objectsSent
= NULL
;
2008 __block
struct SOSDigestVector dvdel
= SOSDigestVectorInit
;
2009 __block
struct SOSDigestVector dvadd
= SOSDigestVectorInit
;
2010 SOSManifestForEach(missing
, ^(CFDataRef key
, bool *stop
) {
2011 CFErrorRef localError
= NULL
;
2012 if (ftello(journalFile
) > kSOSBackupMaxFileSize
) {
2013 // Highwatermark hit on file.
2015 } else if (SOSBackupEventWriteDelete(journalFile
, key
, &localError
)) {
2016 SOSDigestVectorAppend(&dvdel
, CFDataGetBytePtr(key
));
2018 SOSEngineLogItemError(engine
, peerID
, key
, NULL
, "in SOSPeerWriteDelete", localError
);
2019 CFErrorPropagate(localError
, error
);
2020 // TODO: Update of missing so proposed is updated properly
2021 *stop
= true; // Disk full?
2025 if (ok
&& SOSManifestGetCount(pendingObjects
)) {
2026 CFMutableArrayRef changes
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
2027 ok
&= SOSDataSourceForEachObject(engine
->dataSource
, pendingObjects
, error
, ^void(CFDataRef key
, SOSObjectRef object
, bool *stop
) {
2028 CFErrorRef localError
= NULL
;
2029 CFDataRef digest
= NULL
;
2030 CFDictionaryRef backupItem
= NULL
;
2031 if (ftello(journalFile
) > kSOSBackupMaxFileSize
) {
2032 // Highwatermark hit on file.
2034 } else if (!object
) {
2035 SOSEngineLogItemError(engine
, peerID
, key
, NULL
, "dropping from manifest: not found in datasource", localError
);
2036 SOSChangesAppendDelete(changes
, key
);
2037 } else if (!(backupItem
= SOSObjectCopyBackup(engine
->dataSource
, object
, kbhandle
, &localError
))
2038 || !(digest
= SOSObjectCopyDigest(engine
->dataSource
, object
, &localError
))) {
2039 if (SecErrorGetOSStatus(localError
) == errSecDecode
) {
2040 // Decode error, we need to drop these objects from our manifests
2041 SOSEngineLogItemError(engine
, peerID
, key
, NULL
, "dropping from manifest", localError
);
2042 SOSChangesAppendDelete(changes
, key
);
2043 CFRelease(localError
);
2045 // Stop iterating and propagate out all other errors.
2046 SOSEngineLogItemError(engine
, peerID
, key
, NULL
, "in SOSDataSourceForEachObject", localError
);
2048 CFErrorPropagate(localError
, error
);
2052 if (!CFEqual(key
, digest
)) {
2053 SOSEngineLogItemError(engine
, peerID
, key
, digest
, "", NULL
);
2054 SOSChangesAppendDelete(changes
, key
);
2055 SOSChangesAppendAdd(changes
, object
); // This is new behaviour but we think it's more correct
2058 if (SOSBackupEventWriteAdd(journalFile
, backupItem
, &localError
)) {
2059 SOSDigestVectorAppend(&dvadd
, CFDataGetBytePtr(digest
));
2061 SOSEngineLogItemError(engine
, peerID
, key
, NULL
, "in SOSPeerWriteAdd", localError
);
2062 *stop
= true; // Disk full?
2063 CFErrorPropagate(localError
, error
);
2067 CFReleaseSafe(backupItem
);
2068 CFReleaseSafe(digest
);
2070 if (CFArrayGetCount(changes
)) {
2071 CFErrorRef localError
= NULL
;
2072 if (!SOSEngineUpdateChanges_locked(engine
, NULL
, kSOSDataSourceTransactionDidCommit
, kSOSDataSourceSOSTransaction
, changes
, &localError
))
2073 secerror("SOSEngineUpdateChanges_locked: %@ failed: %@", changes
, localError
);
2074 CFReleaseSafe(localError
);
2075 // Since calling SOSEngineUpdateChanges_locked might cause local to change and might cause the backup peer to update proposed, refetch them here.
2076 CFAssignRetained(local
, SOSEngineCopyLocalPeerManifest_locked(engine
, peer
, error
));
2077 proposed
= SOSPeerGetProposedManifest(peer
);
2079 CFReleaseSafe(changes
);
2082 if (dvadd
.count
|| (proposed
&& dvdel
.count
)) {
2084 SOSManifestRef deleted
= SOSManifestCreateWithDigestVector(&dvdel
, error
);
2085 SOSManifestRef objectsSent
= SOSManifestCreateWithDigestVector(&dvadd
, error
);
2086 SOSManifestRef newProposed
= SOSManifestCreateWithPatch(proposed
, deleted
, objectsSent
, error
);
2087 CFReleaseSafe(deleted
);
2088 CFReleaseSafe(objectsSent
);
2089 SOSPeerSetProposedManifest(peer
, newProposed
);
2090 CFReleaseSafe(newProposed
);
2091 proposed
= SOSPeerGetProposedManifest(peer
);
2093 SOSDigestVectorFree(&dvdel
);
2094 SOSDigestVectorFree(&dvadd
);
2096 // TODO: If proposed is NULL, and local is empty we should still consider ourselves done.
2097 // It so happens this can't happen in practice today since there is at least a backupbag
2098 // in the backup, but this is a bug waiting to rear its head in the future.
2099 if (ok
&& CFEqualSafe(local
, proposed
)) {
2100 CFErrorRef localError
= NULL
;
2101 if (SOSBackupEventWriteCompleteMarker(journalFile
, 899, &localError
)) {
2102 SOSPeerSetSendObjects(peer
, true);
2104 secnoticeq("backup", "%@:%@ backup done%s", engine
->myID
, peerID
, notify
? " notifying sbd" : "");
2105 // TODO: Now switch to changes based writing to backup sync.
2106 // Currently we leave changes enabled but we probably shouldn't
2108 secwarning("%@:%@ in SOSBackupPeerWriteCompleteMarker: %@", engine
->myID
, peerID
, localError
);
2111 CFErrorPropagate(localError
, error
);
2114 secnoticeq("backup", "%@:%@ backup incomplete [%zu/%zu]%s", engine
->myID
, peerID
, SOSManifestGetCount(local
), SOSManifestGetCount(proposed
), notify
? " notifying sbd" : "");
2117 CFReleaseNull(objectsSent
);
2120 SOSBackupPeerPostNotification("writing changes to backup");
2123 CFReleaseSafe(local
);
2124 CFReleaseNull(pendingObjects
);
2125 CFReleaseNull(missing
);
2130 bool SOSEngineSyncWithPeers(SOSEngineRef engine
, CFTypeRef idsTransport
, CFTypeRef kvsTransport
, CFErrorRef
*error
) {
2131 __block
bool ok
= true;
2132 __block
bool incomplete
= false;
2133 ok
&= SOSEngineDoTxnOnQueue(engine
, error
, ^(SOSTransactionRef txn
, bool *commit
) {
2134 __block
bool dirty
= false;
2135 SOSEngineForEachBackupPeer_locked(engine
, ^(SOSPeerRef peer
) {
2136 ok
= SOSEngineWriteToBackup_locked(engine
, peer
, false, &dirty
, &incomplete
, error
);
2140 ok
= SOSEngineSave(engine
, txn
, error
);
2143 // Ensure we get called again in a while (after a backup timeout)
2144 // sbd will do this since we never wrote a complete marker.
2145 // TODO: This relies on us not writing complete marker for update
2146 // event while we havn't finished a full backup, which we currently still do.
2151 bool SOSEngineHandleMessage(SOSEngineRef engine
, CFStringRef peerID
,
2152 CFDataRef raw_message
, CFErrorRef
*error
)
2154 __block
bool result
= true;
2155 __block
bool somethingChanged
= false;
2156 SOSMessageRef message
= SOSMessageCreateWithData(kCFAllocatorDefault
, raw_message
, error
);
2157 result
&= message
&& SOSEngineDoTxnOnQueue(engine
, error
, ^(SOSTransactionRef txn
, bool *commit
) {
2158 result
= SOSEngineHandleMessage_locked(engine
, peerID
, message
, txn
, commit
, &somethingChanged
, error
);
2160 CFReleaseSafe(message
);
2161 if (somethingChanged
)
2162 SecKeychainChanged(false);
2166 void SOSEngineCircleChanged(SOSEngineRef engine
, CFStringRef myPeerID
, CFArrayRef trustedPeers
, CFArrayRef untrustedPeers
) {
2167 __block
bool peersOrViewsChanged
= false;
2168 SOSEngineDoOnQueue(engine
, ^{
2169 peersOrViewsChanged
= SOSEngineCircleChanged_locked(engine
, myPeerID
, trustedPeers
, untrustedPeers
);
2172 __block
bool ok
= true;
2173 __block CFErrorRef localError
= NULL
;
2174 ok
&= SOSEngineDoTxnOnQueue(engine
, &localError
, ^(SOSTransactionRef txn
, bool *commit
) {
2175 ok
= *commit
= SOSEngineSave(engine
, txn
, &localError
);
2178 secerror("failed to save engine state: %@", localError
);
2179 CFReleaseSafe(localError
);
2182 if (peersOrViewsChanged
)
2183 SOSCCSyncWithAllPeers();
2186 SOSManifestRef
SOSEngineCopyManifest(SOSEngineRef engine
, CFErrorRef
*error
) {
2187 __block SOSManifestRef result
= NULL
;
2188 SOSEngineDoOnQueue(engine
, ^{
2189 result
= SOSEngineCopyManifestWithViewNameSet_locked(engine
, SOSViewsGetV0ViewSet(), error
);
2194 SOSManifestRef
SOSEngineCopyLocalPeerManifest(SOSEngineRef engine
, SOSPeerRef peer
, CFErrorRef
*error
) {
2195 __block SOSManifestRef result
= NULL
;
2196 SOSEngineDoOnQueue(engine
, ^{
2197 result
= SOSEngineCopyLocalPeerManifest_locked(engine
, peer
, error
);
2202 bool SOSEngineUpdateChanges(SOSEngineRef engine
, SOSDataSourceTransactionSource source
, CFArrayRef changes
, CFErrorRef
*error
) {
2203 __block
bool result
= true;
2204 SOSEngineDoOnQueue(engine
, ^{
2205 result
= SOSEngineUpdateChanges_locked(engine
, NULL
, kSOSDataSourceTransactionDidCommit
, source
, changes
, error
);
2211 // Peer state layout. WRONG! It's an array now
2212 // The peer state is an array.
2213 // The first element of the array is a dictionary with any number of keys and
2214 // values in it (for future expansion) such as changing the digest size or type
2215 // or remebering boolean flags for a peers sake.
2216 // The next three are special in that they are manifest digests with special
2217 // meaning and rules as to how they are treated (These are dynamically updated
2218 // based on database activity so they have a fully history of all changes made
2219 // to the local db. The first is the manifest representing the pendingObjects
2220 // to send to the other peer. This is normally only ever appending to, and in
2221 // particular with transactions originating from the Keychain API that affect
2222 // syncable items will need to add the new objects digests to the pendingObjects list
2223 // while adding the digests of any tombstones encountered to the extra list.
2225 SOSPeerRef
SOSEngineCopyPeerWithID(SOSEngineRef engine
, CFStringRef peer_id
, CFErrorRef
*error
) {
2226 __block SOSPeerRef peer
= NULL
;
2227 SOSEngineDoOnQueue(engine
, ^{
2228 peer
= SOSEngineCopyPeerWithID_locked(engine
, peer_id
, error
);
2233 bool SOSEngineForPeerID(SOSEngineRef engine
, CFStringRef peerID
, CFErrorRef
*error
, void (^forPeer
)(SOSPeerRef peer
)) {
2234 __block
bool ok
= true;
2235 SOSEngineDoOnQueue(engine
, ^{
2236 SOSPeerRef peer
= SOSEngineCopyPeerWithID_locked(engine
, peerID
, error
);
2247 bool SOSEngineWithPeerID(SOSEngineRef engine
, CFStringRef peerID
, CFErrorRef
*error
, void (^with
)(SOSPeerRef peer
, SOSDataSourceRef dataSource
, SOSTransactionRef txn
, bool *forceSaveState
)) {
2248 __block
bool result
= true;
2249 result
&= SOSEngineDoTxnOnQueue(engine
, error
, ^(SOSTransactionRef txn
, bool *commit
) {
2250 SOSPeerRef peer
= SOSEngineCopyPeerWithID_locked(engine
, peerID
, error
);
2252 result
= SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("Engine has no peer for %@"), peerID
);
2254 bool saveState
= false;
2255 with(peer
, engine
->dataSource
, txn
, &saveState
);
2256 CFReleaseSafe(peer
);
2258 result
= SOSEngineSave(engine
, txn
, error
);
2259 // TODO: Don't commit if engineSave fails?
2266 CFDataRef
SOSEngineCreateMessageToSyncToPeer(SOSEngineRef engine
, CFStringRef peerID
, SOSEnginePeerMessageSentBlock
*sentBlock
, CFErrorRef
*error
) {
2267 __block CFDataRef message
= NULL
;
2268 SOSEngineForPeerID(engine
, peerID
, error
, ^(SOSPeerRef peer
) {
2269 message
= SOSEngineCreateMessage_locked(engine
, peer
, error
, sentBlock
);
2274 bool SOSEnginePeerDidConnect(SOSEngineRef engine
, CFStringRef peerID
, CFErrorRef
*error
) {
2275 return SOSEngineWithPeerID(engine
, peerID
, error
, ^(SOSPeerRef peer
, SOSDataSourceRef dataSource
, SOSTransactionRef txn
, bool *saveState
) {
2276 *saveState
= SOSPeerDidConnect(peer
);
2280 bool SOSEngineSetPeerConfirmedManifest(SOSEngineRef engine
, CFStringRef backupName
,
2281 CFDataRef keybagDigest
, CFDataRef manifestData
, CFErrorRef
*error
) {
2282 __block
bool ok
= true;
2284 ok
&= SOSEngineForPeerID(engine
, backupName
, error
, ^(SOSPeerRef peer
) {
2286 bool incomplete
= false;
2287 SOSManifestRef confirmed
= NULL
;
2288 CFDataRef keybag
= SOSPeerGetKeyBag(peer
);
2289 CFDataRef computedKeybagDigest
= keybag
? CFDataCopySHA1Digest(keybag
, NULL
) : NULL
;
2290 if (CFEqualSafe(keybagDigest
, computedKeybagDigest
)) {
2291 ok
= confirmed
= SOSManifestCreateWithData(manifestData
, error
);
2293 // Set both confirmed and proposed (confirmed is just
2294 // for debug status, proposed is actually what's used
2295 // by the backup peer).
2296 SOSPeerSetConfirmedManifest(peer
, confirmed
);
2297 SOSPeerSetProposedManifest(peer
, confirmed
);
2300 // sbd missed a reset event, send it again
2301 // Force SOSEngineWriteToBackup_locked to call SOSPeerWriteReset, which clears
2302 // confirmed and proposed manifests and writes the keybag to the journal.
2303 SOSPeerSetMustSendMessage(peer
, true);
2306 // Stop changes from writing complete markers, unless SOSEngineWriteToBackup_locked() detects we are in sync
2307 SOSPeerSetSendObjects(peer
, false);
2308 // Write data for this peer if we can, technically not needed for non legacy protocol support all the time.
2309 ok
= SOSEngineWriteToBackup_locked(engine
, peer
, true, &dirty
, &incomplete
, error
);
2311 CFReleaseSafe(confirmed
);
2312 CFReleaseSafe(computedKeybagDigest
);
2317 CFArrayRef
SOSEngineCopyBackupPeerNames(SOSEngineRef engine
, CFErrorRef
*error
) {
2318 __block CFMutableArrayRef backupNames
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
2319 SOSEngineForEachBackupPeer(engine
, ^(SOSPeerRef peer
) {
2320 CFArrayAppendValue(backupNames
, SOSPeerGetID(peer
));
2325 static CFStringRef
CFStringCreateWithViewNameSet(CFSetRef vns
) {
2326 CFIndex count
= CFSetGetCount(vns
);
2327 CFMutableArrayRef mvn
= CFArrayCreateMutableForCFTypesWithCapacity(kCFAllocatorDefault
, count
);
2328 CFSetForEach(vns
, ^(const void *value
) {
2329 CFArrayAppendValue(mvn
, value
);
2331 CFArraySortValues(mvn
, CFRangeMake(0, count
), (CFComparatorFunction
)CFStringCompare
, 0);
2332 CFStringRef string
= CFStringCreateByCombiningStrings(kCFAllocatorDefault
, mvn
, CFSTR(":"));
2337 static CFStringRef
CFStringCreateWithLabelAndViewNameSetDescription(CFStringRef label
, CFStringRef peerID
, CFSetRef vns
, SOSManifestRef manifest
) {
2338 CFStringRef vnsDesc
= CFStringCreateWithViewNameSet(vns
);
2341 desc
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%@ %@ (%@) [%lu]"), label
, peerID
, vnsDesc
, SOSManifestGetCount(manifest
));
2343 desc
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%@ %@ (%@)"), label
, peerID
, vnsDesc
);
2344 CFReleaseSafe(vnsDesc
);
2348 static void CFArrayAppendConfirmedDigestsEntry(CFMutableArrayRef array
, CFStringRef label
, CFStringRef peerID
, CFSetRef vns
, SOSManifestRef manifest
) {
2349 CFStringRef desc
= CFStringCreateWithLabelAndViewNameSetDescription(label
, peerID
, vns
, manifest
);
2350 CFArrayAppendValue(array
, desc
);
2351 CFReleaseSafe(desc
);
2352 CFDataRef digest
= SOSManifestGetDigest(manifest
, NULL
);
2354 CFArrayAppendValue(array
, digest
);
2356 CFDataRef nullDigest
= CFDataCreate(kCFAllocatorDefault
, NULL
, 0);
2357 CFArrayAppendValue(array
, nullDigest
);
2358 CFReleaseSafe(nullDigest
);
2362 static CFArrayRef
SOSEngineCopyPeerConfirmedDigests_locked(SOSEngineRef engine
, CFErrorRef
*error
) {
2363 CFMutableArrayRef result
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
2364 CFDictionaryForEach(engine
->viewNameSet2ChangeTracker
, ^(const void *vns
, const void *ct
) {
2365 SOSManifestRef manifest
= SOSEngineCopyManifestWithViewNameSet_locked(engine
, vns
, error
);
2366 CFArrayAppendConfirmedDigestsEntry(result
, CFSTR("local "), engine
->myID
, (CFSetRef
)vns
, manifest
);
2367 CFReleaseSafe(manifest
);
2370 // Copy other peers even if we aren't in the circle, since we're observing it.
2371 SOSEngineForEachPeer_locked(engine
, ^(SOSPeerRef peer
) {
2372 CFArrayAppendConfirmedDigestsEntry(result
, CFSTR("remote"), SOSPeerGetID(peer
), SOSPeerGetViewNameSet(peer
),
2373 SOSPeerGetConfirmedManifest(peer
));
2378 CFArrayRef
SOSEngineCopyPeerConfirmedDigests(SOSEngineRef engine
, CFErrorRef
*error
) {
2379 __block CFArrayRef result
= NULL
;
2380 SOSEngineDoOnQueue(engine
, ^{
2381 result
= SOSEngineCopyPeerConfirmedDigests_locked(engine
, error
);
2386 SOSDataSourceRef
SOSEngineGetDataSource(SOSEngineRef engine
) {
2387 return engine
->dataSource
;