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