]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SOSCircle/SecureObjectSync/SOSEngine.c
Security-57337.40.85.tar.gz
[apple/security.git] / OSX / sec / SOSCircle / SecureObjectSync / SOSEngine.c
1 /*
2 * Copyright (c) 2012-2015 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 <Security/SecureObjectSync/SOSChangeTracker.h>
30 #include <Security/SecureObjectSync/SOSEngine.h>
31 #include <Security/SecureObjectSync/SOSDigestVector.h>
32 #include <Security/SecureObjectSync/SOSInternal.h>
33 #include <Security/SecureObjectSync/SOSPeer.h>
34 #include <Security/SecureObjectSync/SOSViews.h>
35 #include <Security/SecureObjectSync/SOSBackupEvent.h>
36 #include <corecrypto/ccder.h>
37 #include <stdlib.h>
38 #include <stdbool.h>
39 #include <utilities/array_size.h>
40 #include <utilities/SecCFCCWrappers.h>
41 #include <utilities/SecCFError.h>
42 #include <utilities/SecCFRelease.h>
43 #include <utilities/SecCFWrappers.h>
44 #include <utilities/der_plist.h>
45 #include <utilities/der_plist_internal.h>
46 #include <utilities/debugging.h>
47 #include <utilities/iCloudKeychainTrace.h>
48 #include <utilities/SecCoreCrypto.h>
49 #include <utilities/SecFileLocations.h>
50 #include <AssertMacros.h>
51 #include <CoreFoundation/CoreFoundation.h>
52
53 #include <securityd/SecItemServer.h> // TODO: We can't leave this here.
54 #include <securityd/SOSCloudCircleServer.h> // TODO: We can't leave this here.
55 #include <Security/SecItem.h> // TODO: We can't leave this here.
56 #include <Security/SecItemPriv.h> // TODO: We can't leave this here.
57 #include <securityd/SecItemSchema.h>
58 #include <securityd/iCloudTrace.h>
59
60 #include <CoreFoundation/CFURL.h>
61
62 //
63 // MARK: SOSEngine The Keychain database with syncable keychain support.
64 //
65
66 // Key in dataSource for general engine state file.
67 // This file only has digest entries in it, no manifests.
68 static const CFStringRef kSOSEngineState = CFSTR("engine-state");
69
70 // Keys in state dictionary
71 static CFStringRef kSOSEngineManifestCacheKey = CFSTR("manifestCache");
72 static CFStringRef kSOSEnginePeerStateKey = CFSTR("peerState");
73 static CFStringRef kSOSEnginePeerIDsKey = CFSTR("peerIDs");
74 static CFStringRef kSOSEngineIDKey = CFSTR("id");
75 static CFStringRef kSOSEngineTraceDateKey = CFSTR("traceDate");
76
77 /* SOSEngine implementation. */
78 struct __OpaqueSOSEngine {
79 CFRuntimeBase _base;
80 SOSDataSourceRef dataSource;
81 CFStringRef myID; // My peerID in the circle
82 // We need to address the issues of corrupt keychain items
83 SOSManifestRef unreadable; // Possibly by having a set of unreadable items, to which we
84 // add any corrupted items in the db that have yet to be deleted.
85 // This happens if we notce corruption during a (read only) query.
86 // We would also perma-subtract unreadable from manifest whenever
87 // anyone asked for manifest. This result would be cached in
88 // The manifestCache below, so we just need a key into the cache
89 CFDataRef localMinusUnreadableDigest; // or a digest (CFDataRef of the right size).
90
91 CFMutableDictionaryRef manifestCache; // digest -> ( refcount, manifest )
92 CFMutableDictionaryRef peerMap; // peerId -> SOSPeerRef
93 CFDictionaryRef viewNameSet2ChangeTracker; // CFSetRef of CFStringRef -> SOSChangeTrackerRef
94 CFDictionaryRef viewName2ChangeTracker; // CFStringRef -> SOSChangeTrackerRef
95 CFArrayRef peerIDs;
96 CFDateRef lastTraceDate; // Last time we did a CloudKeychainTrace
97
98 dispatch_queue_t queue; // Engine queue
99
100 dispatch_source_t save_timer; // Engine state save timer
101
102 dispatch_queue_t syncCompleteQueue; // Non-retained queue for async notificaion
103 CFMutableDictionaryRef syncCompleteListeners; // Map from PeerID->notification block
104 };
105
106 static bool SOSEngineLoad(SOSEngineRef engine, CFErrorRef *error);
107
108
109 static CFStringRef SOSPeerIDArrayCreateString(CFArrayRef peerIDs) {
110 return peerIDs ? CFStringCreateByCombiningStrings(kCFAllocatorDefault, peerIDs, CFSTR(" ")) : CFSTR("");
111 }
112
113 static CFStringRef SOSEngineCopyFormattingDesc(CFTypeRef cf, CFDictionaryRef formatOptions) {
114 SOSEngineRef engine = (SOSEngineRef)cf;
115 CFStringRef tpDesc = SOSPeerIDArrayCreateString(engine->peerIDs);
116 CFStringRef desc = CFStringCreateWithFormat(kCFAllocatorDefault, formatOptions, CFSTR("<Engine %@ peers %@ MC[%d] PS[%d]>"), engine->myID, tpDesc, engine->manifestCache ? (int)CFDictionaryGetCount(engine->manifestCache) : 0, engine->peerMap ? (int)CFDictionaryGetCount(engine->peerMap) : 0);
117 CFReleaseSafe(tpDesc);
118 return desc;
119 }
120
121 static CFStringRef SOSEngineCopyDebugDesc(CFTypeRef cf) {
122 return SOSEngineCopyFormattingDesc(cf, NULL);
123 }
124
125 static dispatch_queue_t sEngineQueue;
126 static CFDictionaryRef sEngineMap;
127
128 CFGiblisWithFunctions(SOSEngine, NULL, NULL, NULL, NULL, NULL, SOSEngineCopyFormattingDesc, SOSEngineCopyDebugDesc, NULL, NULL, ^{
129 sEngineQueue = dispatch_queue_create("SOSEngine queue", DISPATCH_QUEUE_SERIAL);
130 sEngineMap = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
131 });
132
133 #define _LOG_RAW_MESSAGES 0
134 void logRawMessage(CFDataRef message, bool sending, uint64_t seqno)
135 {
136 #if _LOG_RAW_MESSAGES
137 CFStringRef hexMessage = NULL;
138 if (message) {
139 hexMessage = CFDataCopyHexString(message);
140 if (sending)
141 secnoticeq("engine", "%s RAW%1d %@", sending ? "send" : "recv", seqno?2:0, hexMessage);
142 else
143 secnoticeq("engine", "%s RAWx %@", sending ? "send" : "recv", hexMessage); // we don't know vers of received msg here
144 }
145 CFReleaseSafe(hexMessage);
146 #endif
147 }
148 //
149 // Peer state layout. WRONG! It's an array now
150 // The peer state is an array.
151 // The first element of the array is a dictionary with any number of keys and
152 // values in it (for future expansion) such as changing the digest size or type
153 // or remembering boolean flags for a peers sake.
154 // The next three are special in that they are manifest digests with special
155 // meaning and rules as to how they are treated (These are dynamically updated
156 // based on database activity so they have a fully history of all changes made
157 // to the local db. The first is the manifest representing the pendingObjects
158 // to send to the other peer. This is normally only ever appending to, and in
159 // particular with transactions originating from the Keychain API that affect
160 // syncable items will need to add the new objects digests to the pendingObjects list
161 // while adding the digests of any tombstones encountered to the extra list.
162
163 CFStringRef SOSEngineGetMyID(SOSEngineRef engine) {
164 // TODO: this should not be needed
165 return engine->myID;
166 }
167
168 // TEMPORARY: Get the list of IDs for cleanup, this shouldn't be used instead it should iterate KVS.
169 CFArrayRef SOSEngineGetPeerIDs(SOSEngineRef engine) {
170 return engine->peerIDs;
171 }
172
173 void SOSEngineClearCache(SOSEngineRef engine){
174 CFReleaseNull(engine->manifestCache);
175 CFReleaseNull(engine->localMinusUnreadableDigest);
176 dispatch_release(engine->queue);
177 }
178
179 static SOSPeerRef SOSEngineCopyPeerWithMapEntry_locked(SOSEngineRef engine, CFStringRef peerID, CFTypeRef mapEntry, CFErrorRef *error) {
180 SOSPeerRef peer = NULL;
181 if (mapEntry && CFGetTypeID(mapEntry) == SOSPeerGetTypeID()) {
182 // The mapEntry is an SOSPeer, so we're done.
183 peer = (SOSPeerRef)CFRetain(mapEntry);
184 } else {
185 // The mapEntry is a peerState, attempt to initialize a new
186 // peer iff peerID is in the set of trusted peerIDs
187 if (engine->peerIDs && CFArrayContainsValue(engine->peerIDs, CFRangeMake(0, CFArrayGetCount(engine->peerIDs)), peerID)) {
188 CFErrorRef localError = NULL;
189 peer = SOSPeerCreateWithState(engine, peerID, mapEntry, &localError);
190 if (!peer) {
191 secerror("error inflating peer: %@: %@ from state: %@", peerID, localError, mapEntry);
192 CFReleaseNull(localError);
193 peer = SOSPeerCreateWithState(engine, peerID, NULL, error);
194 }
195 if (peer) {
196 // Replace the map entry with the inflated peer.
197 CFDictionarySetValue(engine->peerMap, peerID, peer);
198 }
199 } else {
200 SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("peer: %@ is untrusted inflating not allowed"), peerID);
201 }
202 }
203 return peer;
204 }
205
206 static SOSPeerRef SOSEngineCopyPeerWithID_locked(SOSEngineRef engine, CFStringRef peerID, CFErrorRef *error) {
207 CFTypeRef mapEntry = CFDictionaryGetValue(engine->peerMap, peerID);
208 SOSPeerRef peer = NULL;
209 if (mapEntry) {
210 peer = SOSEngineCopyPeerWithMapEntry_locked(engine, peerID, mapEntry, error);
211 } else {
212 peer = NULL;
213 secerror("peer: %@ not found, peerMap: %@, engine: %@", peerID, engine->peerMap, engine);
214 SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("peer: %@ not found"), peerID);
215 }
216 return peer;
217 }
218
219 struct SOSEngineWithPeerContext {
220 SOSEngineRef engine;
221 void (^with)(SOSPeerRef peer);
222 };
223
224 static void SOSEngineWithPeerMapEntry_locked(const void *peerID, const void *mapEntry, void *context) {
225 struct SOSEngineWithPeerContext *ewp = context;
226 SOSPeerRef peer = SOSEngineCopyPeerWithMapEntry_locked(ewp->engine, peerID, mapEntry, NULL);
227 if (peer) {
228 ewp->with(peer);
229 CFRelease(peer);
230 }
231 }
232
233 static void SOSEngineForEachPeer_locked(SOSEngineRef engine, void (^with)(SOSPeerRef peer)) {
234 struct SOSEngineWithPeerContext ewp = { .engine = engine, .with = with };
235 CFDictionaryRef peerMapCopy = CFDictionaryCreateCopy(NULL, engine->peerMap);
236 CFDictionaryApplyFunction(peerMapCopy, SOSEngineWithPeerMapEntry_locked, &ewp);
237 CFRelease(peerMapCopy);
238 }
239
240 static void SOSEngineWithBackupPeerMapEntry_locked(const void *peerID, const void *mapEntry, void *context) {
241 struct SOSEngineWithPeerContext *ewp = context;
242 // v0 backup peer is always in map but we only consider it a backup peer if it has a keybag.
243 if (SOSPeerMapEntryIsBackup(mapEntry)) {
244 SOSPeerRef peer = SOSEngineCopyPeerWithMapEntry_locked(ewp->engine, peerID, mapEntry, NULL);
245 if (peer) {
246 ewp->with(peer);
247 CFRelease(peer);
248 }
249 }
250 }
251
252 static void SOSEngineForEachBackupPeer_locked(SOSEngineRef engine, void (^with)(SOSPeerRef peer)) {
253 struct SOSEngineWithPeerContext ewp = { .engine = engine, .with = with };
254 CFDictionaryRef peerMapCopy = CFDictionaryCreateCopy(NULL, engine->peerMap);
255 CFDictionaryApplyFunction(peerMapCopy, SOSEngineWithBackupPeerMapEntry_locked, &ewp);
256 CFRelease(peerMapCopy);
257 }
258
259 //
260 // Manifest cache
261 //
262 SOSManifestRef SOSEngineGetManifestForDigest(SOSEngineRef engine, CFDataRef digest) {
263 if (!engine->manifestCache || !digest) return NULL;
264 SOSManifestRef manifest = (SOSManifestRef)CFDictionaryGetValue(engine->manifestCache, digest);
265 if (!manifest) return NULL;
266 if (CFGetTypeID(manifest) != SOSManifestGetTypeID()) {
267 secerror("dropping corrupt manifest for %@ from cache", digest);
268 CFDictionaryRemoveValue(engine->manifestCache, digest);
269 return NULL;
270 }
271
272 return manifest;
273 }
274
275 void SOSEngineAddManifest(SOSEngineRef engine, SOSManifestRef manifest) {
276 CFDataRef digest = SOSManifestGetDigest(manifest, NULL);
277 if (digest) {
278 if (!engine->manifestCache)
279 engine->manifestCache = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
280 CFDictionaryAddValue(engine->manifestCache, digest, manifest);
281 }
282 }
283
284 CFDataRef SOSEnginePatchRecordAndCopyDigest(SOSEngineRef engine, SOSManifestRef base, SOSManifestRef removals, SOSManifestRef additions, CFErrorRef *error) {
285 CFDataRef digest = NULL;
286 SOSManifestRef manifest = SOSManifestCreateWithPatch(base, removals, additions, error);
287 if (manifest) {
288 SOSEngineAddManifest(engine, manifest);
289 digest = CFRetainSafe(SOSManifestGetDigest(manifest, NULL));
290 }
291 CFReleaseSafe(manifest);
292 return digest;
293 }
294
295 SOSManifestRef SOSEngineCopyPersistedManifest(SOSEngineRef engine, CFDictionaryRef persisted, CFStringRef key) {
296 return CFRetainSafe(SOSEngineGetManifestForDigest(engine, asData(CFDictionaryGetValue(persisted, key), NULL)));
297 }
298
299 CFMutableArrayRef SOSEngineCopyPersistedManifestArray(SOSEngineRef engine, CFDictionaryRef persisted, CFStringRef key, CFErrorRef *error) {
300 CFMutableArrayRef manifests = NULL;
301 CFArrayRef digests = NULL;
302 CFDataRef digest;
303 if (asArrayOptional(CFDictionaryGetValue(persisted, key), &digests, error))
304 manifests = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
305 if (digests) CFArrayForEachC(digests, digest) {
306 SOSManifestRef manifest = SOSEngineGetManifestForDigest(engine, digest);
307 if (manifest)
308 CFArrayAppendValue(manifests, manifest);
309 }
310 return manifests;
311 }
312
313 static CFDictionaryRef SOSEngineCopyEncodedManifestCache_locked(SOSEngineRef engine, CFErrorRef *error) {
314 CFMutableDictionaryRef mfc = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
315 SOSEngineForEachPeer_locked(engine, ^(SOSPeerRef peer) {
316 SOSPeerAddManifestsInUse(peer, mfc);
317 });
318 return mfc;
319 }
320
321 #if 0
322 static bool SOSEngineGCManifests_locked(SOSEngineRef engine, CFErrorRef *error) {
323 __block struct SOSDigestVector mdInCache = SOSDigestVectorInit;
324 __block struct SOSDigestVector mdInUse = SOSDigestVectorInit;
325 struct SOSDigestVector mdUnused = SOSDigestVectorInit;
326 struct SOSDigestVector mdMissing = SOSDigestVectorInit;
327 bool ok = true;
328
329 SOSEngineForEachPeer_locked(engine, ^(SOSPeerRef peer) {
330 SOSPeerMarkDigestsInUse(peer, &mdInUse);
331 });
332
333 if (engine->manifestCache) {
334 CFDictionaryForEach(engine->manifestCache, ^(const void *key, const void *value) {
335 CFDataRef digest = (CFDataRef)key;
336 if (isData(digest))
337 SOSDigestVectorAppend(&mdInCache, CFDataGetBytePtr(digest));
338 });
339
340 // Delete unused manifests.
341 SOSDigestVectorDiff(&mdInCache, &mdInUse, &mdUnused, &mdMissing);
342 SOSManifestRef unused = SOSManifestCreateWithDigestVector(&mdUnused, NULL);
343 SOSManifestForEach(unused, ^(CFDataRef digest, bool *stop) {
344 if (digest)
345 CFDictionaryRemoveValue(engine->manifestCache, digest);
346 });
347 CFReleaseSafe(unused);
348 }
349
350 SOSDigestVectorFree(&mdInCache);
351 SOSDigestVectorFree(&mdInUse);
352 SOSDigestVectorFree(&mdUnused);
353 SOSDigestVectorFree(&mdMissing);
354 return ok;
355 }
356 #endif
357
358 //
359 // End of Manifest cache
360 //
361
362 static bool SOSEngineGCPeerState_locked(SOSEngineRef engine, CFErrorRef *error) {
363 bool ok = true;
364
365 //require_quiet(ok = SOSEngineGCManifests_locked(engine, error), exit);
366
367 //exit:
368 return ok;
369 }
370
371 static CFMutableDictionaryRef SOSEngineCopyPeerState_locked(SOSEngineRef engine, CFErrorRef *error) {
372 CFMutableDictionaryRef peerState = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
373 CFDictionaryForEach(engine->peerMap, ^(const void *key, const void *value) {
374 CFDictionaryRef state = NULL;
375 if (value && CFGetTypeID(value) == SOSPeerGetTypeID()) {
376 CFErrorRef localError = NULL;
377 // Inflated peer
378 state = SOSPeerCopyState((SOSPeerRef)value, &localError);
379 if (!state)
380 secnotice("engine", "%@ failed to encode peer: %@", key, localError);
381 CFReleaseNull(localError);
382 // TODO: Potentially replace inflated peer with deflated peer in peerMap
383 } else if (value) {
384 // We have a deflated peer.
385 state = CFRetainSafe(value);
386 }
387
388 if (state) {
389 CFDictionarySetValue(peerState, key, state);
390 CFReleaseSafe(state);
391 }
392 });
393 return peerState;
394 }
395
396 static CFDataRef SOSEngineCopyState(SOSEngineRef engine, CFErrorRef *error) {
397 CFDataRef der = NULL;
398 CFMutableDictionaryRef state = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
399 if (engine->myID) CFDictionarySetValue(state, kSOSEngineIDKey, engine->myID);
400 if (engine->peerIDs) CFDictionarySetValue(state, kSOSEnginePeerIDsKey, engine->peerIDs);
401 if (engine->lastTraceDate) CFDictionarySetValue(state, kSOSEngineTraceDateKey, engine->lastTraceDate);
402 CFTypeRef peerState = SOSEngineCopyPeerState_locked(engine, error);
403 if (peerState) CFDictionarySetValue(state, kSOSEnginePeerStateKey, peerState);
404 CFReleaseSafe(peerState);
405 CFDictionaryRef mfc = SOSEngineCopyEncodedManifestCache_locked(engine, error);
406 if (mfc) {
407 CFDictionarySetValue(state, kSOSEngineManifestCacheKey, mfc);
408 CFReleaseSafe(mfc);
409 }
410 der = CFPropertyListCreateDERData(kCFAllocatorDefault, state, error);
411 CFReleaseSafe(state);
412 secnotice("engine", "%@", engine);
413 return der;
414 }
415
416 static bool SOSEngineDoSave(SOSEngineRef engine, SOSTransactionRef txn, CFErrorRef *error) {
417 CFDataRef derState = SOSEngineCopyState(engine, error);
418 bool ok = derState && SOSDataSourceSetStateWithKey(engine->dataSource, txn, kSOSEngineState, kSecAttrAccessibleAlways, derState, error);
419 CFReleaseSafe(derState);
420 return ok;
421 }
422
423 #define SOSENGINE_SAVE_TIMEOUT (NSEC_PER_MSEC * 500ull)
424 #define SOSENGINE_SAVE_LEEWAY (NSEC_PER_MSEC * 500ull)
425 #define SOSENGINE_SAVE_MAX_DELAY (NSEC_PER_MSEC * 500ull)
426
427 #if !(TARGET_IPHONE_SIMULATOR)
428 static void SOSEngineShouldSave(SOSEngineRef engine) {
429 if (engine->save_timer) {
430 // Possibly defer timer further up to engine->save_deadline
431 return;
432 }
433
434 // Schedule the timer to fire on a concurrent queue, so we can follow
435 // the proper procedure of acquiring a dataSource and then engine queues.
436 engine->save_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0));
437 dispatch_source_set_event_handler(engine->save_timer, ^{
438 CFErrorRef dsWithError = NULL;
439 if (engine->dataSource) {
440 if (!SOSDataSourceWith(engine->dataSource, &dsWithError, ^(SOSTransactionRef txn, bool *commit) {
441 dispatch_sync(engine->queue, ^{
442 CFErrorRef saveError = NULL;
443 if (!SOSEngineDoSave(engine, txn, &saveError)) {
444 secerrorq("Failed to save engine state: %@", saveError);
445 CFReleaseNull(saveError);
446 }
447 });
448 })) {
449 secerrorq("Failed to open dataSource to save engine state: %@", dsWithError);
450 CFReleaseNull(dsWithError);
451 }
452 }
453 xpc_transaction_end();
454 });
455 // Set the timer's fire time to now + SOSENGINE_SAVE_TIMEOUT seconds with a SOSENGINE_SAVE_LEEWAY fuzz factor.
456 dispatch_source_set_timer(engine->save_timer,
457 dispatch_time(DISPATCH_TIME_NOW, SOSENGINE_SAVE_TIMEOUT),
458 DISPATCH_TIME_FOREVER, SOSENGINE_SAVE_LEEWAY);
459 // Start a trasaction, then start the timer, the handler for the timer will end
460 // the transaction.
461 xpc_transaction_begin();
462 dispatch_resume(engine->save_timer);
463 }
464 #endif
465
466 static bool SOSEngineSave(SOSEngineRef engine, SOSTransactionRef txn, CFErrorRef *error) {
467 // Don't save engine state from tests
468 if (!engine->dataSource)
469 return true;
470 #if (TARGET_IPHONE_SIMULATOR)
471 return SOSEngineDoSave(engine, txn, error);
472 #else
473 SOSEngineShouldSave(engine);
474 #endif
475 return true;
476 }
477
478 static SOSManifestRef SOSEngineCreateManifestWithViewNameSet_locked(SOSEngineRef engine, CFSetRef viewNameSet, CFErrorRef *error) {
479 // TODO: Potentially tell all changeTrackers to track manifests ( //forall ct do SOSChangeTrackerSetConcrete(ct, true);
480 // and read the entire dataSource and pass all objects though the filter here, instead of
481 // forcing the datasource to be able to do "smart" queries
482 return SOSDataSourceCopyManifestWithViewNameSet(engine->dataSource, viewNameSet, error);
483 }
484
485 static SOSChangeTrackerRef SOSEngineCopyChangeTrackerWithViewNameSet_locked(SOSEngineRef engine, CFSetRef viewNameSet, CFErrorRef *error) {
486 SOSChangeTrackerRef ct = (SOSChangeTrackerRef)CFDictionaryGetValue(engine->viewNameSet2ChangeTracker, viewNameSet);
487 if (!ct)
488 SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("no change tracker for view set %@"), viewNameSet);
489 return CFRetainSafe(ct);
490 }
491
492 static SOSManifestRef SOSEngineCopyManifestWithViewNameSet_locked(SOSEngineRef engine, CFSetRef viewNameSet, CFErrorRef *error) {
493 SOSChangeTrackerRef ct = SOSEngineCopyChangeTrackerWithViewNameSet_locked(engine, viewNameSet, error);
494 if (!ct)
495 return NULL;
496
497 SOSManifestRef manifest = SOSChangeTrackerCopyManifest(ct, NULL);
498 if (!manifest) {
499 manifest = SOSEngineCreateManifestWithViewNameSet_locked(engine, viewNameSet, error); // Do the SQL query
500 SOSChangeTrackerSetManifest(ct, manifest);
501 }
502 CFReleaseSafe(ct);
503 return manifest;
504 }
505
506 SOSManifestRef SOSEngineCopyLocalPeerManifest_locked(SOSEngineRef engine, SOSPeerRef peer, CFErrorRef *error) {
507 return SOSEngineCopyManifestWithViewNameSet_locked(engine, SOSPeerGetViewNameSet(peer), error);
508 }
509
510 #define withViewAndBackup(VIEW) do { with(VIEW); if (!isTomb) with(VIEW ## _tomb); } while(0)
511
512
513 // Invoke with once for each view an object is in.
514 // TODO: Move this function into the DataSource
515 static void SOSEngineObjectWithView(SOSEngineRef engine, SOSObjectRef object, void (^with)(CFStringRef view)) {
516 // Filter items into v0 only view here
517 SecDbItemRef item = (SecDbItemRef)object; // TODO: Layer violation, breaks tests
518 if (isDictionary(object)) {
519 CFTypeRef isTombValue = CFDictionaryGetValue((CFDictionaryRef)object, kSecAttrTombstone);
520 bool isTomb = isTombValue && CFBooleanGetValue(isTombValue);
521 // We are in the test just assume v0 and v2 views.
522 withViewAndBackup(kSOSViewKeychainV0);
523 } else if (SecDbItemIsSyncableOrCorrupted(item)) {
524 const SecDbClass *iclass = SecDbItemGetClass(item);
525 CFTypeRef pdmn = SecDbItemGetCachedValueWithName(item, kSecAttrAccessible);
526 if ((iclass == &genp_class || iclass == &inet_class || iclass == &keys_class || iclass == &cert_class)
527 && isString(pdmn)
528 && (CFEqual(pdmn, kSecAttrAccessibleWhenUnlocked)
529 || CFEqual(pdmn, kSecAttrAccessibleAfterFirstUnlock)
530 || CFEqual(pdmn, kSecAttrAccessibleAlways)
531 || CFEqual(pdmn, kSecAttrAccessibleWhenUnlockedThisDeviceOnly)
532 || CFEqual(pdmn, kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly)
533 || CFEqual(pdmn, kSecAttrAccessibleAlwaysThisDeviceOnly)))
534 {
535 CFTypeRef tomb = SecDbItemGetCachedValueWithName(item, kSecAttrTombstone);
536 char cvalue = 0;
537 bool isTomb = (isNumber(tomb) && CFNumberGetValue(tomb, kCFNumberCharType, &cvalue) && cvalue == 1);
538 CFTypeRef viewHint = SecDbItemGetCachedValueWithName(item, kSecAttrSyncViewHint);
539 if (viewHint == NULL) {
540 if (iclass == &cert_class) {
541 withViewAndBackup(kSOSViewOtherSyncable);
542 } else {
543 if (!SecDbItemGetCachedValueWithName(item, kSecAttrTokenID)) {
544 withViewAndBackup(kSOSViewKeychainV0);
545 }
546 CFTypeRef agrp = SecDbItemGetCachedValueWithName(item, kSecAttrAccessGroup);
547 if (iclass == &keys_class && CFEqualSafe(agrp, CFSTR("com.apple.security.sos"))) {
548 withViewAndBackup(kSOSViewiCloudIdentity);
549 } else if (CFEqualSafe(agrp, CFSTR("com.apple.cfnetwork"))) {
550 withViewAndBackup(kSOSViewAutofillPasswords);
551 } else if (CFEqualSafe(agrp, CFSTR("com.apple.safari.credit-cards"))) {
552 withViewAndBackup(kSOSViewSafariCreditCards);
553 } else if (iclass == &genp_class) {
554 if (CFEqualSafe(agrp, CFSTR("apple")) &&
555 CFEqualSafe(SecDbItemGetCachedValueWithName(item, kSecAttrService), CFSTR("AirPort"))) {
556 withViewAndBackup(kSOSViewWiFi);
557 } else if (CFEqualSafe(agrp, CFSTR("com.apple.sbd"))) {
558 withViewAndBackup(kSOSViewBackupBagV0);
559 } else {
560 withViewAndBackup(kSOSViewOtherSyncable); // (genp)
561 }
562 } else {
563 withViewAndBackup(kSOSViewOtherSyncable); // (inet || keys)
564 }
565 }
566 } else {
567 with(viewHint);
568 if (!isTomb) {
569 CFStringRef viewHintTomb = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@-tomb"), viewHint);
570 if (viewHintTomb) {
571 with(viewHintTomb);
572 CFRelease(viewHintTomb);
573 }
574 }
575 }
576 }
577 } else {
578 // TODO: general queries
579 #if 0
580 SOSViewRef view;
581 CFArrayForEachC(engine->views, view) {
582 bool inView = SOSViewQueryMatchItem(view, item);
583 if (inView) {
584 CFStringRef viewName = SOSViewCopyName(view);
585 with(viewName);
586 CFReleaseSafe(viewName);
587 }
588 }
589 #endif
590 }
591 }
592
593 //
594 // SOSChangeMapper - Helper for SOSEngineUpdateChanges_locked
595 //
596 struct SOSChangeMapper {
597 SOSEngineRef engine;
598 SOSTransactionRef txn;
599 SOSDataSourceTransactionPhase phase;
600 SOSDataSourceTransactionSource source;
601 CFMutableDictionaryRef ct2changes;
602 };
603
604 static void SOSChangeMapperInit(struct SOSChangeMapper *cm, SOSEngineRef engine, SOSTransactionRef txn, SOSDataSourceTransactionPhase phase, SOSDataSourceTransactionSource source) {
605 cm->engine = engine;
606 cm->txn = txn;
607 cm->phase = phase;
608 cm->source = source;
609 cm->ct2changes = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
610 }
611
612 static void SOSChangeMapperFree(struct SOSChangeMapper *cm) {
613 CFReleaseSafe(cm->ct2changes);
614 }
615
616 static void SOSChangeMapperAppendObject(struct SOSChangeMapper *cm, SOSChangeTrackerRef ct, bool isAdd, CFTypeRef object) {
617 CFMutableArrayRef changes = (CFMutableArrayRef)CFDictionaryGetValue(cm->ct2changes, ct);
618 if (!changes) {
619 changes = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
620 CFDictionarySetValue(cm->ct2changes, ct, changes);
621 CFReleaseSafe(changes);
622 }
623 isAdd ? SOSChangesAppendAdd(changes, object) : SOSChangesAppendDelete(changes, object);
624 }
625
626 static bool SOSChangeMapperIngestChange(struct SOSChangeMapper *cm, bool isAdd, CFTypeRef change) {
627 bool someoneCares = false;
628 if (isData(change)) {
629 // TODO: Reenable assertion once the tests have been updated
630 //assert(!isAdd);
631 // We got a digest for a deleted object. Our dataSource probably couldn't find
632 // an object with this digest, probably because it went missing, or it was
633 // discovered to be corrupted.
634 // Tell all our changeTrackers about this digest since we don't know who might need it.
635 CFDictionaryForEach(cm->engine->viewNameSet2ChangeTracker, ^(const void *viewNameSet, const void *ct) {
636 SOSChangeMapperAppendObject(cm, (SOSChangeTrackerRef)ct, isAdd, change);
637 });
638 someoneCares = CFDictionaryGetCount(cm->engine->viewNameSet2ChangeTracker);
639 } else {
640 // We got an object let's figure out which views it's in and schedule it for
641 // delivery to all changeTrackers interested in any of those views.
642 SOSObjectRef object = (SOSObjectRef)change;
643 CFMutableSetRef changeTrackerSet = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
644 // First gather all the changeTrackers interested in this object (eleminating dupes by collecting them in a set)
645 SOSEngineObjectWithView(cm->engine, object, ^(CFStringRef viewName) {
646 const void *ctorset = CFDictionaryGetValue(cm->engine->viewName2ChangeTracker, viewName);
647 if (isSet(ctorset)) {
648 CFSetForEach((CFSetRef)ctorset, ^(const void *ct) { CFSetAddValue(changeTrackerSet, ct); });
649 } else if (ctorset) {
650 CFSetAddValue(changeTrackerSet, ctorset);
651 }
652 });
653 // Then append the object to the changes array in the ct2changes dictionary keyed by viewSet
654 CFSetForEach(changeTrackerSet, ^(const void *ct) {
655 SOSChangeMapperAppendObject(cm, (SOSChangeTrackerRef)ct, isAdd, object);
656 });
657 someoneCares = CFSetGetCount(changeTrackerSet);
658 CFReleaseSafe(changeTrackerSet);
659 }
660 return someoneCares;
661 }
662
663 static bool SOSChangeMapperSend(struct SOSChangeMapper *cm, CFErrorRef *error) {
664 __block bool ok = true;
665 CFDictionaryForEach(cm->ct2changes, ^(const void *ct, const void *changes) {
666 ok &= SOSChangeTrackerTrackChanges((SOSChangeTrackerRef)ct, cm->engine, cm->txn, cm->source, cm->phase, (CFArrayRef)changes, error);
667 });
668 return ok;
669 }
670
671 static bool SOSEngineUpdateChanges_locked(SOSEngineRef engine, SOSTransactionRef txn, SOSDataSourceTransactionPhase phase, SOSDataSourceTransactionSource source, CFArrayRef changes, CFErrorRef *error)
672 {
673 secnoticeq("engine", "%@: %s %s %ld changes", engine->myID, phase == kSOSDataSourceTransactionWillCommit ? "will-commit" : phase == kSOSDataSourceTransactionDidCommit ? "did-commit" : "did-rollback", source == kSOSDataSourceSOSTransaction ? "sos" : "api", CFArrayGetCount(changes));
674 bool ok = true;
675 switch (phase) {
676 case kSOSDataSourceTransactionDidRollback:
677 ok &= SOSEngineLoad(engine, error);
678 break;
679 case kSOSDataSourceTransactionWillCommit:
680 case kSOSDataSourceTransactionDidCommit:
681 {
682 struct SOSChangeMapper cm;
683 SOSChangeMapperInit(&cm, engine, txn, phase, source);
684 SecDbEventRef event;
685 CFArrayForEachC(changes, event) {
686 CFTypeRef deleted = NULL;
687 CFTypeRef inserted = NULL;
688 SecDbEventGetComponents(event, &deleted, &inserted, error);
689 if (deleted)
690 SOSChangeMapperIngestChange(&cm, false, deleted);
691 if (inserted) {
692 bool someoneCares = SOSChangeMapperIngestChange(&cm, true, inserted);
693 if (!someoneCares && !isData(inserted) && SecDbItemIsTombstone((SecDbItemRef)inserted) && !CFEqualSafe(SecDbItemGetValue((SecDbItemRef)inserted, &v7utomb, NULL), kCFBooleanTrue)) {
694 CFErrorRef localError = NULL;
695 // A tombstone was inserted but there is no changetracker that
696 // cares about it.
697 if (!SecDbItemDoDeleteSilently((SecDbItemRef)inserted, (SecDbConnectionRef)txn, &localError)) {
698 secerror("failed to delete tombstone %@ that no one cares about: %@", inserted, localError);
699 CFReleaseNull(localError);
700 }
701 }
702 }
703 }
704
705 ok &= SOSChangeMapperSend(&cm, error);
706 SOSChangeMapperFree(&cm);
707
708 if (phase == kSOSDataSourceTransactionDidCommit) {
709 // We are being called outside a transaction, beware, that any
710 // db changes we attempt to make here will cause deadlock!
711 } else {
712 // Write SOSEngine and SOSPeer state to disk
713 // TODO: Only do this if dirty
714 ok &= SOSEngineSave(engine, txn, error);
715 }
716 break;
717 }
718 }
719 return ok;
720 }
721
722 static void SOSEngineSetNotifyPhaseBlock(SOSEngineRef engine) {
723 SOSDataSourceSetNotifyPhaseBlock(engine->dataSource, engine->queue, ^(SOSDataSourceRef ds, SOSTransactionRef txn, SOSDataSourceTransactionPhase phase, SOSDataSourceTransactionSource source, CFArrayRef changes) {
724 CFErrorRef localError = NULL;
725 if (!SOSEngineUpdateChanges_locked(engine, txn, phase, source, changes, &localError)) {
726 secerror("updateChanged failed: %@", localError);
727 }
728 CFReleaseSafe(localError);
729 });
730 }
731
732 #if 0 // TODO: update these checks
733 static void SOSEngineCircleChanged_sanitycheck(SOSEngineRef engine, CFStringRef myPeerID, CFArrayRef trustedPeers, CFArrayRef untrustedPeers) {
734 // Logging code
735 CFMutableArrayRef addedPeers = CFArrayCreateDifference(kCFAllocatorDefault, trustedPeers, engine->peerIDs);
736 CFMutableArrayRef deletedPeers = CFArrayCreateDifference(kCFAllocatorDefault, engine->peerIDs, trustedPeers);
737 CFMutableArrayRef addedUntrustedPeers = CFArrayCreateDifference(kCFAllocatorDefault, untrustedPeers, engine->peerIDs);
738 CFMutableArrayRef deletedUntrustedPeers = CFArrayCreateDifference(kCFAllocatorDefault, engine->peerIDs, untrustedPeers);
739
740 CFStringRef tpDesc = SOSPeerIDArrayCreateString(trustedPeers);
741 CFStringRef apDesc = SOSPeerIDArrayCreateString(addedPeers);
742 CFStringRef dpDesc = SOSPeerIDArrayCreateString(deletedPeers);
743 CFStringRef aupDesc = SOSPeerIDArrayCreateString(addedUntrustedPeers);
744 CFStringRef dupDesc = SOSPeerIDArrayCreateString(deletedUntrustedPeers);
745 secnotice("engine", "trusted %@ added %@ removed %@ add ut: %@ rem ut: %@", tpDesc, apDesc, dpDesc, aupDesc, dupDesc);
746 CFReleaseSafe(dupDesc);
747 CFReleaseSafe(aupDesc);
748 CFReleaseSafe(dpDesc);
749 CFReleaseSafe(apDesc);
750 CFReleaseSafe(tpDesc);
751
752 // Assertions:
753 // Ensure SOSAccount isn't giving us the runaround.
754 // Assert that trustedPeers, untrustedPeers and myPeerId are disjoint sets
755 if (trustedPeers) {
756 CFMutableArrayRef allTrustedPeers = CFArrayCreateDifference(kCFAllocatorDefault, trustedPeers, untrustedPeers);
757 assert(CFEqual(trustedPeers, allTrustedPeers));
758 CFReleaseSafe(allTrustedPeers);
759 assert(!CFArrayContainsValue(trustedPeers, CFRangeMake(0, CFArrayGetCount(trustedPeers)), myPeerID));
760 }
761 if (untrustedPeers) {
762 CFMutableArrayRef allUntrustedPeers = CFArrayCreateDifference(kCFAllocatorDefault, untrustedPeers, trustedPeers);
763 assert(CFEqual(untrustedPeers, allUntrustedPeers));
764 CFReleaseSafe(allUntrustedPeers);
765 assert(!CFArrayContainsValue(untrustedPeers, CFRangeMake(0, CFArrayGetCount(trustedPeers)), myPeerID));
766 }
767
768 CFReleaseNull(deletedUntrustedPeers);
769 CFReleaseNull(addedUntrustedPeers);
770 CFReleaseNull(deletedPeers);
771 CFReleaseNull(addedPeers);
772
773 // End of logging and asertions, actual code here.
774 }
775 #endif
776
777 static SOSChangeTrackerRef SOSReferenceAndGetChangeTracker(CFDictionaryRef lookup, CFMutableDictionaryRef referenced, CFSetRef viewNameSet) {
778 SOSChangeTrackerRef ct = (SOSChangeTrackerRef)CFDictionaryGetValue(referenced, viewNameSet);
779 if (!ct) {
780 ct = (SOSChangeTrackerRef)CFDictionaryGetValue(lookup, viewNameSet);
781 if (ct) {
782 SOSChangeTrackerResetRegistration(ct);
783 CFDictionarySetValue(referenced, viewNameSet, ct);
784 } else {
785 ct = SOSChangeTrackerCreate(kCFAllocatorDefault, false, NULL, NULL);
786 CFDictionarySetValue(referenced, viewNameSet, ct);
787 CFReleaseSafe(ct);
788 }
789 }
790 return ct;
791 }
792
793 static CFStringRef CFStringCreateWithViewNameSet(CFSetRef vns);
794
795 static void CFStringAppendPeerIDAndViews(CFMutableStringRef desc, CFStringRef peerID, CFSetRef vns) {
796 if (vns) {
797 CFStringRef vnsDesc = CFStringCreateWithViewNameSet(vns);
798 CFStringAppendFormat(desc, NULL, CFSTR(" %@ (%@)"), peerID, vnsDesc);
799 CFReleaseSafe(vnsDesc);
800 } else {
801 CFStringAppendFormat(desc, NULL, CFSTR(" %@ NULL"), peerID);
802 }
803 }
804
805 // Must be called after updating viewNameSet2ChangeTracker
806 static void SOSEngineUpdateViewName2ChangeTracker(SOSEngineRef engine) {
807 // Create the mapping from viewName -> ChangeTracker used for lookup during change notification
808 CFMutableDictionaryRef newViewName2ChangeTracker = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
809 CFDictionaryForEach(engine->viewNameSet2ChangeTracker, ^(const void *viewNameSet, const void *ct) {
810 CFSetForEach(viewNameSet, ^(const void *viewName) {
811 const void *ctorset = NULL;
812 if (CFDictionaryGetValueIfPresent(newViewName2ChangeTracker, viewName, &ctorset)) {
813 if (isSet(ctorset)) {
814 CFSetAddValue((CFMutableSetRef)ctorset, ct);
815 } else if (!CFEqual(ct, ctorset)) {
816 CFMutableSetRef set = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
817 CFSetAddValue(set, ctorset);
818 CFSetAddValue(set, ct);
819 CFDictionaryReplaceValue(newViewName2ChangeTracker, viewName, set);
820 CFRelease(set);
821 }
822 } else {
823 CFDictionarySetValue(newViewName2ChangeTracker, viewName, ct);
824 }
825 });
826 });
827 CFAssignRetained(engine->viewName2ChangeTracker, newViewName2ChangeTracker);
828 }
829
830 static void SOSEngineSetBackupBag(SOSEngineRef engine, SOSObjectRef bagItem);
831
832 // This is called only if we are in a circle and we should listen for keybag changes
833 static void SOSEngineRegisterBackupBagV0Tracker(SOSEngineRef engine, CFMutableDictionaryRef newViewNameSet2ChangeTracker, CFMutableStringRef desc) {
834 SOSChangeTrackerRef bbct = SOSReferenceAndGetChangeTracker(engine->viewNameSet2ChangeTracker, newViewNameSet2ChangeTracker, SOSViewsGetV0BackupBagViewSet());
835 SOSChangeTrackerRegisterChangeUpdate(bbct, ^bool(SOSChangeTrackerRef ct, SOSEngineRef engine, SOSTransactionRef txn, SOSDataSourceTransactionSource source, SOSDataSourceTransactionPhase phase, CFArrayRef changes, CFErrorRef *error) {
836 SOSChangeRef change;
837 CFArrayForEachC(changes, change) {
838 CFTypeRef object = NULL;
839 bool isAdd = SOSChangeGetObject(change, &object);
840 SecDbItemRef dbi = (SecDbItemRef)object;
841 if (!isData(object) &&
842 CFEqualSafe(SecDbItemGetCachedValueWithName(dbi, kSecAttrService), CFSTR("SecureBackupService")) &&
843 CFEqualSafe(SecDbItemGetCachedValueWithName(dbi, kSecAttrAccessible), kSecAttrAccessibleWhenUnlocked) &&
844 CFEqualSafe(SecDbItemGetCachedValueWithName(dbi, kSecAttrAccount), CFSTR("SecureBackupPublicKeybag"))) {
845 SOSEngineSetBackupBag(engine, isAdd ? (SOSObjectRef)object : NULL);
846 }
847 }
848 return true;
849 });
850 }
851
852 static void SOSEngineReferenceBackupPeer(SOSEngineRef engine, CFStringRef peerID, CFSetRef viewNameSet, CFDataRef keyBag, CFMutableDictionaryRef newViewNameSet2ChangeTracker, CFMutableDictionaryRef newPeerMap) {
853 CFTypeRef oldEntry = CFDictionaryGetValue(engine->peerMap, peerID);
854 CFTypeRef newEntry = SOSPeerOrStateSetViewsKeyBagAndCreateCopy(oldEntry, viewNameSet, keyBag);
855 if (newEntry) {
856 if (isDictionary(newEntry)) {
857 // Backup peers, are always inflated
858 CFAssignRetained(newEntry, SOSPeerCreateWithState(engine, peerID, newEntry, NULL));
859 // If !oldEntry this is an edge (first creation of a peer).
860 if (!oldEntry) {
861 SOSPeerKeyBagDidChange((SOSPeerRef)newEntry);
862 }
863 }
864 CFDictionarySetValue(newPeerMap, peerID, newEntry);
865 CFRelease(newEntry);
866
867 if (keyBag) {
868 SOSChangeTrackerRef ct = SOSReferenceAndGetChangeTracker(engine->viewNameSet2ChangeTracker, newViewNameSet2ChangeTracker, viewNameSet);
869
870 SOSChangeTrackerUpdatesChanges child = Block_copy(^bool(SOSChangeTrackerRef ct, SOSEngineRef engine, SOSTransactionRef txn, SOSDataSourceTransactionSource source, SOSDataSourceTransactionPhase phase, CFArrayRef changes, CFErrorRef *error) {
871 return SOSPeerDataSourceWillChange((SOSPeerRef)newEntry, SOSEngineGetDataSource(engine), source, changes, error);
872 });
873
874 SOSChangeTrackerRegisterChangeUpdate(ct, child);
875 Block_release(child);
876 }
877 }
878 }
879
880 static void SOSEngineReferenceSyncPeer(SOSEngineRef engine, CFStringRef peerID, CFSetRef viewNameSet, CFMutableDictionaryRef newViewNameSet2ChangeTracker, CFMutableDictionaryRef newPeerMap) {
881 CFTypeRef newEntry = SOSPeerOrStateSetViewsKeyBagAndCreateCopy(CFDictionaryGetValue(engine->peerMap, peerID), viewNameSet, NULL);
882 if (newEntry) {
883 SOSChangeTrackerRef ct = SOSReferenceAndGetChangeTracker(engine->viewNameSet2ChangeTracker, newViewNameSet2ChangeTracker, viewNameSet);
884 // Standard peer, inflated on demand
885 SOSChangeTrackerUpdatesManifests trackManifest;
886 if (isDictionary(newEntry)) {
887 // Uninflated peer, inflate on first notification.
888 trackManifest = ^bool(SOSChangeTrackerRef ct, SOSEngineRef engine, SOSTransactionRef txn, SOSDataSourceTransactionSource source, SOSDataSourceTransactionPhase phase, SOSManifestRef removals, SOSManifestRef additions, CFErrorRef *error) {
889 CFErrorRef localError = NULL;
890 SOSPeerRef peer = SOSEngineCopyPeerWithID_locked(engine, peerID, &localError);
891 bool ok;
892 if (!peer) {
893 secerror("%@: peer failed to inflate: %@", peerID, localError);
894 CFReleaseSafe(localError);
895 ok = false;
896 } else {
897 ok = SOSPeerDataSourceWillCommit(peer, source, removals, additions, error);
898 }
899 CFReleaseSafe(peer);
900 return ok;
901 };
902 } else {
903 // Inflated peer, just forward the changes to the peer
904 trackManifest = ^bool(SOSChangeTrackerRef ct, SOSEngineRef engine, SOSTransactionRef txn, SOSDataSourceTransactionSource source, SOSDataSourceTransactionPhase phase, SOSManifestRef removals, SOSManifestRef additions, CFErrorRef *error) {
905 return SOSPeerDataSourceWillCommit((SOSPeerRef)newEntry, source, removals, additions, error);
906 };
907 }
908 SOSChangeTrackerUpdatesManifests trackManifestCopy = Block_copy(trackManifest);
909 SOSChangeTrackerRegisterManifestUpdate(ct, trackManifestCopy);
910 Block_release(trackManifestCopy);
911
912 CFDictionarySetValue(newPeerMap, peerID, newEntry);
913 CFRelease(newEntry);
914 }
915 }
916
917
918 static void SOSEngineReferenceTrustedPeer(SOSEngineRef engine, SOSPeerMetaRef peerMeta, CFMutableDictionaryRef newViewNameSet2ChangeTracker, CFMutableDictionaryRef newPeerMap, CFMutableArrayRef peerIDs, CFMutableStringRef desc) {
919 CFSetRef viewNameSet = NULL;
920 CFDataRef keyBag = NULL;
921 CFStringRef peerID = SOSPeerMetaGetComponents(peerMeta, &viewNameSet, &keyBag, NULL);
922 // We trust peerID so append it to peerIDs
923 CFArrayAppendValue(peerIDs, peerID);
924 if (desc) CFStringAppendPeerIDAndViews(desc, peerID, viewNameSet);
925 // Update the viewNameSet for this peer, to appease tests, default to a viewset of the V0 view.
926 if (!viewNameSet)
927 viewNameSet = SOSViewsGetV0ViewSet();
928
929 // Always inflate backup peers, since they need to register with their changeTrackers right away.
930 if (keyBag) {
931 SOSEngineReferenceBackupPeer(engine, peerID, viewNameSet, keyBag, newViewNameSet2ChangeTracker, newPeerMap);
932 } else {
933 SOSEngineReferenceSyncPeer(engine, peerID, viewNameSet, newViewNameSet2ChangeTracker, newPeerMap);
934 }
935 }
936
937 static CFDataRef SOSEngineLoadV0KeyBag(SOSEngineRef engine, CFErrorRef *error) {
938 // Return the keybag for the given peerID.
939 /*
940 Values for V0 are:
941 kSecAttrAccessGroup ==> CFSTR("com.apple.sbd")
942 kSecAttrAccessible ==> kSecAttrAccessibleWhenUnlocked
943 kSecAttrAccount ==> CFSTR("SecureBackupPublicKeybag")
944 kSecAttrService ==> CFSTR("SecureBackupService")
945 */
946
947 CFMutableDictionaryRef keys = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault,
948 kSecAttrAccessGroup, CFSTR("com.apple.sbd"),
949 kSecAttrAccount, CFSTR("SecureBackupPublicKeybag"),
950 kSecAttrService, CFSTR("SecureBackupService"),
951 kSecAttrAccessible, kSecAttrAccessibleWhenUnlocked,
952 kSecAttrSynchronizable, kCFBooleanTrue,
953 NULL);
954
955 CFDataRef keybag = engine->dataSource->dsCopyItemDataWithKeys(engine->dataSource, keys, error);
956 CFReleaseSafe(keys);
957
958 return keybag;
959 }
960
961 static void SOSEngineReferenceBackupV0Peer(SOSEngineRef engine, CFMutableDictionaryRef newViewNameSet2ChangeTracker, CFMutableDictionaryRef newPeerMap, CFMutableArrayRef newPeerIDs, CFMutableStringRef desc) {
962 SOSPeerRef backupPeer = (SOSPeerRef)CFDictionaryGetValue(engine->peerMap, kSOSViewKeychainV0_tomb);
963 CFDataRef bag = NULL;
964 if (backupPeer && CFGetTypeID(backupPeer) == SOSPeerGetTypeID()) {
965 bag = SOSPeerGetKeyBag(backupPeer);
966 } else {
967 CFErrorRef localError = NULL;
968 if (!(bag = SOSEngineLoadV0KeyBag(engine, &localError))) {
969 secnotice("engine", "No keybag found for v0 backup peer: %@", localError);
970 CFReleaseSafe(localError);
971 }
972 }
973 SOSEngineReferenceBackupPeer(engine, kSOSViewKeychainV0_tomb, SOSViewsGetV0BackupViewSet(), bag, newViewNameSet2ChangeTracker, newPeerMap);
974 }
975
976 static void SOSEngineReferenceTrustedPeers(SOSEngineRef engine, CFMutableDictionaryRef newViewNameSet2ChangeTracker, CFMutableDictionaryRef newPeerMap, CFMutableArrayRef newPeerIDs, CFArrayRef trustedPeerMetas, CFMutableStringRef desc) {
977 // Then update the views for all trusted peers and add them to newPeerMap.
978 if (trustedPeerMetas != NULL && CFArrayGetCount(trustedPeerMetas) != 0) {
979 if (desc) CFStringAppend(desc, CFSTR(" trusted"));
980 // Remake engine->peerIDs
981 SOSPeerMetaRef peerMeta;
982 CFArrayForEachC(trustedPeerMetas, peerMeta) {
983 SOSEngineReferenceTrustedPeer(engine, peerMeta, newViewNameSet2ChangeTracker, newPeerMap, newPeerIDs, desc);
984 }
985 }
986 }
987
988 static void SOSEngineReferenceUntrustedPeers(SOSEngineRef engine, CFMutableDictionaryRef newPeerMap, CFArrayRef untrustedPeerMetas, CFMutableStringRef description) {
989 // Copy any untrustedPeers to newPeerMap as well if we have a state
990 // for them, if not no big deal. We also serialize all the untrustedPeers
991 // since they don't need to be deserializable
992 if (untrustedPeerMetas != NULL && CFArrayGetCount(untrustedPeerMetas) != 0) {
993 if (description) CFStringAppend(description, CFSTR(" untrusted"));
994 SOSPeerMetaRef peerMeta;
995 CFArrayForEachC(untrustedPeerMetas, peerMeta) {
996 CFSetRef views = NULL;
997 CFStringRef peerID = SOSPeerMetaGetComponents(peerMeta, &views, NULL, NULL);
998 if (description) CFStringAppendPeerIDAndViews(description, peerID, views);
999 CFSetRef nviews = NULL;
1000 if (!views)
1001 views = nviews = CFSetCreate(kCFAllocatorDefault, NULL, 0, &kCFTypeSetCallBacks);
1002 CFTypeRef newEntry = SOSPeerOrStateSetViewsAndCopyState(CFDictionaryGetValue(engine->peerMap, peerID), views);
1003 CFReleaseSafe(nviews);
1004 if (newEntry) {
1005 CFDictionarySetValue(newPeerMap, peerID, newEntry);
1006 CFReleaseSafe(newEntry);
1007 }
1008 }
1009 }
1010 }
1011
1012 static void SOSEngineReferenceChangeTrackers(SOSEngineRef engine, CFArrayRef trustedPeerMetas, CFArrayRef untrustedPeerMetas, CFMutableStringRef desc) {
1013 CFMutableArrayRef newPeerIDs = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1014 CFMutableDictionaryRef newPeerMap = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1015 CFMutableDictionaryRef newViewNameSet2ChangeTracker = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1016
1017 if (engine->myID) {
1018 // We have an engineID => in a circle (with 0 or more peers)
1019 // Ensure we have a v0 backup peer and it's listening for backup bag changes
1020 SOSEngineReferenceBackupV0Peer(engine, newViewNameSet2ChangeTracker, newPeerMap, newPeerIDs, desc);
1021 SOSEngineRegisterBackupBagV0Tracker(engine, newViewNameSet2ChangeTracker, desc);
1022 }
1023 SOSEngineReferenceTrustedPeers(engine, newViewNameSet2ChangeTracker, newPeerMap, newPeerIDs, trustedPeerMetas, desc);
1024 SOSEngineReferenceUntrustedPeers(engine, newPeerMap, untrustedPeerMetas, desc);
1025
1026 CFAssignRetained(engine->peerIDs, newPeerIDs);
1027 CFAssignRetained(engine->peerMap, newPeerMap);
1028 CFAssignRetained(engine->viewNameSet2ChangeTracker, newViewNameSet2ChangeTracker);
1029 SOSEngineUpdateViewName2ChangeTracker(engine);
1030 }
1031
1032 // Return true iff peers or views changed
1033 static bool SOSEngineSetPeers_locked(SOSEngineRef engine, SOSPeerMetaRef myPeerMeta, CFArrayRef trustedPeerMetas, CFArrayRef untrustedPeerMetas) {
1034 CFErrorRef error = NULL;
1035 CFSetRef myViews = NULL;
1036 CFDataRef myKeyBag = NULL;
1037 CFMutableStringRef desc = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, CFSTR("me"));
1038 CFStringRef myPeerID = myPeerMeta ? SOSPeerMetaGetComponents(myPeerMeta, &myViews, &myKeyBag, &error) : NULL;
1039 if (desc) CFStringAppendPeerIDAndViews(desc, myPeerID, myViews);
1040
1041 CFRetainAssign(engine->myID, myPeerID);
1042
1043 // Remake engine->peerMap from both trusted and untrusted peers
1044 SOSEngineReferenceChangeTrackers(engine, trustedPeerMetas, untrustedPeerMetas, desc);
1045
1046 secnotice("engine", "%@", desc);
1047 CFReleaseSafe(desc);
1048 return true;
1049 }
1050
1051 static void SOSEngineApplyPeerState(SOSEngineRef engine, CFDictionaryRef peerStateMap) {
1052 if (peerStateMap) CFDictionaryForEach(peerStateMap, ^(const void *peerID, const void *peerState) {
1053 CFTypeRef mapEntry = CFDictionaryGetValue(engine->peerMap, peerID);
1054 if (mapEntry && CFGetTypeID(mapEntry) == SOSPeerGetTypeID()) {
1055 // Update the state of any already inflated peers
1056 SOSPeerRef peer = (SOSPeerRef)mapEntry;
1057 CFErrorRef localError = NULL;
1058 if (!SOSPeerSetState(peer, engine, peerState, &localError)) {
1059 CFStringRef stateHex = NULL;
1060 stateHex = CFDataCopyHexString(peerState);
1061 secerror("peer: %@: bad state: %@ in engine state: %@", peerID, localError, stateHex);
1062 CFReleaseSafe(stateHex);
1063 CFReleaseNull(localError);
1064 // Possibly ask for an ensurePeerRegistration so we have a good list of peers again.
1065 }
1066 } else {
1067 // Just record the state for non inflated peers for now.
1068 CFDictionarySetValue(engine->peerMap, peerID, peerState);
1069 }
1070 });
1071 }
1072
1073 static void SOSEngineSynthesizePeerMetas(SOSEngineRef engine, CFMutableArrayRef trustedPeersMetas, CFMutableArrayRef untrustedPeers) {
1074 CFSetRef trustedPeerSet = engine->peerIDs ? CFSetCreateCopyOfArrayForCFTypes(engine->peerIDs) : NULL;
1075 CFDictionaryForEach(engine->peerMap, ^(const void *peerID, const void *peerState) {
1076 SOSPeerMetaRef meta = NULL;
1077 if (peerState && CFGetTypeID(peerState) == SOSPeerGetTypeID()) {
1078 SOSPeerRef peer = (SOSPeerRef)peerState;
1079 meta = SOSPeerMetaCreateWithComponents(peerID, SOSPeerGetViewNameSet(peer), SOSPeerGetKeyBag(peer));
1080 } else {
1081 // We don't need to add the meta for the backup case, since
1082 // SOSEngineReferenceBackupV0Peer will do the right thing
1083 if (!CFEqualSafe(peerID, kSOSViewKeychainV0_tomb)) {
1084 meta = SOSPeerMetaCreateWithState(peerID, peerState);
1085 }
1086 }
1087 // Any peer in peerStateMap that is not in trustedPeers is an untrustedPeer unless it's the v0 backup peer
1088 if ((trustedPeerSet && CFSetContainsValue(trustedPeerSet, peerID)) || CFEqualSafe(peerID, kSOSViewKeychainV0_tomb)) {
1089 if (meta) {
1090 CFArrayAppendValue(trustedPeersMetas, meta);
1091 }
1092 } else {
1093 CFArrayAppendValue(untrustedPeers, peerID);
1094 }
1095 CFReleaseNull(meta);
1096 });
1097 CFReleaseNull(trustedPeerSet);
1098 }
1099
1100 static void SOSEngineSetBackupBag(SOSEngineRef engine, SOSObjectRef bagItem) {
1101 CFMutableStringRef desc = NULL;
1102 SOSPeerRef backupPeer = SOSEngineCopyPeerWithID_locked(engine, kSOSViewKeychainV0_tomb, NULL);
1103 CFDataRef keybag = NULL;
1104 if (bagItem) {
1105 keybag = SecDbItemGetValue((SecDbItemRef)bagItem, &v6v_Data, NULL);
1106 }
1107
1108 // Since SOSPeerSetKeyBag() doesn't notify on the edge from NULL->initial keybag, since
1109 // that is the right behaviour for non v0 backup peers, we need to do it here for the v0 peer.
1110 bool hadBag = SOSPeerGetKeyBag(backupPeer);
1111 SOSPeerSetKeyBag(backupPeer, keybag);
1112 if (!hadBag)
1113 SOSPeerKeyBagDidChange(backupPeer);
1114
1115 CFReleaseSafe(backupPeer);
1116
1117 CFMutableArrayRef untrustedPeerMetas = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1118 CFMutableArrayRef trustedPeersMetas = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1119 SOSEngineSynthesizePeerMetas(engine, trustedPeersMetas, untrustedPeerMetas);
1120 SOSEngineReferenceChangeTrackers(engine, trustedPeersMetas, untrustedPeerMetas, desc);
1121 CFReleaseSafe(trustedPeersMetas);
1122 CFReleaseSafe(untrustedPeerMetas);
1123 }
1124
1125 #define SECONDS_PER_DAY (86400.0)
1126
1127 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
1128 #define TRACE_INTERVAL (7 * SECONDS_PER_DAY)
1129 #elif (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
1130 #define TRACE_INTERVAL (1 * SECONDS_PER_DAY)
1131 #endif
1132
1133 #ifdef TRACE_INTERVAL
1134 static void SOSEngineCloudKeychainTrace(SOSEngineRef engine, CFAbsoluteTime now) {
1135 CFAssignRetained(engine->lastTraceDate, CFDateCreate(kCFAllocatorDefault, now));
1136 CFIndex num_peers = engine->peerIDs ? 1 + CFArrayGetCount(engine->peerIDs) : 1;
1137 SOSManifestRef manifest = SOSEngineCopyManifestWithViewNameSet_locked(engine, SOSViewsGetV0ViewSet(), NULL);
1138 if (!manifest)
1139 manifest = SOSDataSourceCopyManifestWithViewNameSet(engine->dataSource, SOSViewsGetV0ViewSet(), NULL);
1140 size_t num_items = SOSManifestGetCount(manifest);
1141 CFReleaseSafe(manifest);
1142
1143 struct _SecServerKeyStats genpStats = { };
1144 struct _SecServerKeyStats inetStats = { };
1145 struct _SecServerKeyStats keysStats = { };
1146
1147 _SecServerGetKeyStats(&genp_class, &genpStats);
1148 _SecServerGetKeyStats(&inet_class, &inetStats);
1149 _SecServerGetKeyStats(&keys_class, &keysStats);
1150
1151 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
1152 CloudKeychainTrace(num_peers, num_items, &genpStats, &inetStats, &keysStats);
1153 });
1154 }
1155 #endif
1156
1157 static void SOSEngineCloudKeychainTraceIfNeeded(SOSEngineRef engine) {
1158 #ifdef TRACE_INTERVAL
1159 if (!engine->myID)
1160 return;
1161 CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
1162 if (engine->lastTraceDate) {
1163 CFAbsoluteTime lastTraceTime = CFDateGetAbsoluteTime(engine->lastTraceDate);
1164 if ((now - lastTraceTime) >= TRACE_INTERVAL) {
1165 SOSEngineCloudKeychainTrace(engine, now);
1166 }
1167 } else {
1168 SOSEngineCloudKeychainTrace(engine, now);
1169 }
1170 #endif
1171 }
1172
1173 // Restore the in-memory state of engine from saved state loaded from the db
1174 static bool SOSEngineSetState(SOSEngineRef engine, CFDataRef state, CFErrorRef *error) {
1175 __block bool ok = true;
1176 if (state) {
1177 CFMutableDictionaryRef dict = NULL;
1178 const uint8_t *der = CFDataGetBytePtr(state);
1179 const uint8_t *der_end = der + CFDataGetLength(state);
1180 ok = der = der_decode_dictionary(kCFAllocatorDefault, kCFPropertyListMutableContainers, (CFDictionaryRef *)&dict, error, der, der_end);
1181 if (der && der != der_end) {
1182 ok = SOSErrorCreate(kSOSErrorDecodeFailure, error, NULL, CFSTR("trailing %td bytes at end of state"), der_end - der);
1183 } else if (ok) {
1184 CFReleaseNull(engine->manifestCache);
1185 CFMutableDictionaryRef mfc = (CFMutableDictionaryRef)CFDictionaryGetValue(dict, kSOSEngineManifestCacheKey);
1186 if (mfc) {
1187 engine->manifestCache = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1188 CFDictionaryForEach(mfc, ^(const void *key, const void *value) {
1189 CFDataRef data = (CFDataRef)value;
1190 if (isData(data)) {
1191 SOSManifestRef mf = SOSManifestCreateWithData(data, NULL);
1192 if (mf)
1193 CFDictionarySetValue(engine->manifestCache, key, mf);
1194 CFReleaseSafe(mf);
1195 }
1196 });
1197 }
1198 CFMutableArrayRef untrustedPeers = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1199 CFMutableArrayRef trustedPeersMetas = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1200 CFRetainAssign(engine->peerIDs, asArray(CFDictionaryGetValue(dict, kSOSEnginePeerIDsKey), NULL));
1201 CFRetainAssign(engine->lastTraceDate, asDate(CFDictionaryGetValue(dict, kSOSEngineTraceDateKey), NULL));
1202 SOSEngineApplyPeerState(engine, asDictionary(CFDictionaryGetValue(dict, kSOSEnginePeerStateKey), NULL));
1203 SOSEngineSynthesizePeerMetas(engine, trustedPeersMetas, untrustedPeers);
1204 SOSEngineSetPeers_locked(engine, (CFStringRef)CFDictionaryGetValue(dict, kSOSEngineIDKey),
1205 trustedPeersMetas, untrustedPeers);
1206 CFReleaseNull(trustedPeersMetas);
1207 CFReleaseNull(untrustedPeers);
1208 }
1209 CFReleaseNull(dict);
1210 }
1211 secnotice("engine", "%@", engine);
1212 return ok;
1213 }
1214
1215 static bool SOSEngineLoad(SOSEngineRef engine, CFErrorRef *error) {
1216 CFDataRef state = SOSDataSourceCopyStateWithKey(engine->dataSource, kSOSEngineState, kSecAttrAccessibleAlways, error);
1217 bool ok = state && SOSEngineSetState(engine, state, error);
1218 CFReleaseSafe(state);
1219 return ok;
1220 }
1221
1222 static bool SOSEngineCircleChanged_locked(SOSEngineRef engine, SOSPeerMetaRef myPeerMeta, CFArrayRef trustedPeers, CFArrayRef untrustedPeers) {
1223 // Sanity check params
1224 // SOSEngineCircleChanged_sanitycheck(engine, myPeerID, trustedPeers, untrustedPeers);
1225
1226 // Transform from SOSPeerInfoRefs to CFDictionaries with the info we want per peer.
1227 // Or, Tell the real SOSPeerRef what the SOSPeerInfoRef is and have it copy out the data it needs.
1228 bool peersOrViewsChanged = SOSEngineSetPeers_locked(engine, myPeerMeta, trustedPeers, untrustedPeers);
1229
1230 // Run though all peers and only cache manifests for peers we still have
1231 CFErrorRef localError = NULL;
1232 if (!SOSEngineGCPeerState_locked(engine, &localError)) {
1233 secerror("SOSEngineGCPeerState_locked failed: %@", localError);
1234 CFReleaseNull(localError);
1235 }
1236 return peersOrViewsChanged;
1237 }
1238
1239 // Initialize the engine if a load fails. Basically this is our first time setup
1240 static bool SOSEngineInit(SOSEngineRef engine, CFErrorRef *error) {
1241 bool ok = true;
1242 secnotice("engine", "new engine for datasource named %@", SOSDataSourceGetName(engine->dataSource));
1243 CFAssignRetained(engine->peerMap, CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault));
1244 CFAssignRetained(engine->viewNameSet2ChangeTracker, CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault));
1245 CFAssignRetained(engine->viewName2ChangeTracker, CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault));
1246 CFReleaseNull(engine->manifestCache);
1247 CFReleaseNull(engine->peerIDs);
1248 // TODO: We shouldn't need to load the backup bag if there was no engine
1249 // state (load failed), since that means there was no circle nor were we an applicant.
1250
1251 // Set up change trackers so we know when a backup peer needs to be created?
1252 // no, since myID is not set, we are not in a circle, so no need to back up
1253 SOSEngineSetPeers_locked(engine, NULL, NULL, NULL);
1254 return ok;
1255 }
1256
1257 // Called by our DataSource in its constructor
1258 SOSEngineRef SOSEngineCreate(SOSDataSourceRef dataSource, CFErrorRef *error) {
1259 SOSEngineRef engine = NULL;
1260 engine = CFTypeAllocate(SOSEngine, struct __OpaqueSOSEngine, kCFAllocatorDefault);
1261 engine->dataSource = dataSource;
1262 engine->queue = dispatch_queue_create("engine", DISPATCH_QUEUE_SERIAL);
1263
1264 engine->peerMap = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1265 engine->viewNameSet2ChangeTracker = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1266 engine->viewName2ChangeTracker = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1267 //engine->syncCompleteQueue = NULL;
1268 engine->syncCompleteListeners = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1269 CFErrorRef engineError = NULL;
1270 if (!SOSEngineLoad(engine, &engineError)) {
1271 secwarning("engine failed load state starting with nothing %@", engineError);
1272 CFReleaseNull(engineError);
1273 if (!SOSEngineInit(engine, error)) {
1274 secerror("engine failed to initialze %@ giving up", error ? *error : NULL);
1275 }
1276 } else {
1277 // Successfully loaded engine state, let's trace if we haven't in a while
1278 SOSEngineCloudKeychainTraceIfNeeded(engine);
1279 }
1280 SOSEngineSetNotifyPhaseBlock(engine);
1281 return engine;
1282 }
1283
1284 // --- Called from off the queue, need to move to on the queue
1285
1286 static void SOSEngineDoOnQueue(SOSEngineRef engine, dispatch_block_t action)
1287 {
1288 dispatch_sync(engine->queue, action);
1289 }
1290
1291 static bool SOSEngineDoTxnOnQueue(SOSEngineRef engine, CFErrorRef *error, void(^transaction)(SOSTransactionRef txn, bool *commit))
1292 {
1293 return SOSDataSourceWith(engine->dataSource, error, ^(SOSTransactionRef txn, bool *commit) {
1294 SOSEngineDoOnQueue(engine, ^{ transaction(txn, commit); });
1295 });
1296 }
1297
1298 //
1299 // MARK: SOSEngine API
1300 //
1301
1302 void SOSEngineDispose(SOSEngineRef engine) {
1303 // NOOP Engines stick around forever to monitor dataSource changes.
1304 engine->dataSource = NULL;
1305 }
1306
1307 void SOSEngineForEachPeer(SOSEngineRef engine, void (^with)(SOSPeerRef peer)) {
1308 SOSEngineDoOnQueue(engine, ^{
1309 SOSEngineForEachPeer_locked(engine, with);
1310 });
1311 }
1312
1313 static void SOSEngineForEachBackupPeer(SOSEngineRef engine, void (^with)(SOSPeerRef peer)) {
1314 SOSEngineDoOnQueue(engine, ^{
1315 SOSEngineForEachBackupPeer_locked(engine, with);
1316 });
1317 }
1318
1319
1320 /* Handle incoming message from peer p. Return false if there was an error, true otherwise. */
1321 bool SOSEngineHandleMessage_locked(SOSEngineRef engine, CFStringRef peerID, SOSMessageRef message,
1322 SOSTransactionRef txn, bool *commit, bool *somethingChanged, CFErrorRef *error) {
1323 SOSPeerRef peer = SOSEngineCopyPeerWithID_locked(engine, peerID, error);
1324 if (!peer) return false;
1325 CFStringRef peerDesc = NULL;
1326 SOSManifestRef localManifest = NULL;
1327 SOSManifestRef allAdditions = NULL;
1328 SOSManifestRef unwanted = NULL;
1329 SOSManifestRef confirmed = NULL;
1330 SOSManifestRef base = NULL;
1331 SOSManifestRef confirmedRemovals = NULL, confirmedAdditions = NULL;
1332 __block struct SOSDigestVector receivedObjects = SOSDigestVectorInit;
1333 __block struct SOSDigestVector unwantedObjects = SOSDigestVectorInit;
1334
1335 // Check for unknown criticial extensions in the message, and handle
1336 // any other extensions we support
1337 __block bool ok = true;
1338 CFMutableArrayRef changes = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1339
1340 require_action_quiet(peer, exit, ok = SOSErrorCreate(errSecParam, error, NULL, CFSTR("Couldn't create peer with Engine for %@"), peerID));
1341 peerDesc = CFCopyDescription(peer);
1342
1343 SOSMessageWithExtensions(message, true, ^(CFDataRef oid, bool isCritical, CFDataRef extension, bool *stop) {
1344 // OMFG a Critical extension what shall I do!
1345 ok = SOSErrorCreate(kSOSErrorNotReady, error, NULL, CFSTR("Unknown criticial extension in peer message"));
1346 *stop = true;
1347 });
1348 require_quiet(ok, exit);
1349
1350 // Merge Objects from the message into our DataSource.
1351 // Should we move the transaction to the SOSAccount level?
1352 // TODO: Filter incoming objects
1353 //if (!SOSDataSourceForEachObjectInViewSet(engine->dataSource, pendingObjects, SOSPeerGetViewNameSet(peer), error, ^void(CFDataRef key, SOSObjectRef object, bool *stop) {
1354 require_quiet(ok &= SOSMessageWithSOSObjects(message, engine->dataSource, error, ^(SOSObjectRef peersObject, bool *stop) {
1355 CFDataRef digest = SOSObjectCopyDigest(engine->dataSource, peersObject, error);
1356 if (!digest) {
1357 *stop = true;
1358 *commit = false;
1359 secerror("%@ peer sent bad object: %@, rolling back changes", SOSPeerGetID(peer), error ? *error : NULL);
1360 return;
1361 }
1362 SOSDigestVectorAppend(&receivedObjects, CFDataGetBytePtr(digest));
1363 SOSObjectRef mergedObject = NULL;
1364 SOSMergeResult mr = SOSDataSourceMergeObject(engine->dataSource, txn, peersObject, &mergedObject, error);
1365 // TODO: If the mr is kSOSMergeLocalObject most of the time (or all of the time),
1366 // consider asking the peer to stop sending us objects, and send it objects instead.
1367 ok &= (mr != kSOSMergeFailure);
1368 if (!ok) {
1369 *stop = true;
1370 *commit = false;
1371 // TODO: Might want to change to warning since the race of us locking after ckd sends us a message could cause db locked errors here.
1372 secerror("%@ SOSDataSourceMergeObject failed %@ rolling back changes", SOSPeerGetID(peer), error ? *error : NULL);
1373 } else if (mr==kSOSMergePeersObject || mr==kSOSMergeCreatedObject) {
1374 *somethingChanged = true;
1375 } else {
1376 // mr == kSOSMergeLocalObject
1377 if (!CFEqual(mergedObject, peersObject)) {
1378 // Record this object as something we don't want peer to ever send us again. By adding it to
1379 // unwantedObjects we'll falsely claim to peer we have it until they tell us they don't have it anymore.
1380 SOSDigestVectorAppend(&unwantedObjects, CFDataGetBytePtr(digest));
1381 }
1382 // Ensure localObject is in local manifest (possible corruption) by posting an update when we are done.
1383 SOSChangesAppendAdd(changes, mergedObject);
1384 }
1385 CFReleaseSafe(mergedObject);
1386 CFReleaseSafe(digest);
1387 }), exit);
1388 struct SOSDigestVector dvunion = SOSDigestVectorInit;
1389 SOSDigestVectorSort(&receivedObjects);
1390 SOSDigestVectorUnionSorted(SOSManifestGetDigestVector(SOSMessageGetAdditions(message)), &receivedObjects, &dvunion);
1391 allAdditions = SOSManifestCreateWithDigestVector(&dvunion, error);
1392 SOSDigestVectorFree(&receivedObjects);
1393 SOSDigestVectorFree(&dvunion);
1394
1395 unwanted = SOSManifestCreateWithDigestVector(&unwantedObjects, error);
1396 SOSDigestVectorFree(&unwantedObjects);
1397
1398 if (CFArrayGetCount(changes)) {
1399 // NOTE: This is always notifiying of all additions that end up choosing local, which should be rare, since we shouldn't
1400 // be receiving objects we already have. When we do we tell ourselves to add them all again so our views will properly
1401 // reflect that we actually have these objects if we didn't already.
1402
1403 // Ensure any objects that we received and have locally already are actually in our local manifest
1404 SOSEngineUpdateChanges_locked(engine, txn, kSOSDataSourceTransactionDidCommit, kSOSDataSourceSOSTransaction, changes, error);
1405 }
1406 CFReleaseSafe(changes);
1407
1408 // ---- Don't use local or peer manifests from above this line,
1409 // ---- since commiting the SOSDataSourceWith transaction might change them ---
1410
1411 // Take a snapshot of our dataSource's local manifest.
1412 require_quiet(ok = localManifest = SOSEngineCopyLocalPeerManifest_locked(engine, peer, error), exit);
1413
1414 CFDataRef baseDigest = SOSMessageGetBaseDigest(message);
1415 CFDataRef proposedDigest = SOSMessageGetProposedDigest(message);
1416
1417 #if 0
1418 // I believe this is no longer needed now that we have eliminated extra,
1419 // since this is handled below once we get a confirmed manifest from our
1420 // peer.
1421
1422 // If we just received a L00 reset pendingObjects to localManifest
1423 if (!baseDigest && !proposedDigest) {
1424 // TODO: This is definitely busted for v0 peers since v0 peers always send a
1425 // L00 (ManifestDigestMessage as an ack) whereas in v2 this is a protocol restart
1426 // However if we can still find a confirmed manifest below we probably
1427 // don't want to do this even for v2.
1428 // Also I don't think we will ever send a ManifestMessage right now in
1429 // response to a ManifestDigest
1430 SOSPeerSetPendingObjects(peer, localManifest);
1431 secnoticeq("engine", "%@:%@ SOSPeerSetPendingObjects: %@", engine->myID, peerID, localManifest);
1432 }
1433 #endif
1434
1435 base = SOSPeerCopyManifestForDigest(peer, baseDigest);
1436 confirmed = SOSPeerCopyManifestForDigest(peer, SOSMessageGetSenderDigest(message));
1437 if (!confirmed) {
1438 if (SOSManifestGetCount(SOSMessageGetRemovals(message)) || SOSManifestGetCount(allAdditions)) {
1439 if (base || !baseDigest) {
1440 confirmed = SOSManifestCreateWithPatch(base, SOSMessageGetRemovals(message), allAdditions, error);
1441 }
1442 if (!confirmed) {
1443 confirmedRemovals = CFRetainSafe(SOSMessageGetRemovals(message));
1444 confirmedAdditions = CFRetainSafe(allAdditions);
1445 }
1446 } else if (baseDigest) {
1447 confirmed = CFRetainSafe(base);
1448 secerror("%@:%@ Protocol error send L00 - figure out later base: %@", engine->myID, peerID, base);
1449 }
1450 }
1451 secnoticeq("engine", "%@:%@ confirmed: %@ base: %@", engine->myID, peerID, confirmed, base);
1452 if (confirmed) {
1453 ok &= SOSManifestDiff(SOSPeerGetConfirmedManifest(peer), confirmed, &confirmedRemovals, &confirmedAdditions, error);
1454 if (SOSManifestGetCount(SOSMessageGetRemovals(message)))
1455 CFAssignRetained(confirmedRemovals, SOSManifestCreateUnion(confirmedRemovals, SOSMessageGetRemovals(message), error));
1456 }
1457 if (SOSManifestGetCount(confirmedRemovals) || SOSManifestGetCount(confirmedAdditions) || SOSManifestGetCount(unwanted))
1458 ok &= SOSPeerDidReceiveRemovalsAndAdditions(peer, confirmedRemovals, confirmedAdditions, unwanted, localManifest, error);
1459 // TODO: We should probably remove the if below and always call SOSPeerSetConfirmedManifest,
1460 // since having a NULL confirmed will force us to send a manifest message to get in sync again.
1461 if (confirmed)
1462 SOSPeerSetConfirmedManifest(peer, confirmed);
1463 else if (SOSPeerGetConfirmedManifest(peer)) {
1464 secnoticeq("engine", "%@:%@ unable to find confirmed in %@, sync protocol reset", engine->myID, peer, message);
1465
1466 SOSPeerSetConfirmedManifest(peer, NULL);
1467 //SOSPeerSetSendObjects(peer, true);
1468 }
1469
1470 // ---- SendObjects and extra->pendingObjects promotion dance ----
1471
1472 // The first block of code below sets peer.sendObjects to true when we receive a L00 and the second block
1473 // moves extra to pendingObjects once we receive a confirmed manifest in or after the L00.
1474 if (!baseDigest && !proposedDigest) {
1475 SOSPeerSetSendObjects(peer, true);
1476 }
1477
1478 if (0 /* confirmed && SOSPeerSendObjects(peer) */) {
1479 SOSManifestRef allExtra = NULL;
1480 ok &= SOSManifestDiff(confirmed, localManifest, NULL, &allExtra, error);
1481 secnoticeq("engine", "%@:%@ confirmed %@ (re)setting O:%@", engine->myID, SOSPeerGetID(peer), confirmed, allExtra);
1482 SOSPeerSetPendingObjects(peer, allExtra);
1483 CFReleaseSafe(allExtra);
1484 }
1485
1486 exit:
1487 secnotice("engine", "recv %@:%@ %@", engine->myID, SOSPeerGetID(peer), message);
1488 secnotice("peer", "recv %@ -> %@", peerDesc, peer);
1489
1490 CFReleaseNull(base);
1491 CFReleaseSafe(confirmed);
1492 CFReleaseSafe(localManifest);
1493 CFReleaseSafe(peerDesc);
1494 CFReleaseSafe(allAdditions);
1495 CFReleaseSafe(unwanted);
1496 CFReleaseSafe(confirmedRemovals);
1497 CFReleaseSafe(confirmedAdditions);
1498 CFReleaseSafe(peer);
1499 return ok;
1500 }
1501
1502 static CFDataRef SOSEngineCopyObjectDER(SOSEngineRef engine, SOSObjectRef object, CFErrorRef *error) {
1503 CFDataRef der = NULL;
1504 CFDictionaryRef plist = SOSObjectCopyPropertyList(engine->dataSource, object, error);
1505 if (plist) {
1506 der = CFPropertyListCreateDERData(kCFAllocatorDefault, plist, error);
1507 CFRelease(plist);
1508 }
1509 return der;
1510 }
1511
1512
1513 /*
1514
1515 +-----------------------------+_
1516 | | | \
1517 | A | T | \
1518 | | | \
1519 _+=============================+ } L
1520 / | | /
1521 / | S | /
1522 / | |_/
1523 / +==============================
1524 / | |
1525 C { | |
1526 \ | M +------------|
1527 \ | | |
1528 \ | | U |
1529 \ | | |
1530 \_+-------------+---------------+
1531
1532 A assumed
1533 T to be sent
1534 S shared
1535 M missing
1536 U unwanted
1537 L local
1538 C confirmed
1539
1540 */
1541 #if 0
1542 static bool SOSAppendRemoveToPatch(CFTypeRef remove, CFMutableDictionaryRef patch, CFErrorRef *error) {
1543 }
1544
1545 static bool SOSAppendAddToPatch(CFTypeRef add, CFMutableDictionaryRef patch, CFErrorRef *error) {
1546 }
1547
1548 static bool SOSAppendDiffToPatch(CFTypeRef left, CFTypeRef right, CFMutableDictionaryRef patch, CFErrorRef *error) {
1549 bool ok = true;
1550 if (!left && right) {
1551 SOSAppendAddToPatch(right, patch, error);
1552 } else if (left && !right) {
1553 SOSAppendRemoveToPatch(left, patch, error);
1554 } else if (left && right) {
1555 CFTypeID ltype = CFGetTypeID(left);
1556 CFTypeID rtype = CFGetTypeID(right);
1557 if (ltype == rtype) {
1558 if (CFArrayGetTypeID() == ltype) {
1559 ok = SecError(errSecParam, error, CFSTR("unsupported type array"), ltype);
1560 } else if (CFBooleanGetTypeID == ltype) {
1561 ok = SecError(errSecParam, error, CFSTR("unsupported type boolean"), ltype);
1562 } else if (CFDataGetTypeID == ltype) {
1563 ok = SecError(errSecParam, error, CFSTR("unsupported type data"), ltype);
1564 } else if (CFDictionaryGetTypeID == ltype) {
1565 __block CFMutableDictionaryRef leftnotright = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1566 __block CFMutableDictionaryRef rightnotleft = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, right);
1567
1568 CFDictionaryForEach(left, ^(const void *key, const void *lvalue) {
1569 const void *rvalue = NULL;
1570 if (CFDictionaryGetValueIfPresent(right, key, &rvalue)) {
1571 CFDictionaryRemoveValue(rightnotleft, key);
1572
1573 CFMutableDictionaryRef subpatch = CFDictionaryCreateForCFTypes(kCFAllocatorDefault);
1574 CFDictionaryAddValue(patch, key, subpatch);
1575 SOSAppendDiffToPatch(lvalue, rvalue, subpatch, error);
1576 CFReleaseSafe(subpatch);
1577 } else {
1578 CFDictionaryAddValue(leftnotright, key, lvalue);
1579 }
1580 });
1581 // Proccess leftnotright and rightnotleft
1582 CFReleaseSafe(leftnotright);
1583 CFReleaseSafe(rightnotleft);
1584 } else if (SOSManifestGetTypeID == ltype) {
1585 SOSManifestRef removed = NULL, added = NULL;
1586 ok &= SOSManifestDiff(left, right, &removed, &added, error);
1587 if (SOSManifestGetCount(removed) || SOSManifestGetCount(added)) {
1588 SOSAppendDiffToPatch(lvalue, rvalue, subpatch, error);
1589 CFStringAppend(, <#CFStringRef appendedString#>)
1590 }
1591 CFReleaseSafe(removed);
1592 CFReleaseSafe(added);
1593 } else if (CFNumberGetTypeID == ltype) {
1594 ok = SecError(errSecParam, error, CFSTR("unsupported type number"), ltype);
1595 } else if (CFSetGetTypeID == ltype) {
1596 ok = SecError(errSecParam, error, CFSTR("unsupported type set"), ltype);
1597 } else if (CFStringGetTypeID == ltype) {
1598 ok = SecError(errSecParam, error, CFSTR("unsupported type string"), ltype);
1599 } else {
1600 ok = SecError(errSecParam, error, CFSTR("unknown type %lu"), ltype);
1601 }
1602 }
1603 } else if (!left && !right) {
1604 // NOOP
1605 }
1606 }
1607 #endif
1608
1609 static __unused bool SOSEngineCheckPeerIntegrity(SOSEngineRef engine, SOSPeerRef peer, CFErrorRef *error) {
1610 #if 0
1611 //static CFMutableDictionaryRef p2amtu;
1612 if (!engine->p2amtu)
1613 engine->p2amtu = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1614 CFDictionaryRef amtu = CFDictionaryGetValue(engine->p2amtu, SOSPeerGetID(peer));
1615 #endif
1616
1617 // Inputs
1618 SOSManifestRef L = SOSEngineCopyLocalPeerManifest_locked(engine, peer, error);
1619 SOSManifestRef T = SOSPeerGetPendingObjects(peer);
1620 SOSManifestRef C = SOSPeerGetConfirmedManifest(peer);
1621 SOSManifestRef U = SOSPeerGetUnwantedManifest(peer);
1622
1623 // Computed
1624 SOSManifestRef CunionU = SOSManifestCreateUnion(C, U, error);
1625 SOSManifestRef S = SOSManifestCreateIntersection(L, CunionU, error);
1626
1627 SOSManifestRef AunionT = NULL, MunionU = NULL;
1628 SOSManifestDiff(L, C, &AunionT, &MunionU, error);
1629
1630 SOSManifestRef A = SOSManifestCreateComplement(T, AunionT, error);
1631 SOSManifestRef M = SOSManifestCreateComplement(U, MunionU, error);
1632
1633 SOSManifestRef SunionAunionT = SOSManifestCreateUnion(S, AunionT, error);
1634 SOSManifestRef SunionMunionU = SOSManifestCreateUnion(S, MunionU, error);
1635
1636 SOSManifestRef AintersectM = SOSManifestCreateIntersection(A, M, error);
1637 SOSManifestRef AintersectS = SOSManifestCreateIntersection(A, S, error);
1638 SOSManifestRef AintersectT = SOSManifestCreateIntersection(A, T, error);
1639 SOSManifestRef AintersectU = SOSManifestCreateIntersection(A, U, error);
1640 SOSManifestRef MintersectS = SOSManifestCreateIntersection(M, S, error);
1641 SOSManifestRef MintersectT = SOSManifestCreateIntersection(M, T, error);
1642 SOSManifestRef MintersectU = SOSManifestCreateIntersection(M, U, error);
1643 SOSManifestRef SintersectT = SOSManifestCreateIntersection(S, T, error);
1644 SOSManifestRef SintersectU = SOSManifestCreateIntersection(S, U, error);
1645 SOSManifestRef TintersectU = SOSManifestCreateIntersection(T, U, error);
1646
1647 #if 0
1648 CFDictionaryRef newAmtu = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, CFSTR("A"), A, CFSTR("M"), M, CFSTR("T"), T, CFSTR("U") U, NULL);
1649 CFDictionarySetValue(engine->p2amtu, SOSPeerGetID(peer), newAmtu);
1650 CFMutableStringRef amtuChanges = CFStringCreateMutable(kCFAllocatorDefault, 0);
1651 SOSAppendDiffToString(amtu, newAmtu, amtuChanges);
1652 secnotice("engine", "%@: %@", SOSPeerGetID(peer), amtuChanges);
1653 #endif
1654
1655 #define SOSASSERT(e) (__builtin_expect(!(e), 0) ? secnotice("engine", "state-assertion %s", #e), assert(e) : (void)0)
1656
1657 SOSASSERT(L ? CFEqual(L, SunionAunionT) : SOSManifestGetCount(SunionAunionT) == 0);
1658 SOSASSERT(C ? CFEqual(C, SunionMunionU) : SOSManifestGetCount(SunionMunionU) == 0);
1659
1660 SOSASSERT(SOSManifestGetCount(AintersectM) == 0);
1661 SOSASSERT(SOSManifestGetCount(AintersectS) == 0);
1662 SOSASSERT(SOSManifestGetCount(AintersectT) == 0);
1663 SOSASSERT(SOSManifestGetCount(AintersectU) == 0);
1664 SOSASSERT(SOSManifestGetCount(MintersectS) == 0);
1665 SOSASSERT(SOSManifestGetCount(MintersectT) == 0);
1666 SOSASSERT(SOSManifestGetCount(MintersectU) == 0);
1667 SOSASSERT(SOSManifestGetCount(SintersectT) == 0);
1668 SOSASSERT(SOSManifestGetCount(SintersectU) == 0);
1669 SOSASSERT(SOSManifestGetCount(TintersectU) == 0);
1670
1671 CFReleaseSafe(AintersectM);
1672 CFReleaseSafe(AintersectS);
1673 CFReleaseSafe(AintersectT);
1674 CFReleaseSafe(AintersectU);
1675 CFReleaseSafe(MintersectS);
1676 CFReleaseSafe(MintersectT);
1677 CFReleaseSafe(MintersectU);
1678 CFReleaseSafe(SintersectT);
1679 CFReleaseSafe(SintersectU);
1680 CFReleaseSafe(TintersectU);
1681
1682 CFReleaseSafe(AunionT);
1683 CFReleaseSafe(MunionU);
1684
1685
1686 CFReleaseSafe(A);
1687 CFReleaseSafe(M);
1688 CFReleaseSafe(S);
1689 //CFReleaseSafe(T); // Get
1690 //CFReleaseSafe(U); // Get
1691 //CFReleaseSafe(C); // Get
1692 CFReleaseSafe(L);
1693 return true;
1694 }
1695
1696 void SOSEngineSetSyncCompleteListener(SOSEngineRef engine, CFStringRef peerID, dispatch_block_t notify_block) {
1697 dispatch_block_t copy = Block_copy(notify_block);
1698 SOSEngineDoOnQueue(engine, ^{
1699 if (notify_block) {
1700 CFDictionarySetValue(engine->syncCompleteListeners, peerID, copy);
1701 } else {
1702 CFDictionaryRemoveValue(engine->syncCompleteListeners, peerID);
1703 }
1704 });
1705 CFReleaseNull(copy);
1706 }
1707
1708 void SOSEngineSetSyncCompleteListenerQueue(SOSEngineRef engine, dispatch_queue_t notify_queue) {
1709 SOSEngineDoOnQueue(engine, ^{
1710 engine->syncCompleteQueue = notify_queue;
1711 });
1712 }
1713
1714 static void SOSEngineCompletedSyncWithPeer(SOSEngineRef engine, SOSPeerRef peer) {
1715 dispatch_block_t notify = CFDictionaryGetValue(engine->syncCompleteListeners, SOSPeerGetID(peer));
1716 if (notify && engine->syncCompleteQueue)
1717 dispatch_async(engine->syncCompleteQueue, notify);
1718
1719 // Delete dictionary entry?
1720 }
1721
1722
1723 CFDataRef SOSEngineCreateMessage_locked(SOSEngineRef engine, SOSPeerRef peer,
1724 CFErrorRef *error, SOSEnginePeerMessageSentBlock *sent) {
1725 SOSManifestRef local = SOSEngineCopyLocalPeerManifest_locked(engine, peer, error);
1726 __block SOSMessageRef message = SOSMessageCreate(kCFAllocatorDefault, SOSPeerGetMessageVersion(peer), error);
1727 SOSManifestRef confirmed = SOSPeerGetConfirmedManifest(peer);
1728 SOSManifestRef pendingObjects = SOSPeerGetPendingObjects(peer);
1729 SOSManifestRef objectsSent = NULL;
1730 SOSManifestRef proposed = NULL;
1731 SOSManifestRef allMissing = NULL;
1732 SOSManifestRef allExtra = NULL;
1733 SOSManifestRef extra = NULL;
1734 SOSManifestRef excessPending = NULL;
1735 SOSManifestRef missing = NULL;
1736 SOSManifestRef unwanted = SOSPeerGetUnwantedManifest(peer);
1737 SOSManifestRef excessUnwanted = NULL;
1738 CFDataRef result = NULL;
1739
1740 // Given (C, L, T, U) compute (T, U, M, A)
1741 // (C \ L) \ U => M
1742 // (L \ C) \ T => A
1743 // we also compute
1744 // U \ (C \ L) => EU
1745 // T \ (L \ C) => ET
1746 // And assert that both EU and ET are empty and if not remove them from U and T respectively
1747 SOSManifestDiff(confirmed, local, &allMissing, &allExtra, error);
1748 SOSManifestDiff(allExtra, pendingObjects, &extra, &excessPending, error);
1749 if (SOSManifestGetCount(excessPending)) {
1750 // T \ (L \ C) => excessPending (items both in L and C or in neither that are still pending)
1751 // Can only happen if a member of T was removed from L without us having a chance to update T
1752 secerror("%@ ASSERTION FAILURE purging excess pendingObjects: %@", peer, excessPending);
1753 SOSManifestRef newPendingObjects = SOSManifestCreateComplement(excessPending, pendingObjects, error);
1754 SOSPeerSetPendingObjects(peer, newPendingObjects);
1755 CFReleaseSafe(newPendingObjects);
1756 pendingObjects = SOSPeerGetPendingObjects(peer);
1757 }
1758 SOSManifestDiff(allMissing, unwanted, &missing, &excessUnwanted, error);
1759 if (SOSManifestGetCount(excessUnwanted)) {
1760 // U \ (C \ L) => excessUnwanted (items both in L and C or in neither that are still unwanted)
1761 // Can only happen if a member of U was added to L without us having a chance to update U.
1762 // Since U only contains items the conflict resolver rejected, this implies L somehow got rolled back
1763 // The other option (and more likely) is a member of U was removed from C and not from U.
1764 secerror("%@ ASSERTION FAILURE purging excess unwanted: %@", peer, excessUnwanted);
1765 SOSManifestRef newUnwanted = SOSManifestCreateComplement(excessUnwanted, unwanted, error);
1766 SOSPeerSetUnwantedManifest(peer, newUnwanted);
1767 CFReleaseSafe(newUnwanted);
1768 unwanted = SOSPeerGetUnwantedManifest(peer);
1769 }
1770
1771 CFReleaseNull(allExtra);
1772 CFReleaseNull(excessPending);
1773 CFReleaseNull(allMissing);
1774 CFReleaseNull(excessUnwanted);
1775
1776 secnoticeq("engine", "%@:%@: send state for peer [%s%s%s][%s%s] P:%zu, E:%zu, M:%zu U:%zu", engine->myID, SOSPeerGetID(peer),
1777 local ? "L":"l",
1778 confirmed ? "C":"0",
1779 pendingObjects ? "P":"0",
1780 SOSPeerSendObjects(peer) ? "O":"o",
1781 SOSPeerMustSendMessage(peer) ? "S":"s",
1782 SOSManifestGetCount(pendingObjects),
1783 SOSManifestGetCount(extra),
1784 SOSManifestGetCount(missing),
1785 SOSManifestGetCount(unwanted)
1786 );
1787
1788 if (confirmed) {
1789 // TODO: Because of not letting things terminate while we have extra left
1790 // we might send objects when we didn't need to, but there is always an
1791 // extra roundtrip required for objects that we assume the other peer
1792 // should have already.
1793 // TODO: If there are extra objects left, calling this function is not
1794 // idempotent we should check if pending is what we are about to send and not send anything in this case.
1795 if (SOSManifestGetCount(pendingObjects) == 0 && SOSManifestGetCount(extra) == 0)
1796 SOSPeerSetSendObjects(peer, false);
1797
1798 // If we aren't missing anything, we've gotten all their data, so we're sync even if they haven't seen ours.
1799 if (missing && SOSManifestGetCount(missing) == 0) {
1800 SOSEngineCompletedSyncWithPeer(engine, peer);
1801 }
1802
1803 if (CFEqualSafe(local, SOSPeerGetProposedManifest(peer)) && !SOSPeerMustSendMessage(peer)) {
1804 bool send = false;
1805 if (CFEqual(confirmed, local)) {
1806 secnoticeq("engine", "synced <No MSG> %@:%@", engine->myID, peer);
1807 } else if (SOSManifestGetCount(pendingObjects) == 0 /* TODO: No entries moved from extra to pendingObjects. */
1808 && SOSManifestGetCount(missing) == 0) {
1809 secnoticeq("engine", "waiting <MSG not resent> %@:%@ extra: %@", engine->myID, peer, extra);
1810 } else {
1811 send = true;
1812 }
1813 if (!send) {
1814 CFReleaseSafe(local);
1815 CFReleaseSafe(message);
1816 CFReleaseNull(extra);
1817 CFReleaseNull(missing);
1818 return CFDataCreate(kCFAllocatorDefault, NULL, 0);
1819 }
1820 }
1821
1822 if (SOSManifestGetCount(pendingObjects)) {
1823 // If we have additions and we need to send objects, do so.
1824 __block size_t objectsSize = 0;
1825 __block struct SOSDigestVector dv = SOSDigestVectorInit;
1826 CFMutableArrayRef changes = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1827 if (!SOSDataSourceForEachObject(engine->dataSource, pendingObjects, error, ^void(CFDataRef key, SOSObjectRef object, bool *stop) {
1828 CFErrorRef localError = NULL;
1829 CFDataRef digest = NULL;
1830 CFDataRef der = NULL;
1831 if (!object) {
1832 const uint8_t *d = CFDataGetBytePtr(key);
1833 secnoticeq("engine", "%@:%@ object %02X%02X%02X%02X dropping from manifest: not found in datasource",
1834 engine->myID, SOSPeerGetID(peer), d[0], d[1], d[2], d[3]);
1835 SOSChangesAppendDelete(changes, key);
1836 } else if (!(der = SOSEngineCopyObjectDER(engine, object, &localError))
1837 || !(digest = SOSObjectCopyDigest(engine->dataSource, object, &localError))) {
1838 if (SecErrorGetOSStatus(localError) == errSecDecode) {
1839 // Decode error, we need to drop these objects from our manifests
1840 const uint8_t *d = CFDataGetBytePtr(key);
1841 secnoticeq("engine", "%@:%@ object %02X%02X%02X%02X dropping from manifest: %@",
1842 engine->myID, SOSPeerGetID(peer), d[0], d[1], d[2], d[3], localError);
1843 SOSChangesAppendDelete(changes, key);
1844 CFRelease(localError);
1845 } else {
1846 // Stop iterating and propagate out all other errors.
1847 const uint8_t *d = CFDataGetBytePtr(key);
1848 secnoticeq("engine", "%@:%@ object %02X%02X%02X%02X in SOSDataSourceForEachObject: %@",
1849 engine->myID, SOSPeerGetID(peer), d[0], d[1], d[2], d[3], localError);
1850 *stop = true;
1851 CFErrorPropagate(localError, error);
1852 CFReleaseNull(message);
1853 }
1854 } else {
1855 if (!CFEqual(key, digest)) {
1856 const uint8_t *d = CFDataGetBytePtr(key);
1857 const uint8_t *e = CFDataGetBytePtr(digest);
1858 secnoticeq("engine", "%@:%@ object %02X%02X%02X%02X is really %02X%02X%02X%02X dropping from local manifest",
1859 engine->myID, SOSPeerGetID(peer), d[0], d[1], d[2], d[3], e[0], e[1], e[2], e[3]);
1860 SOSChangesAppendDelete(changes, key);
1861 SOSChangesAppendAdd(changes, object); // This is new behaviour but we think it's more correct
1862 }
1863
1864 size_t objectLen = (size_t)CFDataGetLength(der);
1865 if (SOSMessageAppendObject(message, der, &localError)) {
1866 SOSDigestVectorAppend(&dv, CFDataGetBytePtr(digest));
1867 } else {
1868 const uint8_t *d = CFDataGetBytePtr(digest);
1869 CFStringRef hexder = CFDataCopyHexString(der);
1870 secnoticeq("engine", "%@:%@ object %02X%02X%02X%02X der: %@ dropping from manifest: %@",
1871 engine->myID, SOSPeerGetID(peer), d[0], d[1], d[2], d[3], hexder, localError);
1872 CFReleaseNull(hexder);
1873 CFReleaseNull(message);
1874 // Since we can't send these objects let's assume they are bad too?
1875 SOSChangesAppendDelete(changes, digest);
1876 }
1877 objectsSize += objectLen;
1878 if (objectsSize > kSOSMessageMaxObjectsSize)
1879 *stop = true;
1880 }
1881 CFReleaseSafe(der);
1882 CFReleaseSafe(digest);
1883 })) {
1884 CFReleaseNull(message);
1885 }
1886 if (dv.count)
1887 objectsSent = SOSManifestCreateWithDigestVector(&dv, error);
1888 if (CFArrayGetCount(changes)) {
1889 CFErrorRef localError = NULL;
1890 if (!SOSEngineUpdateChanges_locked(engine, NULL, kSOSDataSourceTransactionDidCommit, kSOSDataSourceSOSTransaction, changes, &localError))
1891 secerror("SOSEngineUpdateChanges_locked: %@ failed: %@", changes, localError);
1892 CFReleaseSafe(localError);
1893 CFAssignRetained(local, SOSEngineCopyLocalPeerManifest_locked(engine, peer, error));
1894 }
1895 CFReleaseSafe(changes);
1896 }
1897 } else {
1898 // If we have no confirmed manifest, we want all pendedObjects going out as a manifest
1899 objectsSent = CFRetainSafe(pendingObjects);
1900 }
1901
1902 if (confirmed || SOSManifestGetCount(missing) || SOSManifestGetCount(extra) || objectsSent) {
1903 SOSManifestRef allExtra = SOSManifestCreateUnion(extra, objectsSent, error);
1904 proposed = SOSManifestCreateWithPatch(confirmed, missing, allExtra, error);
1905 CFReleaseNull(allExtra);
1906 }
1907
1908 SOSManifestRef sender = local;
1909 // We actually send the remote peer its own digest.
1910 // Note that both pendingObjects and unwanted may have been changed, so we get them again
1911 if (SOSManifestGetCount(SOSPeerGetPendingObjects(peer))==0 && SOSManifestGetCount(extra)==0 &&
1912 SOSManifestGetCount(missing)==0 && SOSManifestGetCount(SOSPeerGetUnwantedManifest(peer))!=0) {
1913 secnoticeq("engine", "%@:%@: only have differences in unwanted set; lying to peer to stop sync",engine->myID, SOSPeerGetID(peer));
1914 sender = confirmed;
1915 }
1916
1917 if (!SOSMessageSetManifests(message, sender, confirmed, proposed, proposed, confirmed ? objectsSent : NULL, error)) {
1918 secnoticeq("engine", "%@:%@: failed to set message manifests",engine->myID, SOSPeerGetID(peer));
1919 CFReleaseNull(message);
1920 }
1921
1922 CFReleaseNull(objectsSent);
1923
1924 if (message) {
1925 result = SOSMessageCreateData(message, SOSPeerNextSequenceNumber(peer), error);
1926 }
1927
1928 if (result) {
1929 // Capture the peer in our block (SOSEnginePeerMessageSentBlock)
1930 CFRetainSafe(peer);
1931 *sent = Block_copy(^(bool success) {
1932 dispatch_async(engine->queue, ^{
1933 if (success) {
1934 SOSPeerSetMustSendMessage(peer, false);
1935 if (!confirmed && !proposed) {
1936 SOSPeerSetSendObjects(peer, true);
1937 secnoticeq("engine", "%@:%@ sendObjects=true L:%@", engine->myID, SOSPeerGetID(peer), local);
1938 }
1939 SOSPeerAddLocalManifest(peer, local);
1940 SOSPeerAddProposedManifest(peer, proposed);
1941 secnoticeq("engine", "send %@:%@ %@", engine->myID, SOSPeerGetID(peer), message);
1942 //SOSEngineCheckPeerIntegrity(engine, peer, NULL);
1943 } else {
1944 secerror("%@:%@ failed to send %@", engine->myID, SOSPeerGetID(peer), message);
1945 }
1946 CFReleaseSafe(peer);
1947 CFReleaseSafe(local);
1948 CFReleaseSafe(proposed);
1949 CFReleaseSafe(message);
1950 });
1951 });
1952 } else {
1953 CFReleaseSafe(local);
1954 CFReleaseSafe(proposed);
1955 CFReleaseSafe(message);
1956 }
1957 CFReleaseNull(extra);
1958 CFReleaseNull(missing);
1959 if (error && *error)
1960 secerror("%@:%@ error in send: %@", engine->myID, SOSPeerGetID(peer), *error);
1961
1962 return result;
1963 }
1964
1965 static void SOSEngineLogItemError(SOSEngineRef engine, CFStringRef peerID, CFDataRef key, CFDataRef optionalDigest, const char *where, CFErrorRef error) {
1966 if (!optionalDigest) {
1967 const uint8_t *d = CFDataGetBytePtr(key);
1968 secwarning("%@:%@ object %02X%02X%02X%02X %s: %@", engine->myID, peerID, d[0], d[1], d[2], d[3], where, error ? (CFTypeRef)error : CFSTR(""));
1969 } else {
1970 const uint8_t *d = CFDataGetBytePtr(key);
1971 const uint8_t *e = CFDataGetBytePtr(optionalDigest);
1972 secwarning("%@:%@ object %02X%02X%02X%02X is really %02X%02X%02X%02X dropping from local manifest", engine->myID, peerID, d[0], d[1], d[2], d[3], e[0], e[1], e[2], e[3]);
1973 }
1974 }
1975
1976 static bool SOSEngineWriteToBackup_locked(SOSEngineRef engine, SOSPeerRef peer, bool rewriteComplete, bool *didWrite, bool *incomplete, CFErrorRef *error) {
1977 __block bool ok = SOSPeerWritePendingReset(peer, error);
1978 if (!ok || !SOSPeerGetKeyBag(peer))
1979 return ok;
1980 __block SOSManifestRef local = SOSEngineCopyLocalPeerManifest_locked(engine, peer, error);
1981 __block SOSManifestRef proposed = SOSPeerGetProposedManifest(peer);
1982 __block bool notify = true;
1983 SOSManifestRef pendingObjects = NULL;
1984 SOSManifestRef missing = NULL;
1985 CFStringRef peerID = SOSPeerGetID(peer);
1986
1987 ok &= SOSManifestDiff(proposed, local, &missing, &pendingObjects, error);
1988
1989 secnoticeq("engine", "%@:%@: Send state for peer [%s%s%s] O: %zu, M: %zu", engine->myID, peerID,
1990 local ? "L":"l",
1991 proposed ? "P":"0",
1992 pendingObjects ? "O":"0",
1993 SOSManifestGetCount(pendingObjects),
1994 SOSManifestGetCount(missing));
1995
1996 if (SOSManifestGetCount(missing) == 0 && SOSManifestGetCount(pendingObjects) == 0) {
1997 // proposed == local (faster test than CFEqualSafe above), since we
1998 // already did the SOSManifestDiff
1999 if (rewriteComplete) {
2000 notify = false;
2001 } else {
2002 secnoticeq("engine", "%@:%@ backup still done", engine->myID, peer);
2003 goto done;
2004 }
2005 }
2006 ok &= SOSPeerAppendToJournal(peer, error, ^(FILE *journalFile, keybag_handle_t kbhandle) {
2007 SOSManifestRef objectsSent = NULL;
2008 __block struct SOSDigestVector dvdel = SOSDigestVectorInit;
2009 __block struct SOSDigestVector dvadd = SOSDigestVectorInit;
2010 SOSManifestForEach(missing, ^(CFDataRef key, bool *stop) {
2011 CFErrorRef localError = NULL;
2012 if (ftello(journalFile) > kSOSBackupMaxFileSize) {
2013 // Highwatermark hit on file.
2014 *stop = true;
2015 } else if (SOSBackupEventWriteDelete(journalFile, key, &localError)) {
2016 SOSDigestVectorAppend(&dvdel, CFDataGetBytePtr(key));
2017 } else {
2018 SOSEngineLogItemError(engine, peerID, key, NULL, "in SOSPeerWriteDelete", localError);
2019 CFErrorPropagate(localError, error);
2020 // TODO: Update of missing so proposed is updated properly
2021 *stop = true; // Disk full?
2022 ok = false;
2023 }
2024 });
2025 if (ok && SOSManifestGetCount(pendingObjects)) {
2026 CFMutableArrayRef changes = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
2027 ok &= SOSDataSourceForEachObject(engine->dataSource, pendingObjects, error, ^void(CFDataRef key, SOSObjectRef object, bool *stop) {
2028 CFErrorRef localError = NULL;
2029 CFDataRef digest = NULL;
2030 CFDictionaryRef backupItem = NULL;
2031 if (ftello(journalFile) > kSOSBackupMaxFileSize) {
2032 // Highwatermark hit on file.
2033 *stop = true;
2034 } else if (!object) {
2035 SOSEngineLogItemError(engine, peerID, key, NULL, "dropping from manifest: not found in datasource", localError);
2036 SOSChangesAppendDelete(changes, key);
2037 } else if (!(backupItem = SOSObjectCopyBackup(engine->dataSource, object, kbhandle, &localError))
2038 || !(digest = SOSObjectCopyDigest(engine->dataSource, object, &localError))) {
2039 if (SecErrorGetOSStatus(localError) == errSecDecode) {
2040 // Decode error, we need to drop these objects from our manifests
2041 SOSEngineLogItemError(engine, peerID, key, NULL, "dropping from manifest", localError);
2042 SOSChangesAppendDelete(changes, key);
2043 CFRelease(localError);
2044 } else {
2045 // Stop iterating and propagate out all other errors.
2046 SOSEngineLogItemError(engine, peerID, key, NULL, "in SOSDataSourceForEachObject", localError);
2047 *stop = true;
2048 CFErrorPropagate(localError, error);
2049 ok = false;
2050 }
2051 } else {
2052 if (!CFEqual(key, digest)) {
2053 SOSEngineLogItemError(engine, peerID, key, digest, "", NULL);
2054 SOSChangesAppendDelete(changes, key);
2055 SOSChangesAppendAdd(changes, object); // This is new behaviour but we think it's more correct
2056 }
2057
2058 if (SOSBackupEventWriteAdd(journalFile, backupItem, &localError)) {
2059 SOSDigestVectorAppend(&dvadd, CFDataGetBytePtr(digest));
2060 } else {
2061 SOSEngineLogItemError(engine, peerID, key, NULL, "in SOSPeerWriteAdd", localError);
2062 *stop = true; // Disk full?
2063 CFErrorPropagate(localError, error);
2064 ok = false;
2065 }
2066 }
2067 CFReleaseSafe(backupItem);
2068 CFReleaseSafe(digest);
2069 });
2070 if (CFArrayGetCount(changes)) {
2071 CFErrorRef localError = NULL;
2072 if (!SOSEngineUpdateChanges_locked(engine, NULL, kSOSDataSourceTransactionDidCommit, kSOSDataSourceSOSTransaction, changes, &localError))
2073 secerror("SOSEngineUpdateChanges_locked: %@ failed: %@", changes, localError);
2074 CFReleaseSafe(localError);
2075 // Since calling SOSEngineUpdateChanges_locked might cause local to change and might cause the backup peer to update proposed, refetch them here.
2076 CFAssignRetained(local, SOSEngineCopyLocalPeerManifest_locked(engine, peer, error));
2077 proposed = SOSPeerGetProposedManifest(peer);
2078 }
2079 CFReleaseSafe(changes);
2080 }
2081
2082 if (dvadd.count || (proposed && dvdel.count)) {
2083 *didWrite = true;
2084 SOSManifestRef deleted = SOSManifestCreateWithDigestVector(&dvdel, error);
2085 SOSManifestRef objectsSent = SOSManifestCreateWithDigestVector(&dvadd, error);
2086 SOSManifestRef newProposed = SOSManifestCreateWithPatch(proposed, deleted, objectsSent, error);
2087 CFReleaseSafe(deleted);
2088 CFReleaseSafe(objectsSent);
2089 SOSPeerSetProposedManifest(peer, newProposed);
2090 CFReleaseSafe(newProposed);
2091 proposed = SOSPeerGetProposedManifest(peer);
2092 }
2093 SOSDigestVectorFree(&dvdel);
2094 SOSDigestVectorFree(&dvadd);
2095
2096 // TODO: If proposed is NULL, and local is empty we should still consider ourselves done.
2097 // It so happens this can't happen in practice today since there is at least a backupbag
2098 // in the backup, but this is a bug waiting to rear its head in the future.
2099 if (ok && CFEqualSafe(local, proposed)) {
2100 CFErrorRef localError = NULL;
2101 if (SOSBackupEventWriteCompleteMarker(journalFile, 899, &localError)) {
2102 SOSPeerSetSendObjects(peer, true);
2103 *didWrite = true;
2104 secnoticeq("backup", "%@:%@ backup done%s", engine->myID, peerID, notify ? " notifying sbd" : "");
2105 // TODO: Now switch to changes based writing to backup sync.
2106 // Currently we leave changes enabled but we probably shouldn't
2107 } else {
2108 secwarning("%@:%@ in SOSBackupPeerWriteCompleteMarker: %@", engine->myID, peerID, localError);
2109 ok = false;
2110 *incomplete = true;
2111 CFErrorPropagate(localError, error);
2112 }
2113 } else {
2114 secnoticeq("backup", "%@:%@ backup incomplete [%zu/%zu]%s", engine->myID, peerID, SOSManifestGetCount(local), SOSManifestGetCount(proposed), notify ? " notifying sbd" : "");
2115 *incomplete = true;
2116 }
2117 CFReleaseNull(objectsSent);
2118 });
2119 if (notify)
2120 SOSBackupPeerPostNotification("writing changes to backup");
2121
2122 done:
2123 CFReleaseSafe(local);
2124 CFReleaseNull(pendingObjects);
2125 CFReleaseNull(missing);
2126
2127 return ok;
2128 }
2129
2130 bool SOSEngineSyncWithPeers(SOSEngineRef engine, CFTypeRef idsTransport, CFTypeRef kvsTransport, CFErrorRef *error) {
2131 __block bool ok = true;
2132 __block bool incomplete = false;
2133 ok &= SOSEngineDoTxnOnQueue(engine, error, ^(SOSTransactionRef txn, bool *commit) {
2134 __block bool dirty = false;
2135 SOSEngineForEachBackupPeer_locked(engine, ^(SOSPeerRef peer) {
2136 ok = SOSEngineWriteToBackup_locked(engine, peer, false, &dirty, &incomplete, error);
2137 });
2138
2139 if (dirty)
2140 ok = SOSEngineSave(engine, txn, error);
2141 });
2142 if (incomplete) {
2143 // Ensure we get called again in a while (after a backup timeout)
2144 // sbd will do this since we never wrote a complete marker.
2145 // TODO: This relies on us not writing complete marker for update
2146 // event while we havn't finished a full backup, which we currently still do.
2147 }
2148 return ok;
2149 }
2150
2151 bool SOSEngineHandleMessage(SOSEngineRef engine, CFStringRef peerID,
2152 CFDataRef raw_message, CFErrorRef *error)
2153 {
2154 __block bool result = true;
2155 __block bool somethingChanged = false;
2156 SOSMessageRef message = SOSMessageCreateWithData(kCFAllocatorDefault, raw_message, error);
2157 result &= message && SOSEngineDoTxnOnQueue(engine, error, ^(SOSTransactionRef txn, bool *commit) {
2158 result = SOSEngineHandleMessage_locked(engine, peerID, message, txn, commit, &somethingChanged, error);
2159 });
2160 CFReleaseSafe(message);
2161 if (somethingChanged)
2162 SecKeychainChanged(false);
2163 return result;
2164 }
2165
2166 void SOSEngineCircleChanged(SOSEngineRef engine, CFStringRef myPeerID, CFArrayRef trustedPeers, CFArrayRef untrustedPeers) {
2167 __block bool peersOrViewsChanged = false;
2168 SOSEngineDoOnQueue(engine, ^{
2169 peersOrViewsChanged = SOSEngineCircleChanged_locked(engine, myPeerID, trustedPeers, untrustedPeers);
2170 });
2171
2172 __block bool ok = true;
2173 __block CFErrorRef localError = NULL;
2174 ok &= SOSEngineDoTxnOnQueue(engine, &localError, ^(SOSTransactionRef txn, bool *commit) {
2175 ok = *commit = SOSEngineSave(engine, txn, &localError);
2176 });
2177 if (!ok) {
2178 secerror("failed to save engine state: %@", localError);
2179 CFReleaseSafe(localError);
2180 }
2181
2182 if (peersOrViewsChanged)
2183 SOSCCSyncWithAllPeers();
2184 }
2185
2186 SOSManifestRef SOSEngineCopyManifest(SOSEngineRef engine, CFErrorRef *error) {
2187 __block SOSManifestRef result = NULL;
2188 SOSEngineDoOnQueue(engine, ^{
2189 result = SOSEngineCopyManifestWithViewNameSet_locked(engine, SOSViewsGetV0ViewSet(), error);
2190 });
2191 return result;
2192 }
2193
2194 SOSManifestRef SOSEngineCopyLocalPeerManifest(SOSEngineRef engine, SOSPeerRef peer, CFErrorRef *error) {
2195 __block SOSManifestRef result = NULL;
2196 SOSEngineDoOnQueue(engine, ^{
2197 result = SOSEngineCopyLocalPeerManifest_locked(engine, peer, error);
2198 });
2199 return result;
2200 }
2201
2202 bool SOSEngineUpdateChanges(SOSEngineRef engine, SOSDataSourceTransactionSource source, CFArrayRef changes, CFErrorRef *error) {
2203 __block bool result = true;
2204 SOSEngineDoOnQueue(engine, ^{
2205 result = SOSEngineUpdateChanges_locked(engine, NULL, kSOSDataSourceTransactionDidCommit, source, changes, error);
2206 });
2207 return result;
2208 }
2209
2210 //
2211 // Peer state layout. WRONG! It's an array now
2212 // The peer state is an array.
2213 // The first element of the array is a dictionary with any number of keys and
2214 // values in it (for future expansion) such as changing the digest size or type
2215 // or remebering boolean flags for a peers sake.
2216 // The next three are special in that they are manifest digests with special
2217 // meaning and rules as to how they are treated (These are dynamically updated
2218 // based on database activity so they have a fully history of all changes made
2219 // to the local db. The first is the manifest representing the pendingObjects
2220 // to send to the other peer. This is normally only ever appending to, and in
2221 // particular with transactions originating from the Keychain API that affect
2222 // syncable items will need to add the new objects digests to the pendingObjects list
2223 // while adding the digests of any tombstones encountered to the extra list.
2224
2225 SOSPeerRef SOSEngineCopyPeerWithID(SOSEngineRef engine, CFStringRef peer_id, CFErrorRef *error) {
2226 __block SOSPeerRef peer = NULL;
2227 SOSEngineDoOnQueue(engine, ^{
2228 peer = SOSEngineCopyPeerWithID_locked(engine, peer_id, error);
2229 });
2230 return peer;
2231 }
2232
2233 bool SOSEngineForPeerID(SOSEngineRef engine, CFStringRef peerID, CFErrorRef *error, void (^forPeer)(SOSPeerRef peer)) {
2234 __block bool ok = true;
2235 SOSEngineDoOnQueue(engine, ^{
2236 SOSPeerRef peer = SOSEngineCopyPeerWithID_locked(engine, peerID, error);
2237 if (peer) {
2238 forPeer(peer);
2239 CFRelease(peer);
2240 } else {
2241 ok = false;
2242 }
2243 });
2244 return ok;
2245 }
2246
2247 bool SOSEngineWithPeerID(SOSEngineRef engine, CFStringRef peerID, CFErrorRef *error, void (^with)(SOSPeerRef peer, SOSDataSourceRef dataSource, SOSTransactionRef txn, bool *forceSaveState)) {
2248 __block bool result = true;
2249 result &= SOSEngineDoTxnOnQueue(engine, error, ^(SOSTransactionRef txn, bool *commit) {
2250 SOSPeerRef peer = SOSEngineCopyPeerWithID_locked(engine, peerID, error);
2251 if (!peer) {
2252 result = SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("Engine has no peer for %@"), peerID);
2253 } else {
2254 bool saveState = false;
2255 with(peer, engine->dataSource, txn, &saveState);
2256 CFReleaseSafe(peer);
2257 if (saveState)
2258 result = SOSEngineSave(engine, txn, error);
2259 // TODO: Don't commit if engineSave fails?
2260 }
2261 });
2262
2263 return result;
2264 }
2265
2266 CFDataRef SOSEngineCreateMessageToSyncToPeer(SOSEngineRef engine, CFStringRef peerID, SOSEnginePeerMessageSentBlock *sentBlock, CFErrorRef *error) {
2267 __block CFDataRef message = NULL;
2268 SOSEngineForPeerID(engine, peerID, error, ^(SOSPeerRef peer) {
2269 message = SOSEngineCreateMessage_locked(engine, peer, error, sentBlock);
2270 });
2271 return message;
2272 }
2273
2274 bool SOSEnginePeerDidConnect(SOSEngineRef engine, CFStringRef peerID, CFErrorRef *error) {
2275 return SOSEngineWithPeerID(engine, peerID, error, ^(SOSPeerRef peer, SOSDataSourceRef dataSource, SOSTransactionRef txn, bool *saveState) {
2276 *saveState = SOSPeerDidConnect(peer);
2277 });
2278 }
2279
2280 bool SOSEngineSetPeerConfirmedManifest(SOSEngineRef engine, CFStringRef backupName,
2281 CFDataRef keybagDigest, CFDataRef manifestData, CFErrorRef *error) {
2282 __block bool ok = true;
2283
2284 ok &= SOSEngineForPeerID(engine, backupName, error, ^(SOSPeerRef peer) {
2285 bool dirty = false;
2286 bool incomplete = false;
2287 SOSManifestRef confirmed = NULL;
2288 CFDataRef keybag = SOSPeerGetKeyBag(peer);
2289 CFDataRef computedKeybagDigest = keybag ? CFDataCopySHA1Digest(keybag, NULL) : NULL;
2290 if (CFEqualSafe(keybagDigest, computedKeybagDigest)) {
2291 ok = confirmed = SOSManifestCreateWithData(manifestData, error);
2292 if (ok) {
2293 // Set both confirmed and proposed (confirmed is just
2294 // for debug status, proposed is actually what's used
2295 // by the backup peer).
2296 SOSPeerSetConfirmedManifest(peer, confirmed);
2297 SOSPeerSetProposedManifest(peer, confirmed);
2298 }
2299 } else {
2300 // sbd missed a reset event, send it again
2301 // Force SOSEngineWriteToBackup_locked to call SOSPeerWriteReset, which clears
2302 // confirmed and proposed manifests and writes the keybag to the journal.
2303 SOSPeerSetMustSendMessage(peer, true);
2304 }
2305
2306 // Stop changes from writing complete markers, unless SOSEngineWriteToBackup_locked() detects we are in sync
2307 SOSPeerSetSendObjects(peer, false);
2308 // Write data for this peer if we can, technically not needed for non legacy protocol support all the time.
2309 ok = SOSEngineWriteToBackup_locked(engine, peer, true, &dirty, &incomplete, error);
2310
2311 CFReleaseSafe(confirmed);
2312 CFReleaseSafe(computedKeybagDigest);
2313 });
2314 return ok;
2315 }
2316
2317 CFArrayRef SOSEngineCopyBackupPeerNames(SOSEngineRef engine, CFErrorRef *error) {
2318 __block CFMutableArrayRef backupNames = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
2319 SOSEngineForEachBackupPeer(engine, ^(SOSPeerRef peer) {
2320 CFArrayAppendValue(backupNames, SOSPeerGetID(peer));
2321 });
2322 return backupNames;
2323 }
2324
2325 static CFStringRef CFStringCreateWithViewNameSet(CFSetRef vns) {
2326 CFIndex count = CFSetGetCount(vns);
2327 CFMutableArrayRef mvn = CFArrayCreateMutableForCFTypesWithCapacity(kCFAllocatorDefault, count);
2328 CFSetForEach(vns, ^(const void *value) {
2329 CFArrayAppendValue(mvn, value);
2330 });
2331 CFArraySortValues(mvn, CFRangeMake(0, count), (CFComparatorFunction)CFStringCompare, 0);
2332 CFStringRef string = CFStringCreateByCombiningStrings(kCFAllocatorDefault, mvn, CFSTR(":"));
2333 CFRelease(mvn);
2334 return string;
2335 }
2336
2337 static CFStringRef CFStringCreateWithLabelAndViewNameSetDescription(CFStringRef label, CFStringRef peerID, CFSetRef vns, SOSManifestRef manifest) {
2338 CFStringRef vnsDesc = CFStringCreateWithViewNameSet(vns);
2339 CFStringRef desc;
2340 if (manifest)
2341 desc = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@ %@ (%@) [%lu]"), label, peerID, vnsDesc, SOSManifestGetCount(manifest));
2342 else
2343 desc = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@ %@ (%@)"), label, peerID, vnsDesc);
2344 CFReleaseSafe(vnsDesc);
2345 return desc;
2346 }
2347
2348 static void CFArrayAppendConfirmedDigestsEntry(CFMutableArrayRef array, CFStringRef label, CFStringRef peerID, CFSetRef vns, SOSManifestRef manifest) {
2349 CFStringRef desc = CFStringCreateWithLabelAndViewNameSetDescription(label, peerID, vns, manifest);
2350 CFArrayAppendValue(array, desc);
2351 CFReleaseSafe(desc);
2352 CFDataRef digest = SOSManifestGetDigest(manifest, NULL);
2353 if (digest) {
2354 CFArrayAppendValue(array, digest);
2355 } else {
2356 CFDataRef nullDigest = CFDataCreate(kCFAllocatorDefault, NULL, 0);
2357 CFArrayAppendValue(array, nullDigest);
2358 CFReleaseSafe(nullDigest);
2359 }
2360 }
2361
2362 static CFArrayRef SOSEngineCopyPeerConfirmedDigests_locked(SOSEngineRef engine, CFErrorRef *error) {
2363 CFMutableArrayRef result = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
2364 CFDictionaryForEach(engine->viewNameSet2ChangeTracker, ^(const void *vns, const void *ct) {
2365 SOSManifestRef manifest = SOSEngineCopyManifestWithViewNameSet_locked(engine, vns, error);
2366 CFArrayAppendConfirmedDigestsEntry(result, CFSTR("local "), engine->myID, (CFSetRef)vns, manifest);
2367 CFReleaseSafe(manifest);
2368 });
2369
2370 // Copy other peers even if we aren't in the circle, since we're observing it.
2371 SOSEngineForEachPeer_locked(engine, ^(SOSPeerRef peer) {
2372 CFArrayAppendConfirmedDigestsEntry(result, CFSTR("remote"), SOSPeerGetID(peer), SOSPeerGetViewNameSet(peer),
2373 SOSPeerGetConfirmedManifest(peer));
2374 });
2375 return result;
2376 }
2377
2378 CFArrayRef SOSEngineCopyPeerConfirmedDigests(SOSEngineRef engine, CFErrorRef *error) {
2379 __block CFArrayRef result = NULL;
2380 SOSEngineDoOnQueue(engine, ^{
2381 result = SOSEngineCopyPeerConfirmedDigests_locked(engine, error);
2382 });
2383 return result;
2384 }
2385
2386 SOSDataSourceRef SOSEngineGetDataSource(SOSEngineRef engine) {
2387 return engine->dataSource;
2388 }