2 * Copyright (c) 2012-2014 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 <SecureObjectSync/SOSEngine.h>
30 #include <SecureObjectSync/SOSDigestVector.h>
31 #include <SecureObjectSync/SOSInternal.h>
32 #include <SecureObjectSync/SOSPeerInfo.h>
33 #include <corecrypto/ccder.h>
36 #include <utilities/SecCFError.h>
37 #include <utilities/SecCFRelease.h>
38 #include <utilities/SecCFWrappers.h>
39 #include <utilities/der_plist.h>
40 #include <utilities/der_plist_internal.h>
41 #include <utilities/debugging.h>
42 #include <utilities/iCloudKeychainTrace.h>
43 #include <AssertMacros.h>
44 #include <CoreFoundation/CoreFoundation.h>
45 #include <securityd/SecItemDataSource.h> // TODO: We can't leave this here.
46 #include <securityd/SecDbItem.h> // TODO: We can't leave this here.
47 #include <securityd/SecItemServer.h>// TODO: We can't leave this here.
48 #include <Security/SecItemPriv.h>// TODO: We can't leave this here.
49 #include <securityd/SOSCloudCircleServer.h>
52 // MARK: SOSEngine The Keychain database with syncable keychain support.
55 // Key in dataSource for general engine state file.
56 // This file only has digest entries in it, no manifests.
57 static const CFStringRef kSOSEngineState
= CFSTR("engine-state");
59 // Keys in state dictionary
60 static CFStringRef kSOSPeerCoderKey
= CFSTR("coder");
61 static CFStringRef kSOSEngineManifestCacheKey
= CFSTR("manifestCache");
62 static CFStringRef kSOSEnginePeerStateKey
= CFSTR("peerState");
63 static CFStringRef kSOSEnginePeerIDsKey
= CFSTR("peerIDs");
64 static CFStringRef kSOSEngineIDKey
= CFSTR("id");
66 /* SOSEngine implementation. */
67 struct __OpaqueSOSEngine
{
69 SOSDataSourceRef dataSource
;
70 CFStringRef myID
; // My peerID in the circle
71 SOSManifestRef manifest
; // Explicitly not in cache since it's not persisted?
72 // We need to address the issues of corrupt keychain items
73 SOSManifestRef unreadble
; // Possibly by having a set of unreadble items, to which we
74 // add any corrupted items in the db that have yet to be deleted.
75 // This happens if we notce corruption during a (read only) query.
76 // We would also perma-subtract unreadable from manifest whenever
77 // anyone asked for manifest. This result would be cached in
78 // The manifestCache below, so we just need a key into the cache
79 CFDataRef localMinusUnreadableDigest
; // or a digest (CFDataRef of the right size).
81 CFMutableDictionaryRef manifestCache
; // digest -> ( refcount, manifest )
82 CFMutableDictionaryRef peerState
; // peerId -> mutable array of digests
85 dispatch_queue_t queue
;
88 static bool SOSEngineLoad(SOSEngineRef engine
, CFErrorRef
*error
);
91 static CFStringRef
SOSPeerIDArrayCreateString(CFArrayRef peerIDs
) {
92 return peerIDs
? CFStringCreateByCombiningStrings(kCFAllocatorDefault
, peerIDs
, CFSTR(" ")) : CFSTR("");
95 static CFStringRef
SOSEngineCopyFormattingDesc(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
96 SOSEngineRef engine
= (SOSEngineRef
)cf
;
97 CFStringRef tpDesc
= SOSPeerIDArrayCreateString(engine
->peerIDs
);
98 CFStringRef desc
= CFStringCreateWithFormat(kCFAllocatorDefault
, formatOptions
, CFSTR("<Engine %@ peers %@ MC[%d] PS[%d]>"), engine
->myID
, tpDesc
, engine
->manifestCache
? (int)CFDictionaryGetCount(engine
->manifestCache
) : 0, engine
->peerState
? (int)CFDictionaryGetCount(engine
->peerState
) : 0);
99 CFReleaseSafe(tpDesc
);
103 static CFStringRef
SOSEngineCopyDebugDesc(CFTypeRef cf
) {
104 return SOSEngineCopyFormattingDesc(cf
, NULL
);
107 static dispatch_queue_t sEngineQueue
;
108 static CFDictionaryRef sEngineMap
;
110 CFGiblisWithFunctions(SOSEngine
, NULL
, NULL
, NULL
, NULL
, NULL
, SOSEngineCopyFormattingDesc
, SOSEngineCopyDebugDesc
, NULL
, NULL
, ^{
111 sEngineQueue
= dispatch_queue_create("SOSEngine queue", DISPATCH_QUEUE_SERIAL
);
112 sEngineMap
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
115 #define _LOG_RAW_MESSAGES 0
116 void logRawMessage(CFDataRef message
, bool sending
, uint64_t seqno
)
118 #if _LOG_RAW_MESSAGES
119 CFStringRef hexMessage
= NULL
;
121 hexMessage
= CFDataCopyHexString(message
);
123 secnoticeq("engine", "%s RAW%1d %@", sending
? "send" : "recv", seqno
?2:0, hexMessage
);
125 secnoticeq("engine", "%s RAWx %@", sending
? "send" : "recv", hexMessage
); // we don't know vers of received msg here
127 CFReleaseSafe(hexMessage
);
131 // Peer state layout. WRONG! It's an array now
132 // The peer state is an array.
133 // The first element of the array is a dictionary with any number of keys and
134 // values in it (for future expansion) such as changing the digest size or type
135 // or remebering boolean flags for a peers sake.
136 // The next three are special in that they are manifest digests with special
137 // meaning and rules as to how they are treated (These are dynamically updated
138 // based on database activity so they have a fully history of all changes made
139 // to the local db. The first is the manifest representing the pendingObjects
140 // to send to the other peer. This is normally only ever appending to, and in
141 // particular with transactions originating from the Keychain API that affect
142 // syncable items will need to add the new objects digests to the pendingObjects list
143 // while adding the digests of any tombstones encountered to the extra list.
145 CFStringRef
SOSEngineGetMyID(SOSEngineRef engine
) {
146 // TODO: this should not be needed
150 // TEMPORARY: Get the list of IDs for cleanup, this shouldn't be used instead it should iterate KVS.
151 CFArrayRef
SOSEngineGetPeerIDs(SOSEngineRef engine
) {
152 return engine
->peerIDs
;
155 SOSManifestRef
SOSEngineGetManifestForDigest(SOSEngineRef engine
, CFDataRef digest
) {
156 if (!engine
->manifestCache
|| !digest
) return NULL
;
157 SOSManifestRef manifest
= (SOSManifestRef
)CFDictionaryGetValue(engine
->manifestCache
, digest
);
158 if (!manifest
) return NULL
;
159 if (CFGetTypeID(manifest
) != SOSManifestGetTypeID()) {
160 secerror("dropping corrupt manifest for %@ from cache", digest
);
161 CFDictionaryRemoveValue(engine
->manifestCache
, digest
);
168 void SOSEngineAddManifest(SOSEngineRef engine
, SOSManifestRef manifest
) {
169 CFDataRef digest
= SOSManifestGetDigest(manifest
, NULL
);
171 if (!engine
->manifestCache
)
172 engine
->manifestCache
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
173 CFDictionaryAddValue(engine
->manifestCache
, digest
, manifest
);
177 CFDataRef
SOSEnginePatchRecordAndCopyDigest(SOSEngineRef engine
, SOSManifestRef base
, SOSManifestRef removals
, SOSManifestRef additions
, CFErrorRef
*error
) {
178 CFDataRef digest
= NULL
;
179 SOSManifestRef manifest
= SOSManifestCreateWithPatch(base
, removals
, additions
, error
);
181 SOSEngineAddManifest(engine
, manifest
);
182 digest
= CFRetainSafe(SOSManifestGetDigest(manifest
, NULL
));
184 CFReleaseSafe(manifest
);
188 static bool SOSEngineHandleManifestUpdates(SOSEngineRef engine
, SOSDataSourceTransactionSource source
, SOSManifestRef removals
, SOSManifestRef additions
, CFErrorRef
*error
) {
189 __block
struct SOSDigestVector mdInCache
= SOSDigestVectorInit
;
190 struct SOSDigestVector mdInUse
= SOSDigestVectorInit
;
191 struct SOSDigestVector mdUnused
= SOSDigestVectorInit
;
192 struct SOSDigestVector mdMissing
= SOSDigestVectorInit
;
193 CFStringRef peerID
= NULL
;
196 require_quiet(engine
->peerState
, exit
); // Not a failure no work to do
199 CFArrayForEachC(engine
->peerIDs
, peerID
) {
200 SOSPeerRef peer
= SOSPeerCreateWithEngine(engine
, peerID
);
201 if (removals
|| additions
)
202 ok
&= SOSPeerDataSourceWillCommit(peer
, source
, removals
, additions
, error
);
203 SOSPeerMarkDigestsInUse(peer
, &mdInUse
);
207 if(engine
->manifestCache
){
208 CFDictionaryForEach(engine
->manifestCache
, ^(const void *key
, const void *value
) {
209 CFDataRef digest
= (CFDataRef
)key
;
211 SOSDigestVectorAppend(&mdInCache
, CFDataGetBytePtr(digest
));
214 // Delete unused manifests.
215 SOSDigestVectorDiff(&mdInCache
, &mdInUse
, &mdUnused
, &mdMissing
);
216 SOSManifestRef unused
= SOSManifestCreateWithDigestVector(&mdUnused
, NULL
);
217 SOSManifestForEach(unused
, ^(CFDataRef digest
, bool *stop
) {
219 CFDictionaryRemoveValue(engine
->manifestCache
, digest
);
221 CFReleaseSafe(unused
);
223 // Delete unused peerState
224 if (engine
->peerState
&& engine
->peerIDs
) {
225 CFMutableDictionaryRef newPeerState
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
226 CFArrayForEachC(engine
->peerIDs
, peerID
) {
227 CFTypeRef value
= CFDictionaryGetValue(engine
->peerState
, peerID
);
229 CFDictionarySetValue(newPeerState
, peerID
, value
);
231 CFDictionaryForEach(engine
->peerState
, ^(const void *key
, const void *value
) {
232 if(isDictionary(value
) && !CFDictionaryContainsKey(newPeerState
, key
)){
233 CFMutableDictionaryRef untrustedStuff
= (CFMutableDictionaryRef
)value
;
234 CFDataRef untrustedCoder
= (CFDataRef
)CFDictionaryGetValue(untrustedStuff
, kSOSPeerCoderKey
);
236 CFMutableDictionaryRef untrustedDict
= CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault
, kSOSPeerCoderKey
, untrustedCoder
, NULL
);
237 CFDictionarySetValue(newPeerState
, key
, untrustedDict
);
238 CFReleaseNull(untrustedDict
);
242 CFReleaseSafe(engine
->peerState
);
243 engine
->peerState
= newPeerState
;
247 SOSDigestVectorFree(&mdInCache
);
248 SOSDigestVectorFree(&mdInUse
);
249 SOSDigestVectorFree(&mdUnused
);
250 SOSDigestVectorFree(&mdMissing
);
254 static CFDataRef
SOSEngineCopyState(SOSEngineRef engine
, CFErrorRef
*error
) {
255 CFDataRef der
= NULL
;
256 CFMutableDictionaryRef state
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
257 if (engine
->myID
) CFDictionarySetValue(state
, kSOSEngineIDKey
, engine
->myID
);
258 if (engine
->peerIDs
) CFDictionarySetValue(state
, kSOSEnginePeerIDsKey
, engine
->peerIDs
);
259 if (engine
->peerState
) CFDictionarySetValue(state
, kSOSEnginePeerStateKey
, engine
->peerState
);
260 if (engine
->manifestCache
) {
261 CFMutableDictionaryRef mfc
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
262 CFDictionarySetValue(state
, kSOSEngineManifestCacheKey
, mfc
);
263 CFDictionaryForEach(engine
->manifestCache
, ^(const void *key
, const void *value
) {
264 SOSManifestRef mf
= (SOSManifestRef
)value
;
265 if (mf
&& (CFGetTypeID(mf
) == SOSManifestGetTypeID()))
266 CFDictionarySetValue(mfc
, key
, SOSManifestGetData(mf
));
270 der
= kc_plist_copy_der(state
, error
);
271 CFReleaseSafe(state
);
272 secnotice("engine", "%@", engine
);
276 static bool SOSEngineSave(SOSEngineRef engine
, SOSTransactionRef txn
, CFErrorRef
*error
) {
277 CFDataRef derState
= SOSEngineCopyState(engine
, error
);
278 bool ok
= derState
&& SOSDataSourceSetStateWithKey(engine
->dataSource
, txn
, kSOSEngineState
, kSecAttrAccessibleAlways
, derState
, error
);
279 CFReleaseSafe(derState
);
283 static bool SOSEngineUpdateLocalManifest_locked(SOSEngineRef engine
, SOSDataSourceTransactionSource source
, SOSManifestRef removals
, SOSManifestRef additions
, CFErrorRef
*error
) {
285 if (engine
->manifest
) {
286 SOSManifestRef updatedManifest
= SOSManifestCreateWithPatch(engine
->manifest
, removals
, additions
, error
);
288 CFAssignRetained(engine
->manifest
, updatedManifest
);
290 // Update Peer Manifests. -- Shouldn't this be deferred until we apply our toAdd and toDel to the local manifest?
291 ok
&= SOSEngineHandleManifestUpdates(engine
, source
, removals
, additions
, error
);
296 static bool SOSEngineUpdateChanges(SOSEngineRef engine
, SOSTransactionRef txn
, SOSDataSourceTransactionPhase phase
, SOSDataSourceTransactionSource source
, SOSManifestRef removals
, SOSManifestRef additions
, CFErrorRef
*error
)
298 secnotice("engine", "%s %s dels:%@ adds:%@", phase
== kSOSDataSourceTransactionWillCommit
? "will-commit" : phase
== kSOSDataSourceTransactionDidCommit
? "did-commit" : "did-rollback", source
== kSOSDataSourceSOSTransaction
? "sos" : "api", removals
, additions
);
301 case kSOSDataSourceTransactionDidRollback
:
302 ok
&= SOSEngineLoad(engine
, error
);
304 case kSOSDataSourceTransactionWillCommit
: {
305 ok
&= SOSEngineUpdateLocalManifest_locked(engine
, source
, removals
, additions
, error
);
306 // Write SOSEngine and SOSPeer state to disk if dirty
307 ok
&= SOSEngineSave(engine
, txn
, error
);
310 case kSOSDataSourceTransactionDidCommit
:
316 static void SOSEngineSetTrustedPeers(SOSEngineRef engine
, CFStringRef myPeerID
, CFArrayRef trustedPeers
) {
317 const bool wasInCircle
= engine
->myID
;
318 const bool isInCircle
= myPeerID
;
319 const bool inCircleChanged
= wasInCircle
!= isInCircle
;
321 CFStringRef peerID
= NULL
;
322 CFRetainAssign(engine
->myID
, myPeerID
);
324 if(trustedPeers
!= NULL
&& CFArrayGetCount(trustedPeers
) != 0){
325 CFReleaseNull(engine
->peerIDs
);
326 engine
->peerIDs
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
327 CFArrayForEachC(trustedPeers
, peerID
){
328 CFArrayAppendValue((CFMutableArrayRef
)engine
->peerIDs
, peerID
);
332 engine
->peerIDs
= NULL
;
334 // If we entered a circle of more than 2 or our last peer left we need to do stuff
335 if (inCircleChanged
) {
337 CFErrorRef dsError
= NULL
;
338 if (!(engine
->manifest
= SOSDataSourceCopyManifest(engine
->dataSource
, &dsError
))) {
339 secerror("failed to load manifest from datasource: %@", dsError
);
340 CFReleaseNull(dsError
);
342 SOSDataSourceSetNotifyPhaseBlock(engine
->dataSource
, ^(SOSDataSourceRef ds
, SOSTransactionRef txn
, SOSDataSourceTransactionPhase phase
, SOSDataSourceTransactionSource source
, struct SOSDigestVector
*removals
, struct SOSDigestVector
*additions
) {
343 SOSManifestRef mfdel
= SOSManifestCreateWithDigestVector(removals
, NULL
);
344 SOSManifestRef mfadd
= SOSManifestCreateWithDigestVector(additions
, NULL
);
345 dispatch_block_t processUpdates
= ^{
346 CFErrorRef localError
= NULL
;
347 if (!SOSEngineUpdateChanges(engine
, txn
, phase
, source
, mfdel
, mfadd
, &localError
)) {
348 secerror("updateChanged failed: %@", localError
);
350 CFReleaseSafe(localError
);
351 CFReleaseSafe(mfdel
);
352 CFReleaseSafe(mfadd
);
355 if (source
== kSOSDataSourceSOSTransaction
) {
358 // WARNING: This will deadlock the engine if you call a
359 // SecItem API function while holding the engine lock!
360 // However making this async right now isn't safe yet either
361 // Due to some code in the enginer using Get v/s copy to
362 // access some of the values that would be modified
363 // asynchronously here since the engine is coded as if
364 // running on a serial queue.
365 dispatch_sync(engine
->queue
, processUpdates
);
369 SOSDataSourceSetNotifyPhaseBlock(engine
->dataSource
, ^(SOSDataSourceRef ds
, SOSTransactionRef txn
, SOSDataSourceTransactionPhase phase
, SOSDataSourceTransactionSource source
, struct SOSDigestVector
*removals
, struct SOSDigestVector
*additions
) {
370 secnoticeq("engine", "No peers to notify"); // TODO: DEBUG - remove this
372 CFReleaseNull(engine
->manifest
);
377 static bool SOSEngineSetState(SOSEngineRef engine
, CFDataRef state
, CFErrorRef
*error
) {
380 CFMutableDictionaryRef dict
= NULL
;
381 const uint8_t *der
= CFDataGetBytePtr(state
);
382 const uint8_t *der_end
= der
+ CFDataGetLength(state
);
383 der
= der_decode_dictionary(kCFAllocatorDefault
, kCFPropertyListMutableContainers
, (CFDictionaryRef
*)&dict
, error
, der
, der_end
);
384 if (der
&& der
!= der_end
) {
385 ok
= SOSErrorCreate(kSOSErrorDecodeFailure
, error
, NULL
, CFSTR("trailing %td bytes at end of state"), der_end
- der
);
388 SOSEngineSetTrustedPeers(engine
, (CFStringRef
)CFDictionaryGetValue(dict
, kSOSEngineIDKey
),
389 (CFArrayRef
)CFDictionaryGetValue(dict
, kSOSEnginePeerIDsKey
));
390 CFRetainAssign(engine
->peerState
, (CFMutableDictionaryRef
)CFDictionaryGetValue(dict
, kSOSEnginePeerStateKey
));
392 CFReleaseNull(engine
->manifestCache
);
393 CFMutableDictionaryRef mfc
= (CFMutableDictionaryRef
)CFDictionaryGetValue(dict
, kSOSEngineManifestCacheKey
);
395 engine
->manifestCache
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
396 CFDictionaryForEach(mfc
, ^(const void *key
, const void *value
) {
397 CFDataRef data
= (CFDataRef
)value
;
399 SOSManifestRef mf
= SOSManifestCreateWithData(data
, NULL
);
401 CFDictionarySetValue(engine
->manifestCache
, key
, mf
);
409 secnotice("engine", "%@", engine
);
413 static bool SOSEngineLoad(SOSEngineRef engine
, CFErrorRef
*error
) {
414 CFDataRef state
= SOSDataSourceCopyStateWithKey(engine
->dataSource
, kSOSEngineState
, kSecAttrAccessibleAlways
, error
);
415 bool ok
= state
&& SOSEngineSetState(engine
, state
, error
);
416 CFReleaseSafe(state
);
420 static void CFArraySubtract(CFMutableArrayRef from
, CFArrayRef remove
) {
422 CFArrayForEach(remove
, ^(const void *value
) {
423 CFArrayRemoveAllValue(from
, value
);
428 static CFMutableArrayRef
CFArrayCreateDifference(CFAllocatorRef alloc
, CFArrayRef set
, CFArrayRef remove
) {
429 CFMutableArrayRef result
;
431 result
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
433 result
= CFArrayCreateMutableCopy(alloc
, 0, set
);
436 CFArraySubtract(result
, remove
);
442 void SOSEngineCircleChanged_locked(SOSEngineRef engine
, CFStringRef myPeerID
, CFArrayRef trustedPeers
, CFArrayRef untrustedPeers
) {
443 CFMutableArrayRef addedPeers
= CFArrayCreateDifference(kCFAllocatorDefault
, trustedPeers
, engine
->peerIDs
);
444 CFMutableArrayRef deletedPeers
= CFArrayCreateDifference(kCFAllocatorDefault
, engine
->peerIDs
, trustedPeers
);
446 CFStringRef tpDesc
= SOSPeerIDArrayCreateString(trustedPeers
);
447 CFStringRef apDesc
= SOSPeerIDArrayCreateString(addedPeers
);
448 CFStringRef dpDesc
= SOSPeerIDArrayCreateString(deletedPeers
);
449 secnotice("engine", "trusted %@ added %@ removed %@", tpDesc
, apDesc
, dpDesc
);
450 CFReleaseSafe(dpDesc
);
451 CFReleaseSafe(apDesc
);
452 CFReleaseSafe(tpDesc
);
454 SOSEngineSetTrustedPeers(engine
, myPeerID
, trustedPeers
);
456 // Remove any cached state for peers we no longer use but keep coders alive
457 if (deletedPeers
&& CFArrayGetCount(deletedPeers
) && engine
->peerState
) {
458 CFStringRef peerID
= NULL
;
459 CFArrayForEachC(deletedPeers
, peerID
) {
460 CFMutableDictionaryRef peer_data
= (CFMutableDictionaryRef
) CFDictionaryGetValue(engine
->peerState
, peerID
);
461 CFDataRef coder_data
= isDictionary(peer_data
) ? (CFDataRef
) CFDictionaryGetValue(peer_data
, kSOSPeerCoderKey
) : NULL
;
463 if(isData(coder_data
) &&
464 untrustedPeers
&& CFArrayContainsValue(untrustedPeers
, CFRangeMake(0, CFArrayGetCount(untrustedPeers
)), peerID
)) {
465 CFRetainSafe(coder_data
);
466 CFDictionaryRemoveAllValues(peer_data
);
467 CFDictionaryAddValue(peer_data
, kSOSPeerCoderKey
, coder_data
);
468 CFReleaseSafe(coder_data
);
470 CFDictionaryRemoveValue(engine
->peerState
, peerID
);
473 // Run though all peers and only cache manifests for peers we still have
474 // TODO: Factor out gc from SOSEngineHandleManifestUpdates and just call that
475 SOSEngineHandleManifestUpdates(engine
, kSOSDataSourceSOSTransaction
, NULL
, NULL
, NULL
);
478 CFReleaseNull(addedPeers
);
479 CFReleaseNull(deletedPeers
);
484 static SOSManifestRef
SOSEngineCopyCleanManifest(SOSEngineRef engine
, CFErrorRef
*error
) {
485 SOSManifestRef localMinusUnreadable
;
489 // Initialize the engine if a load fails. Basically this is our first time setup
490 static bool SOSEngineInit(SOSEngineRef engine
, CFErrorRef
*error
) {
492 secnotice("engine", "new engine for datasource named %@", SOSDataSourceGetName(engine
->dataSource
));
496 // Called by our DataSource in its constructor
497 SOSEngineRef
SOSEngineCreate(SOSDataSourceRef dataSource
, CFErrorRef
*error
) {
498 SOSEngineRef engine
= NULL
;
499 engine
= CFTypeAllocate(SOSEngine
, struct __OpaqueSOSEngine
, kCFAllocatorDefault
);
500 engine
->dataSource
= dataSource
;
501 engine
->queue
= dispatch_queue_create("engine", DISPATCH_QUEUE_SERIAL
);
502 CFErrorRef engineError
= NULL
;
503 if (!SOSEngineLoad(engine
, &engineError
)) {
504 secwarning("engine failed load state starting with nothing %@", engineError
);
505 CFReleaseNull(engineError
);
506 if (!SOSEngineInit(engine
, error
)) {
507 secerror("engine failed to initialze %@ giving up", engineError
);
515 // MARK: SOSEngine API
518 void SOSEngineDispose(SOSEngineRef engine
) {
519 // NOOP Engines stick around forever to monitor dataSource changes.
522 static SOSManifestRef
SOSEngineCopyManifest_locked(SOSEngineRef engine
, CFErrorRef
*error
) {
523 return CFRetainSafe(engine
->manifest
);
526 /* Handle incoming message from peer p. Return false if there was an error, true otherwise. */
527 static bool SOSEngineHandleMessage_locked(SOSEngineRef engine
, CFStringRef peerID
, SOSMessageRef message
,
528 SOSTransactionRef txn
, bool *commit
, bool *somethingChanged
, CFErrorRef
*error
) {
529 SOSPeerRef peer
= SOSPeerCreateWithEngine(engine
, peerID
);
530 CFStringRef peerDesc
= NULL
;
531 SOSManifestRef localManifest
= NULL
;
532 SOSManifestRef allAdditions
= NULL
;
533 SOSManifestRef confirmed
= NULL
;
534 SOSManifestRef base
= NULL
;
535 SOSManifestRef confirmedRemovals
= NULL
, confirmedAdditions
= NULL
;
536 __block
struct SOSDigestVector receivedObjects
= SOSDigestVectorInit
;
538 // Check for unknown criticial extensions in the message, and handle
539 // any other extensions we support
540 __block
bool ok
= true;
541 __block
struct SOSDigestVector dvadd
= SOSDigestVectorInit
;
543 require_action_quiet(peer
, exit
, ok
= SOSErrorCreate(errSecParam
, error
, NULL
, CFSTR("Couldn't create peer with Engine for %@"), peerID
));
544 peerDesc
= CFCopyDescription(peer
);
546 SOSMessageWithExtensions(message
, true, ^(CFDataRef oid
, bool isCritical
, CFDataRef extension
, bool *stop
) {
547 // OMFG a Critical extension what shall I do!
548 ok
= SOSErrorCreate(kSOSErrorNotReady
, error
, NULL
, CFSTR("Unknown criticial extension in peer message"));
551 require_quiet(ok
, exit
);
553 // Merge Objects from the message into our DataSource.
554 // Should we move the transaction to the SOSAccount level?
555 require_quiet(ok
&= SOSMessageWithSOSObjects(message
, engine
->dataSource
, error
, ^(SOSObjectRef peersObject
, bool *stop
) {
556 CFDataRef digest
= SOSObjectCopyDigest(engine
->dataSource
, peersObject
, error
);
560 secerror("%@ peer sent bad object: %@, rolling back changes", SOSPeerGetID(peer
), error
? *error
: NULL
);
563 SOSDigestVectorAppend(&receivedObjects
, CFDataGetBytePtr(digest
));
564 SOSMergeResult mr
= SOSDataSourceMergeObject(engine
->dataSource
, txn
, peersObject
, NULL
, error
);
565 // TODO: If the mr is kSOSMergeLocalObject most of the time (or all of the time),
566 // consider asking the peer to stop sending us objects, and send it objects instead.
567 ok
&= (mr
!= kSOSMergeFailure
);
571 // 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.
572 secerror("%@ SOSDataSourceMergeObject failed %@ rolling back changes", SOSPeerGetID(peer
), error
? *error
: NULL
);
573 } else if (mr
==kSOSMergePeersObject
|| mr
==kSOSMergeCreatedObject
) {
574 *somethingChanged
= true;
576 // mr == kSOSMergeLocalObject
577 // Ensure localObject is in local manifest (possible corruption) by posting an update when we are done.
578 SOSDigestVectorAppend(&dvadd
, CFDataGetBytePtr(digest
));
580 CFReleaseSafe(digest
);
582 struct SOSDigestVector dvunion
= SOSDigestVectorInit
;
583 SOSDigestVectorSort(&receivedObjects
);
584 SOSDigestVectorUnionSorted(SOSManifestGetDigestVector(SOSMessageGetAdditions(message
)), &receivedObjects
, &dvunion
);
585 allAdditions
= SOSManifestCreateWithDigestVector(&dvunion
, error
);
586 SOSDigestVectorFree(&receivedObjects
);
587 SOSDigestVectorFree(&dvunion
);
590 // Ensure any objects that we received and have localally already are actually in our local manifest
591 SOSManifestRef mfadd
= SOSManifestCreateWithDigestVector(&dvadd
, error
);
592 SOSDigestVectorFree(&dvadd
);
593 SOSEngineUpdateLocalManifest_locked(engine
, kSOSDataSourceSOSTransaction
, NULL
, mfadd
, error
);
594 CFReleaseSafe(mfadd
);
597 // ---- Don't use local or peer manifests from above this line, since commiting the SOSDataSourceWith transaction might change them ---
599 // Take a snapshot of our dataSource's local manifest.
600 require_quiet(ok
= localManifest
= SOSEngineCopyManifest_locked(engine
, error
), exit
);
602 CFDataRef baseDigest
= SOSMessageGetBaseDigest(message
);
603 CFDataRef proposedDigest
= SOSMessageGetProposedDigest(message
);
606 // I believe this is no longer needed now that we have eliminated extra,
607 // Since this is handeled below once we get a confirmed manifest from our
610 // If we just received a L00 reset pendingObjects to localManifest
611 if (!baseDigest
&& !proposedDigest
) {
612 SOSPeerSetPendingObjects(peer
, localManifest
);
613 secnotice("engine", "SOSPeerSetPendingObjects: %@", localManifest
);
617 base
= CFRetainSafe(SOSEngineGetManifestForDigest(engine
, baseDigest
));
618 confirmed
= CFRetainSafe(SOSEngineGetManifestForDigest(engine
, SOSMessageGetSenderDigest(message
)));
620 if (SOSManifestGetCount(SOSMessageGetRemovals(message
)) || SOSManifestGetCount(allAdditions
)) {
621 confirmed
= SOSManifestCreateWithPatch(base
, SOSMessageGetRemovals(message
), allAdditions
, error
);
623 confirmedRemovals
= CFRetainSafe(SOSMessageGetRemovals(message
));
624 confirmedAdditions
= CFRetainSafe(allAdditions
);
626 } else if (baseDigest
) {
627 confirmed
= CFRetainSafe(base
);
628 secerror("Protocol error send L00 - figure out later base: %@", base
);
631 secnotice("engine", "Confirmed: %@ base: %@", confirmed
, base
);
633 ok
&= SOSManifestDiff(SOSPeerGetConfirmedManifest(peer
), confirmed
, &confirmedRemovals
, &confirmedAdditions
, error
);
634 if (confirmedRemovals
|| confirmedAdditions
)
635 ok
&= SOSPeerDidReceiveRemovalsAndAdditions(peer
, confirmedRemovals
, confirmedAdditions
, localManifest
, error
);
636 SOSPeerSetConfirmedManifest(peer
, confirmed
);
638 // ---- SendObjects and extra->pendingObjects promotion dance ----
640 // The first block of code below sets peer.sendObjects to true when we receive a L00 and the second block
641 // moves extra to pendingObjects once we receive a confirmed manifest in or after the L00.
642 if (!baseDigest
&& !proposedDigest
) {
643 SOSPeerSetSendObjects(peer
, true);
646 // TODO: should this not depend on SOSPeerSendObjects?:
647 if (confirmed
/* && SOSPeerSendObjects(peer)*/) {
648 SOSManifestRef allExtra
= NULL
;
649 ok
&= SOSManifestDiff(confirmed
, localManifest
, NULL
, &allExtra
, error
);
650 secnotice("engine", "%@ confirmed %@ setting O:%@", SOSPeerGetID(peer
), confirmed
, allExtra
);
651 SOSPeerSetPendingObjects(peer
, allExtra
);
652 CFReleaseSafe(allExtra
);
656 secnoticeq("engine", "recv %@ %@", SOSPeerGetID(peer
), message
);
657 secnoticeq("peer", "recv %@ -> %@", peerDesc
, peer
);
660 CFReleaseSafe(confirmed
);
661 CFReleaseSafe(localManifest
);
662 CFReleaseSafe(peerDesc
);
663 CFReleaseSafe(allAdditions
);
664 CFReleaseSafe(confirmedRemovals
);
665 CFReleaseSafe(confirmedAdditions
);
670 static CFDataRef
SOSEngineCopyObjectDER(SOSEngineRef engine
, SOSObjectRef object
, CFErrorRef
*error
) {
671 CFDataRef der
= NULL
;
672 CFDictionaryRef plist
= SOSObjectCopyPropertyList(engine
->dataSource
, object
, error
);
674 der
= kc_plist_copy_der(plist
, error
);
680 static CFDataRef
SOSEngineCreateMessage_locked(SOSEngineRef engine
, SOSPeerRef peer
,
681 CFErrorRef
*error
, SOSEnginePeerMessageSentBlock
*sent
) {
682 SOSManifestRef local
= SOSEngineCopyManifest_locked(engine
, error
);
683 __block SOSMessageRef message
= SOSMessageCreate(kCFAllocatorDefault
, SOSPeerGetMessageVersion(peer
), error
);
684 SOSManifestRef confirmed
= SOSPeerGetConfirmedManifest(peer
);
685 SOSManifestRef pendingObjects
= SOSPeerGetPendingObjects(peer
);
686 SOSManifestRef objectsSent
= NULL
;
687 SOSManifestRef proposed
= NULL
;
688 SOSManifestRef allMissing
= NULL
;
689 SOSManifestRef allExtra
= NULL
;
690 SOSManifestRef extra
= NULL
;
691 SOSManifestRef excessPending
= NULL
;
692 SOSManifestRef missing
= NULL
;
693 SOSManifestRef deleted
= SOSPeerGetPendingDeletes(peer
);
694 SOSManifestRef excessDeleted
= NULL
;
695 CFDataRef result
= NULL
;
698 ok
= SOSManifestDiff(confirmed
, local
, &allMissing
, &allExtra
, error
);
699 ok
= ok
&& SOSManifestDiff(allExtra
, pendingObjects
, &extra
, &excessPending
, error
);
700 if (SOSManifestGetCount(excessPending
)) {
701 secerror("%@ ASSERTION FAILURE excess pendingObjects: %@", peer
, excessPending
);
702 // Remove excessPending from pendingObjects since they are either
703 // already in confirmed or not in local, either way there is no point
704 // keeping them in pendingObjects.
706 pendingObjects
= SOSManifestCreateComplement(excessPending
, pendingObjects
, error
);
707 SOSPeerSetPendingObjects(peer
, pendingObjects
);
708 CFReleaseSafe(pendingObjects
);
711 ok
= ok
&& SOSManifestDiff(allMissing
, deleted
, &missing
, &excessDeleted
, error
);
712 if (SOSManifestGetCount(excessDeleted
)) {
713 secerror("%@ ASSERTION FAILURE excess deleted: %@", peer
, excessDeleted
);
716 (void)ok
; // Dead store
717 CFReleaseNull(allExtra
);
718 CFReleaseNull(excessPending
);
719 CFReleaseNull(allMissing
);
720 CFReleaseNull(excessDeleted
);
722 // Send state for peer 7T0M+TD+A7HZ0frC5oHZnmdR0G: [LCP][os] P: 0, E: 0, M: 0
723 secnoticeq("engine", "Send state for peer %@: [%s%s%s][%s%s] P: %zu, E: %zu, M: %zu", SOSPeerGetID(peer
),
726 pendingObjects
? "P":"0",
727 SOSPeerSendObjects(peer
) ? "O":"o",
728 SOSPeerMustSendMessage(peer
) ? "S":"s",
729 SOSManifestGetCount(pendingObjects
),
730 SOSManifestGetCount(extra
),
731 SOSManifestGetCount(missing
)
735 // TODO: Because of not letting things terminate while we have extra left
736 // we might send objects when we didn't need to, but there is always an
737 // extra roundtrip required for objects that we assume the other peer
738 // should have already.
739 // TODO: If there are extra objects left, calling this function is not
740 // idempotent we should check if pending is what we are about to send and not send anything in this case.
741 if (SOSManifestGetCount(pendingObjects
) == 0 && SOSManifestGetCount(extra
) == 0)
742 SOSPeerSetSendObjects(peer
, false);
744 if (CFEqualSafe(local
, SOSPeerGetProposedManifest(peer
)) && !SOSPeerMustSendMessage(peer
)) {
746 if (CFEqual(confirmed
, local
)) {
747 secnoticeq("engine", "synced <No MSG> %@", peer
);
748 } else if (SOSManifestGetCount(pendingObjects
) == 0 /* TODO: No entries moved from extra to pendingObjects. */
749 && SOSManifestGetCount(missing
) == 0) {
750 secnoticeq("engine", "waiting <MSG not resent> %@", peer
);
755 CFReleaseSafe(local
);
756 CFReleaseSafe(message
);
757 CFReleaseNull(extra
);
758 CFReleaseNull(missing
);
759 return CFDataCreate(kCFAllocatorDefault
, NULL
, 0);
763 if (SOSManifestGetCount(pendingObjects
)) {
764 // If we have additions and we need to send objects send them.
765 __block
size_t objectsSize
= 0;
766 __block
struct SOSDigestVector dv
= SOSDigestVectorInit
;
767 __block
struct SOSDigestVector dvdel
= SOSDigestVectorInit
;
768 if (!SOSDataSourceForEachObject(engine
->dataSource
, pendingObjects
, error
, ^void(CFDataRef key
, SOSObjectRef object
, bool *stop
) {
769 CFErrorRef localError
= NULL
;
770 CFDataRef digest
= NULL
;
771 CFDataRef der
= NULL
;
773 const uint8_t *d
= CFDataGetBytePtr(key
);
774 secerrorq("%@ object %02X%02X%02X%02X dropping from manifest: not found in datasource",
775 SOSPeerGetID(peer
), d
[0], d
[1], d
[2], d
[3]);
776 SOSDigestVectorAppend(&dvdel
, CFDataGetBytePtr(key
));
777 } else if (!(der
= SOSEngineCopyObjectDER(engine
, object
, &localError
))
778 || !(digest
= SOSObjectCopyDigest(engine
->dataSource
, object
, &localError
))) {
779 if (SecErrorGetOSStatus(localError
) == errSecDecode
) {
780 // Decode error, we need to drop these objects from our manifests
781 const uint8_t *d
= CFDataGetBytePtr(key
);
782 secerrorq("%@ object %02X%02X%02X%02X dropping from manifest: %@",
783 SOSPeerGetID(peer
), d
[0], d
[1], d
[2], d
[3], localError
);
784 SOSDigestVectorAppend(&dvdel
, CFDataGetBytePtr(key
));
785 CFRelease(localError
);
787 // Stop iterating and propagate out all other errors.
788 const uint8_t *d
= CFDataGetBytePtr(key
);
789 secwarning("%@ object %02X%02X%02X%02X in SOSDataSourceForEachObject: %@",
790 SOSPeerGetID(peer
), d
[0], d
[1], d
[2], d
[3], localError
);
792 CFErrorPropagate(localError
, error
);
793 CFReleaseNull(message
);
796 if (!CFEqual(key
, digest
)) {
797 const uint8_t *d
= CFDataGetBytePtr(key
);
798 const uint8_t *e
= CFDataGetBytePtr(digest
);
799 secwarning("@ object %02X%02X%02X%02X is really %02X%02X%02X%02X dropping from local manifest", d
[0], d
[1], d
[2], d
[3], e
[0], e
[1], e
[2], e
[3]);
800 SOSDigestVectorAppend(&dvdel
, CFDataGetBytePtr(key
));
803 size_t objectLen
= (size_t)CFDataGetLength(der
);
804 if (SOSMessageAppendObject(message
, der
, &localError
)) {
805 SOSDigestVectorAppend(&dv
, CFDataGetBytePtr(digest
));
807 const uint8_t *d
= CFDataGetBytePtr(digest
);
808 CFStringRef hexder
= CFDataCopyHexString(der
);
809 secerrorq("%@ object %02X%02X%02X%02X der: %@ dropping from manifest: %@",
810 SOSPeerGetID(peer
), d
[0], d
[1], d
[2], d
[3], hexder
, localError
);
811 CFReleaseNull(hexder
);
812 CFReleaseNull(message
);
813 // Since we can't send these objects let's assume they are bad too?
814 SOSDigestVectorAppend(&dvdel
, CFDataGetBytePtr(digest
));
816 objectsSize
+= objectLen
;
817 if (objectsSize
> kSOSMessageMaxObjectsSize
)
821 CFReleaseSafe(digest
);
823 CFReleaseNull(message
);
826 objectsSent
= SOSManifestCreateWithDigestVector(&dv
, error
);
828 CFErrorRef localError
= NULL
;
829 SOSManifestRef mfdel
= SOSManifestCreateWithDigestVector(&dvdel
, error
);
830 SOSDigestVectorFree(&dvdel
);
831 if (!SOSEngineUpdateLocalManifest_locked(engine
, kSOSDataSourceSOSTransaction
, mfdel
, NULL
, &localError
))
832 secerror("SOSEngineUpdateLocalManifest deleting: %@ failed: %@", mfdel
, localError
);
833 CFReleaseSafe(localError
);
834 CFReleaseSafe(mfdel
);
835 CFAssignRetained(local
, SOSEngineCopyManifest_locked(engine
, error
));
837 SOSDigestVectorFree(&dv
);
840 // If we have no confirmed manifest, we want all pendedObjects going out as a manifest
841 objectsSent
= CFRetainSafe(pendingObjects
);
844 if (confirmed
|| SOSManifestGetCount(missing
) || SOSManifestGetCount(extra
) || objectsSent
) {
845 SOSManifestRef allExtra
= SOSManifestCreateUnion(extra
, objectsSent
, error
);
846 proposed
= SOSManifestCreateWithPatch(confirmed
, missing
, allExtra
, error
);
847 CFReleaseNull(allExtra
);
850 if (!SOSMessageSetManifests(message
, local
, confirmed
, proposed
, proposed
, confirmed
? objectsSent
: NULL
, error
))
851 CFReleaseNull(message
);
853 CFReleaseNull(objectsSent
);
856 result
= SOSMessageCreateData(message
, SOSPeerNextSequenceNumber(peer
), error
);
860 // Capture the peer in our block (SOSEnginePeerMessageSentBlock)
862 *sent
= Block_copy(^(bool success
) {
863 dispatch_async(engine
->queue
, ^{
865 if (!confirmed
&& !proposed
) {
866 SOSPeerSetSendObjects(peer
, true);
867 secnotice("engine", "SOSPeerSetSendObjects(true) L:%@", local
);
869 SOSPeerAddLocalManifest(peer
, local
);
870 SOSPeerAddProposedManifest(peer
, proposed
);
871 secnoticeq("engine", "send %@ %@", SOSPeerGetID(peer
), message
);
873 secerror("%@ failed to send %@", SOSPeerGetID(peer
), message
);
876 CFReleaseSafe(local
);
877 CFReleaseSafe(proposed
);
878 CFReleaseSafe(message
);
882 CFReleaseSafe(local
);
883 CFReleaseSafe(proposed
);
884 CFReleaseSafe(message
);
886 CFReleaseNull(extra
);
887 CFReleaseNull(missing
);
889 secerror("%@ error in send: %@", SOSPeerGetID(peer
), *error
);
894 static CFDataRef
SOSEngineCreateMessageToSyncToPeer_locked(SOSEngineRef engine
, CFStringRef peerID
, SOSEnginePeerMessageSentBlock
*sentBlock
, CFErrorRef
*error
)
896 SOSPeerRef peer
= SOSPeerCreateWithEngine(engine
, peerID
);
897 CFDataRef message
= SOSEngineCreateMessage_locked(engine
, peer
, error
, sentBlock
);
903 bool SOSEngineHandleMessage(SOSEngineRef engine
, CFStringRef peerID
,
904 CFDataRef raw_message
, CFErrorRef
*error
)
906 __block
bool result
= false;
907 __block
bool somethingChanged
= false;
908 SOSMessageRef message
= SOSMessageCreateWithData(kCFAllocatorDefault
, raw_message
, error
);
909 result
= message
&& SOSDataSourceWith(engine
->dataSource
, error
, ^(SOSTransactionRef txn
, bool *commit
) {
910 result
= SOSEngineHandleMessage_locked(engine
, peerID
, message
, txn
, commit
, &somethingChanged
, error
);
912 CFReleaseSafe(message
);
913 if (somethingChanged
)
914 SecKeychainChanged(false);
918 // --- Called from off the queue, need to move to on the queue
920 static void SOSEngineDoOnQueue(SOSEngineRef engine
, dispatch_block_t action
)
922 dispatch_sync(engine
->queue
, action
);
925 void SOSEngineCircleChanged(SOSEngineRef engine
, CFStringRef myPeerID
, CFArrayRef trustedPeers
, CFArrayRef untrustedPeers
) {
926 SOSEngineDoOnQueue(engine
, ^{
927 SOSEngineCircleChanged_locked(engine
, myPeerID
, trustedPeers
, untrustedPeers
);
930 __block CFErrorRef localError
= NULL
;
931 SOSDataSourceWith(engine
->dataSource
, &localError
, ^(SOSTransactionRef txn
, bool *commit
) {
932 SOSEngineDoOnQueue(engine
, ^{
933 *commit
= SOSEngineSave(engine
, txn
, &localError
);
937 secerror("failed to save engine state: %@", localError
);
938 CFReleaseSafe(localError
);
942 SOSManifestRef
SOSEngineCopyManifest(SOSEngineRef engine
, CFErrorRef
*error
) {
943 __block SOSManifestRef result
= NULL
;
944 SOSEngineDoOnQueue(engine
, ^{
945 result
= SOSEngineCopyManifest_locked(engine
, error
);
950 bool SOSEngineUpdateLocalManifest(SOSEngineRef engine
, SOSDataSourceTransactionSource source
, struct SOSDigestVector
*removals
, struct SOSDigestVector
*additions
, CFErrorRef
*error
) {
951 __block
bool result
= true;
952 SOSManifestRef mfdel
= SOSManifestCreateWithDigestVector(removals
, error
);
953 SOSManifestRef mfadd
= SOSManifestCreateWithDigestVector(additions
, error
);
954 SOSEngineDoOnQueue(engine
, ^{
955 // Safe to run async if needed...
956 result
= SOSEngineUpdateLocalManifest_locked(engine
, source
, mfdel
, mfadd
, error
);
957 CFReleaseSafe(mfdel
);
958 CFReleaseSafe(mfadd
);
963 static bool SOSEngineSetCoderData_locked(SOSEngineRef engine
, CFStringRef peer_id
, CFDataRef data
, CFErrorRef
*error
) {
964 CFMutableDictionaryRef state
= NULL
;
966 if (!engine
->peerState
) {
967 engine
->peerState
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
968 state
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
971 state
= (CFMutableDictionaryRef
)CFDictionaryGetValue(engine
->peerState
, peer_id
);
973 state
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
975 CFDictionarySetValue(state
, kSOSPeerCoderKey
, data
);
976 CFDictionarySetValue(engine
->peerState
, peer_id
, state
);
978 }else if (engine
->peerState
) {
979 if(CFDictionaryContainsKey(engine
->peerState
, peer_id
)){
980 CFMutableDictionaryRef state
= (CFMutableDictionaryRef
)CFDictionaryGetValue(engine
->peerState
, peer_id
);
981 if(CFDictionaryContainsKey(state
, kSOSPeerCoderKey
))
982 CFDictionaryRemoveValue(state
, kSOSPeerCoderKey
);
984 if (CFDictionaryGetCount(engine
->peerState
) == 0) {
985 CFReleaseNull(engine
->peerState
);
991 bool SOSEngineSetCoderData(SOSEngineRef engine
, CFStringRef peer_id
, CFDataRef data
, CFErrorRef
*error
) {
992 __block
bool result
= false;
994 SOSDataSourceWith(engine
->dataSource
, error
, ^(SOSTransactionRef txn
, bool *commit
) {
995 dispatch_sync(engine
->queue
, ^{
996 result
= SOSEngineSetCoderData_locked(engine
, peer_id
, data
, error
);
1003 static CFDataRef
SOSEngineGetCoderData_locked(SOSEngineRef engine
, CFStringRef peer_id
) {
1004 // TODO: probably remove these secnotices
1005 CFDataRef result
= NULL
;
1006 CFMutableDictionaryRef peerState
= NULL
;
1008 if (!engine
->peerState
)
1009 secdebug("engine", "No engine coderData");
1011 peerState
= (CFMutableDictionaryRef
)CFDictionaryGetValue(engine
->peerState
, peer_id
);
1013 secdebug("engine", "No peerState for peer %@", peer_id
);
1015 result
= CFDictionaryGetValue(peerState
, kSOSPeerCoderKey
);
1017 secdebug("engine", "No coder data for peer %@", peer_id
);
1023 CFDataRef
SOSEngineGetCoderData(SOSEngineRef engine
, CFStringRef peer_id
) {
1024 __block CFDataRef result
= NULL
;
1025 SOSDataSourceWith(engine
->dataSource
, NULL
, ^(SOSTransactionRef txn
, bool *commit
) {
1026 dispatch_sync(engine
->queue
, ^{
1027 result
= SOSEngineGetCoderData_locked(engine
, peer_id
);
1035 // Peer state layout. WRONG! It's an array now
1036 // The peer state is an array.
1037 // The first element of the array is a dictionary with any number of keys and
1038 // values in it (for future expansion) such as changing the digest size or type
1039 // or remebering boolean flags for a peers sake.
1040 // The next three are special in that they are manifest digests with special
1041 // meaning and rules as to how they are treated (These are dynamically updated
1042 // based on database activity so they have a fully history of all changes made
1043 // to the local db. The first is the manifest representing the pendingObjects
1044 // to send to the other peer. This is normally only ever appending to, and in
1045 // particular with transactions originating from the Keychain API that affect
1046 // syncable items will need to add the new objects digests to the pendingObjects list
1047 // while adding the digests of any tombstones encountered to the extra list.
1049 CFMutableDictionaryRef
SOSEngineGetPeerState(SOSEngineRef engine
, CFStringRef peerID
) {
1050 CFMutableDictionaryRef peerState
= NULL
;
1051 if (engine
->peerState
)
1052 peerState
= (CFMutableDictionaryRef
)CFDictionaryGetValue(engine
->peerState
, peerID
);
1054 engine
->peerState
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1056 peerState
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1057 CFDictionaryAddValue(engine
->peerState
, peerID
, peerState
);
1058 CFReleaseSafe(peerState
);
1063 CFDataRef
SOSEngineCreateMessageToSyncToPeer(SOSEngineRef engine
, CFStringRef peerID
, SOSEnginePeerMessageSentBlock
*sentBlock
, CFErrorRef
*error
) {
1064 __block CFDataRef result
= NULL
;
1065 SOSEngineDoOnQueue(engine
, ^{
1066 result
= SOSEngineCreateMessageToSyncToPeer_locked(engine
, peerID
, sentBlock
, error
);
1071 bool SOSEnginePeerDidConnect(SOSEngineRef engine
, CFStringRef peerID
, CFErrorRef
*error
) {
1072 __block
bool result
= true;
1073 result
&= SOSDataSourceWith(engine
->dataSource
, error
, ^(SOSTransactionRef txn
, bool *commit
) {
1074 dispatch_sync(engine
->queue
, ^{
1075 SOSPeerRef peer
= SOSPeerCreateWithEngine(engine
, peerID
);
1077 result
= SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("Engine has no peer for %@"), peerID
);
1079 SOSPeerDidConnect(peer
);
1080 result
= SOSEngineSave(engine
, txn
, error
);
1081 CFReleaseSafe(peer
);