2 #include "SOSAccountPriv.h" 
   4 #include <Security/SecureObjectSync/SOSTransportCircle.h> 
   5 #include <Security/SecureObjectSync/SOSTransportMessage.h> 
   6 #include <Security/SecureObjectSync/SOSTransportMessageIDS.h> 
   7 #include <Security/SecureObjectSync/SOSKVSKeys.h> 
   8 #include <Security/SecureObjectSync/SOSTransport.h> 
   9 #include <Security/SecureObjectSync/SOSTransportKeyParameter.h> 
  10 #include <Security/SecureObjectSync/SOSTransportKeyParameterKVS.h> 
  12 #include <SOSCircle/CKBridge/SOSCloudKeychainClient.h> 
  14 #include <CoreFoundation/CoreFoundation.h> 
  16 #include <utilities/SecCFError.h> 
  18 // MARK: Engine Logging 
  19 #define LOG_ENGINE_STATE_INTERVAL 20 
  21 static void SOSAccountConsiderLoggingEngineState(SOSAccountTransactionRef txn
) { 
  22     static int engineLogCountDown 
= 0; 
  24     if(engineLogCountDown 
<= 0) { 
  25         SOSEngineRef engine 
= SOSTransportMessageGetEngine(txn
->account
->kvs_message_transport
); 
  27         SOSEngineLogState(engine
); 
  28         engineLogCountDown 
= LOG_ENGINE_STATE_INTERVAL
; 
  34 static bool SOSAccountIsThisPeerIDMe(SOSAccountRef account
, CFStringRef peerID
) { 
  35     SOSPeerInfoRef mypi 
= SOSFullPeerInfoGetPeerInfo(account
->my_identity
); 
  36     CFStringRef myPeerID 
= SOSPeerInfoGetPeerID(mypi
); 
  38     return myPeerID 
&& CFEqualSafe(myPeerID
, peerID
); 
  41 bool SOSAccountSendIKSPSyncList(SOSAccountRef account
, CFErrorRef 
*error
){ 
  43     __block CFErrorRef localError 
= NULL
; 
  44     __block CFMutableArrayRef ids 
= NULL
; 
  45     SOSCircleRef circle 
= NULL
; 
  47     require_action_quiet(SOSAccountIsInCircle(account
, NULL
), xit
, 
  48                          SOSCreateError(kSOSErrorNoCircle
, CFSTR("This device is not in circle"), 
  51     circle  
= SOSAccountGetCircle(account
, error
); 
  52     ids 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
  54     SOSCircleForEachValidPeer(circle
, account
->user_public
, ^(SOSPeerInfoRef peer
) { 
  55         if (!SOSAccountIsThisPeerIDMe(account
, SOSPeerInfoGetPeerID(peer
))) { 
  56             if(SOSPeerInfoShouldUseIDSTransport(SOSFullPeerInfoGetPeerInfo(account
->my_identity
), peer
) && 
  57                SOSPeerInfoShouldUseIDSMessageFragmentation(SOSFullPeerInfoGetPeerInfo(account
->my_identity
), peer
) && 
  58                !SOSPeerInfoShouldUseACKModel(SOSFullPeerInfoGetPeerInfo(account
->my_identity
), peer
)){ 
  59                 SOSTransportMessageIDSSetFragmentationPreference(account
->ids_message_transport
, kCFBooleanTrue
); 
  60                 CFStringRef deviceID 
= SOSPeerInfoCopyDeviceID(peer
); 
  62                     CFArrayAppendValue(ids
, deviceID
); 
  64                 CFReleaseNull(deviceID
); 
  68     require_quiet(CFArrayGetCount(ids
) != 0, xit
); 
  69     secnotice("IDS Transport", "List of IDS Peers to ping: %@", ids
); 
  71     SOSCloudKeychainGetIDSDeviceAvailability(ids
, SOSAccountGetMyPeerID(account
), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), ^(CFDictionaryRef returnedValues
, CFErrorRef sync_error
) { 
  72         bool success 
= (sync_error 
== NULL
); 
  74             secerror("Failed to send list of IDS peers to IDSKSP: %@", sync_error
); 
  77     if(error 
&& *error 
!= NULL
) 
  78         secerror("SOSAccountSendIKSPSyncList had an error: %@", *error
); 
  81         secerror("SOSAccountSendIKSPSyncList had an error: %@", localError
); 
  84     CFReleaseNull(localError
); 
  92 static bool SOSAccountSyncWithKVSPeers(SOSAccountTransactionRef txn
, CFSetRef peerIDs
, CFErrorRef 
*error
) { 
  93     SOSAccountRef account 
= txn
->account
; 
  94     CFErrorRef localError 
= NULL
; 
  97     require_quiet(SOSAccountIsInCircle(account
, &localError
), xit
); 
  99     result 
= SOSTransportMessageSyncWithPeers(account
->kvs_message_transport
, peerIDs
, &localError
); 
 102         SetCloudKeychainTraceValueForKey(kCloudKeychainNumberOfTimesSyncedWithPeers
, 1); 
 106         // Tell account to update SOSEngine with current trusted peers 
 107         if (isSOSErrorCoded(localError
, kSOSErrorPeerNotFound
)) { 
 108             secnotice("Account", "Arming account to update SOSEngine with current trusted peers"); 
 109             account
->engine_peer_state_needs_repair 
= true; 
 111         CFErrorPropagate(localError
, error
); 
 118 bool SOSAccountSyncWithKVSPeerWithMessage(SOSAccountTransactionRef txn
, CFStringRef peerid
, CFDataRef message
, CFErrorRef 
*error
) { 
 119     SOSAccountRef account 
= txn
->account
; 
 121     CFErrorRef localError 
= NULL
; 
 122     CFDictionaryRef encapsulatedMessage 
= NULL
; 
 124     secnotice("KVS Transport","Syncing with KVS capable peer: %@", peerid
); 
 125     secnotice("KVS Transport", "message: %@", message
); 
 127     require_quiet(message
, xit
); 
 128     require_quiet(peerid
, xit
); 
 130     encapsulatedMessage 
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, peerid
, message
, NULL
); 
 132     result 
= SOSTransportMessageSendMessages(account
->kvs_message_transport
, encapsulatedMessage
, &localError
); 
 133     secerror("KVS sync %s. (%@)", result 
? "succeeded" : "failed", localError
); 
 135     SOSAccountConsiderLoggingEngineState(txn
); 
 138     CFReleaseNull(encapsulatedMessage
); 
 139     CFErrorPropagate(localError
, error
); 
 145 static bool SOSAccountSyncWithKVSPeer(SOSAccountTransactionRef txn
, CFStringRef peerID
, CFErrorRef 
*error
) 
 148     CFErrorRef localError 
= NULL
; 
 150     secnotice("KVS Transport","Syncing with KVS capable peer: %@", peerID
); 
 152     CFMutableSetRef peerIDs 
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
); 
 153     CFSetAddValue(peerIDs
, peerID
); 
 155     result 
= SOSAccountSyncWithKVSPeers(txn
, peerIDs
, &localError
); 
 156     secerror("KVS sync %s. (%@)", result 
? "succeeded" : "failed", localError
); 
 158     CFReleaseNull(peerIDs
); 
 159     CFErrorPropagate(localError
, error
); 
 165 static CFMutableArrayRef 
SOSAccountCopyPeerIDsForDSID(SOSAccountRef account
, CFStringRef deviceID
, CFErrorRef
* error
) { 
 166     CFMutableArrayRef peerIDs 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
 168     SOSCircleForEachValidPeer(account
->trusted_circle
, account
->user_public
, ^(SOSPeerInfoRef peer
) { 
 169         CFStringRef peerDeviceID 
= SOSPeerInfoCopyDeviceID(peer
); 
 170         if(peerDeviceID 
!= NULL 
&& CFStringCompare(peerDeviceID
, deviceID
, 0) == 0){ 
 171             CFArrayAppendValue(peerIDs
, SOSPeerInfoGetPeerID(peer
)); 
 173         CFReleaseNull(peerDeviceID
); 
 176     if (peerIDs 
== NULL 
|| CFArrayGetCount(peerIDs
) == 0) { 
 177         CFReleaseNull(peerIDs
); 
 178         SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("No peer with DSID: %@"), deviceID
); 
 184 static bool SOSAccountSyncWithKVSPeerFromPing(SOSAccountRef account
, CFArrayRef peerIDs
, CFErrorRef 
*error
) { 
 186     CFErrorRef localError 
= NULL
; 
 189     CFSetRef peerSet 
= CFSetCreateCopyOfArrayForCFTypes(peerIDs
); 
 190     result 
= SOSTransportMessageSyncWithPeers(account
->kvs_message_transport
, peerSet
, &localError
); 
 192     CFReleaseNull(peerSet
); 
 197 bool SOSAccountSyncWithKVSUsingIDSID(SOSAccountRef account
, CFStringRef deviceID
, CFErrorRef 
*error
) { 
 199     CFErrorRef localError 
= NULL
; 
 201     secnotice("KVS Transport","Syncing with KVS capable peer via DSID: %@", deviceID
); 
 203     CFArrayRef peerIDs 
= SOSAccountCopyPeerIDsForDSID(account
, deviceID
, &localError
); 
 204     require_quiet(peerIDs
, xit
); 
 206     CFStringArrayPerfromWithDescription(peerIDs
, ^(CFStringRef peerIDList
) { 
 207         secnotice("KVS Transport", "Syncing with KVS capable peers: %@", peerIDList
); 
 210     result 
= SOSAccountSyncWithKVSPeerFromPing(account
, peerIDs
, &localError
); 
 211     secerror("KVS sync %s. (%@)", result 
? "succeeded" : "failed", localError
); 
 214     CFReleaseNull(peerIDs
); 
 215     CFErrorPropagate(localError
, error
); 
 220 static __nonnull CF_RETURNS_RETAINED CFSetRef 
SOSAccountSyncWithPeersOverKVS(SOSAccountTransactionRef txn
, CFSetRef peers
) { 
 221     CFMutableSetRef handled 
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
); 
 223     CFSetForEach(peers
, ^(const void *value
) { 
 224         CFStringRef peerID 
= asString(value
, NULL
); 
 225         CFErrorRef localError 
= NULL
; 
 226         if (peerID 
&& SOSAccountSyncWithKVSPeer(txn
, peerID
, &localError
)) { 
 227             CFSetAddValue(handled
, peerID
); 
 228             secnotice("KVS Transport", "synced with peer: %@", peerID
); 
 230             secnotice("KVS Transport", "failed to sync with peer: %@ error: %@", peerID
, localError
); 
 237 static __nonnull CF_RETURNS_RETAINED CFSetRef 
SOSAccountSyncWithPeersOverIDS(SOSAccountTransactionRef txn
, __nonnull CFSetRef peers
) { 
 238     CFErrorRef localError 
= NULL
; 
 240     CFStringSetPerformWithDescription(peers
, ^(CFStringRef peerDescription
) { 
 241         secnotice("IDS Transport","Syncing with IDS capable peers: %@", peerDescription
); 
 244     // We should change this to return a set of peers we succeeded with, but for now assume they all worked. 
 245     bool result 
= SOSTransportMessageSyncWithPeers(txn
->account
->ids_message_transport
, peers
, &localError
); 
 246     secnotice("IDS Transport", "IDS Sync result: %d", result
); 
 248     return CFRetainSafe(peers
); 
 251 static CF_RETURNS_RETAINED CFMutableSetRef 
SOSAccountSyncWithPeers(SOSAccountTransactionRef txn
, CFSetRef 
/* CFStringRef */ peerIDs
, CFErrorRef 
*error
) { 
 252     CFMutableSetRef notMePeers 
= NULL
; 
 253     CFMutableSetRef handledPeerIDs 
= NULL
; 
 254     CFMutableSetRef peersForIDS 
= NULL
; 
 255     CFMutableSetRef peersForKVS 
= NULL
; 
 257     SOSAccountRef account 
= txn
->account
; 
 259     require_action_quiet(SOSAccountIsInCircle(account
, error
), done
, 
 260                          handledPeerIDs 
= CFSetCreateMutableCopy(kCFAllocatorDefault
, 0, peerIDs
)); 
 262     // Kick getting our device ID if we don't have it, and find out if we're setup to use IDS. 
 263     bool canUseIDS 
= SOSTransportMessageIDSGetIDSDeviceID(account
); 
 265     handledPeerIDs 
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
); 
 266     peersForIDS 
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
); 
 267     peersForKVS 
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
); 
 269     SOSPeerInfoRef myPeerInfo 
= SOSAccountGetMyPeerInfo(account
); 
 270     require(myPeerInfo
, done
); 
 272     CFStringRef myPeerID 
= SOSPeerInfoGetPeerID(myPeerInfo
); 
 274     notMePeers 
= CFSetCreateMutableCopy(kCFAllocatorDefault
, 0, peerIDs
); 
 275     CFSetRemoveValue(notMePeers
, myPeerID
); 
 277     if(!SOSAccountSendIKSPSyncList(account
, error
)){ 
 279             secnotice("IDS Transport", "Did not send list of peers to ping (pre-E): %@", *error
); 
 282     CFSetForEach(notMePeers
, ^(const void *value
) { 
 283         CFErrorRef localError 
= NULL
; 
 284         CFStringRef peerID 
= asString(value
, &localError
); 
 285         SOSPeerInfoRef peerInfo 
= NULL
; 
 287         require_quiet(peerID
, skip
); 
 289         peerInfo 
= SOSCircleCopyPeerWithID(account
->trusted_circle
, peerID
, NULL
); 
 290         if (peerInfo 
&& SOSCircleHasValidSyncingPeer(account
->trusted_circle
, peerInfo
, account
->user_public
, NULL
)) { 
 291             if (canUseIDS 
&& SOSPeerInfoShouldUseIDSTransport(myPeerInfo
, peerInfo
) && SOSPeerInfoShouldUseACKModel(myPeerInfo
, peerInfo
)) { 
 292                 CFSetAddValue(peersForIDS
, peerID
); 
 294                 CFSetAddValue(peersForKVS
, peerID
); 
 297             CFSetAddValue(handledPeerIDs
, peerID
); 
 301         CFReleaseNull(peerInfo
); 
 303             secnotice("sync-with-peers", "Skipped peer ID: %@ due to %@", peerID
, localError
); 
 305         CFReleaseNull(localError
); 
 308     CFSetRef handledIDSPeerIDs 
= SOSAccountSyncWithPeersOverIDS(txn
, peersForIDS
); 
 309     CFSetUnion(handledPeerIDs
, handledIDSPeerIDs
); 
 310     CFReleaseNull(handledIDSPeerIDs
); 
 312     CFSetRef handledKVSPeerIDs 
= SOSAccountSyncWithPeersOverKVS(txn
, peersForKVS
); 
 313     CFSetUnion(handledPeerIDs
, handledKVSPeerIDs
); 
 314     CFReleaseNull(handledKVSPeerIDs
); 
 316     SOSAccountConsiderLoggingEngineState(txn
); 
 319     CFReleaseNull(notMePeers
); 
 320     CFReleaseNull(peersForIDS
); 
 321     CFReleaseNull(peersForKVS
); 
 322     return handledPeerIDs
; 
 325 bool SOSAccountClearPeerMessageKey(SOSAccountTransactionRef txn
, CFStringRef peerID
, CFErrorRef 
*error
) 
 327     SOSAccountRef account 
= txn
->account
; 
 329     secnotice("IDS Transport", "clearing peer message for %@", peerID
); 
 330     CFTypeRef dsid 
= SOSAccountGetValue(account
, kSOSDSIDKey
, error
); 
 335     CFStringRef message_to_peer_key 
= SOSMessageKeyCreateFromTransportToPeer(account
->kvs_message_transport
, peerID
); 
 336     CFDictionaryRef a_message_to_a_peer 
= CFDictionaryCreateForCFTypes(NULL
, message_to_peer_key
, kCFNull
, kSOSKVSRequiredKey
, dsid
, NULL
); 
 338     CloudKeychainReplyBlock log_error 
= ^(CFDictionaryRef returnedValues __unused
, CFErrorRef block_error
) { 
 340             secerror("Error putting: %@", block_error
); 
 344     SOSCloudKeychainPutObjectsInCloud(a_message_to_a_peer
, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), log_error
); 
 346     CFReleaseNull(a_message_to_a_peer
); 
 347     CFReleaseNull(message_to_peer_key
); 
 352 CF_RETURNS_RETAINED CFSetRef 
SOSAccountProcessSyncWithPeers(SOSAccountTransactionRef txn
, CFSetRef 
/* CFStringRef */ peers
, CFSetRef 
/* CFStringRef */ backupPeers
, CFErrorRef 
*error
) 
 354     CFErrorRef localError 
= NULL
; 
 355     CFMutableSetRef handled 
= SOSAccountSyncWithPeers(txn
, peers
, &localError
); 
 357     SOSTransportMessageIDSGetIDSDeviceID(txn
->account
); 
 360         secnotice("account-sync", "Peer Sync failed: %@", localError
); 
 361         handled 
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
); 
 363     CFReleaseNull(localError
); 
 365     SOSEngineRef engine 
= SOSTransportMessageGetEngine(txn
->account
->kvs_message_transport
); 
 366     CFSetRef engineHandled 
= SOSEngineSyncWithBackupPeers(engine
, backupPeers
, error
); 
 369         CFSetUnion(handled
, engineHandled
); 
 371         secnotice("account-sync", "Engine Backup Sync failed: %@", localError
); 
 373     CFReleaseNull(localError
); 
 374     CFReleaseNull(engineHandled
); 
 379 bool SOSAccountRequestSyncWithAllPeers(SOSAccountTransactionRef txn
, CFErrorRef 
*error
) 
 381     bool success 
= false; 
 382     CFMutableSetRef allSyncingPeers 
= NULL
; 
 384     require_quiet(SOSAccountIsInCircle(txn
->account
, error
), xit
); 
 386     SOSTransportMessageIDSGetIDSDeviceID(txn
->account
); 
 388     allSyncingPeers 
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
); 
 390     SOSCircleForEachValidSyncingPeer(txn
->account
->trusted_circle
, txn
->account
->user_public
, ^(SOSPeerInfoRef peer
) { 
 391         CFSetAddValue(allSyncingPeers
, SOSPeerInfoGetPeerID(peer
)); 
 394     SOSAccountTransactionAddSyncRequestForAllPeerIDs(txn
, allSyncingPeers
); 
 399     CFReleaseNull(allSyncingPeers
); 
 404 // MARK: Syncing status functions 
 406 bool SOSAccountMessageFromPeerIsPending(SOSAccountTransactionRef txn
, SOSPeerInfoRef peer
, CFErrorRef 
*error
) { 
 407     bool success 
= false; 
 408     require_quiet(SOSAccountIsInCircle(txn
->account
, error
), xit
); 
 410     // This translation belongs inside KVS..way down in CKD, but for now we reach over and do it here. 
 411     CFStringRef peerMessage 
= SOSMessageKeyCreateFromPeerToTransport(txn
->account
->kvs_message_transport
, SOSPeerInfoGetPeerID(peer
)); 
 413     success 
= SOSCloudKeychainHasPendingKey(peerMessage
, error
); 
 419 bool SOSAccountSendToPeerIsPending(SOSAccountTransactionRef txn
, SOSPeerInfoRef peer
, CFErrorRef 
*error
) { 
 420     bool success 
= false; 
 421     require_quiet(SOSAccountIsInCircle(txn
->account
, error
), xit
); 
 423     success 
= SOSCCIsSyncPendingFor(SOSPeerInfoGetPeerID(peer
), error
);