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 // WARNING: This will deadlock the engine if you call a
356 // SecItem API function while holding the engine lock!
357 // However making this async right now isn't safe yet either
358 // Due to some code in the enginer using Get v/s copy to
359 // access some of the values that would be modified
360 // asynchronously here since the engine is coded as if
361 // running on a serial queue.
362 dispatch_sync(engine
->queue
, processUpdates
);
365 SOSDataSourceSetNotifyPhaseBlock(engine
->dataSource
, ^(SOSDataSourceRef ds
, SOSTransactionRef txn
, SOSDataSourceTransactionPhase phase
, SOSDataSourceTransactionSource source
, struct SOSDigestVector
*removals
, struct SOSDigestVector
*additions
) {
366 secnoticeq("engine", "No peers to notify"); // TODO: DEBUG - remove this
368 CFReleaseNull(engine
->manifest
);
373 static bool SOSEngineSetState(SOSEngineRef engine
, CFDataRef state
, CFErrorRef
*error
) {
376 CFMutableDictionaryRef dict
= NULL
;
377 const uint8_t *der
= CFDataGetBytePtr(state
);
378 const uint8_t *der_end
= der
+ CFDataGetLength(state
);
379 der
= der_decode_dictionary(kCFAllocatorDefault
, kCFPropertyListMutableContainers
, (CFDictionaryRef
*)&dict
, error
, der
, der_end
);
380 if (der
&& der
!= der_end
) {
381 ok
= SOSErrorCreate(kSOSErrorDecodeFailure
, error
, NULL
, CFSTR("trailing %td bytes at end of state"), der_end
- der
);
384 SOSEngineSetTrustedPeers(engine
, (CFStringRef
)CFDictionaryGetValue(dict
, kSOSEngineIDKey
),
385 (CFArrayRef
)CFDictionaryGetValue(dict
, kSOSEnginePeerIDsKey
));
386 CFRetainAssign(engine
->peerState
, (CFMutableDictionaryRef
)CFDictionaryGetValue(dict
, kSOSEnginePeerStateKey
));
388 CFReleaseNull(engine
->manifestCache
);
389 CFMutableDictionaryRef mfc
= (CFMutableDictionaryRef
)CFDictionaryGetValue(dict
, kSOSEngineManifestCacheKey
);
391 engine
->manifestCache
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
392 CFDictionaryForEach(mfc
, ^(const void *key
, const void *value
) {
393 CFDataRef data
= (CFDataRef
)value
;
395 SOSManifestRef mf
= SOSManifestCreateWithData(data
, NULL
);
397 CFDictionarySetValue(engine
->manifestCache
, key
, mf
);
405 secnotice("engine", "%@", engine
);
409 static bool SOSEngineLoad(SOSEngineRef engine
, CFErrorRef
*error
) {
410 CFDataRef state
= SOSDataSourceCopyStateWithKey(engine
->dataSource
, kSOSEngineState
, kSecAttrAccessibleAlways
, error
);
411 bool ok
= state
&& SOSEngineSetState(engine
, state
, error
);
412 CFReleaseSafe(state
);
416 static void CFArraySubtract(CFMutableArrayRef from
, CFArrayRef remove
) {
418 CFArrayForEach(remove
, ^(const void *value
) {
419 CFArrayRemoveAllValue(from
, value
);
424 static CFMutableArrayRef
CFArrayCreateDifference(CFAllocatorRef alloc
, CFArrayRef set
, CFArrayRef remove
) {
425 CFMutableArrayRef result
;
427 result
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
429 result
= CFArrayCreateMutableCopy(alloc
, 0, set
);
432 CFArraySubtract(result
, remove
);
438 void SOSEngineCircleChanged_locked(SOSEngineRef engine
, CFStringRef myPeerID
, CFArrayRef trustedPeers
, CFArrayRef untrustedPeers
) {
439 CFMutableArrayRef addedPeers
= CFArrayCreateDifference(kCFAllocatorDefault
, trustedPeers
, engine
->peerIDs
);
440 CFMutableArrayRef deletedPeers
= CFArrayCreateDifference(kCFAllocatorDefault
, engine
->peerIDs
, trustedPeers
);
442 CFStringRef tpDesc
= SOSPeerIDArrayCreateString(trustedPeers
);
443 CFStringRef apDesc
= SOSPeerIDArrayCreateString(addedPeers
);
444 CFStringRef dpDesc
= SOSPeerIDArrayCreateString(deletedPeers
);
445 secnotice("engine", "trusted %@ added %@ removed %@", tpDesc
, apDesc
, dpDesc
);
446 CFReleaseSafe(dpDesc
);
447 CFReleaseSafe(apDesc
);
448 CFReleaseSafe(tpDesc
);
450 SOSEngineSetTrustedPeers(engine
, myPeerID
, trustedPeers
);
452 // Remove any cached state for peers we no longer use but keep coders alive
453 if (deletedPeers
&& CFArrayGetCount(deletedPeers
) && engine
->peerState
) {
454 CFStringRef peerID
= NULL
;
455 CFArrayForEachC(deletedPeers
, peerID
) {
456 CFMutableDictionaryRef peer_data
= (CFMutableDictionaryRef
) CFDictionaryGetValue(engine
->peerState
, peerID
);
457 CFDataRef coder_data
= isDictionary(peer_data
) ? (CFDataRef
) CFDictionaryGetValue(peer_data
, kSOSPeerCoderKey
) : NULL
;
459 if(isData(coder_data
) &&
460 untrustedPeers
&& CFArrayContainsValue(untrustedPeers
, CFRangeMake(0, CFArrayGetCount(untrustedPeers
)), peerID
)) {
461 CFRetainSafe(coder_data
);
462 CFDictionaryRemoveAllValues(peer_data
);
463 CFDictionaryAddValue(peer_data
, kSOSPeerCoderKey
, coder_data
);
464 CFReleaseSafe(coder_data
);
466 CFDictionaryRemoveValue(engine
->peerState
, peerID
);
469 // Run though all peers and only cache manifests for peers we still have
470 // TODO: Factor out gc from SOSEngineHandleManifestUpdates and just call that
471 SOSEngineHandleManifestUpdates(engine
, kSOSDataSourceSOSTransaction
, NULL
, NULL
, NULL
);
474 CFReleaseNull(addedPeers
);
475 CFReleaseNull(deletedPeers
);
480 static SOSManifestRef
SOSEngineCopyCleanManifest(SOSEngineRef engine
, CFErrorRef
*error
) {
481 SOSManifestRef localMinusUnreadable
;
485 // Initialize the engine if a load fails. Basically this is our first time setup
486 static bool SOSEngineInit(SOSEngineRef engine
, CFErrorRef
*error
) {
488 secnotice("engine", "new engine for datasource named %@", SOSDataSourceGetName(engine
->dataSource
));
492 // Called by our DataSource in its constructor
493 SOSEngineRef
SOSEngineCreate(SOSDataSourceRef dataSource
, CFErrorRef
*error
) {
494 SOSEngineRef engine
= NULL
;
495 engine
= CFTypeAllocate(SOSEngine
, struct __OpaqueSOSEngine
, kCFAllocatorDefault
);
496 engine
->dataSource
= dataSource
;
497 engine
->queue
= dispatch_queue_create("engine", DISPATCH_QUEUE_SERIAL
);
498 CFErrorRef engineError
= NULL
;
499 if (!SOSEngineLoad(engine
, &engineError
)) {
500 secwarning("engine failed load state starting with nothing %@", engineError
);
501 CFReleaseNull(engineError
);
502 if (!SOSEngineInit(engine
, error
)) {
503 secerror("engine failed to initialze %@ giving up", engineError
);
511 // MARK: SOSEngine API
514 void SOSEngineDispose(SOSEngineRef engine
) {
515 // NOOP Engines stick around forever to monitor dataSource changes.
518 static SOSManifestRef
SOSEngineCopyManifest_locked(SOSEngineRef engine
, CFErrorRef
*error
) {
519 return CFRetainSafe(engine
->manifest
);
522 /* Handle incoming message from peer p. Return false if there was an error, true otherwise. */
523 static bool SOSEngineHandleMessage_locked(SOSEngineRef engine
, CFStringRef peerID
, SOSMessageRef message
,
524 SOSTransactionRef txn
, bool *commit
, bool *somethingChanged
, CFErrorRef
*error
) {
525 SOSPeerRef peer
= SOSPeerCreateWithEngine(engine
, peerID
);
526 CFStringRef peerDesc
= NULL
;
527 SOSManifestRef localManifest
= NULL
;
528 SOSManifestRef allAdditions
= NULL
;
529 SOSManifestRef confirmed
= NULL
;
530 SOSManifestRef base
= NULL
;
531 SOSManifestRef confirmedRemovals
= NULL
, confirmedAdditions
= NULL
;
532 __block
struct SOSDigestVector receivedObjects
= SOSDigestVectorInit
;
534 // Check for unknown criticial extensions in the message, and handle
535 // any other extensions we support
536 __block
bool ok
= true;
537 __block
struct SOSDigestVector dvadd
= SOSDigestVectorInit
;
539 require_action_quiet(peer
, exit
, ok
= SOSErrorCreate(errSecParam
, error
, NULL
, CFSTR("Couldn't create peer with Engine for %@"), peerID
));
540 peerDesc
= CFCopyDescription(peer
);
542 SOSMessageWithExtensions(message
, true, ^(CFDataRef oid
, bool isCritical
, CFDataRef extension
, bool *stop
) {
543 // OMFG a Critical extension what shall I do!
544 ok
= SOSErrorCreate(kSOSErrorNotReady
, error
, NULL
, CFSTR("Unknown criticial extension in peer message"));
547 require_quiet(ok
, exit
);
549 // Merge Objects from the message into our DataSource.
550 // Should we move the transaction to the SOSAccount level?
551 require_quiet(ok
&= SOSMessageWithSOSObjects(message
, engine
->dataSource
, error
, ^(SOSObjectRef peersObject
, bool *stop
) {
552 CFDataRef digest
= SOSObjectCopyDigest(engine
->dataSource
, peersObject
, error
);
556 secerror("%@ peer sent bad object: %@, rolling back changes", SOSPeerGetID(peer
), error
? *error
: NULL
);
559 SOSDigestVectorAppend(&receivedObjects
, CFDataGetBytePtr(digest
));
560 SOSMergeResult mr
= SOSDataSourceMergeObject(engine
->dataSource
, txn
, peersObject
, NULL
, error
);
561 // TODO: If the mr is kSOSMergeLocalObject most of the time (or all of the time),
562 // consider asking the peer to stop sending us objects, and send it objects instead.
563 ok
&= (mr
!= kSOSMergeFailure
);
567 // 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.
568 secerror("%@ SOSDataSourceMergeObject failed %@ rolling back changes", SOSPeerGetID(peer
), error
? *error
: NULL
);
569 } else if (mr
==kSOSMergePeersObject
|| mr
==kSOSMergeCreatedObject
) {
570 *somethingChanged
= true;
572 // mr == kSOSMergeLocalObject
573 // Ensure localObject is in local manifest (possible corruption) by posting an update when we are done.
574 SOSDigestVectorAppend(&dvadd
, CFDataGetBytePtr(digest
));
576 CFReleaseSafe(digest
);
578 struct SOSDigestVector dvunion
= SOSDigestVectorInit
;
579 SOSDigestVectorSort(&receivedObjects
);
580 SOSDigestVectorUnionSorted(SOSManifestGetDigestVector(SOSMessageGetAdditions(message
)), &receivedObjects
, &dvunion
);
581 allAdditions
= SOSManifestCreateWithDigestVector(&dvunion
, error
);
582 SOSDigestVectorFree(&receivedObjects
);
583 SOSDigestVectorFree(&dvunion
);
586 // Ensure any objects that we received and have localally already are actually in our local manifest
587 SOSManifestRef mfadd
= SOSManifestCreateWithDigestVector(&dvadd
, error
);
588 SOSDigestVectorFree(&dvadd
);
589 SOSEngineUpdateLocalManifest_locked(engine
, kSOSDataSourceSOSTransaction
, NULL
, mfadd
, error
);
590 CFReleaseSafe(mfadd
);
593 // ---- Don't use local or peer manifests from above this line, since commiting the SOSDataSourceWith transaction might change them ---
595 // Take a snapshot of our dataSource's local manifest.
596 require_quiet(ok
= localManifest
= SOSEngineCopyManifest_locked(engine
, error
), exit
);
598 CFDataRef baseDigest
= SOSMessageGetBaseDigest(message
);
599 CFDataRef proposedDigest
= SOSMessageGetProposedDigest(message
);
602 // I believe this is no longer needed now that we have eliminated extra,
603 // Since this is handeled below once we get a confirmed manifest from our
606 // If we just received a L00 reset pendingObjects to localManifest
607 if (!baseDigest
&& !proposedDigest
) {
608 SOSPeerSetPendingObjects(peer
, localManifest
);
609 secnotice("engine", "SOSPeerSetPendingObjects: %@", localManifest
);
613 base
= CFRetainSafe(SOSEngineGetManifestForDigest(engine
, baseDigest
));
614 confirmed
= CFRetainSafe(SOSEngineGetManifestForDigest(engine
, SOSMessageGetSenderDigest(message
)));
616 if (SOSManifestGetCount(SOSMessageGetRemovals(message
)) || SOSManifestGetCount(allAdditions
)) {
617 confirmed
= SOSManifestCreateWithPatch(base
, SOSMessageGetRemovals(message
), allAdditions
, error
);
619 confirmedRemovals
= CFRetainSafe(SOSMessageGetRemovals(message
));
620 confirmedAdditions
= CFRetainSafe(allAdditions
);
622 } else if (baseDigest
) {
623 confirmed
= CFRetainSafe(base
);
624 secerror("Protocol error send L00 - figure out later base: %@", base
);
627 secnotice("engine", "Confirmed: %@ base: %@", confirmed
, base
);
629 ok
&= SOSManifestDiff(SOSPeerGetConfirmedManifest(peer
), confirmed
, &confirmedRemovals
, &confirmedAdditions
, error
);
630 if (confirmedRemovals
|| confirmedAdditions
)
631 ok
&= SOSPeerDidReceiveRemovalsAndAdditions(peer
, confirmedRemovals
, confirmedAdditions
, localManifest
, error
);
632 SOSPeerSetConfirmedManifest(peer
, confirmed
);
634 // ---- SendObjects and extra->pendingObjects promotion dance ----
636 // The first block of code below sets peer.sendObjects to true when we receive a L00 and the second block
637 // moves extra to pendingObjects once we receive a confirmed manifest in or after the L00.
638 if (!baseDigest
&& !proposedDigest
) {
639 SOSPeerSetSendObjects(peer
, true);
642 // TODO: should this not depend on SOSPeerSendObjects?:
643 if (confirmed
/* && SOSPeerSendObjects(peer)*/) {
644 SOSManifestRef allExtra
= NULL
;
645 ok
&= SOSManifestDiff(confirmed
, localManifest
, NULL
, &allExtra
, error
);
646 secnotice("engine", "%@ confirmed %@ setting O:%@", SOSPeerGetID(peer
), confirmed
, allExtra
);
647 SOSPeerSetPendingObjects(peer
, allExtra
);
648 CFReleaseSafe(allExtra
);
652 secnoticeq("engine", "recv %@ %@", SOSPeerGetID(peer
), message
);
653 secnoticeq("peer", "recv %@ -> %@", peerDesc
, peer
);
656 CFReleaseSafe(confirmed
);
657 CFReleaseSafe(localManifest
);
658 CFReleaseSafe(peerDesc
);
659 CFReleaseSafe(allAdditions
);
660 CFReleaseSafe(confirmedRemovals
);
661 CFReleaseSafe(confirmedAdditions
);
666 static CFDataRef
SOSEngineCopyObjectDER(SOSEngineRef engine
, SOSObjectRef object
, CFErrorRef
*error
) {
667 CFDataRef der
= NULL
;
668 CFDictionaryRef plist
= SOSObjectCopyPropertyList(engine
->dataSource
, object
, error
);
670 der
= kc_plist_copy_der(plist
, error
);
676 static CFDataRef
SOSEngineCreateMessage_locked(SOSEngineRef engine
, SOSPeerRef peer
,
677 CFErrorRef
*error
, SOSEnginePeerMessageSentBlock
*sent
) {
678 SOSManifestRef local
= SOSEngineCopyManifest_locked(engine
, error
);
679 __block SOSMessageRef message
= SOSMessageCreate(kCFAllocatorDefault
, SOSPeerGetMessageVersion(peer
), error
);
680 SOSManifestRef confirmed
= SOSPeerGetConfirmedManifest(peer
);
681 SOSManifestRef pendingObjects
= SOSPeerGetPendingObjects(peer
);
682 SOSManifestRef objectsSent
= NULL
;
683 SOSManifestRef proposed
= NULL
;
684 SOSManifestRef allMissing
= NULL
;
685 SOSManifestRef allExtra
= NULL
;
686 SOSManifestRef extra
= NULL
;
687 SOSManifestRef excessPending
= NULL
;
688 SOSManifestRef missing
= NULL
;
689 SOSManifestRef deleted
= SOSPeerGetPendingDeletes(peer
);
690 SOSManifestRef excessDeleted
= NULL
;
691 CFDataRef result
= NULL
;
694 ok
= SOSManifestDiff(confirmed
, local
, &allMissing
, &allExtra
, error
);
695 ok
= ok
&& SOSManifestDiff(allExtra
, pendingObjects
, &extra
, &excessPending
, error
);
696 if (SOSManifestGetCount(excessPending
)) {
697 secerror("%@ ASSERTION FAILURE excess pendingObjects: %@", peer
, excessPending
);
698 // Remove excessPending from pendingObjects since they are either
699 // already in confirmed or not in local, either way there is no point
700 // keeping them in pendingObjects.
702 pendingObjects
= SOSManifestCreateComplement(excessPending
, pendingObjects
, error
);
703 SOSPeerSetPendingObjects(peer
, pendingObjects
);
704 CFReleaseSafe(pendingObjects
);
707 ok
= ok
&& SOSManifestDiff(allMissing
, deleted
, &missing
, &excessDeleted
, error
);
708 if (SOSManifestGetCount(excessDeleted
)) {
709 secerror("%@ ASSERTION FAILURE excess deleted: %@", peer
, excessDeleted
);
712 (void)ok
; // Dead store
713 CFReleaseNull(allExtra
);
714 CFReleaseNull(excessPending
);
715 CFReleaseNull(allMissing
);
716 CFReleaseNull(excessDeleted
);
718 // Send state for peer 7T0M+TD+A7HZ0frC5oHZnmdR0G: [LCP][os] P: 0, E: 0, M: 0
719 secnoticeq("engine", "Send state for peer %@: [%s%s%s][%s%s] P: %zu, E: %zu, M: %zu", SOSPeerGetID(peer
),
722 pendingObjects
? "P":"0",
723 SOSPeerSendObjects(peer
) ? "O":"o",
724 SOSPeerMustSendMessage(peer
) ? "S":"s",
725 SOSManifestGetCount(pendingObjects
),
726 SOSManifestGetCount(extra
),
727 SOSManifestGetCount(missing
)
731 // TODO: Because of not letting things terminate while we have extra left
732 // we might send objects when we didn't need to, but there is always an
733 // extra roundtrip required for objects that we assume the other peer
734 // should have already.
735 // TODO: If there are extra objects left, calling this function is not
736 // idempotent we should check if pending is what we are about to send and not send anything in this case.
737 if (SOSManifestGetCount(pendingObjects
) == 0 && SOSManifestGetCount(extra
) == 0)
738 SOSPeerSetSendObjects(peer
, false);
740 if (CFEqualSafe(local
, SOSPeerGetProposedManifest(peer
)) && !SOSPeerMustSendMessage(peer
)) {
742 if (CFEqual(confirmed
, local
)) {
743 secnoticeq("engine", "synced <No MSG> %@", peer
);
744 } else if (SOSManifestGetCount(pendingObjects
) == 0 /* TODO: No entries moved from extra to pendingObjects. */
745 && SOSManifestGetCount(missing
) == 0) {
746 secnoticeq("engine", "waiting <MSG not resent> %@", peer
);
751 CFReleaseSafe(local
);
752 CFReleaseSafe(message
);
753 CFReleaseNull(extra
);
754 CFReleaseNull(missing
);
755 return CFDataCreate(kCFAllocatorDefault
, NULL
, 0);
759 if (SOSManifestGetCount(pendingObjects
)) {
760 // If we have additions and we need to send objects send them.
761 __block
size_t objectsSize
= 0;
762 __block
struct SOSDigestVector dv
= SOSDigestVectorInit
;
763 __block
struct SOSDigestVector dvdel
= SOSDigestVectorInit
;
764 if (!SOSDataSourceForEachObject(engine
->dataSource
, pendingObjects
, error
, ^void(CFDataRef key
, SOSObjectRef object
, bool *stop
) {
765 CFErrorRef localError
= NULL
;
766 CFDataRef digest
= NULL
;
767 CFDataRef der
= NULL
;
769 const uint8_t *d
= CFDataGetBytePtr(key
);
770 secerrorq("%@ object %02X%02X%02X%02X dropping from manifest: not found in datasource",
771 SOSPeerGetID(peer
), d
[0], d
[1], d
[2], d
[3]);
772 SOSDigestVectorAppend(&dvdel
, CFDataGetBytePtr(key
));
773 } else if (!(der
= SOSEngineCopyObjectDER(engine
, object
, &localError
))
774 || !(digest
= SOSObjectCopyDigest(engine
->dataSource
, object
, &localError
))) {
775 if (SecErrorGetOSStatus(localError
) == errSecDecode
) {
776 // Decode error, we need to drop these objects from our manifests
777 const uint8_t *d
= CFDataGetBytePtr(key
);
778 secerrorq("%@ object %02X%02X%02X%02X dropping from manifest: %@",
779 SOSPeerGetID(peer
), d
[0], d
[1], d
[2], d
[3], localError
);
780 SOSDigestVectorAppend(&dvdel
, CFDataGetBytePtr(key
));
781 CFRelease(localError
);
783 // Stop iterating and propagate out all other errors.
784 const uint8_t *d
= CFDataGetBytePtr(key
);
785 secwarning("%@ object %02X%02X%02X%02X in SOSDataSourceForEachObject: %@",
786 SOSPeerGetID(peer
), d
[0], d
[1], d
[2], d
[3], localError
);
788 CFErrorPropagate(localError
, error
);
789 CFReleaseNull(message
);
792 if (!CFEqual(key
, digest
)) {
793 const uint8_t *d
= CFDataGetBytePtr(key
);
794 const uint8_t *e
= CFDataGetBytePtr(digest
);
795 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]);
796 SOSDigestVectorAppend(&dvdel
, CFDataGetBytePtr(key
));
799 size_t objectLen
= (size_t)CFDataGetLength(der
);
800 if (SOSMessageAppendObject(message
, der
, &localError
)) {
801 SOSDigestVectorAppend(&dv
, CFDataGetBytePtr(digest
));
803 const uint8_t *d
= CFDataGetBytePtr(digest
);
804 CFStringRef hexder
= CFDataCopyHexString(der
);
805 secerrorq("%@ object %02X%02X%02X%02X der: %@ dropping from manifest: %@",
806 SOSPeerGetID(peer
), d
[0], d
[1], d
[2], d
[3], hexder
, localError
);
807 CFReleaseNull(hexder
);
808 CFReleaseNull(message
);
809 // Since we can't send these objects let's assume they are bad too?
810 SOSDigestVectorAppend(&dvdel
, CFDataGetBytePtr(digest
));
812 objectsSize
+= objectLen
;
813 if (objectsSize
> kSOSMessageMaxObjectsSize
)
817 CFReleaseSafe(digest
);
819 CFReleaseNull(message
);
822 objectsSent
= SOSManifestCreateWithDigestVector(&dv
, error
);
824 CFErrorRef localError
= NULL
;
825 SOSManifestRef mfdel
= SOSManifestCreateWithDigestVector(&dvdel
, error
);
826 SOSDigestVectorFree(&dvdel
);
827 if (!SOSEngineUpdateLocalManifest_locked(engine
, kSOSDataSourceSOSTransaction
, mfdel
, NULL
, &localError
))
828 secerror("SOSEngineUpdateLocalManifest deleting: %@ failed: %@", mfdel
, localError
);
829 CFReleaseSafe(localError
);
830 CFReleaseSafe(mfdel
);
831 CFAssignRetained(local
, SOSEngineCopyManifest_locked(engine
, error
));
833 SOSDigestVectorFree(&dv
);
836 // If we have no confirmed manifest, we want all pendedObjects going out as a manifest
837 objectsSent
= CFRetainSafe(pendingObjects
);
840 if (confirmed
|| SOSManifestGetCount(missing
) || SOSManifestGetCount(extra
) || objectsSent
) {
841 SOSManifestRef allExtra
= SOSManifestCreateUnion(extra
, objectsSent
, error
);
842 proposed
= SOSManifestCreateWithPatch(confirmed
, missing
, allExtra
, error
);
843 CFReleaseNull(allExtra
);
846 if (!SOSMessageSetManifests(message
, local
, confirmed
, proposed
, proposed
, confirmed
? objectsSent
: NULL
, error
))
847 CFReleaseNull(message
);
849 CFReleaseNull(objectsSent
);
852 result
= SOSMessageCreateData(message
, SOSPeerNextSequenceNumber(peer
), error
);
856 // Capture the peer in our block (SOSEnginePeerMessageSentBlock)
858 *sent
= Block_copy(^(bool success
) {
859 dispatch_async(engine
->queue
, ^{
861 if (!confirmed
&& !proposed
) {
862 SOSPeerSetSendObjects(peer
, true);
863 secnotice("engine", "SOSPeerSetSendObjects(true) L:%@", local
);
865 SOSPeerAddLocalManifest(peer
, local
);
866 SOSPeerAddProposedManifest(peer
, proposed
);
867 secnoticeq("engine", "send %@ %@", SOSPeerGetID(peer
), message
);
869 secerror("%@ failed to send %@", SOSPeerGetID(peer
), message
);
872 CFReleaseSafe(local
);
873 CFReleaseSafe(proposed
);
874 CFReleaseSafe(message
);
878 CFReleaseSafe(local
);
879 CFReleaseSafe(proposed
);
880 CFReleaseSafe(message
);
882 CFReleaseNull(extra
);
883 CFReleaseNull(missing
);
885 secerror("%@ error in send: %@", SOSPeerGetID(peer
), *error
);
890 static CFDataRef
SOSEngineCreateMessageToSyncToPeer_locked(SOSEngineRef engine
, CFStringRef peerID
, SOSEnginePeerMessageSentBlock
*sentBlock
, CFErrorRef
*error
)
892 SOSPeerRef peer
= SOSPeerCreateWithEngine(engine
, peerID
);
893 CFDataRef message
= SOSEngineCreateMessage_locked(engine
, peer
, error
, sentBlock
);
899 bool SOSEngineHandleMessage(SOSEngineRef engine
, CFStringRef peerID
,
900 CFDataRef raw_message
, CFErrorRef
*error
)
902 __block
bool result
= false;
903 __block
bool somethingChanged
= false;
904 SOSMessageRef message
= SOSMessageCreateWithData(kCFAllocatorDefault
, raw_message
, error
);
905 result
= message
&& SOSDataSourceWith(engine
->dataSource
, error
, ^(SOSTransactionRef txn
, bool *commit
) {
906 dispatch_sync(engine
->queue
, ^{
907 result
= SOSEngineHandleMessage_locked(engine
, peerID
, message
, txn
, commit
, &somethingChanged
, error
);
910 CFReleaseSafe(message
);
911 if (somethingChanged
)
912 SecKeychainChanged(false);
916 // --- Called from off the queue, need to move to on the queue
918 static void SOSEngineDoOnQueue(SOSEngineRef engine
, dispatch_block_t action
)
920 dispatch_sync(engine
->queue
, action
);
923 void SOSEngineCircleChanged(SOSEngineRef engine
, CFStringRef myPeerID
, CFArrayRef trustedPeers
, CFArrayRef untrustedPeers
) {
924 SOSEngineDoOnQueue(engine
, ^{
925 SOSEngineCircleChanged_locked(engine
, myPeerID
, trustedPeers
, untrustedPeers
);
928 __block CFErrorRef localError
= NULL
;
929 SOSDataSourceWith(engine
->dataSource
, &localError
, ^(SOSTransactionRef txn
, bool *commit
) {
930 SOSEngineDoOnQueue(engine
, ^{
931 *commit
= SOSEngineSave(engine
, txn
, &localError
);
935 secerror("failed to save engine state: %@", localError
);
936 CFReleaseSafe(localError
);
940 SOSManifestRef
SOSEngineCopyManifest(SOSEngineRef engine
, CFErrorRef
*error
) {
941 __block SOSManifestRef result
= NULL
;
942 SOSEngineDoOnQueue(engine
, ^{
943 result
= SOSEngineCopyManifest_locked(engine
, error
);
948 bool SOSEngineUpdateLocalManifest(SOSEngineRef engine
, SOSDataSourceTransactionSource source
, struct SOSDigestVector
*removals
, struct SOSDigestVector
*additions
, CFErrorRef
*error
) {
949 __block
bool result
= true;
950 SOSManifestRef mfdel
= SOSManifestCreateWithDigestVector(removals
, error
);
951 SOSManifestRef mfadd
= SOSManifestCreateWithDigestVector(additions
, error
);
952 SOSEngineDoOnQueue(engine
, ^{
953 // Safe to run async if needed...
954 result
= SOSEngineUpdateLocalManifest_locked(engine
, source
, mfdel
, mfadd
, error
);
955 CFReleaseSafe(mfdel
);
956 CFReleaseSafe(mfadd
);
961 static bool SOSEngineSetCoderData_locked(SOSEngineRef engine
, CFStringRef peer_id
, CFDataRef data
, CFErrorRef
*error
) {
962 CFMutableDictionaryRef state
= NULL
;
964 if (!engine
->peerState
) {
965 engine
->peerState
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
966 state
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
969 state
= (CFMutableDictionaryRef
)CFDictionaryGetValue(engine
->peerState
, peer_id
);
971 state
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
973 CFDictionarySetValue(state
, kSOSPeerCoderKey
, data
);
974 CFDictionarySetValue(engine
->peerState
, peer_id
, state
);
976 }else if (engine
->peerState
) {
977 if(CFDictionaryContainsKey(engine
->peerState
, peer_id
)){
978 CFMutableDictionaryRef state
= (CFMutableDictionaryRef
)CFDictionaryGetValue(engine
->peerState
, peer_id
);
979 if(CFDictionaryContainsKey(state
, kSOSPeerCoderKey
))
980 CFDictionaryRemoveValue(state
, kSOSPeerCoderKey
);
982 if (CFDictionaryGetCount(engine
->peerState
) == 0) {
983 CFReleaseNull(engine
->peerState
);
989 bool SOSEngineSetCoderData(SOSEngineRef engine
, CFStringRef peer_id
, CFDataRef data
, CFErrorRef
*error
) {
990 __block
bool result
= false;
992 SOSDataSourceWith(engine
->dataSource
, error
, ^(SOSTransactionRef txn
, bool *commit
) {
993 dispatch_sync(engine
->queue
, ^{
994 result
= SOSEngineSetCoderData_locked(engine
, peer_id
, data
, error
);
1001 static CFDataRef
SOSEngineGetCoderData_locked(SOSEngineRef engine
, CFStringRef peer_id
) {
1002 // TODO: probably remove these secnotices
1003 CFDataRef result
= NULL
;
1004 CFMutableDictionaryRef peerState
= NULL
;
1006 if (!engine
->peerState
)
1007 secdebug("engine", "No engine coderData");
1009 peerState
= (CFMutableDictionaryRef
)CFDictionaryGetValue(engine
->peerState
, peer_id
);
1011 secdebug("engine", "No peerState for peer %@", peer_id
);
1013 result
= CFDictionaryGetValue(peerState
, kSOSPeerCoderKey
);
1015 secdebug("engine", "No coder data for peer %@", peer_id
);
1021 CFDataRef
SOSEngineGetCoderData(SOSEngineRef engine
, CFStringRef peer_id
) {
1022 __block CFDataRef result
= NULL
;
1023 SOSDataSourceWith(engine
->dataSource
, NULL
, ^(SOSTransactionRef txn
, bool *commit
) {
1024 dispatch_sync(engine
->queue
, ^{
1025 result
= SOSEngineGetCoderData_locked(engine
, peer_id
);
1033 // Peer state layout. WRONG! It's an array now
1034 // The peer state is an array.
1035 // The first element of the array is a dictionary with any number of keys and
1036 // values in it (for future expansion) such as changing the digest size or type
1037 // or remebering boolean flags for a peers sake.
1038 // The next three are special in that they are manifest digests with special
1039 // meaning and rules as to how they are treated (These are dynamically updated
1040 // based on database activity so they have a fully history of all changes made
1041 // to the local db. The first is the manifest representing the pendingObjects
1042 // to send to the other peer. This is normally only ever appending to, and in
1043 // particular with transactions originating from the Keychain API that affect
1044 // syncable items will need to add the new objects digests to the pendingObjects list
1045 // while adding the digests of any tombstones encountered to the extra list.
1047 CFMutableDictionaryRef
SOSEngineGetPeerState(SOSEngineRef engine
, CFStringRef peerID
) {
1048 CFMutableDictionaryRef peerState
= NULL
;
1049 if (engine
->peerState
)
1050 peerState
= (CFMutableDictionaryRef
)CFDictionaryGetValue(engine
->peerState
, peerID
);
1052 engine
->peerState
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1054 peerState
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1055 CFDictionaryAddValue(engine
->peerState
, peerID
, peerState
);
1056 CFReleaseSafe(peerState
);
1061 CFDataRef
SOSEngineCreateMessageToSyncToPeer(SOSEngineRef engine
, CFStringRef peerID
, SOSEnginePeerMessageSentBlock
*sentBlock
, CFErrorRef
*error
) {
1062 __block CFDataRef result
= NULL
;
1063 SOSEngineDoOnQueue(engine
, ^{
1064 result
= SOSEngineCreateMessageToSyncToPeer_locked(engine
, peerID
, sentBlock
, error
);
1069 bool SOSEnginePeerDidConnect(SOSEngineRef engine
, CFStringRef peerID
, CFErrorRef
*error
) {
1070 __block
bool result
= true;
1071 result
&= SOSDataSourceWith(engine
->dataSource
, error
, ^(SOSTransactionRef txn
, bool *commit
) {
1072 dispatch_sync(engine
->queue
, ^{
1073 SOSPeerRef peer
= SOSPeerCreateWithEngine(engine
, peerID
);
1075 result
= SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("Engine has no peer for %@"), peerID
);
1077 SOSPeerDidConnect(peer
);
1078 result
= SOSEngineSave(engine
, txn
, error
);
1079 CFReleaseSafe(peer
);