]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SOSCircle/SecureObjectSync/SOSAccountSync.m
Security-58286.60.28.tar.gz
[apple/security.git] / OSX / sec / SOSCircle / SecureObjectSync / SOSAccountSync.m
1
2 #include "SOSAccountPriv.h"
3 #include "SOSAccount.h"
4
5 #include <Security/SecureObjectSync/SOSKVSKeys.h>
6 #include <Security/SecureObjectSync/SOSTransportCircle.h>
7 #include <Security/SecureObjectSync/SOSTransportCircleKVS.h>
8 #include <Security/SecureObjectSync/SOSTransportMessage.h>
9 #include <Security/SecureObjectSync/SOSTransportMessageIDS.h>
10 #include <Security/SecureObjectSync/SOSKVSKeys.h>
11 #include <Security/SecureObjectSync/SOSTransport.h>
12 #include <Security/SecureObjectSync/SOSTransportKeyParameter.h>
13 #include <SOSCircle/CKBridge/SOSCloudKeychainClient.h>
14
15 #import <Security/SecureObjectSync/SOSAccountTrust.h>
16 #import <Security/SecureObjectSync/SOSTransport.h>
17 #import <Security/SecureObjectSync/SOSTransportKeyParameter.h>
18 #import <Security/SecureObjectSync/SOSTransportMessage.h>
19 #import "Security/SecureObjectSync/SOSTransportMessageIDS.h"
20 #import <Security/SecureObjectSync/SOSTransportMessageKVS.h>
21 #include <SOSCircle/CKBridge/SOSCloudKeychainClient.h>
22 #include <Security/SecureObjectSync/SOSAccountTrustClassic+Circle.h>
23 #include <Security/SecureObjectSync/SOSInternal.h>
24
25 #include <CoreFoundation/CoreFoundation.h>
26
27 #include <utilities/SecCFError.h>
28
29 // MARK: Engine Logging
30 #define LOG_ENGINE_STATE_INTERVAL 20
31
32 void SOSAccountConsiderLoggingEngineState(SOSAccountTransaction* txn) {
33 static int engineLogCountDown = 0;
34
35 if(engineLogCountDown <= 0) {
36 SOSAccount* acct = txn.account;
37 CFTypeRef engine = [acct.kvs_message_transport SOSTransportMessageGetEngine];
38
39 SOSEngineLogState((SOSEngineRef)engine);
40 engineLogCountDown = LOG_ENGINE_STATE_INTERVAL;
41 } else {
42 engineLogCountDown--;
43 }
44 }
45
46 bool SOSAccountInflateTransports(SOSAccount* account, CFStringRef circleName, CFErrorRef *error){
47 bool success = false;
48
49 if(account.key_transport)
50 SOSUnregisterTransportKeyParameter(account.key_transport);
51 if(account.circle_transport)
52 SOSUnregisterTransportCircle(account.circle_transport);
53 if(account.ids_message_transport)
54 SOSUnregisterTransportMessage((SOSMessage*)account.ids_message_transport);
55 if(account.kvs_message_transport)
56 SOSUnregisterTransportMessage((SOSMessage*)account.kvs_message_transport);
57
58 account.key_transport = [[CKKeyParameter alloc] initWithAccount:account];
59 account.circle_transport = [[SOSKVSCircleStorageTransport alloc]initWithAccount:account andCircleName:(__bridge NSString *)(circleName)];
60
61 require_quiet(account.key_transport, fail);
62 require_quiet(account.circle_transport, fail);
63
64 account.ids_message_transport = [[SOSMessageIDS alloc] initWithAccount:account andName:(__bridge NSString *)(circleName)];
65 require_quiet(account.ids_message_transport, fail);
66
67 account.kvs_message_transport = [[SOSMessageKVS alloc] initWithAccount:account andName:(__bridge NSString*)circleName];
68 require_quiet(account.kvs_message_transport, fail);
69
70 success = true;
71
72 fail:
73 return success;
74 }
75
76 static bool SOSAccountIsThisPeerIDMe(SOSAccount* account, CFStringRef peerID) {
77 NSString* myPeerID = account.peerID;
78
79 return myPeerID && [myPeerID isEqualToString: (__bridge NSString*) peerID];
80 }
81
82 bool SOSAccountSendIKSPSyncList(SOSAccount* account, CFErrorRef *error){
83 bool result = true;
84 __block CFErrorRef localError = NULL;
85 __block CFMutableArrayRef ids = NULL;
86 SOSCircleRef circle = NULL;
87 SOSFullPeerInfoRef identity = NULL;
88
89 if(![account.trust isInCircle:NULL])
90 {
91 SOSCreateError(kSOSErrorNoCircle, CFSTR("This device is not in circle"), NULL, &localError);
92 if(error && *error != NULL)
93 secerror("SOSAccountSendIKSPSyncList had an error: %@", *error);
94
95 if(localError)
96 secerror("SOSAccountSendIKSPSyncList had an error: %@", localError);
97
98 CFReleaseNull(ids);
99 CFReleaseNull(localError);
100
101 return result;
102 }
103
104 circle = account.trust.trustedCircle;
105 identity = account.fullPeerInfo;
106 ids = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
107
108 SOSCircleForEachValidPeer(circle, account.accountKey, ^(SOSPeerInfoRef peer) {
109 if (!SOSAccountIsThisPeerIDMe(account, SOSPeerInfoGetPeerID(peer))) {
110 if(SOSPeerInfoShouldUseIDSTransport(SOSFullPeerInfoGetPeerInfo(identity), peer) &&
111 SOSPeerInfoShouldUseIDSMessageFragmentation(SOSFullPeerInfoGetPeerInfo(identity), peer) &&
112 !SOSPeerInfoShouldUseACKModel(SOSFullPeerInfoGetPeerInfo(identity), peer)){
113 [account.ids_message_transport SOSTransportMessageIDSSetFragmentationPreference:account.ids_message_transport pref:kCFBooleanTrue];
114 CFStringRef deviceID = SOSPeerInfoCopyDeviceID(peer);
115 if(deviceID != NULL){
116 CFArrayAppendValue(ids, deviceID);
117 }
118 CFReleaseNull(deviceID);
119 }
120 }
121 });
122 require_quiet(CFArrayGetCount(ids) != 0, xit);
123 secnotice("IDS Transport", "List of IDS Peers to ping: %@", ids);
124
125 SOSCloudKeychainGetIDSDeviceAvailability(ids, (__bridge CFStringRef)(account.peerID), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFDictionaryRef returnedValues, CFErrorRef sync_error) {
126 bool success = (sync_error == NULL);
127 if(!success)
128 secerror("Failed to send list of IDS peers to IDSKSP: %@", sync_error);
129 });
130 xit:
131 if(error && *error != NULL)
132 secerror("SOSAccountSendIKSPSyncList had an error: %@", *error);
133
134 if(localError)
135 secerror("SOSAccountSendIKSPSyncList had an error: %@", localError);
136
137 CFReleaseNull(ids);
138 CFReleaseNull(localError);
139
140 return result;
141 }
142 //
143 // MARK: KVS Syncing
144 //
145
146 static bool SOSAccountSyncWithKVSPeers(SOSAccountTransaction* txn, CFSetRef peerIDs, CFErrorRef *error) {
147 SOSAccount* account = txn.account;
148 CFErrorRef localError = NULL;
149 bool result = false;
150
151 require_quiet([account.trust isInCircle:error], xit);
152
153 result =[account.kvs_message_transport SOSTransportMessageSyncWithPeers:account.kvs_message_transport p:peerIDs err:&localError];
154
155 if (result)
156 SetCloudKeychainTraceValueForKey(kCloudKeychainNumberOfTimesSyncedWithPeers, 1);
157
158 xit:
159 if (!result) {
160 // Tell account to update SOSEngine with current trusted peers
161 if (isSOSErrorCoded(localError, kSOSErrorPeerNotFound)) {
162 secnotice("Account", "Arming account to update SOSEngine with current trusted peers");
163 account.engine_peer_state_needs_repair = true;
164 }
165 CFErrorPropagate(localError, error);
166 localError = NULL;
167 }
168 return result;
169
170 }
171
172 bool SOSAccountSyncWithKVSPeerWithMessage(SOSAccountTransaction* txn, CFStringRef peerid, CFDataRef message, CFErrorRef *error) {
173 SOSAccount* account = txn.account;
174 bool result = false;
175 CFErrorRef localError = NULL;
176 CFDictionaryRef encapsulatedMessage = NULL;
177
178 secnotice("KVS Transport","Syncing with KVS capable peer: %@", peerid);
179 secnotice("KVS Transport", "message: %@", message);
180
181 require_quiet(message, xit);
182 require_quiet(peerid && CFStringGetLength(peerid) <= kSOSPeerIDLengthMax, xit);
183
184 encapsulatedMessage = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, peerid, message, NULL);
185
186 result = [account.kvs_message_transport SOSTransportMessageSendMessages:account.kvs_message_transport pm:encapsulatedMessage err:&localError];
187 secerror("KVS sync %s. (%@)", result ? "succeeded" : "failed", localError);
188
189 SOSAccountConsiderLoggingEngineState(txn);
190
191 xit:
192 CFReleaseNull(encapsulatedMessage);
193 CFErrorPropagate(localError, error);
194
195 return result;
196 }
197
198
199 static bool SOSAccountSyncWithKVSPeer(SOSAccountTransaction* txn, CFStringRef peerID, CFErrorRef *error)
200 {
201 bool result = false;
202 CFErrorRef localError = NULL;
203
204 secnotice("KVS Transport","Syncing with KVS capable peer: %@", peerID);
205
206 CFMutableSetRef peerIDs = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
207 CFSetAddValue(peerIDs, peerID);
208
209 result = SOSAccountSyncWithKVSPeers(txn, peerIDs, &localError);
210 secerror("KVS sync %s. (%@)", result ? "succeeded" : "failed", localError);
211
212 CFReleaseNull(peerIDs);
213 CFErrorPropagate(localError, error);
214
215 return result;
216 }
217
218 static CFMutableArrayRef SOSAccountCopyPeerIDsForDSID(SOSAccount* account, CFStringRef deviceID, CFErrorRef* error) {
219 CFMutableArrayRef peerIDs = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
220
221 SOSCircleForEachValidPeer(account.trust.trustedCircle, account.accountKey, ^(SOSPeerInfoRef peer) {
222 CFStringRef peerDeviceID = SOSPeerInfoCopyDeviceID(peer);
223 if(peerDeviceID != NULL && CFStringCompare(peerDeviceID, deviceID, 0) == 0){
224 CFArrayAppendValue(peerIDs, SOSPeerInfoGetPeerID(peer));
225 }
226 CFReleaseNull(peerDeviceID);
227 });
228
229 if (peerIDs == NULL || CFArrayGetCount(peerIDs) == 0) {
230 CFReleaseNull(peerIDs);
231 SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("No peer with DSID: %@"), deviceID);
232 }
233
234 return peerIDs;
235 }
236
237 static bool SOSAccountSyncWithKVSPeerFromPing(SOSAccount* account, CFArrayRef peerIDs, CFErrorRef *error) {
238
239 CFErrorRef localError = NULL;
240 bool result = false;
241
242 CFSetRef peerSet = CFSetCreateCopyOfArrayForCFTypes(peerIDs);
243 result = [account.kvs_message_transport SOSTransportMessageSyncWithPeers:account.kvs_message_transport p:peerSet err:&localError];
244
245 CFReleaseNull(peerSet);
246
247 return result;
248 }
249
250 bool SOSAccountSyncWithKVSUsingIDSID(SOSAccount* account, CFStringRef deviceID, CFErrorRef *error) {
251 bool result = false;
252 CFErrorRef localError = NULL;
253
254 secnotice("KVS Transport","Syncing with KVS capable peer via DSID: %@", deviceID);
255
256 CFArrayRef peerIDs = SOSAccountCopyPeerIDsForDSID(account, deviceID, &localError);
257 require_quiet(peerIDs, xit);
258
259 CFStringArrayPerfromWithDescription(peerIDs, ^(CFStringRef peerIDList) {
260 secnotice("KVS Transport", "Syncing with KVS capable peers: %@", peerIDList);
261 });
262
263 result = SOSAccountSyncWithKVSPeerFromPing(account, peerIDs, &localError);
264 secerror("KVS sync %s. (%@)", result ? "succeeded" : "failed", localError);
265
266 xit:
267 CFReleaseNull(peerIDs);
268 CFErrorPropagate(localError, error);
269
270 return result;
271 }
272
273 CFSetRef SOSAccountSyncWithPeersOverKVS(SOSAccountTransaction* txn, CFSetRef peers) {
274 CFMutableSetRef handled = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
275
276 CFSetForEach(peers, ^(const void *value) {
277 CFStringRef peerID = asString(value, NULL);
278 CFErrorRef localError = NULL;
279 if (peerID && SOSAccountSyncWithKVSPeer(txn, peerID, &localError)) {
280 CFSetAddValue(handled, peerID);
281 secnotice("KVS Transport", "synced with peer: %@", peerID);
282 } else {
283 secnotice("KVS Transport", "failed to sync with peer: %@ error: %@", peerID, localError);
284 }
285 });
286
287 return handled;
288 }
289
290 CF_RETURNS_RETAINED CFSetRef SOSAccountSyncWithPeersOverIDS(SOSAccountTransaction* txn, CFSetRef peers) {
291 CFErrorRef localError = NULL;
292 SOSAccount* account = txn.account;
293
294 CFStringSetPerformWithDescription(peers, ^(CFStringRef peerDescription) {
295 secnotice("IDS Transport","Syncing with IDS capable peers: %@", peerDescription);
296 });
297
298 // We should change this to return a set of peers we succeeded with, but for now assume they all worked.
299 bool result = [account.ids_message_transport SOSTransportMessageSyncWithPeers:account.ids_message_transport p:peers err:&localError];
300 secnotice("IDS Transport", "IDS Sync result: %d", result);
301
302 return CFSetCreateCopy(kCFAllocatorDefault, peers);
303 }
304
305 CF_RETURNS_RETAINED CFMutableSetRef SOSAccountSyncWithPeers(SOSAccountTransaction* txn, CFSetRef /* CFStringRef */ peerIDs, CFErrorRef *error) {
306 CFMutableSetRef notMePeers = NULL;
307 CFMutableSetRef handledPeerIDs = NULL;
308 CFMutableSetRef peersForIDS = NULL;
309 CFMutableSetRef peersForKVS = NULL;
310
311 SOSAccount* account = txn.account;
312
313 // Kick getting our device ID if we don't have it, and find out if we're setup to use IDS.
314 bool canUseIDS = [account.ids_message_transport SOSTransportMessageIDSGetIDSDeviceID:account];
315
316 if(![account.trust isInCircle:error])
317 {
318 handledPeerIDs = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, peerIDs);
319 CFReleaseNull(notMePeers);
320 CFReleaseNull(peersForIDS);
321 CFReleaseNull(peersForKVS);
322 return handledPeerIDs;
323 }
324
325 handledPeerIDs = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
326 peersForIDS = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
327 peersForKVS = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
328
329 SOSPeerInfoRef myPeerInfo = account.peerInfo;
330 if(!myPeerInfo)
331 {
332 CFReleaseNull(notMePeers);
333 CFReleaseNull(peersForIDS);
334 CFReleaseNull(peersForKVS);
335 return handledPeerIDs;
336
337 }
338 CFStringRef myPeerID = SOSPeerInfoGetPeerID(myPeerInfo);
339
340 notMePeers = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, peerIDs);
341 CFSetRemoveValue(notMePeers, myPeerID);
342
343 if(!SOSAccountSendIKSPSyncList(account, error)){
344 if(error != NULL)
345 secnotice("IDS Transport", "Did not send list of peers to ping (pre-E): %@", *error);
346 }
347
348 CFSetForEach(notMePeers, ^(const void *value) {
349 CFErrorRef localError = NULL;
350 CFStringRef peerID = asString(value, &localError);
351 SOSPeerInfoRef peerInfo = NULL;
352 SOSCircleRef circle = NULL;
353 SOSAccountTrustClassic *trust = account.trust;
354 circle = trust.trustedCircle;
355 require_quiet(peerID, skip);
356
357 peerInfo = SOSCircleCopyPeerWithID(circle, peerID, NULL);
358 if (peerInfo && SOSCircleHasValidSyncingPeer(circle, peerInfo, account.accountKey, NULL)) {
359 if (ENABLE_IDS && canUseIDS && SOSPeerInfoShouldUseIDSTransport(myPeerInfo, peerInfo) && SOSPeerInfoShouldUseACKModel(myPeerInfo, peerInfo)) {
360 CFSetAddValue(peersForIDS, peerID);
361 } else {
362 CFSetAddValue(peersForKVS, peerID);
363 }
364 } else {
365 CFSetAddValue(handledPeerIDs, peerID);
366 }
367
368 skip:
369 CFReleaseNull(peerInfo);
370 if (localError) {
371 secnotice("sync-with-peers", "Skipped peer ID: %@ due to %@", peerID, localError);
372 }
373 CFReleaseNull(localError);
374 });
375
376 CFSetRef handledIDSPeerIDs = SOSAccountSyncWithPeersOverIDS(txn, peersForIDS);
377 CFSetUnion(handledPeerIDs, handledIDSPeerIDs);
378 CFReleaseNull(handledIDSPeerIDs);
379
380 CFSetRef handledKVSPeerIDs = SOSAccountSyncWithPeersOverKVS(txn, peersForKVS);
381 CFSetUnion(handledPeerIDs, handledKVSPeerIDs);
382 CFReleaseNull(handledKVSPeerIDs);
383
384 SOSAccountConsiderLoggingEngineState(txn);
385
386 CFReleaseNull(notMePeers);
387 CFReleaseNull(peersForIDS);
388 CFReleaseNull(peersForKVS);
389 return handledPeerIDs;
390 }
391
392 bool SOSAccountClearPeerMessageKey(SOSAccountTransaction* txn, CFStringRef peerID, CFErrorRef *error)
393 {
394 if (peerID == NULL) {
395 return false;
396 }
397
398 SOSAccount* account = txn.account;
399
400 secnotice("IDS Transport", "clearing peer message for %@", peerID);
401 CFTypeRef dsid = SOSAccountGetValue(account, kSOSDSIDKey, error);
402
403 if(dsid == NULL)
404 dsid = kCFNull;
405
406 CFStringRef myID = (__bridge CFStringRef)(account.peerID);
407 CFStringRef message_to_peer_key = SOSMessageKeyCreateFromTransportToPeer(account.kvs_message_transport, myID, peerID);
408 CFDictionaryRef a_message_to_a_peer = CFDictionaryCreateForCFTypes(NULL, message_to_peer_key, kCFNull, kSOSKVSRequiredKey, dsid, NULL);
409
410 CloudKeychainReplyBlock log_error = ^(CFDictionaryRef returnedValues __unused, CFErrorRef block_error) {
411 if (block_error) {
412 secerror("Error putting: %@", block_error);
413 }
414 };
415
416 SOSCloudKeychainPutObjectsInCloud(a_message_to_a_peer, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), log_error);
417
418 CFReleaseNull(a_message_to_a_peer);
419 CFReleaseNull(message_to_peer_key);
420
421 return true;
422 }
423
424 CF_RETURNS_RETAINED CFSetRef SOSAccountProcessSyncWithPeers(SOSAccountTransaction* txn, CFSetRef /* CFStringRef */ peers, CFSetRef /* CFStringRef */ backupPeers, CFErrorRef *error)
425 {
426 CFErrorRef localError = NULL;
427 SOSAccount* account = txn.account;
428
429 CFMutableSetRef handled = SOSAccountSyncWithPeers(txn, peers, &localError);
430
431 [account.ids_message_transport SOSTransportMessageIDSGetIDSDeviceID:account];
432
433 if (!handled) {
434 secnotice("account-sync", "Peer Sync failed: %@", localError);
435 handled = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
436 }
437 CFReleaseNull(localError);
438
439 CFTypeRef engine = [account.kvs_message_transport SOSTransportMessageGetEngine];
440 CFSetRef engineHandled = SOSEngineSyncWithBackupPeers((SOSEngineRef)engine, backupPeers, false, error);
441
442 if (engineHandled) {
443 CFSetUnion(handled, engineHandled);
444 } else {
445 secnotice("account-sync", "Engine Backup Sync failed: %@", localError);
446 }
447 CFReleaseNull(localError);
448 CFReleaseNull(engineHandled);
449
450 return handled;
451 }
452
453 CF_RETURNS_RETAINED CFSetRef SOSAccountCopyBackupPeersAndForceSync(SOSAccountTransaction* txn, CFErrorRef *error)
454 {
455 SOSEngineRef engine = (SOSEngineRef) [txn.account.kvs_message_transport SOSTransportMessageGetEngine];
456
457 NSArray* backupPeersArray = (NSArray*) CFBridgingRelease(SOSEngineCopyBackupPeerNames(engine, error));
458 NSSet* backupPeers = [[NSSet alloc] initWithArray: backupPeersArray];
459 return SOSEngineSyncWithBackupPeers(engine, (__bridge CFSetRef) backupPeers, true, error);
460 }
461
462 bool SOSAccountRequestSyncWithAllPeers(SOSAccountTransaction* txn, CFErrorRef *error)
463 {
464 SOSAccount* account = txn.account;
465 SOSAccountTrustClassic *trust = account.trust;
466
467 if (![account.trust isInCircle:error])
468 return false;
469
470 NSMutableSet<NSString*>* allSyncingPeers = [NSMutableSet set];
471 SOSCircleRef circle = trust.trustedCircle;
472
473 // Tickle IDS in case we haven't even tried when we're syncing.
474 [account.ids_message_transport SOSTransportMessageIDSGetIDSDeviceID:account];
475
476 SOSCircleForEachValidSyncingPeer(circle, account.accountKey, ^(SOSPeerInfoRef peer) {
477 [allSyncingPeers addObject: (__bridge NSString*) SOSPeerInfoGetPeerID(peer)];
478 });
479
480 [txn requestSyncWithPeers:allSyncingPeers];
481
482 return true;
483 }
484
485 //
486 // MARK: Syncing status functions
487 //
488 bool SOSAccountMessageFromPeerIsPending(SOSAccountTransaction* txn, SOSPeerInfoRef peer, CFErrorRef *error) {
489 bool success = false;
490 SOSAccount* account = txn.account;
491 require_quiet([account.trust isInCircle:error], xit);
492
493 // This translation belongs inside KVS..way down in CKD, but for now we reach over and do it here.
494 CFStringRef peerMessage = SOSMessageKeyCreateFromPeerToTransport([account kvs_message_transport], (__bridge CFStringRef)(account.peerID), SOSPeerInfoGetPeerID(peer));
495
496 success = SOSCloudKeychainHasPendingKey(peerMessage, error);
497 CFReleaseNull(peerMessage);
498
499 xit:
500 return success;
501 }
502
503 bool SOSAccountSendToPeerIsPending(SOSAccountTransaction* txn, SOSPeerInfoRef peer, CFErrorRef *error) {
504 bool success = false;
505 SOSAccount* account = txn.account;
506 require_quiet([account.trust isInCircle:error], xit);
507
508 success = SOSCCIsSyncPendingFor(SOSPeerInfoGetPeerID(peer), error);
509 xit:
510 return success;
511
512
513 }
514