]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SOSCircle/SecureObjectSync/SOSAccountSync.c
e7907c7991b7784cdbe025efdb14430759812219
[apple/security.git] / OSX / sec / SOSCircle / SecureObjectSync / SOSAccountSync.c
1
2 #include "SOSAccountPriv.h"
3
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>
11
12 #include <SOSCircle/CKBridge/SOSCloudKeychainClient.h>
13
14 #include <CoreFoundation/CoreFoundation.h>
15
16 #include <utilities/SecCFError.h>
17
18 // MARK: Engine Logging
19 #define LOG_ENGINE_STATE_INTERVAL 20
20
21 static void SOSAccountConsiderLoggingEngineState(SOSAccountTransactionRef txn) {
22 static int engineLogCountDown = 0;
23
24 if(engineLogCountDown <= 0) {
25 SOSEngineRef engine = SOSTransportMessageGetEngine(txn->account->kvs_message_transport);
26
27 SOSEngineLogState(engine);
28 engineLogCountDown = LOG_ENGINE_STATE_INTERVAL;
29 } else {
30 engineLogCountDown--;
31 }
32 }
33
34 static bool SOSAccountIsThisPeerIDMe(SOSAccountRef account, CFStringRef peerID) {
35 SOSPeerInfoRef mypi = SOSFullPeerInfoGetPeerInfo(account->my_identity);
36 CFStringRef myPeerID = SOSPeerInfoGetPeerID(mypi);
37
38 return myPeerID && CFEqualSafe(myPeerID, peerID);
39 }
40
41 bool SOSAccountSendIKSPSyncList(SOSAccountRef account, CFErrorRef *error){
42 bool result = true;
43 __block CFErrorRef localError = NULL;
44 __block CFMutableArrayRef ids = NULL;
45 SOSCircleRef circle = NULL;
46
47 require_action_quiet(SOSAccountIsInCircle(account, NULL), xit,
48 SOSCreateError(kSOSErrorNoCircle, CFSTR("This device is not in circle"),
49 NULL, &localError));
50
51 circle = SOSAccountGetCircle(account, error);
52 ids = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
53
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);
61 if(deviceID != NULL){
62 CFArrayAppendValue(ids, deviceID);
63 }
64 CFReleaseNull(deviceID);
65 }
66 }
67 });
68 require_quiet(CFArrayGetCount(ids) != 0, xit);
69 secnotice("IDS Transport", "List of IDS Peers to ping: %@", ids);
70
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);
73 if(!success)
74 secerror("Failed to send list of IDS peers to IDSKSP: %@", sync_error);
75 });
76 xit:
77 if(error && *error != NULL)
78 secerror("SOSAccountSendIKSPSyncList had an error: %@", *error);
79
80 if(localError)
81 secerror("SOSAccountSendIKSPSyncList had an error: %@", localError);
82
83 CFReleaseNull(ids);
84 CFReleaseNull(localError);
85
86 return result;
87 }
88 //
89 // MARK: KVS Syncing
90 //
91
92 static bool SOSAccountSyncWithKVSPeers(SOSAccountTransactionRef txn, CFSetRef peerIDs, CFErrorRef *error) {
93 SOSAccountRef account = txn->account;
94 CFErrorRef localError = NULL;
95 bool result = false;
96
97 require_quiet(SOSAccountIsInCircle(account, &localError), xit);
98
99 result = SOSTransportMessageSyncWithPeers(account->kvs_message_transport, peerIDs, &localError);
100
101 if (result)
102 SetCloudKeychainTraceValueForKey(kCloudKeychainNumberOfTimesSyncedWithPeers, 1);
103
104 xit:
105 if (!result) {
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;
110 }
111 CFErrorPropagate(localError, error);
112 localError = NULL;
113 }
114 return result;
115
116 }
117
118 bool SOSAccountSyncWithKVSPeerWithMessage(SOSAccountTransactionRef txn, CFStringRef peerid, CFDataRef message, CFErrorRef *error) {
119 SOSAccountRef account = txn->account;
120 bool result = false;
121 CFErrorRef localError = NULL;
122 CFDictionaryRef encapsulatedMessage = NULL;
123
124 secnotice("KVS Transport","Syncing with KVS capable peer: %@", peerid);
125 secnotice("KVS Transport", "message: %@", message);
126
127 require_quiet(message, xit);
128 require_quiet(peerid, xit);
129
130 encapsulatedMessage = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, peerid, message, NULL);
131
132 result = SOSTransportMessageSendMessages(account->kvs_message_transport, encapsulatedMessage, &localError);
133 secerror("KVS sync %s. (%@)", result ? "succeeded" : "failed", localError);
134
135 SOSAccountConsiderLoggingEngineState(txn);
136
137 xit:
138 CFReleaseNull(encapsulatedMessage);
139 CFErrorPropagate(localError, error);
140
141 return result;
142 }
143
144
145 static bool SOSAccountSyncWithKVSPeer(SOSAccountTransactionRef txn, CFStringRef peerID, CFErrorRef *error)
146 {
147 bool result = false;
148 CFErrorRef localError = NULL;
149
150 secnotice("KVS Transport","Syncing with KVS capable peer: %@", peerID);
151
152 CFMutableSetRef peerIDs = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
153 CFSetAddValue(peerIDs, peerID);
154
155 result = SOSAccountSyncWithKVSPeers(txn, peerIDs, &localError);
156 secerror("KVS sync %s. (%@)", result ? "succeeded" : "failed", localError);
157
158 CFReleaseNull(peerIDs);
159 CFErrorPropagate(localError, error);
160
161 return result;
162 }
163
164
165 static CFMutableArrayRef SOSAccountCopyPeerIDsForDSID(SOSAccountRef account, CFStringRef deviceID, CFErrorRef* error) {
166 CFMutableArrayRef peerIDs = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
167
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));
172 }
173 CFReleaseNull(peerDeviceID);
174 });
175
176 if (peerIDs == NULL || CFArrayGetCount(peerIDs) == 0) {
177 CFReleaseNull(peerIDs);
178 SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("No peer with DSID: %@"), deviceID);
179 }
180
181 return peerIDs;
182 }
183
184 static bool SOSAccountSyncWithKVSPeerFromPing(SOSAccountRef account, CFArrayRef peerIDs, CFErrorRef *error) {
185
186 CFErrorRef localError = NULL;
187 bool result = false;
188
189 CFSetRef peerSet = CFSetCreateCopyOfArrayForCFTypes(peerIDs);
190 result = SOSTransportMessageSyncWithPeers(account->kvs_message_transport, peerSet, &localError);
191
192 CFReleaseNull(peerSet);
193
194 return result;
195 }
196
197 bool SOSAccountSyncWithKVSUsingIDSID(SOSAccountRef account, CFStringRef deviceID, CFErrorRef *error) {
198 bool result = false;
199 CFErrorRef localError = NULL;
200
201 secnotice("KVS Transport","Syncing with KVS capable peer via DSID: %@", deviceID);
202
203 CFArrayRef peerIDs = SOSAccountCopyPeerIDsForDSID(account, deviceID, &localError);
204 require_quiet(peerIDs, xit);
205
206 CFStringArrayPerfromWithDescription(peerIDs, ^(CFStringRef peerIDList) {
207 secnotice("KVS Transport", "Syncing with KVS capable peers: %@", peerIDList);
208 });
209
210 result = SOSAccountSyncWithKVSPeerFromPing(account, peerIDs, &localError);
211 secerror("KVS sync %s. (%@)", result ? "succeeded" : "failed", localError);
212
213 xit:
214 CFReleaseNull(peerIDs);
215 CFErrorPropagate(localError, error);
216
217 return result;
218 }
219
220 static __nonnull CF_RETURNS_RETAINED CFSetRef SOSAccountSyncWithPeersOverKVS(SOSAccountTransactionRef txn, CFSetRef peers) {
221 CFMutableSetRef handled = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
222
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);
229 } else {
230 secnotice("KVS Transport", "failed to sync with peer: %@ error: %@", peerID, localError);
231 }
232 });
233
234 return handled;
235 }
236
237 static __nonnull CF_RETURNS_RETAINED CFSetRef SOSAccountSyncWithPeersOverIDS(SOSAccountTransactionRef txn, __nonnull CFSetRef peers) {
238 CFErrorRef localError = NULL;
239
240 CFStringSetPerformWithDescription(peers, ^(CFStringRef peerDescription) {
241 secnotice("IDS Transport","Syncing with IDS capable peers: %@", peerDescription);
242 });
243
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);
247
248 return CFRetainSafe(peers);
249 }
250
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;
256
257 SOSAccountRef account = txn->account;
258
259 require_action_quiet(SOSAccountIsInCircle(account, error), done,
260 handledPeerIDs = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, peerIDs));
261
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);
264
265 handledPeerIDs = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
266 peersForIDS = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
267 peersForKVS = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
268
269 SOSPeerInfoRef myPeerInfo = SOSAccountGetMyPeerInfo(account);
270 require(myPeerInfo, done);
271
272 CFStringRef myPeerID = SOSPeerInfoGetPeerID(myPeerInfo);
273
274 notMePeers = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, peerIDs);
275 CFSetRemoveValue(notMePeers, myPeerID);
276
277 if(!SOSAccountSendIKSPSyncList(account, error)){
278 if(error != NULL)
279 secnotice("IDS Transport", "Did not send list of peers to ping (pre-E): %@", *error);
280 }
281
282 CFSetForEach(notMePeers, ^(const void *value) {
283 CFErrorRef localError = NULL;
284 CFStringRef peerID = asString(value, &localError);
285 SOSPeerInfoRef peerInfo = NULL;
286
287 require_quiet(peerID, skip);
288
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);
293 } else {
294 CFSetAddValue(peersForKVS, peerID);
295 }
296 } else {
297 CFSetAddValue(handledPeerIDs, peerID);
298 }
299
300 skip:
301 CFReleaseNull(peerInfo);
302 if (localError) {
303 secnotice("sync-with-peers", "Skipped peer ID: %@ due to %@", peerID, localError);
304 }
305 CFReleaseNull(localError);
306 });
307
308 CFSetRef handledIDSPeerIDs = SOSAccountSyncWithPeersOverIDS(txn, peersForIDS);
309 CFSetUnion(handledPeerIDs, handledIDSPeerIDs);
310 CFReleaseNull(handledIDSPeerIDs);
311
312 CFSetRef handledKVSPeerIDs = SOSAccountSyncWithPeersOverKVS(txn, peersForKVS);
313 CFSetUnion(handledPeerIDs, handledKVSPeerIDs);
314 CFReleaseNull(handledKVSPeerIDs);
315
316 SOSAccountConsiderLoggingEngineState(txn);
317
318 done:
319 CFReleaseNull(notMePeers);
320 CFReleaseNull(peersForIDS);
321 CFReleaseNull(peersForKVS);
322 return handledPeerIDs;
323 }
324
325 bool SOSAccountClearPeerMessageKey(SOSAccountTransactionRef txn, CFStringRef peerID, CFErrorRef *error)
326 {
327 SOSAccountRef account = txn->account;
328
329 secnotice("IDS Transport", "clearing peer message for %@", peerID);
330 CFTypeRef dsid = SOSAccountGetValue(account, kSOSDSIDKey, error);
331
332 if(dsid == NULL)
333 dsid = kCFNull;
334
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);
337
338 CloudKeychainReplyBlock log_error = ^(CFDictionaryRef returnedValues __unused, CFErrorRef block_error) {
339 if (block_error) {
340 secerror("Error putting: %@", block_error);
341 }
342 };
343
344 SOSCloudKeychainPutObjectsInCloud(a_message_to_a_peer, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), log_error);
345
346 CFReleaseNull(a_message_to_a_peer);
347 CFReleaseNull(message_to_peer_key);
348
349 return true;
350 }
351
352 CF_RETURNS_RETAINED CFSetRef SOSAccountProcessSyncWithPeers(SOSAccountTransactionRef txn, CFSetRef /* CFStringRef */ peers, CFSetRef /* CFStringRef */ backupPeers, CFErrorRef *error)
353 {
354 CFErrorRef localError = NULL;
355 CFMutableSetRef handled = SOSAccountSyncWithPeers(txn, peers, &localError);
356
357 SOSTransportMessageIDSGetIDSDeviceID(txn->account);
358
359 if (!handled) {
360 secnotice("account-sync", "Peer Sync failed: %@", localError);
361 handled = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
362 }
363 CFReleaseNull(localError);
364
365 SOSEngineRef engine = SOSTransportMessageGetEngine(txn->account->kvs_message_transport);
366 CFSetRef engineHandled = SOSEngineSyncWithBackupPeers(engine, backupPeers, error);
367
368 if (engineHandled) {
369 CFSetUnion(handled, engineHandled);
370 } else {
371 secnotice("account-sync", "Engine Backup Sync failed: %@", localError);
372 }
373 CFReleaseNull(localError);
374 CFReleaseNull(engineHandled);
375
376 return handled;
377 }
378
379 bool SOSAccountRequestSyncWithAllPeers(SOSAccountTransactionRef txn, CFErrorRef *error)
380 {
381 bool success = false;
382 CFMutableSetRef allSyncingPeers = NULL;
383
384 require_quiet(SOSAccountIsInCircle(txn->account, error), xit);
385
386 SOSTransportMessageIDSGetIDSDeviceID(txn->account);
387
388 allSyncingPeers = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
389
390 SOSCircleForEachValidSyncingPeer(txn->account->trusted_circle, txn->account->user_public, ^(SOSPeerInfoRef peer) {
391 CFSetAddValue(allSyncingPeers, SOSPeerInfoGetPeerID(peer));
392 });
393
394 SOSAccountTransactionAddSyncRequestForAllPeerIDs(txn, allSyncingPeers);
395
396 success = true;
397
398 xit:
399 CFReleaseNull(allSyncingPeers);
400 return success;
401 }
402
403 //
404 // MARK: Syncing status functions
405 //
406 bool SOSAccountMessageFromPeerIsPending(SOSAccountTransactionRef txn, SOSPeerInfoRef peer, CFErrorRef *error) {
407 bool success = false;
408 require_quiet(SOSAccountIsInCircle(txn->account, error), xit);
409
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));
412
413 success = SOSCloudKeychainHasPendingKey(peerMessage, error);
414
415 xit:
416 return success;
417 }
418
419 bool SOSAccountSendToPeerIsPending(SOSAccountTransactionRef txn, SOSPeerInfoRef peer, CFErrorRef *error) {
420 bool success = false;
421 require_quiet(SOSAccountIsInCircle(txn->account, error), xit);
422
423 success = SOSCCIsSyncPendingFor(SOSPeerInfoGetPeerID(peer), error);
424 xit:
425 return success;
426
427
428 }