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