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