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