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