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
);