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