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