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