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