]> git.saurik.com Git - apple/security.git/blob - Security/sec/SOSCircle/SecureObjectSync/SOSEngine.c
4c8b7cebb3ee25c3b72f29b405451e2daf1ac23b
[apple/security.git] / Security / sec / SOSCircle / SecureObjectSync / SOSEngine.c
1 /*
2 * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25 /*
26 * SOSEngine.c - Implementation of a secure object syncing engine
27 */
28
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>
34 #include <stdlib.h>
35 #include <stdbool.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>
50
51 //
52 // MARK: SOSEngine The Keychain database with syncable keychain support.
53 //
54
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");
58
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");
65
66 /* SOSEngine implementation. */
67 struct __OpaqueSOSEngine {
68 CFRuntimeBase _base;
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).
80
81 CFMutableDictionaryRef manifestCache; // digest -> ( refcount, manifest )
82 CFMutableDictionaryRef peerState; // peerId -> mutable array of digests
83 CFArrayRef peerIDs;
84
85 dispatch_queue_t queue;
86 };
87
88 static bool SOSEngineLoad(SOSEngineRef engine, CFErrorRef *error);
89
90
91 static CFStringRef SOSPeerIDArrayCreateString(CFArrayRef peerIDs) {
92 return peerIDs ? CFStringCreateByCombiningStrings(kCFAllocatorDefault, peerIDs, CFSTR(" ")) : CFSTR("");
93 }
94
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);
100 return desc;
101 }
102
103 static CFStringRef SOSEngineCopyDebugDesc(CFTypeRef cf) {
104 return SOSEngineCopyFormattingDesc(cf, NULL);
105 }
106
107 static dispatch_queue_t sEngineQueue;
108 static CFDictionaryRef sEngineMap;
109
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);
113 });
114
115 #define _LOG_RAW_MESSAGES 0
116 void logRawMessage(CFDataRef message, bool sending, uint64_t seqno)
117 {
118 #if _LOG_RAW_MESSAGES
119 CFStringRef hexMessage = NULL;
120 if (message) {
121 hexMessage = CFDataCopyHexString(message);
122 if (sending)
123 secnoticeq("engine", "%s RAW%1d %@", sending ? "send" : "recv", seqno?2:0, hexMessage);
124 else
125 secnoticeq("engine", "%s RAWx %@", sending ? "send" : "recv", hexMessage); // we don't know vers of received msg here
126 }
127 CFReleaseSafe(hexMessage);
128 #endif
129 }
130 //
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.
144
145 CFStringRef SOSEngineGetMyID(SOSEngineRef engine) {
146 // TODO: this should not be needed
147 return engine->myID;
148 }
149
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;
153 }
154
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);
162 return NULL;
163 }
164
165 return manifest;
166 }
167
168 void SOSEngineAddManifest(SOSEngineRef engine, SOSManifestRef manifest) {
169 CFDataRef digest = SOSManifestGetDigest(manifest, NULL);
170 if (digest) {
171 if (!engine->manifestCache)
172 engine->manifestCache = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
173 CFDictionaryAddValue(engine->manifestCache, digest, manifest);
174 }
175 }
176
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);
180 if (manifest) {
181 SOSEngineAddManifest(engine, manifest);
182 digest = CFRetainSafe(SOSManifestGetDigest(manifest, NULL));
183 }
184 CFReleaseSafe(manifest);
185 return digest;
186 }
187
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;
194 bool ok = true;
195
196 require_quiet(engine->peerState, exit); // Not a failure no work to do
197
198 if(engine->peerIDs){
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);
204 CFReleaseSafe(peer);
205 }
206 }
207 if(engine->manifestCache){
208 CFDictionaryForEach(engine->manifestCache, ^(const void *key, const void *value) {
209 CFDataRef digest = (CFDataRef)key;
210 if (isData(digest))
211 SOSDigestVectorAppend(&mdInCache, CFDataGetBytePtr(digest));
212 });
213
214 // Delete unused manifests.
215 SOSDigestVectorDiff(&mdInCache, &mdInUse, &mdUnused, &mdMissing);
216 SOSManifestRef unused = SOSManifestCreateWithDigestVector(&mdUnused, NULL);
217 SOSManifestForEach(unused, ^(CFDataRef digest, bool *stop) {
218 if (digest)
219 CFDictionaryRemoveValue(engine->manifestCache, digest);
220 });
221 CFReleaseSafe(unused);
222 }
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);
228 if (value)
229 CFDictionarySetValue(newPeerState, peerID, value);
230 }
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);
235 if(untrustedCoder){
236 CFMutableDictionaryRef untrustedDict = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kSOSPeerCoderKey, untrustedCoder, NULL);
237 CFDictionarySetValue(newPeerState, key, untrustedDict);
238 CFReleaseNull(untrustedDict);
239 }
240 }
241 });
242 CFReleaseSafe(engine->peerState);
243 engine->peerState = newPeerState;
244 }
245
246 exit:
247 SOSDigestVectorFree(&mdInCache);
248 SOSDigestVectorFree(&mdInUse);
249 SOSDigestVectorFree(&mdUnused);
250 SOSDigestVectorFree(&mdMissing);
251 return ok;
252 }
253
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));
267 });
268 CFReleaseSafe(mfc);
269 }
270 der = kc_plist_copy_der(state, error);
271 CFReleaseSafe(state);
272 secnotice("engine", "%@", engine);
273 return der;
274 }
275
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);
280 return ok;
281 }
282
283 static bool SOSEngineUpdateLocalManifest_locked(SOSEngineRef engine, SOSDataSourceTransactionSource source, SOSManifestRef removals, SOSManifestRef additions, CFErrorRef *error) {
284 bool ok = true;
285 if (engine->manifest) {
286 SOSManifestRef updatedManifest = SOSManifestCreateWithPatch(engine->manifest, removals, additions, error);
287 if (updatedManifest)
288 CFAssignRetained(engine->manifest, updatedManifest);
289
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);
292 }
293 return ok;
294 }
295
296 static bool SOSEngineUpdateChanges(SOSEngineRef engine, SOSTransactionRef txn, SOSDataSourceTransactionPhase phase, SOSDataSourceTransactionSource source, SOSManifestRef removals, SOSManifestRef additions, CFErrorRef *error)
297 {
298 secnotice("engine", "%s %s dels:%@ adds:%@", phase == kSOSDataSourceTransactionWillCommit ? "will-commit" : phase == kSOSDataSourceTransactionDidCommit ? "did-commit" : "did-rollback", source == kSOSDataSourceSOSTransaction ? "sos" : "api", removals, additions);
299 bool ok = true;
300 switch (phase) {
301 case kSOSDataSourceTransactionDidRollback:
302 ok &= SOSEngineLoad(engine, error);
303 break;
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);
308 break;
309 }
310 case kSOSDataSourceTransactionDidCommit:
311 break;
312 }
313 return ok;
314 }
315
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;
320
321 CFStringRef peerID = NULL;
322 CFRetainAssign(engine->myID, myPeerID);
323
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);
329 };
330 }
331 else{
332 engine->peerIDs = NULL;
333 }
334 // If we entered a circle of more than 2 or our last peer left we need to do stuff
335 if (inCircleChanged) {
336 if (isInCircle) {
337 CFErrorRef dsError = NULL;
338 if (!(engine->manifest = SOSDataSourceCopyManifest(engine->dataSource, &dsError))) {
339 secerror("failed to load manifest from datasource: %@", dsError);
340 CFReleaseNull(dsError);
341 }
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);
349 }
350 CFReleaseSafe(localError);
351 CFReleaseSafe(mfdel);
352 CFReleaseSafe(mfadd);
353 };
354
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);
363 });
364 } else {
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
367 });
368 CFReleaseNull(engine->manifest);
369 }
370 }
371 }
372
373 static bool SOSEngineSetState(SOSEngineRef engine, CFDataRef state, CFErrorRef *error) {
374 bool ok = true;
375 if (state) {
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);
382 }
383 if (ok) {
384 SOSEngineSetTrustedPeers(engine, (CFStringRef)CFDictionaryGetValue(dict, kSOSEngineIDKey),
385 (CFArrayRef)CFDictionaryGetValue(dict, kSOSEnginePeerIDsKey));
386 CFRetainAssign(engine->peerState, (CFMutableDictionaryRef)CFDictionaryGetValue(dict, kSOSEnginePeerStateKey));
387
388 CFReleaseNull(engine->manifestCache);
389 CFMutableDictionaryRef mfc = (CFMutableDictionaryRef)CFDictionaryGetValue(dict, kSOSEngineManifestCacheKey);
390 if (mfc) {
391 engine->manifestCache = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
392 CFDictionaryForEach(mfc, ^(const void *key, const void *value) {
393 CFDataRef data = (CFDataRef)value;
394 if (isData(data)) {
395 SOSManifestRef mf = SOSManifestCreateWithData(data, NULL);
396 if (mf)
397 CFDictionarySetValue(engine->manifestCache, key, mf);
398 CFReleaseSafe(mf);
399 }
400 });
401 }
402 }
403 CFReleaseNull(dict);
404 }
405 secnotice("engine", "%@", engine);
406 return ok;
407 }
408
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);
413 return ok;
414 }
415
416 static void CFArraySubtract(CFMutableArrayRef from, CFArrayRef remove) {
417 if (remove) {
418 CFArrayForEach(remove, ^(const void *value) {
419 CFArrayRemoveAllValue(from, value);
420 });
421 }
422 }
423
424 static CFMutableArrayRef CFArrayCreateDifference(CFAllocatorRef alloc, CFArrayRef set, CFArrayRef remove) {
425 CFMutableArrayRef result;
426 if (!set) {
427 result = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
428 } else {
429 result = CFArrayCreateMutableCopy(alloc, 0, set);
430
431 if (remove)
432 CFArraySubtract(result, remove);
433 }
434
435 return result;
436 }
437
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);
441
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);
449
450 SOSEngineSetTrustedPeers(engine, myPeerID, trustedPeers);
451
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;
458
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);
465 } else {
466 CFDictionaryRemoveValue(engine->peerState, peerID);
467 }
468 }
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);
472 }
473
474 CFReleaseNull(addedPeers);
475 CFReleaseNull(deletedPeers);
476
477 }
478
479 #if 0
480 static SOSManifestRef SOSEngineCopyCleanManifest(SOSEngineRef engine, CFErrorRef *error) {
481 SOSManifestRef localMinusUnreadable;
482 }
483 #endif
484
485 // Initialize the engine if a load fails. Basically this is our first time setup
486 static bool SOSEngineInit(SOSEngineRef engine, CFErrorRef *error) {
487 bool ok = true;
488 secnotice("engine", "new engine for datasource named %@", SOSDataSourceGetName(engine->dataSource));
489 return ok;
490 }
491
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);
504 }
505 }
506 return engine;
507 }
508
509
510 //
511 // MARK: SOSEngine API
512 //
513
514 void SOSEngineDispose(SOSEngineRef engine) {
515 // NOOP Engines stick around forever to monitor dataSource changes.
516 }
517
518 static SOSManifestRef SOSEngineCopyManifest_locked(SOSEngineRef engine, CFErrorRef *error) {
519 return CFRetainSafe(engine->manifest);
520 }
521
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;
533
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;
538
539 require_action_quiet(peer, exit, ok = SOSErrorCreate(errSecParam, error, NULL, CFSTR("Couldn't create peer with Engine for %@"), peerID));
540 peerDesc = CFCopyDescription(peer);
541
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"));
545 *stop = true;
546 });
547 require_quiet(ok, exit);
548
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);
553 if (!digest) {
554 *stop = true;
555 *commit = false;
556 secerror("%@ peer sent bad object: %@, rolling back changes", SOSPeerGetID(peer), error ? *error : NULL);
557 return;
558 }
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);
564 if (!ok) {
565 *stop = true;
566 *commit = false;
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;
571 } else {
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));
575 }
576 CFReleaseSafe(digest);
577 }), exit);
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);
584
585 if (dvadd.count) {
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);
591 }
592
593 // ---- Don't use local or peer manifests from above this line, since commiting the SOSDataSourceWith transaction might change them ---
594
595 // Take a snapshot of our dataSource's local manifest.
596 require_quiet(ok = localManifest = SOSEngineCopyManifest_locked(engine, error), exit);
597
598 CFDataRef baseDigest = SOSMessageGetBaseDigest(message);
599 CFDataRef proposedDigest = SOSMessageGetProposedDigest(message);
600
601 #if 0
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
604 // peer.
605
606 // If we just received a L00 reset pendingObjects to localManifest
607 if (!baseDigest && !proposedDigest) {
608 SOSPeerSetPendingObjects(peer, localManifest);
609 secnotice("engine", "SOSPeerSetPendingObjects: %@", localManifest);
610 }
611 #endif
612
613 base = CFRetainSafe(SOSEngineGetManifestForDigest(engine, baseDigest));
614 confirmed = CFRetainSafe(SOSEngineGetManifestForDigest(engine, SOSMessageGetSenderDigest(message)));
615 if (!confirmed) {
616 if (SOSManifestGetCount(SOSMessageGetRemovals(message)) || SOSManifestGetCount(allAdditions)) {
617 if (base || !baseDigest) {
618 confirmed = SOSManifestCreateWithPatch(base, SOSMessageGetRemovals(message), allAdditions, error);
619 }
620 if (!confirmed) {
621 confirmedRemovals = CFRetainSafe(SOSMessageGetRemovals(message));
622 confirmedAdditions = CFRetainSafe(allAdditions);
623 }
624 } else if (baseDigest) {
625 confirmed = CFRetainSafe(base);
626 secerror("Protocol error send L00 - figure out later base: %@", base);
627 }
628 }
629 secnotice("engine", "Confirmed: %@ base: %@", confirmed, base);
630 if (confirmed)
631 ok &= SOSManifestDiff(SOSPeerGetConfirmedManifest(peer), confirmed, &confirmedRemovals, &confirmedAdditions, error);
632 if (confirmedRemovals || confirmedAdditions)
633 ok &= SOSPeerDidReceiveRemovalsAndAdditions(peer, confirmedRemovals, confirmedAdditions, localManifest, error);
634 SOSPeerSetConfirmedManifest(peer, confirmed);
635
636 // ---- SendObjects and extra->pendingObjects promotion dance ----
637
638 // The first block of code below sets peer.sendObjects to true when we receive a L00 and the second block
639 // moves extra to pendingObjects once we receive a confirmed manifest in or after the L00.
640 if (!baseDigest && !proposedDigest) {
641 SOSPeerSetSendObjects(peer, true);
642 }
643
644 // TODO: should this not depend on SOSPeerSendObjects?:
645 if (confirmed /* && SOSPeerSendObjects(peer)*/) {
646 SOSManifestRef allExtra = NULL;
647 ok &= SOSManifestDiff(confirmed, localManifest, NULL, &allExtra, error);
648 secnotice("engine", "%@ confirmed %@ setting O:%@", SOSPeerGetID(peer), confirmed, allExtra);
649 SOSPeerSetPendingObjects(peer, allExtra);
650 CFReleaseSafe(allExtra);
651 }
652
653 exit:
654 secnoticeq("engine", "recv %@ %@", SOSPeerGetID(peer), message);
655 secnoticeq("peer", "recv %@ -> %@", peerDesc, peer);
656
657 CFReleaseNull(base);
658 CFReleaseSafe(confirmed);
659 CFReleaseSafe(localManifest);
660 CFReleaseSafe(peerDesc);
661 CFReleaseSafe(allAdditions);
662 CFReleaseSafe(confirmedRemovals);
663 CFReleaseSafe(confirmedAdditions);
664 CFReleaseSafe(peer);
665 return ok;
666 }
667
668 static CFDataRef SOSEngineCopyObjectDER(SOSEngineRef engine, SOSObjectRef object, CFErrorRef *error) {
669 CFDataRef der = NULL;
670 CFDictionaryRef plist = SOSObjectCopyPropertyList(engine->dataSource, object, error);
671 if (plist) {
672 der = kc_plist_copy_der(plist, error);
673 CFRelease(plist);
674 }
675 return der;
676 }
677
678 static CFDataRef SOSEngineCreateMessage_locked(SOSEngineRef engine, SOSPeerRef peer,
679 CFErrorRef *error, SOSEnginePeerMessageSentBlock *sent) {
680 SOSManifestRef local = SOSEngineCopyManifest_locked(engine, error);
681 __block SOSMessageRef message = SOSMessageCreate(kCFAllocatorDefault, SOSPeerGetMessageVersion(peer), error);
682 SOSManifestRef confirmed = SOSPeerGetConfirmedManifest(peer);
683 SOSManifestRef pendingObjects = SOSPeerGetPendingObjects(peer);
684 SOSManifestRef objectsSent = NULL;
685 SOSManifestRef proposed = NULL;
686 SOSManifestRef allMissing = NULL;
687 SOSManifestRef allExtra = NULL;
688 SOSManifestRef extra = NULL;
689 SOSManifestRef excessPending = NULL;
690 SOSManifestRef missing = NULL;
691 SOSManifestRef deleted = SOSPeerGetPendingDeletes(peer);
692 SOSManifestRef excessDeleted = NULL;
693 CFDataRef result = NULL;
694 bool ok;
695
696 ok = SOSManifestDiff(confirmed, local, &allMissing, &allExtra, error);
697 ok = ok && SOSManifestDiff(allExtra, pendingObjects, &extra, &excessPending, error);
698 if (SOSManifestGetCount(excessPending)) {
699 secerror("%@ ASSERTION FAILURE excess pendingObjects: %@", peer, excessPending);
700 // Remove excessPending from pendingObjects since they are either
701 // already in confirmed or not in local, either way there is no point
702 // keeping them in pendingObjects.
703
704 pendingObjects = SOSManifestCreateComplement(excessPending, pendingObjects, error);
705 SOSPeerSetPendingObjects(peer, pendingObjects);
706 CFReleaseSafe(pendingObjects);
707 ok = false;
708 }
709 ok = ok && SOSManifestDiff(allMissing, deleted, &missing, &excessDeleted, error);
710 if (SOSManifestGetCount(excessDeleted)) {
711 secerror("%@ ASSERTION FAILURE excess deleted: %@", peer, excessDeleted);
712 ok = false;
713 }
714 (void)ok; // Dead store
715 CFReleaseNull(allExtra);
716 CFReleaseNull(excessPending);
717 CFReleaseNull(allMissing);
718 CFReleaseNull(excessDeleted);
719
720 // Send state for peer 7T0M+TD+A7HZ0frC5oHZnmdR0G: [LCP][os] P: 0, E: 0, M: 0
721 secnoticeq("engine", "Send state for peer %@: [%s%s%s][%s%s] P: %zu, E: %zu, M: %zu", SOSPeerGetID(peer),
722 local ? "L":"l",
723 confirmed ? "C":"0",
724 pendingObjects ? "P":"0",
725 SOSPeerSendObjects(peer) ? "O":"o",
726 SOSPeerMustSendMessage(peer) ? "S":"s",
727 SOSManifestGetCount(pendingObjects),
728 SOSManifestGetCount(extra),
729 SOSManifestGetCount(missing)
730 );
731
732 if (confirmed) {
733 // TODO: Because of not letting things terminate while we have extra left
734 // we might send objects when we didn't need to, but there is always an
735 // extra roundtrip required for objects that we assume the other peer
736 // should have already.
737 // TODO: If there are extra objects left, calling this function is not
738 // idempotent we should check if pending is what we are about to send and not send anything in this case.
739 if (SOSManifestGetCount(pendingObjects) == 0 && SOSManifestGetCount(extra) == 0)
740 SOSPeerSetSendObjects(peer, false);
741
742 if (CFEqualSafe(local, SOSPeerGetProposedManifest(peer)) && !SOSPeerMustSendMessage(peer)) {
743 bool send = false;
744 if (CFEqual(confirmed, local)) {
745 secnoticeq("engine", "synced <No MSG> %@", peer);
746 } else if (SOSManifestGetCount(pendingObjects) == 0 /* TODO: No entries moved from extra to pendingObjects. */
747 && SOSManifestGetCount(missing) == 0) {
748 secnoticeq("engine", "waiting <MSG not resent> %@", peer);
749 } else {
750 send = true;
751 }
752 if (!send) {
753 CFReleaseSafe(local);
754 CFReleaseSafe(message);
755 CFReleaseNull(extra);
756 CFReleaseNull(missing);
757 return CFDataCreate(kCFAllocatorDefault, NULL, 0);
758 }
759 }
760
761 if (SOSManifestGetCount(pendingObjects)) {
762 // If we have additions and we need to send objects send them.
763 __block size_t objectsSize = 0;
764 __block struct SOSDigestVector dv = SOSDigestVectorInit;
765 __block struct SOSDigestVector dvdel = SOSDigestVectorInit;
766 if (!SOSDataSourceForEachObject(engine->dataSource, pendingObjects, error, ^void(CFDataRef key, SOSObjectRef object, bool *stop) {
767 CFErrorRef localError = NULL;
768 CFDataRef digest = NULL;
769 CFDataRef der = NULL;
770 if (!object) {
771 const uint8_t *d = CFDataGetBytePtr(key);
772 secerrorq("%@ object %02X%02X%02X%02X dropping from manifest: not found in datasource",
773 SOSPeerGetID(peer), d[0], d[1], d[2], d[3]);
774 SOSDigestVectorAppend(&dvdel, CFDataGetBytePtr(key));
775 } else if (!(der = SOSEngineCopyObjectDER(engine, object, &localError))
776 || !(digest = SOSObjectCopyDigest(engine->dataSource, object, &localError))) {
777 if (SecErrorGetOSStatus(localError) == errSecDecode) {
778 // Decode error, we need to drop these objects from our manifests
779 const uint8_t *d = CFDataGetBytePtr(key);
780 secerrorq("%@ object %02X%02X%02X%02X dropping from manifest: %@",
781 SOSPeerGetID(peer), d[0], d[1], d[2], d[3], localError);
782 SOSDigestVectorAppend(&dvdel, CFDataGetBytePtr(key));
783 CFRelease(localError);
784 } else {
785 // Stop iterating and propagate out all other errors.
786 const uint8_t *d = CFDataGetBytePtr(key);
787 secwarning("%@ object %02X%02X%02X%02X in SOSDataSourceForEachObject: %@",
788 SOSPeerGetID(peer), d[0], d[1], d[2], d[3], localError);
789 *stop = true;
790 CFErrorPropagate(localError, error);
791 CFReleaseNull(message);
792 }
793 } else {
794 if (!CFEqual(key, digest)) {
795 const uint8_t *d = CFDataGetBytePtr(key);
796 const uint8_t *e = CFDataGetBytePtr(digest);
797 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]);
798 SOSDigestVectorAppend(&dvdel, CFDataGetBytePtr(key));
799 }
800
801 size_t objectLen = (size_t)CFDataGetLength(der);
802 if (SOSMessageAppendObject(message, der, &localError)) {
803 SOSDigestVectorAppend(&dv, CFDataGetBytePtr(digest));
804 } else {
805 const uint8_t *d = CFDataGetBytePtr(digest);
806 CFStringRef hexder = CFDataCopyHexString(der);
807 secerrorq("%@ object %02X%02X%02X%02X der: %@ dropping from manifest: %@",
808 SOSPeerGetID(peer), d[0], d[1], d[2], d[3], hexder, localError);
809 CFReleaseNull(hexder);
810 CFReleaseNull(message);
811 // Since we can't send these objects let's assume they are bad too?
812 SOSDigestVectorAppend(&dvdel, CFDataGetBytePtr(digest));
813 }
814 objectsSize += objectLen;
815 if (objectsSize > kSOSMessageMaxObjectsSize)
816 *stop = true;
817 }
818 CFReleaseSafe(der);
819 CFReleaseSafe(digest);
820 })) {
821 CFReleaseNull(message);
822 }
823 if (dv.count)
824 objectsSent = SOSManifestCreateWithDigestVector(&dv, error);
825 if (dvdel.count) {
826 CFErrorRef localError = NULL;
827 SOSManifestRef mfdel = SOSManifestCreateWithDigestVector(&dvdel, error);
828 SOSDigestVectorFree(&dvdel);
829 if (!SOSEngineUpdateLocalManifest_locked(engine, kSOSDataSourceSOSTransaction, mfdel, NULL, &localError))
830 secerror("SOSEngineUpdateLocalManifest deleting: %@ failed: %@", mfdel, localError);
831 CFReleaseSafe(localError);
832 CFReleaseSafe(mfdel);
833 CFAssignRetained(local, SOSEngineCopyManifest_locked(engine, error));
834 }
835 SOSDigestVectorFree(&dv);
836 }
837 } else {
838 // If we have no confirmed manifest, we want all pendedObjects going out as a manifest
839 objectsSent = CFRetainSafe(pendingObjects);
840 }
841
842 if (confirmed || SOSManifestGetCount(missing) || SOSManifestGetCount(extra) || objectsSent) {
843 SOSManifestRef allExtra = SOSManifestCreateUnion(extra, objectsSent, error);
844 proposed = SOSManifestCreateWithPatch(confirmed, missing, allExtra, error);
845 CFReleaseNull(allExtra);
846 }
847
848 if (!SOSMessageSetManifests(message, local, confirmed, proposed, proposed, confirmed ? objectsSent : NULL, error))
849 CFReleaseNull(message);
850
851 CFReleaseNull(objectsSent);
852
853 if (message) {
854 result = SOSMessageCreateData(message, SOSPeerNextSequenceNumber(peer), error);
855 }
856
857 if (result) {
858 // Capture the peer in our block (SOSEnginePeerMessageSentBlock)
859 CFRetainSafe(peer);
860 *sent = Block_copy(^(bool success) {
861 dispatch_async(engine->queue, ^{
862 if (success) {
863 if (!confirmed && !proposed) {
864 SOSPeerSetSendObjects(peer, true);
865 secnotice("engine", "SOSPeerSetSendObjects(true) L:%@", local);
866 }
867 SOSPeerAddLocalManifest(peer, local);
868 SOSPeerAddProposedManifest(peer, proposed);
869 secnoticeq("engine", "send %@ %@", SOSPeerGetID(peer), message);
870 } else {
871 secerror("%@ failed to send %@", SOSPeerGetID(peer), message);
872 }
873 CFReleaseSafe(peer);
874 CFReleaseSafe(local);
875 CFReleaseSafe(proposed);
876 CFReleaseSafe(message);
877 });
878 });
879 } else {
880 CFReleaseSafe(local);
881 CFReleaseSafe(proposed);
882 CFReleaseSafe(message);
883 }
884 CFReleaseNull(extra);
885 CFReleaseNull(missing);
886 if (error && *error)
887 secerror("%@ error in send: %@", SOSPeerGetID(peer), *error);
888
889 return result;
890 }
891
892 static CFDataRef SOSEngineCreateMessageToSyncToPeer_locked(SOSEngineRef engine, CFStringRef peerID, SOSEnginePeerMessageSentBlock *sentBlock, CFErrorRef *error)
893 {
894 SOSPeerRef peer = SOSPeerCreateWithEngine(engine, peerID);
895 CFDataRef message = SOSEngineCreateMessage_locked(engine, peer, error, sentBlock);
896 CFReleaseSafe(peer);
897
898 return message;
899 }
900
901 bool SOSEngineHandleMessage(SOSEngineRef engine, CFStringRef peerID,
902 CFDataRef raw_message, CFErrorRef *error)
903 {
904 __block bool result = false;
905 __block bool somethingChanged = false;
906 SOSMessageRef message = SOSMessageCreateWithData(kCFAllocatorDefault, raw_message, error);
907 result = message && SOSDataSourceWith(engine->dataSource, error, ^(SOSTransactionRef txn, bool *commit) {
908 dispatch_sync(engine->queue, ^{
909 result = SOSEngineHandleMessage_locked(engine, peerID, message, txn, commit, &somethingChanged, error);
910 });
911 });
912 CFReleaseSafe(message);
913 if (somethingChanged)
914 SecKeychainChanged(false);
915 return result;
916 }
917
918 // --- Called from off the queue, need to move to on the queue
919
920 static void SOSEngineDoOnQueue(SOSEngineRef engine, dispatch_block_t action)
921 {
922 dispatch_sync(engine->queue, action);
923 }
924
925 void SOSEngineCircleChanged(SOSEngineRef engine, CFStringRef myPeerID, CFArrayRef trustedPeers, CFArrayRef untrustedPeers) {
926 SOSEngineDoOnQueue(engine, ^{
927 SOSEngineCircleChanged_locked(engine, myPeerID, trustedPeers, untrustedPeers);
928 });
929
930 __block CFErrorRef localError = NULL;
931 SOSDataSourceWith(engine->dataSource, &localError, ^(SOSTransactionRef txn, bool *commit) {
932 SOSEngineDoOnQueue(engine, ^{
933 *commit = SOSEngineSave(engine, txn, &localError);
934 });
935 });
936 if (localError)
937 secerror("failed to save engine state: %@", localError);
938 CFReleaseSafe(localError);
939
940 }
941
942 SOSManifestRef SOSEngineCopyManifest(SOSEngineRef engine, CFErrorRef *error) {
943 __block SOSManifestRef result = NULL;
944 SOSEngineDoOnQueue(engine, ^{
945 result = SOSEngineCopyManifest_locked(engine, error);
946 });
947 return result;
948 }
949
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);
959 });
960 return result;
961 }
962
963 static bool SOSEngineSetCoderData_locked(SOSEngineRef engine, CFStringRef peer_id, CFDataRef data, CFErrorRef *error) {
964 CFMutableDictionaryRef state = NULL;
965 if (data) {
966 if (!engine->peerState) {
967 engine->peerState = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
968 state = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
969 }
970 else{
971 state = (CFMutableDictionaryRef)CFDictionaryGetValue(engine->peerState, peer_id);
972 if(!state)
973 state = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
974 }
975 CFDictionarySetValue(state, kSOSPeerCoderKey, data);
976 CFDictionarySetValue(engine->peerState, peer_id, state);
977
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);
983 }
984 if (CFDictionaryGetCount(engine->peerState) == 0) {
985 CFReleaseNull(engine->peerState);
986 }
987 }
988 return true;
989 }
990
991 bool SOSEngineSetCoderData(SOSEngineRef engine, CFStringRef peer_id, CFDataRef data, CFErrorRef *error) {
992 __block bool result = false;
993
994 SOSDataSourceWith(engine->dataSource, error, ^(SOSTransactionRef txn, bool *commit) {
995 dispatch_sync(engine->queue, ^{
996 result = SOSEngineSetCoderData_locked(engine, peer_id, data, error);
997 });
998 });
999
1000 return true;
1001 }
1002
1003 static CFDataRef SOSEngineGetCoderData_locked(SOSEngineRef engine, CFStringRef peer_id) {
1004 // TODO: probably remove these secnotices
1005 CFDataRef result = NULL;
1006 CFMutableDictionaryRef peerState = NULL;
1007
1008 if (!engine->peerState)
1009 secdebug("engine", "No engine coderData");
1010 else
1011 peerState = (CFMutableDictionaryRef)CFDictionaryGetValue(engine->peerState, peer_id);
1012 if (!peerState)
1013 secdebug("engine", "No peerState for peer %@", peer_id);
1014 else{
1015 result = CFDictionaryGetValue(peerState, kSOSPeerCoderKey);
1016 if(!result)
1017 secdebug("engine", "No coder data for peer %@", peer_id);
1018 }
1019 return result;
1020 }
1021
1022
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);
1028 });
1029 });
1030
1031 return result;
1032 }
1033
1034 //
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.
1048
1049 CFMutableDictionaryRef SOSEngineGetPeerState(SOSEngineRef engine, CFStringRef peerID) {
1050 CFMutableDictionaryRef peerState = NULL;
1051 if (engine->peerState)
1052 peerState = (CFMutableDictionaryRef)CFDictionaryGetValue(engine->peerState, peerID);
1053 else
1054 engine->peerState = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1055 if (!peerState) {
1056 peerState = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1057 CFDictionaryAddValue(engine->peerState, peerID, peerState);
1058 CFReleaseSafe(peerState);
1059 }
1060 return peerState;
1061 }
1062
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);
1067 });
1068 return result;
1069 }
1070
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);
1076 if (!peer) {
1077 result = SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("Engine has no peer for %@"), peerID);
1078 } else {
1079 SOSPeerDidConnect(peer);
1080 result = SOSEngineSave(engine, txn, error);
1081 CFReleaseSafe(peer);
1082 }
1083 });
1084 });
1085
1086 return result;
1087 }