1/* 2 * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 25/* 26 * SOSEngine.c - Implementation of a secure object syncing engine 27 */ 28 29#include <SecureObjectSync/SOSEngine.h> 30#include <SecureObjectSync/SOSDigestVector.h> 31#include <SecureObjectSync/SOSInternal.h> 32#include <SecureObjectSync/SOSPeerInfo.h> 33#include <corecrypto/ccder.h> 34#include <stdlib.h> 35#include <stdbool.h> 36#include <utilities/SecCFError.h> 37#include <utilities/SecCFRelease.h> 38#include <utilities/SecCFWrappers.h> 39#include <utilities/der_plist.h> 40#include <utilities/der_plist_internal.h> 41#include <utilities/debugging.h> 42#include <utilities/iCloudKeychainTrace.h> 43#include <AssertMacros.h> 44#include <CoreFoundation/CoreFoundation.h> 45#include <securityd/SecItemDataSource.h> // TODO: We can't leave this here. 46#include <securityd/SecDbItem.h> // TODO: We can't leave this here. 47#include <securityd/SecItemServer.h>// TODO: We can't leave this here. 48#include <Security/SecItemPriv.h>// TODO: We can't leave this here. 49#include <securityd/SOSCloudCircleServer.h> 50 51// 52// MARK: SOSEngine The Keychain database with syncable keychain support. 53// 54 55// Key in dataSource for general engine state file. 56// This file only has digest entries in it, no manifests. 57static const CFStringRef kSOSEngineState = CFSTR("engine-state"); 58 59// Keys in state dictionary 60static CFStringRef kSOSPeerCoderKey = CFSTR("coder"); 61static CFStringRef kSOSEngineManifestCacheKey = CFSTR("manifestCache"); 62static CFStringRef kSOSEnginePeerStateKey = CFSTR("peerState"); 63static CFStringRef kSOSEnginePeerIDsKey = CFSTR("peerIDs"); 64static CFStringRef kSOSEngineIDKey = CFSTR("id"); 65 66/* SOSEngine implementation. */ 67struct __OpaqueSOSEngine { 68 CFRuntimeBase _base; 69 SOSDataSourceRef dataSource; 70 CFStringRef myID; // My peerID in the circle 71 SOSManifestRef manifest; // Explicitly not in cache since it's not persisted? 72 // We need to address the issues of corrupt keychain items 73 SOSManifestRef unreadble; // Possibly by having a set of unreadble items, to which we 74 // add any corrupted items in the db that have yet to be deleted. 75 // This happens if we notce corruption during a (read only) query. 76 // We would also perma-subtract unreadable from manifest whenever 77 // anyone asked for manifest. This result would be cached in 78 // The manifestCache below, so we just need a key into the cache 79 CFDataRef localMinusUnreadableDigest; // or a digest (CFDataRef of the right size). 80 81 CFMutableDictionaryRef manifestCache; // digest -> ( refcount, manifest ) 82 CFMutableDictionaryRef peerState; // peerId -> mutable array of digests 83 CFArrayRef peerIDs; 84 85 dispatch_queue_t queue; 86}; 87 88static bool SOSEngineLoad(SOSEngineRef engine, CFErrorRef *error); 89 90 91static CFStringRef SOSPeerIDArrayCreateString(CFArrayRef peerIDs) { 92 return peerIDs ? CFStringCreateByCombiningStrings(kCFAllocatorDefault, peerIDs, CFSTR(" ")) : CFSTR(""); 93 } 94 95static CFStringRef SOSEngineCopyFormattingDesc(CFTypeRef cf, CFDictionaryRef formatOptions) { 96 SOSEngineRef engine = (SOSEngineRef)cf; 97 CFStringRef tpDesc = SOSPeerIDArrayCreateString(engine->peerIDs); 98 CFStringRef desc = CFStringCreateWithFormat(kCFAllocatorDefault, formatOptions, CFSTR("<Engine %@ peers %@ MC[%d] PS[%d]>"), engine->myID, tpDesc, engine->manifestCache ? (int)CFDictionaryGetCount(engine->manifestCache) : 0, engine->peerState ? (int)CFDictionaryGetCount(engine->peerState) : 0); 99 CFReleaseSafe(tpDesc); 100 return desc; 101 } 102 103static CFStringRef SOSEngineCopyDebugDesc(CFTypeRef cf) { 104 return SOSEngineCopyFormattingDesc(cf, NULL); 105 } 106 107static dispatch_queue_t sEngineQueue; 108static CFDictionaryRef sEngineMap; 109 110CFGiblisWithFunctions(SOSEngine, NULL, NULL, NULL, NULL, NULL, SOSEngineCopyFormattingDesc, SOSEngineCopyDebugDesc, NULL, NULL, ^{ 111 sEngineQueue = dispatch_queue_create("SOSEngine queue", DISPATCH_QUEUE_SERIAL); 112 sEngineMap = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); 113}); 114 115#define _LOG_RAW_MESSAGES 0 116void logRawMessage(CFDataRef message, bool sending, uint64_t seqno) 117{ 118#if _LOG_RAW_MESSAGES 119 CFStringRef hexMessage = NULL; 120 if (message) { 121 hexMessage = CFDataCopyHexString(message); 122 if (sending) 123 secnoticeq("engine", "%s RAW%1d %@", sending ? "send" : "recv", seqno?2:0, hexMessage); 124 else 125 secnoticeq("engine", "%s RAWx %@", sending ? "send" : "recv", hexMessage); // we don't know vers of received msg here 126 } 127 CFReleaseSafe(hexMessage); 128#endif 129} 130// 131// Peer state layout. WRONG! It's an array now 132// The peer state is an array. 133// The first element of the array is a dictionary with any number of keys and 134// values in it (for future expansion) such as changing the digest size or type 135// or remebering boolean flags for a peers sake. 136// The next three are special in that they are manifest digests with special 137// meaning and rules as to how they are treated (These are dynamically updated 138// based on database activity so they have a fully history of all changes made 139// to the local db. The first is the manifest representing the pendingObjects 140// to send to the other peer. This is normally only ever appending to, and in 141// particular with transactions originating from the Keychain API that affect 142// syncable items will need to add the new objects digests to the pendingObjects list 143// while adding the digests of any tombstones encountered to the extra list. 144 145CFStringRef SOSEngineGetMyID(SOSEngineRef engine) { 146 // TODO: this should not be needed 147 return engine->myID; 148} 149 150// TEMPORARY: Get the list of IDs for cleanup, this shouldn't be used instead it should iterate KVS. 151CFArrayRef SOSEngineGetPeerIDs(SOSEngineRef engine) { 152 return engine->peerIDs; 153} 154 155SOSManifestRef SOSEngineGetManifestForDigest(SOSEngineRef engine, CFDataRef digest) { 156 if (!engine->manifestCache || !digest) return NULL; 157 SOSManifestRef manifest = (SOSManifestRef)CFDictionaryGetValue(engine->manifestCache, digest); 158 if (!manifest) return NULL; 159 if (CFGetTypeID(manifest) != SOSManifestGetTypeID()) { 160 secerror("dropping corrupt manifest for %@ from cache", digest); 161 CFDictionaryRemoveValue(engine->manifestCache, digest); 162 return NULL; 163 } 164 165 return manifest; 166} 167 168void SOSEngineAddManifest(SOSEngineRef engine, SOSManifestRef manifest) { 169 CFDataRef digest = SOSManifestGetDigest(manifest, NULL); 170 if (digest) { 171 if (!engine->manifestCache) 172 engine->manifestCache = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); 173 CFDictionaryAddValue(engine->manifestCache, digest, manifest); 174 } 175} 176 177CFDataRef SOSEnginePatchRecordAndCopyDigest(SOSEngineRef engine, SOSManifestRef base, SOSManifestRef removals, SOSManifestRef additions, CFErrorRef *error) { 178 CFDataRef digest = NULL; 179 SOSManifestRef manifest = SOSManifestCreateWithPatch(base, removals, additions, error); 180 if (manifest) { 181 SOSEngineAddManifest(engine, manifest); 182 digest = CFRetainSafe(SOSManifestGetDigest(manifest, NULL)); 183 } 184 CFReleaseSafe(manifest); 185 return digest; 186} 187 188static bool SOSEngineHandleManifestUpdates(SOSEngineRef engine, SOSDataSourceTransactionSource source, SOSManifestRef removals, SOSManifestRef additions, CFErrorRef *error) { 189 __block struct SOSDigestVector mdInCache = SOSDigestVectorInit; 190 struct SOSDigestVector mdInUse = SOSDigestVectorInit; 191 struct SOSDigestVector mdUnused = SOSDigestVectorInit; 192 struct SOSDigestVector mdMissing = SOSDigestVectorInit; 193 CFStringRef peerID = NULL; 194 bool ok = true; 195 196 require_quiet(engine->peerState, exit); // Not a failure no work to do 197 198 if(engine->peerIDs){ 199 CFArrayForEachC(engine->peerIDs, peerID) { 200 SOSPeerRef peer = SOSPeerCreateWithEngine(engine, peerID); 201 if (removals || additions) 202 ok &= SOSPeerDataSourceWillCommit(peer, source, removals, additions, error); 203 SOSPeerMarkDigestsInUse(peer, &mdInUse); 204 CFReleaseSafe(peer); 205 } 206 } 207 if(engine->manifestCache){ 208 CFDictionaryForEach(engine->manifestCache, ^(const void *key, const void *value) { 209 CFDataRef digest = (CFDataRef)key; 210 if (isData(digest)) 211 SOSDigestVectorAppend(&mdInCache, CFDataGetBytePtr(digest)); 212 }); 213 214 // Delete unused manifests. 215 SOSDigestVectorDiff(&mdInCache, &mdInUse, &mdUnused, &mdMissing); 216 SOSManifestRef unused = SOSManifestCreateWithDigestVector(&mdUnused, NULL); 217 SOSManifestForEach(unused, ^(CFDataRef digest, bool *stop) { 218 if (digest) 219 CFDictionaryRemoveValue(engine->manifestCache, digest); 220 }); 221 CFReleaseSafe(unused); 222 } 223 // Delete unused peerState 224 if (engine->peerState && engine->peerIDs) { 225 CFMutableDictionaryRef newPeerState = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); 226 CFArrayForEachC(engine->peerIDs, peerID) { 227 CFTypeRef value = CFDictionaryGetValue(engine->peerState, peerID); 228 if (value) 229 CFDictionarySetValue(newPeerState, peerID, value); 230 } 231 CFDictionaryForEach(engine->peerState, ^(const void *key, const void *value) { 232 if(isDictionary(value) && !CFDictionaryContainsKey(newPeerState, key)){ 233 CFMutableDictionaryRef untrustedStuff = (CFMutableDictionaryRef)value; 234 CFDataRef untrustedCoder = (CFDataRef)CFDictionaryGetValue(untrustedStuff, kSOSPeerCoderKey); 235 if(untrustedCoder){ 236 CFMutableDictionaryRef untrustedDict = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kSOSPeerCoderKey, untrustedCoder, NULL); 237 CFDictionarySetValue(newPeerState, key, untrustedDict); 238 CFReleaseNull(untrustedDict); 239 } 240 } 241 }); 242 CFReleaseSafe(engine->peerState); 243 engine->peerState = newPeerState; 244 } 245 246exit: 247 SOSDigestVectorFree(&mdInCache); 248 SOSDigestVectorFree(&mdInUse); 249 SOSDigestVectorFree(&mdUnused); 250 SOSDigestVectorFree(&mdMissing); 251 return ok; 252} 253 254static CFDataRef SOSEngineCopyState(SOSEngineRef engine, CFErrorRef *error) { 255 CFDataRef der = NULL; 256 CFMutableDictionaryRef state = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); 257 if (engine->myID) CFDictionarySetValue(state, kSOSEngineIDKey, engine->myID); 258 if (engine->peerIDs) CFDictionarySetValue(state, kSOSEnginePeerIDsKey, engine->peerIDs); 259 if (engine->peerState) CFDictionarySetValue(state, kSOSEnginePeerStateKey, engine->peerState); 260 if (engine->manifestCache) { 261 CFMutableDictionaryRef mfc = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); 262 CFDictionarySetValue(state, kSOSEngineManifestCacheKey, mfc); 263 CFDictionaryForEach(engine->manifestCache, ^(const void *key, const void *value) { 264 SOSManifestRef mf = (SOSManifestRef)value; 265 if (mf && (CFGetTypeID(mf) == SOSManifestGetTypeID())) 266 CFDictionarySetValue(mfc, key, SOSManifestGetData(mf)); 267 }); 268 CFReleaseSafe(mfc); 269 } 270 der = kc_plist_copy_der(state, error); 271 CFReleaseSafe(state); 272 secnotice("engine", "%@", engine); 273 return der; 274} 275 276static bool SOSEngineSave(SOSEngineRef engine, SOSTransactionRef txn, CFErrorRef *error) { 277 CFDataRef derState = SOSEngineCopyState(engine, error); 278 bool ok = derState && SOSDataSourceSetStateWithKey(engine->dataSource, txn, kSOSEngineState, kSecAttrAccessibleAlways, derState, error); 279 CFReleaseSafe(derState); 280 return ok; 281} 282 283static bool SOSEngineUpdateLocalManifest_locked(SOSEngineRef engine, SOSDataSourceTransactionSource source, SOSManifestRef removals, SOSManifestRef additions, CFErrorRef *error) { 284 bool ok = true; 285 if (engine->manifest) { 286 SOSManifestRef updatedManifest = SOSManifestCreateWithPatch(engine->manifest, removals, additions, error); 287 if (updatedManifest) 288 CFAssignRetained(engine->manifest, updatedManifest); 289 290 // Update Peer Manifests. -- Shouldn't this be deferred until we apply our toAdd and toDel to the local manifest? 291 ok &= SOSEngineHandleManifestUpdates(engine, source, removals, additions, error); 292 } 293 return ok; 294} 295 296static bool SOSEngineUpdateChanges(SOSEngineRef engine, SOSTransactionRef txn, SOSDataSourceTransactionPhase phase, SOSDataSourceTransactionSource source, SOSManifestRef removals, SOSManifestRef additions, CFErrorRef *error) 297{ 298 secnotice("engine", "%s %s dels:%@ adds:%@", phase == kSOSDataSourceTransactionWillCommit ? "will-commit" : phase == kSOSDataSourceTransactionDidCommit ? "did-commit" : "did-rollback", source == kSOSDataSourceSOSTransaction ? "sos" : "api", removals, additions); 299 bool ok = true; 300 switch (phase) { 301 case kSOSDataSourceTransactionDidRollback: 302 ok &= SOSEngineLoad(engine, error); 303 break; 304 case kSOSDataSourceTransactionWillCommit: { 305 ok &= SOSEngineUpdateLocalManifest_locked(engine, source, removals, additions, error); 306 // Write SOSEngine and SOSPeer state to disk if dirty 307 ok &= SOSEngineSave(engine, txn, error); 308 break; 309 } 310 case kSOSDataSourceTransactionDidCommit: 311 break; 312 } 313 return ok; 314} 315 316static void SOSEngineSetTrustedPeers(SOSEngineRef engine, CFStringRef myPeerID, CFArrayRef trustedPeers) { 317 const bool wasInCircle = engine->myID; 318 const bool isInCircle = myPeerID; 319 const bool inCircleChanged = wasInCircle != isInCircle; 320 321 CFStringRef peerID = NULL; 322 CFRetainAssign(engine->myID, myPeerID); 323 324 if(trustedPeers != NULL && CFArrayGetCount(trustedPeers) != 0){ 325 CFReleaseNull(engine->peerIDs); 326 engine->peerIDs = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); 327 CFArrayForEachC(trustedPeers, peerID){ 328 CFArrayAppendValue((CFMutableArrayRef)engine->peerIDs, peerID); 329 }; 330 } 331 else{ 332 engine->peerIDs = NULL; 333 } 334 // If we entered a circle of more than 2 or our last peer left we need to do stuff 335 if (inCircleChanged) { 336 if (isInCircle) { 337 CFErrorRef dsError = NULL; 338 if (!(engine->manifest = SOSDataSourceCopyManifest(engine->dataSource, &dsError))) { 339 secerror("failed to load manifest from datasource: %@", dsError); 340 CFReleaseNull(dsError); 341 } 342 SOSDataSourceSetNotifyPhaseBlock(engine->dataSource, ^(SOSDataSourceRef ds, SOSTransactionRef txn, SOSDataSourceTransactionPhase phase, SOSDataSourceTransactionSource source, struct SOSDigestVector *removals, struct SOSDigestVector *additions) { 343 SOSManifestRef mfdel = SOSManifestCreateWithDigestVector(removals, NULL); 344 SOSManifestRef mfadd = SOSManifestCreateWithDigestVector(additions, NULL); 345 dispatch_block_t processUpdates = ^{ 346 CFErrorRef localError = NULL; 347 if (!SOSEngineUpdateChanges(engine, txn, phase, source, mfdel, mfadd, &localError)) { 348 secerror("updateChanged failed: %@", localError); 349 } 350 CFReleaseSafe(localError); 351 CFReleaseSafe(mfdel); 352 CFReleaseSafe(mfadd); 353 }; 354 355 if (source == kSOSDataSourceSOSTransaction) { 356 processUpdates(); 357 } else { 358 // WARNING: This will deadlock the engine if you call a 359 // SecItem API function while holding the engine lock! 360 // However making this async right now isn't safe yet either 361 // Due to some code in the enginer using Get v/s copy to 362 // access some of the values that would be modified 363 // asynchronously here since the engine is coded as if 364 // running on a serial queue. 365 dispatch_sync(engine->queue, processUpdates); 366 } 367 }); 368 } else { 369 SOSDataSourceSetNotifyPhaseBlock(engine->dataSource, ^(SOSDataSourceRef ds, SOSTransactionRef txn, SOSDataSourceTransactionPhase phase, SOSDataSourceTransactionSource source, struct SOSDigestVector *removals, struct SOSDigestVector *additions) { 370 secnoticeq("engine", "No peers to notify"); // TODO: DEBUG - remove this 371 }); 372 CFReleaseNull(engine->manifest); 373 } 374 } 375} 376 377static bool SOSEngineSetState(SOSEngineRef engine, CFDataRef state, CFErrorRef *error) { 378 bool ok = true; 379 if (state) { 380 CFMutableDictionaryRef dict = NULL; 381 const uint8_t *der = CFDataGetBytePtr(state); 382 const uint8_t *der_end = der + CFDataGetLength(state); 383 der = der_decode_dictionary(kCFAllocatorDefault, kCFPropertyListMutableContainers, (CFDictionaryRef *)&dict, error, der, der_end); 384 if (der && der != der_end) { 385 ok = SOSErrorCreate(kSOSErrorDecodeFailure, error, NULL, CFSTR("trailing %td bytes at end of state"), der_end - der); 386 } 387 if (ok) { 388 SOSEngineSetTrustedPeers(engine, (CFStringRef)CFDictionaryGetValue(dict, kSOSEngineIDKey), 389 (CFArrayRef)CFDictionaryGetValue(dict, kSOSEnginePeerIDsKey)); 390 CFRetainAssign(engine->peerState, (CFMutableDictionaryRef)CFDictionaryGetValue(dict, kSOSEnginePeerStateKey)); 391 392 CFReleaseNull(engine->manifestCache); 393 CFMutableDictionaryRef mfc = (CFMutableDictionaryRef)CFDictionaryGetValue(dict, kSOSEngineManifestCacheKey); 394 if (mfc) { 395 engine->manifestCache = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); 396 CFDictionaryForEach(mfc, ^(const void *key, const void *value) { 397 CFDataRef data = (CFDataRef)value; 398 if (isData(data)) { 399 SOSManifestRef mf = SOSManifestCreateWithData(data, NULL); 400 if (mf) 401 CFDictionarySetValue(engine->manifestCache, key, mf); 402 CFReleaseSafe(mf); 403 } 404 }); 405 } 406 } 407 CFReleaseNull(dict); 408 } 409 secnotice("engine", "%@", engine); 410 return ok; 411} 412 413static bool SOSEngineLoad(SOSEngineRef engine, CFErrorRef *error) { 414 CFDataRef state = SOSDataSourceCopyStateWithKey(engine->dataSource, kSOSEngineState, kSecAttrAccessibleAlways, error); 415 bool ok = state && SOSEngineSetState(engine, state, error); 416 CFReleaseSafe(state); 417 return ok; 418} 419 420static void CFArraySubtract(CFMutableArrayRef from, CFArrayRef remove) { 421 if (remove) { 422 CFArrayForEach(remove, ^(const void *value) { 423 CFArrayRemoveAllValue(from, value); 424 }); 425 } 426} 427 428static CFMutableArrayRef CFArrayCreateDifference(CFAllocatorRef alloc, CFArrayRef set, CFArrayRef remove) { 429 CFMutableArrayRef result; 430 if (!set) { 431 result = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); 432 } else { 433 result = CFArrayCreateMutableCopy(alloc, 0, set); 434 435 if (remove) 436 CFArraySubtract(result, remove); 437 } 438 439 return result; 440} 441 442void SOSEngineCircleChanged_locked(SOSEngineRef engine, CFStringRef myPeerID, CFArrayRef trustedPeers, CFArrayRef untrustedPeers) { 443 CFMutableArrayRef addedPeers = CFArrayCreateDifference(kCFAllocatorDefault, trustedPeers, engine->peerIDs); 444 CFMutableArrayRef deletedPeers = CFArrayCreateDifference(kCFAllocatorDefault, engine->peerIDs, trustedPeers); 445 446 CFStringRef tpDesc = SOSPeerIDArrayCreateString(trustedPeers); 447 CFStringRef apDesc = SOSPeerIDArrayCreateString(addedPeers); 448 CFStringRef dpDesc = SOSPeerIDArrayCreateString(deletedPeers); 449 secnotice("engine", "trusted %@ added %@ removed %@", tpDesc, apDesc, dpDesc); 450 CFReleaseSafe(dpDesc); 451 CFReleaseSafe(apDesc); 452 CFReleaseSafe(tpDesc); 453 454 SOSEngineSetTrustedPeers(engine, myPeerID, trustedPeers); 455 456 // Remove any cached state for peers we no longer use but keep coders alive 457 if (deletedPeers && CFArrayGetCount(deletedPeers) && engine->peerState) { 458 CFStringRef peerID = NULL; 459 CFArrayForEachC(deletedPeers, peerID) { 460 CFMutableDictionaryRef peer_data = (CFMutableDictionaryRef) CFDictionaryGetValue(engine->peerState, peerID); 461 CFDataRef coder_data = isDictionary(peer_data) ? (CFDataRef) CFDictionaryGetValue(peer_data, kSOSPeerCoderKey) : NULL; 462 463 if(isData(coder_data) && 464 untrustedPeers && CFArrayContainsValue(untrustedPeers, CFRangeMake(0, CFArrayGetCount(untrustedPeers)), peerID)) { 465 CFRetainSafe(coder_data); 466 CFDictionaryRemoveAllValues(peer_data); 467 CFDictionaryAddValue(peer_data, kSOSPeerCoderKey, coder_data); 468 CFReleaseSafe(coder_data); 469 } else { 470 CFDictionaryRemoveValue(engine->peerState, peerID); 471 } 472 } 473 // Run though all peers and only cache manifests for peers we still have 474 // TODO: Factor out gc from SOSEngineHandleManifestUpdates and just call that 475 SOSEngineHandleManifestUpdates(engine, kSOSDataSourceSOSTransaction, NULL, NULL, NULL); 476 } 477 478 CFReleaseNull(addedPeers); 479 CFReleaseNull(deletedPeers); 480 481} 482 483#if 0 484static SOSManifestRef SOSEngineCopyCleanManifest(SOSEngineRef engine, CFErrorRef *error) { 485 SOSManifestRef localMinusUnreadable; 486 } 487#endif 488 489// Initialize the engine if a load fails. Basically this is our first time setup 490static bool SOSEngineInit(SOSEngineRef engine, CFErrorRef *error) { 491 bool ok = true; 492 secnotice("engine", "new engine for datasource named %@", SOSDataSourceGetName(engine->dataSource)); 493 return ok; 494} 495 496// Called by our DataSource in its constructor 497SOSEngineRef SOSEngineCreate(SOSDataSourceRef dataSource, CFErrorRef *error) { 498 SOSEngineRef engine = NULL; 499 engine = CFTypeAllocate(SOSEngine, struct __OpaqueSOSEngine, kCFAllocatorDefault); 500 engine->dataSource = dataSource; 501 engine->queue = dispatch_queue_create("engine", DISPATCH_QUEUE_SERIAL); 502 CFErrorRef engineError = NULL; 503 if (!SOSEngineLoad(engine, &engineError)) { 504 secwarning("engine failed load state starting with nothing %@", engineError); 505 CFReleaseNull(engineError); 506 if (!SOSEngineInit(engine, error)) { 507 secerror("engine failed to initialze %@ giving up", engineError); 508 } 509 } 510 return engine; 511} 512 513 514// 515// MARK: SOSEngine API 516// 517 518void SOSEngineDispose(SOSEngineRef engine) { 519 // NOOP Engines stick around forever to monitor dataSource changes. 520} 521 522static SOSManifestRef SOSEngineCopyManifest_locked(SOSEngineRef engine, CFErrorRef *error) { 523 return CFRetainSafe(engine->manifest); 524} 525 526/* Handle incoming message from peer p. Return false if there was an error, true otherwise. */ 527static bool SOSEngineHandleMessage_locked(SOSEngineRef engine, CFStringRef peerID, SOSMessageRef message, 528 SOSTransactionRef txn, bool *commit, bool *somethingChanged, CFErrorRef *error) { 529 SOSPeerRef peer = SOSPeerCreateWithEngine(engine, peerID); 530 CFStringRef peerDesc = NULL; 531 SOSManifestRef localManifest = NULL; 532 SOSManifestRef allAdditions = NULL; 533 SOSManifestRef confirmed = NULL; 534 SOSManifestRef base = NULL; 535 SOSManifestRef confirmedRemovals = NULL, confirmedAdditions = NULL; 536 __block struct SOSDigestVector receivedObjects = SOSDigestVectorInit; 537 538 // Check for unknown criticial extensions in the message, and handle 539 // any other extensions we support 540 __block bool ok = true; 541 __block struct SOSDigestVector dvadd = SOSDigestVectorInit; 542 543 require_action_quiet(peer, exit, ok = SOSErrorCreate(errSecParam, error, NULL, CFSTR("Couldn't create peer with Engine for %@"), peerID)); 544 peerDesc = CFCopyDescription(peer); 545 546 SOSMessageWithExtensions(message, true, ^(CFDataRef oid, bool isCritical, CFDataRef extension, bool *stop) { 547 // OMFG a Critical extension what shall I do! 548 ok = SOSErrorCreate(kSOSErrorNotReady, error, NULL, CFSTR("Unknown criticial extension in peer message")); 549 *stop = true; 550 }); 551 require_quiet(ok, exit); 552 553 // Merge Objects from the message into our DataSource. 554 // Should we move the transaction to the SOSAccount level? 555 require_quiet(ok &= SOSMessageWithSOSObjects(message, engine->dataSource, error, ^(SOSObjectRef peersObject, bool *stop) { 556 CFDataRef digest = SOSObjectCopyDigest(engine->dataSource, peersObject, error); 557 if (!digest) { 558 *stop = true; 559 *commit = false; 560 secerror("%@ peer sent bad object: %@, rolling back changes", SOSPeerGetID(peer), error ? *error : NULL); 561 return; 562 } 563 SOSDigestVectorAppend(&receivedObjects, CFDataGetBytePtr(digest)); 564 SOSMergeResult mr = SOSDataSourceMergeObject(engine->dataSource, txn, peersObject, NULL, error); 565 // TODO: If the mr is kSOSMergeLocalObject most of the time (or all of the time), 566 // consider asking the peer to stop sending us objects, and send it objects instead. 567 ok &= (mr != kSOSMergeFailure); 568 if (!ok) { 569 *stop = true; 570 *commit = false; 571 // 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. 572 secerror("%@ SOSDataSourceMergeObject failed %@ rolling back changes", SOSPeerGetID(peer), error ? *error : NULL); 573 } else if (mr==kSOSMergePeersObject || mr==kSOSMergeCreatedObject) { 574 *somethingChanged = true; 575 } else { 576 // mr == kSOSMergeLocalObject 577 // Ensure localObject is in local manifest (possible corruption) by posting an update when we are done. 578 SOSDigestVectorAppend(&dvadd, CFDataGetBytePtr(digest)); 579 } 580 CFReleaseSafe(digest); 581 }), exit); 582 struct SOSDigestVector dvunion = SOSDigestVectorInit; 583 SOSDigestVectorSort(&receivedObjects); 584 SOSDigestVectorUnionSorted(SOSManifestGetDigestVector(SOSMessageGetAdditions(message)), &receivedObjects, &dvunion); 585 allAdditions = SOSManifestCreateWithDigestVector(&dvunion, error); 586 SOSDigestVectorFree(&receivedObjects); 587 SOSDigestVectorFree(&dvunion); 588 589 if (dvadd.count) { 590 // Ensure any objects that we received and have localally already are actually in our local manifest 591 SOSManifestRef mfadd = SOSManifestCreateWithDigestVector(&dvadd, error); 592 SOSDigestVectorFree(&dvadd); 593 SOSEngineUpdateLocalManifest_locked(engine, kSOSDataSourceSOSTransaction, NULL, mfadd, error); 594 CFReleaseSafe(mfadd); 595 } 596 597 // ---- Don't use local or peer manifests from above this line, since commiting the SOSDataSourceWith transaction might change them --- 598 599 // Take a snapshot of our dataSource's local manifest. 600 require_quiet(ok = localManifest = SOSEngineCopyManifest_locked(engine, error), exit); 601 602 CFDataRef baseDigest = SOSMessageGetBaseDigest(message); 603 CFDataRef proposedDigest = SOSMessageGetProposedDigest(message); 604 605#if 0 606 // I believe this is no longer needed now that we have eliminated extra, 607 // Since this is handeled below once we get a confirmed manifest from our 608 // peer. 609 610 // If we just received a L00 reset pendingObjects to localManifest 611 if (!baseDigest && !proposedDigest) { 612 SOSPeerSetPendingObjects(peer, localManifest); 613 secnotice("engine", "SOSPeerSetPendingObjects: %@", localManifest); 614 } 615#endif 616 617 base = CFRetainSafe(SOSEngineGetManifestForDigest(engine, baseDigest)); 618 confirmed = CFRetainSafe(SOSEngineGetManifestForDigest(engine, SOSMessageGetSenderDigest(message))); 619 if (!confirmed) { 620 if (SOSManifestGetCount(SOSMessageGetRemovals(message)) || SOSManifestGetCount(allAdditions)) { 621 confirmed = SOSManifestCreateWithPatch(base, SOSMessageGetRemovals(message), allAdditions, error); 622 if (!confirmed) { 623 confirmedRemovals = CFRetainSafe(SOSMessageGetRemovals(message)); 624 confirmedAdditions = CFRetainSafe(allAdditions); 625 } 626 } else if (baseDigest) { 627 confirmed = CFRetainSafe(base); 628 secerror("Protocol error send L00 - figure out later base: %@", base); 629 } 630 } 631 secnotice("engine", "Confirmed: %@ base: %@", confirmed, base); 632 if (confirmed) 633 ok &= SOSManifestDiff(SOSPeerGetConfirmedManifest(peer), confirmed, &confirmedRemovals, &confirmedAdditions, error); 634 if (confirmedRemovals || confirmedAdditions) 635 ok &= SOSPeerDidReceiveRemovalsAndAdditions(peer, confirmedRemovals, confirmedAdditions, localManifest, error); 636 SOSPeerSetConfirmedManifest(peer, confirmed); 637 638 // ---- SendObjects and extra->pendingObjects promotion dance ---- 639 640 // The first block of code below sets peer.sendObjects to true when we receive a L00 and the second block 641 // moves extra to pendingObjects once we receive a confirmed manifest in or after the L00. 642 if (!baseDigest && !proposedDigest) { 643 SOSPeerSetSendObjects(peer, true); 644 } 645 646 // TODO: should this not depend on SOSPeerSendObjects?: 647 if (confirmed /* && SOSPeerSendObjects(peer)*/) { 648 SOSManifestRef allExtra = NULL; 649 ok &= SOSManifestDiff(confirmed, localManifest, NULL, &allExtra, error); 650 secnotice("engine", "%@ confirmed %@ setting O:%@", SOSPeerGetID(peer), confirmed, allExtra); 651 SOSPeerSetPendingObjects(peer, allExtra); 652 CFReleaseSafe(allExtra); 653 } 654 655exit: 656 secnoticeq("engine", "recv %@ %@", SOSPeerGetID(peer), message); 657 secnoticeq("peer", "recv %@ -> %@", peerDesc, peer); 658 659 CFReleaseNull(base); 660 CFReleaseSafe(confirmed); 661 CFReleaseSafe(localManifest); 662 CFReleaseSafe(peerDesc); 663 CFReleaseSafe(allAdditions); 664 CFReleaseSafe(confirmedRemovals); 665 CFReleaseSafe(confirmedAdditions); 666 CFReleaseSafe(peer); 667 return ok; 668} 669 670static CFDataRef SOSEngineCopyObjectDER(SOSEngineRef engine, SOSObjectRef object, CFErrorRef *error) { 671 CFDataRef der = NULL; 672 CFDictionaryRef plist = SOSObjectCopyPropertyList(engine->dataSource, object, error); 673 if (plist) { 674 der = kc_plist_copy_der(plist, error); 675 CFRelease(plist); 676 } 677 return der; 678 } 679 680static CFDataRef SOSEngineCreateMessage_locked(SOSEngineRef engine, SOSPeerRef peer, 681 CFErrorRef *error, SOSEnginePeerMessageSentBlock *sent) { 682 SOSManifestRef local = SOSEngineCopyManifest_locked(engine, error); 683 __block SOSMessageRef message = SOSMessageCreate(kCFAllocatorDefault, SOSPeerGetMessageVersion(peer), error); 684 SOSManifestRef confirmed = SOSPeerGetConfirmedManifest(peer); 685 SOSManifestRef pendingObjects = SOSPeerGetPendingObjects(peer); 686 SOSManifestRef objectsSent = NULL; 687 SOSManifestRef proposed = NULL; 688 SOSManifestRef allMissing = NULL; 689 SOSManifestRef allExtra = NULL; 690 SOSManifestRef extra = NULL; 691 SOSManifestRef excessPending = NULL; 692 SOSManifestRef missing = NULL; 693 SOSManifestRef deleted = SOSPeerGetPendingDeletes(peer); 694 SOSManifestRef excessDeleted = NULL; 695 CFDataRef result = NULL; 696 bool ok; 697 698 ok = SOSManifestDiff(confirmed, local, &allMissing, &allExtra, error); 699 ok = ok && SOSManifestDiff(allExtra, pendingObjects, &extra, &excessPending, error); 700 if (SOSManifestGetCount(excessPending)) { 701 secerror("%@ ASSERTION FAILURE excess pendingObjects: %@", peer, excessPending); 702 // Remove excessPending from pendingObjects since they are either 703 // already in confirmed or not in local, either way there is no point 704 // keeping them in pendingObjects. 705 706 pendingObjects = SOSManifestCreateComplement(excessPending, pendingObjects, error); 707 SOSPeerSetPendingObjects(peer, pendingObjects); 708 CFReleaseSafe(pendingObjects); 709 ok = false; 710 } 711 ok = ok && SOSManifestDiff(allMissing, deleted, &missing, &excessDeleted, error); 712 if (SOSManifestGetCount(excessDeleted)) { 713 secerror("%@ ASSERTION FAILURE excess deleted: %@", peer, excessDeleted); 714 ok = false; 715 } 716 (void)ok; // Dead store 717 CFReleaseNull(allExtra); 718 CFReleaseNull(excessPending); 719 CFReleaseNull(allMissing); 720 CFReleaseNull(excessDeleted); 721 722 // Send state for peer 7T0M+TD+A7HZ0frC5oHZnmdR0G: [LCP][os] P: 0, E: 0, M: 0 723 secnoticeq("engine", "Send state for peer %@: [%s%s%s][%s%s] P: %zu, E: %zu, M: %zu", SOSPeerGetID(peer), 724 local ? "L":"l", 725 confirmed ? "C":"0", 726 pendingObjects ? "P":"0", 727 SOSPeerSendObjects(peer) ? "O":"o", 728 SOSPeerMustSendMessage(peer) ? "S":"s", 729 SOSManifestGetCount(pendingObjects), 730 SOSManifestGetCount(extra), 731 SOSManifestGetCount(missing) 732 ); 733 734 if (confirmed) { 735 // TODO: Because of not letting things terminate while we have extra left 736 // we might send objects when we didn't need to, but there is always an 737 // extra roundtrip required for objects that we assume the other peer 738 // should have already. 739 // TODO: If there are extra objects left, calling this function is not 740 // idempotent we should check if pending is what we are about to send and not send anything in this case. 741 if (SOSManifestGetCount(pendingObjects) == 0 && SOSManifestGetCount(extra) == 0) 742 SOSPeerSetSendObjects(peer, false); 743 744 if (CFEqualSafe(local, SOSPeerGetProposedManifest(peer)) && !SOSPeerMustSendMessage(peer)) { 745 bool send = false; 746 if (CFEqual(confirmed, local)) { 747 secnoticeq("engine", "synced <No MSG> %@", peer); 748 } else if (SOSManifestGetCount(pendingObjects) == 0 /* TODO: No entries moved from extra to pendingObjects. */ 749 && SOSManifestGetCount(missing) == 0) { 750 secnoticeq("engine", "waiting <MSG not resent> %@", peer); 751 } else { 752 send = true; 753 } 754 if (!send) { 755 CFReleaseSafe(local); 756 CFReleaseSafe(message); 757 CFReleaseNull(extra); 758 CFReleaseNull(missing); 759 return CFDataCreate(kCFAllocatorDefault, NULL, 0); 760 } 761 } 762 763 if (SOSManifestGetCount(pendingObjects)) { 764 // If we have additions and we need to send objects send them. 765 __block size_t objectsSize = 0; 766 __block struct SOSDigestVector dv = SOSDigestVectorInit; 767 __block struct SOSDigestVector dvdel = SOSDigestVectorInit; 768 if (!SOSDataSourceForEachObject(engine->dataSource, pendingObjects, error, ^void(CFDataRef key, SOSObjectRef object, bool *stop) { 769 CFErrorRef localError = NULL; 770 CFDataRef digest = NULL; 771 CFDataRef der = NULL; 772 if (!object) { 773 const uint8_t *d = CFDataGetBytePtr(key); 774 secerrorq("%@ object %02X%02X%02X%02X dropping from manifest: not found in datasource", 775 SOSPeerGetID(peer), d[0], d[1], d[2], d[3]); 776 SOSDigestVectorAppend(&dvdel, CFDataGetBytePtr(key)); 777 } else if (!(der = SOSEngineCopyObjectDER(engine, object, &localError)) 778 || !(digest = SOSObjectCopyDigest(engine->dataSource, object, &localError))) { 779 if (SecErrorGetOSStatus(localError) == errSecDecode) { 780 // Decode error, we need to drop these objects from our manifests 781 const uint8_t *d = CFDataGetBytePtr(key); 782 secerrorq("%@ object %02X%02X%02X%02X dropping from manifest: %@", 783 SOSPeerGetID(peer), d[0], d[1], d[2], d[3], localError); 784 SOSDigestVectorAppend(&dvdel, CFDataGetBytePtr(key)); 785 CFRelease(localError); 786 } else { 787 // Stop iterating and propagate out all other errors. 788 const uint8_t *d = CFDataGetBytePtr(key); 789 secwarning("%@ object %02X%02X%02X%02X in SOSDataSourceForEachObject: %@", 790 SOSPeerGetID(peer), d[0], d[1], d[2], d[3], localError); 791 *stop = true; 792 CFErrorPropagate(localError, error); 793 CFReleaseNull(message); 794 } 795 } else { 796 if (!CFEqual(key, digest)) { 797 const uint8_t *d = CFDataGetBytePtr(key); 798 const uint8_t *e = CFDataGetBytePtr(digest); 799 secwarning("@ object %02X%02X%02X%02X is really %02X%02X%02X%02X dropping from local manifest", d[0], d[1], d[2], d[3], e[0], e[1], e[2], e[3]); 800 SOSDigestVectorAppend(&dvdel, CFDataGetBytePtr(key)); 801 } 802 803 size_t objectLen = (size_t)CFDataGetLength(der); 804 if (SOSMessageAppendObject(message, der, &localError)) { 805 SOSDigestVectorAppend(&dv, CFDataGetBytePtr(digest)); 806 } else { 807 const uint8_t *d = CFDataGetBytePtr(digest); 808 CFStringRef hexder = CFDataCopyHexString(der); 809 secerrorq("%@ object %02X%02X%02X%02X der: %@ dropping from manifest: %@", 810 SOSPeerGetID(peer), d[0], d[1], d[2], d[3], hexder, localError); 811 CFReleaseNull(hexder); 812 CFReleaseNull(message); 813 // Since we can't send these objects let's assume they are bad too? 814 SOSDigestVectorAppend(&dvdel, CFDataGetBytePtr(digest)); 815 } 816 objectsSize += objectLen; 817 if (objectsSize > kSOSMessageMaxObjectsSize) 818 *stop = true; 819 } 820 CFReleaseSafe(der); 821 CFReleaseSafe(digest); 822 })) { 823 CFReleaseNull(message); 824 } 825 if (dv.count) 826 objectsSent = SOSManifestCreateWithDigestVector(&dv, error); 827 if (dvdel.count) { 828 CFErrorRef localError = NULL; 829 SOSManifestRef mfdel = SOSManifestCreateWithDigestVector(&dvdel, error); 830 SOSDigestVectorFree(&dvdel); 831 if (!SOSEngineUpdateLocalManifest_locked(engine, kSOSDataSourceSOSTransaction, mfdel, NULL, &localError)) 832 secerror("SOSEngineUpdateLocalManifest deleting: %@ failed: %@", mfdel, localError); 833 CFReleaseSafe(localError); 834 CFReleaseSafe(mfdel); 835 CFAssignRetained(local, SOSEngineCopyManifest_locked(engine, error)); 836 } 837 SOSDigestVectorFree(&dv); 838 } 839 } else { 840 // If we have no confirmed manifest, we want all pendedObjects going out as a manifest 841 objectsSent = CFRetainSafe(pendingObjects); 842 } 843 844 if (confirmed || SOSManifestGetCount(missing) || SOSManifestGetCount(extra) || objectsSent) { 845 SOSManifestRef allExtra = SOSManifestCreateUnion(extra, objectsSent, error); 846 proposed = SOSManifestCreateWithPatch(confirmed, missing, allExtra, error); 847 CFReleaseNull(allExtra); 848 } 849 850 if (!SOSMessageSetManifests(message, local, confirmed, proposed, proposed, confirmed ? objectsSent : NULL, error)) 851 CFReleaseNull(message); 852 853 CFReleaseNull(objectsSent); 854 855 if (message) { 856 result = SOSMessageCreateData(message, SOSPeerNextSequenceNumber(peer), error); 857 } 858 859 if (result) { 860 // Capture the peer in our block (SOSEnginePeerMessageSentBlock) 861 CFRetainSafe(peer); 862 *sent = Block_copy(^(bool success) { 863 dispatch_async(engine->queue, ^{ 864 if (success) { 865 if (!confirmed && !proposed) { 866 SOSPeerSetSendObjects(peer, true); 867 secnotice("engine", "SOSPeerSetSendObjects(true) L:%@", local); 868 } 869 SOSPeerAddLocalManifest(peer, local); 870 SOSPeerAddProposedManifest(peer, proposed); 871 secnoticeq("engine", "send %@ %@", SOSPeerGetID(peer), message); 872 } else { 873 secerror("%@ failed to send %@", SOSPeerGetID(peer), message); 874 } 875 CFReleaseSafe(peer); 876 CFReleaseSafe(local); 877 CFReleaseSafe(proposed); 878 CFReleaseSafe(message); 879 }); 880 }); 881 } else { 882 CFReleaseSafe(local); 883 CFReleaseSafe(proposed); 884 CFReleaseSafe(message); 885 } 886 CFReleaseNull(extra); 887 CFReleaseNull(missing); 888 if (error && *error) 889 secerror("%@ error in send: %@", SOSPeerGetID(peer), *error); 890 891 return result; 892} 893 894static CFDataRef SOSEngineCreateMessageToSyncToPeer_locked(SOSEngineRef engine, CFStringRef peerID, SOSEnginePeerMessageSentBlock *sentBlock, CFErrorRef *error) 895{ 896 SOSPeerRef peer = SOSPeerCreateWithEngine(engine, peerID); 897 CFDataRef message = SOSEngineCreateMessage_locked(engine, peer, error, sentBlock); 898 CFReleaseSafe(peer); 899 900 return message; 901} 902 903bool SOSEngineHandleMessage(SOSEngineRef engine, CFStringRef peerID, 904 CFDataRef raw_message, CFErrorRef *error) 905{ 906 __block bool result = false; 907 __block bool somethingChanged = false; 908 SOSMessageRef message = SOSMessageCreateWithData(kCFAllocatorDefault, raw_message, error); 909 result = message && SOSDataSourceWith(engine->dataSource, error, ^(SOSTransactionRef txn, bool *commit) { 910 result = SOSEngineHandleMessage_locked(engine, peerID, message, txn, commit, &somethingChanged, error); 911 }); 912 CFReleaseSafe(message); 913 if (somethingChanged) 914 SecKeychainChanged(false); 915 return result; 916} 917 918// --- Called from off the queue, need to move to on the queue 919 920static void SOSEngineDoOnQueue(SOSEngineRef engine, dispatch_block_t action) 921{ 922 dispatch_sync(engine->queue, action); 923} 924 925void SOSEngineCircleChanged(SOSEngineRef engine, CFStringRef myPeerID, CFArrayRef trustedPeers, CFArrayRef untrustedPeers) { 926 SOSEngineDoOnQueue(engine, ^{ 927 SOSEngineCircleChanged_locked(engine, myPeerID, trustedPeers, untrustedPeers); 928 }); 929 930 __block CFErrorRef localError = NULL; 931 SOSDataSourceWith(engine->dataSource, &localError, ^(SOSTransactionRef txn, bool *commit) { 932 SOSEngineDoOnQueue(engine, ^{ 933 *commit = SOSEngineSave(engine, txn, &localError); 934 }); 935 }); 936 if (localError) 937 secerror("failed to save engine state: %@", localError); 938 CFReleaseSafe(localError); 939 940} 941 942SOSManifestRef SOSEngineCopyManifest(SOSEngineRef engine, CFErrorRef *error) { 943 __block SOSManifestRef result = NULL; 944 SOSEngineDoOnQueue(engine, ^{ 945 result = SOSEngineCopyManifest_locked(engine, error); 946 }); 947 return result; 948} 949 950bool SOSEngineUpdateLocalManifest(SOSEngineRef engine, SOSDataSourceTransactionSource source, struct SOSDigestVector *removals, struct SOSDigestVector *additions, CFErrorRef *error) { 951 __block bool result = true; 952 SOSManifestRef mfdel = SOSManifestCreateWithDigestVector(removals, error); 953 SOSManifestRef mfadd = SOSManifestCreateWithDigestVector(additions, error); 954 SOSEngineDoOnQueue(engine, ^{ 955 // Safe to run async if needed... 956 result = SOSEngineUpdateLocalManifest_locked(engine, source, mfdel, mfadd, error); 957 CFReleaseSafe(mfdel); 958 CFReleaseSafe(mfadd); 959 }); 960 return result; 961} 962 963static bool SOSEngineSetCoderData_locked(SOSEngineRef engine, CFStringRef peer_id, CFDataRef data, CFErrorRef *error) { 964 CFMutableDictionaryRef state = NULL; 965 if (data) { 966 if (!engine->peerState) { 967 engine->peerState = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); 968 state = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); 969 } 970 else{ 971 state = (CFMutableDictionaryRef)CFDictionaryGetValue(engine->peerState, peer_id); 972 if(!state) 973 state = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); 974 } 975 CFDictionarySetValue(state, kSOSPeerCoderKey, data); 976 CFDictionarySetValue(engine->peerState, peer_id, state); 977 978 }else if (engine->peerState) { 979 if(CFDictionaryContainsKey(engine->peerState, peer_id)){ 980 CFMutableDictionaryRef state = (CFMutableDictionaryRef)CFDictionaryGetValue(engine->peerState, peer_id); 981 if(CFDictionaryContainsKey(state, kSOSPeerCoderKey)) 982 CFDictionaryRemoveValue(state, kSOSPeerCoderKey); 983 } 984 if (CFDictionaryGetCount(engine->peerState) == 0) { 985 CFReleaseNull(engine->peerState); 986 } 987 } 988 return true; 989} 990 991bool SOSEngineSetCoderData(SOSEngineRef engine, CFStringRef peer_id, CFDataRef data, CFErrorRef *error) { 992 __block bool result = false; 993 994 SOSDataSourceWith(engine->dataSource, error, ^(SOSTransactionRef txn, bool *commit) { 995 dispatch_sync(engine->queue, ^{ 996 result = SOSEngineSetCoderData_locked(engine, peer_id, data, error); 997 }); 998 }); 999 1000 return true; 1001} 1002 1003static CFDataRef SOSEngineGetCoderData_locked(SOSEngineRef engine, CFStringRef peer_id) { 1004 // TODO: probably remove these secnotices 1005 CFDataRef result = NULL; 1006 CFMutableDictionaryRef peerState = NULL; 1007 1008 if (!engine->peerState) 1009 secdebug("engine", "No engine coderData"); 1010 else 1011 peerState = (CFMutableDictionaryRef)CFDictionaryGetValue(engine->peerState, peer_id); 1012 if (!peerState) 1013 secdebug("engine", "No peerState for peer %@", peer_id); 1014 else{ 1015 result = CFDictionaryGetValue(peerState, kSOSPeerCoderKey); 1016 if(!result) 1017 secdebug("engine", "No coder data for peer %@", peer_id); 1018 } 1019 return result; 1020} 1021 1022 1023CFDataRef SOSEngineGetCoderData(SOSEngineRef engine, CFStringRef peer_id) { 1024 __block CFDataRef result = NULL; 1025 SOSDataSourceWith(engine->dataSource, NULL, ^(SOSTransactionRef txn, bool *commit) { 1026 dispatch_sync(engine->queue, ^{ 1027 result = SOSEngineGetCoderData_locked(engine, peer_id); 1028 }); 1029 }); 1030 1031 return result; 1032} 1033 1034// 1035// Peer state layout. WRONG! It's an array now 1036// The peer state is an array. 1037// The first element of the array is a dictionary with any number of keys and 1038// values in it (for future expansion) such as changing the digest size or type 1039// or remebering boolean flags for a peers sake. 1040// The next three are special in that they are manifest digests with special 1041// meaning and rules as to how they are treated (These are dynamically updated 1042// based on database activity so they have a fully history of all changes made 1043// to the local db. The first is the manifest representing the pendingObjects 1044// to send to the other peer. This is normally only ever appending to, and in 1045// particular with transactions originating from the Keychain API that affect 1046// syncable items will need to add the new objects digests to the pendingObjects list 1047// while adding the digests of any tombstones encountered to the extra list. 1048 1049CFMutableDictionaryRef SOSEngineGetPeerState(SOSEngineRef engine, CFStringRef peerID) { 1050 CFMutableDictionaryRef peerState = NULL; 1051 if (engine->peerState) 1052 peerState = (CFMutableDictionaryRef)CFDictionaryGetValue(engine->peerState, peerID); 1053 else 1054 engine->peerState = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); 1055 if (!peerState) { 1056 peerState = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); 1057 CFDictionaryAddValue(engine->peerState, peerID, peerState); 1058 CFReleaseSafe(peerState); 1059 } 1060 return peerState; 1061} 1062 1063CFDataRef SOSEngineCreateMessageToSyncToPeer(SOSEngineRef engine, CFStringRef peerID, SOSEnginePeerMessageSentBlock *sentBlock, CFErrorRef *error) { 1064 __block CFDataRef result = NULL; 1065 SOSEngineDoOnQueue(engine, ^{ 1066 result = SOSEngineCreateMessageToSyncToPeer_locked(engine, peerID, sentBlock, error); 1067 }); 1068 return result; 1069} 1070 1071bool SOSEnginePeerDidConnect(SOSEngineRef engine, CFStringRef peerID, CFErrorRef *error) { 1072 __block bool result = true; 1073 result &= SOSDataSourceWith(engine->dataSource, error, ^(SOSTransactionRef txn, bool *commit) { 1074 dispatch_sync(engine->queue, ^{ 1075 SOSPeerRef peer = SOSPeerCreateWithEngine(engine, peerID); 1076 if (!peer) { 1077 result = SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("Engine has no peer for %@"), peerID); 1078 } else { 1079 SOSPeerDidConnect(peer); 1080 result = SOSEngineSave(engine, txn, error); 1081 CFReleaseSafe(peer); 1082 } 1083 }); 1084 }); 1085 1086 return result; 1087} 1088