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