]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SOSCircle/SecureObjectSync/SOSAccount.m
Security-58286.60.28.tar.gz
[apple/security.git] / OSX / sec / SOSCircle / SecureObjectSync / SOSAccount.m
1 /*
2 * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
3 */
4
5 /*
6 * SOSAccount.c - Implementation of the secure object syncing account.
7 * An account contains a SOSCircle for each protection domain synced.
8 */
9
10 #import <Foundation/Foundation.h>
11
12 #include <Security/SecureObjectSync/SOSAccount.h>
13 #include <Security/SecureObjectSync/SOSPeerInfo.h>
14 #include <Security/SecureObjectSync/SOSPeerInfoV2.h>
15 #include <Security/SecureObjectSync/SOSPeerInfoCollections.h>
16 #import "Security/SecureObjectSync/SOSTransportCircle.h"
17 #import "Security/SecureObjectSync/SOSTransportCircleKVS.h"
18 #include <Security/SecureObjectSync/SOSTransportMessage.h>
19 #include <Security/SecureObjectSync/SOSTransportMessageKVS.h>
20 #include <Security/SecureObjectSync/SOSTransportMessageIDS.h>
21 #include <Security/SecureObjectSync/SOSTransportKeyParameter.h>
22 #include <Security/SecureObjectSync/SOSKVSKeys.h>
23 #include <Security/SecureObjectSync/SOSTransport.h>
24 #include <Security/SecureObjectSync/SOSPeerCoder.h>
25 #include <Security/SecureObjectSync/SOSInternal.h>
26 #include <Security/SecureObjectSync/SOSRing.h>
27 #include <Security/SecureObjectSync/SOSRingUtils.h>
28 #include <Security/SecureObjectSync/SOSRingRecovery.h>
29 #include <Security/SecureObjectSync/SOSPeerInfoSecurityProperties.h>
30 #include <Security/SecureObjectSync/SOSAccountTransaction.h>
31 #include <Security/SecureObjectSync/SOSAccountGhost.h>
32 #include <Security/SecureObjectSync/SOSPiggyback.h>
33 #include <Security/SecureObjectSync/SOSControlHelper.h>
34
35 #import <Security/SecureObjectSync/SOSAccountTrust.h>
36 #import <Security/SecureObjectSync/SOSAccountTrustClassic.h>
37 #import "Security/SecureObjectSync/SOSAccountTrustClassic+Circle.h"
38 #import "Security/SecureObjectSync/SOSAccountTrustClassic+Expansion.h"
39 #import "Security/SecureObjectSync/SOSAccountTrustClassic+Identity.h"
40 #import "Security/SecureObjectSync/SOSAccountTrustClassic+Retirement.h"
41 #import "Security/SecureObjectSync/SOSPeerOTRTimer.h"
42 #import "Security/SecureObjectSync/SOSPeerRateLimiter.h"
43 #import "Security/SecureObjectSync/SOSTypes.h"
44 #if OCTAGON
45 #import "keychain/ckks/CKKSViewManager.h"
46 #import "keychain/ckks/CKKSLockStateTracker.h"
47 #import "keychain/ot/OTContext.h"
48 #endif
49 #include <Security/SecItemInternal.h>
50 #include <Security/SecEntitlements.h>
51 #include <SOSCircle/CKBridge/SOSCloudKeychainClient.h>
52
53 #include <SecItemServer.h>
54
55 #import "SecdWatchdog.h"
56
57 #include <utilities/SecCFWrappers.h>
58 #include <utilities/SecCFError.h>
59 #include <utilities/SecADWrapper.h>
60
61 #include <os/activity.h>
62 #include <os/state_private.h>
63
64 #include <utilities/SecCoreCrypto.h>
65
66 #include <utilities/der_plist.h>
67 #include <utilities/der_plist_internal.h>
68 #include <corecrypto/ccder.h>
69
70 const CFStringRef kSOSEscrowRecord = CFSTR("EscrowRecord");
71 const CFStringRef kSOSUnsyncedViewsKey = CFSTR("unsynced");
72 const CFStringRef kSOSInitialSyncTimeoutV0 = CFSTR("initialsynctimeout");
73 const CFStringRef kSOSPendingEnableViewsToBeSetKey = CFSTR("pendingEnableViews");
74 const CFStringRef kSOSPendingDisableViewsToBeSetKey = CFSTR("pendingDisableViews");
75 const CFStringRef kSOSTestV2Settings = CFSTR("v2dictionary");
76 const CFStringRef kSOSRecoveryKey = CFSTR("RecoveryKey");
77 const CFStringRef kSOSRecoveryRing = CFSTR("RecoveryRing");
78 const CFStringRef kSOSAccountUUID = CFSTR("UUID");
79 const CFStringRef kSOSRateLimitingCounters = CFSTR("RateLimitCounters");
80 const CFStringRef kSOSAccountPeerNegotiationTimeouts = CFSTR("PeerNegotiationTimeouts"); //Dictionary<CFStringRef, CFNumberRef>
81 const CFStringRef kSOSAccountPeerLastSentTimestamp = CFSTR("kSOSAccountPeerLastSentTimestamp"); //Dictionary<CFStringRef, CFDateRef>
82 const CFStringRef kSOSAccountRenegotiationRetryCount = CFSTR("NegotiationRetryCount");
83 const CFStringRef kOTRConfigVersion = CFSTR("OTRConfigVersion");
84 NSString* const SecSOSAggdReattemptOTRNegotiation = @"com.apple.security.sos.otrretry";
85
86 const uint64_t max_packet_size_over_idms = 500;
87
88
89 #define kPublicKeyNotAvailable "com.apple.security.publickeynotavailable"
90
91 #define DATE_LENGTH 25
92 const CFStringRef kSOSAccountDebugScope = CFSTR("Scope");
93
94 @implementation SOSAccount
95
96 // Auto synthesis for most fields works great.
97 // A few CF fields need retention work when assigning.
98
99 -(id) init
100 {
101 self = [super init];
102 if(self){
103 self.gestalt = [NSMutableDictionary dictionary];
104 self.backup_key = nil;
105 self.deviceID = nil;
106
107 self.trust = [SOSAccountTrustClassic trustClassic];
108
109 self.queue = NULL;
110 self.user_private_timer = NULL;
111 self.factory = NULL;
112
113 self._password_tmp = nil;
114 self.isListeningForSync = false;
115 self.lock_notification_token = -1;
116
117 self.key_transport = nil;
118 self.circle_transport = NULL;
119 self.kvs_message_transport = nil;
120 self.ids_message_transport = nil;
121 self.ck_storage = nil;
122
123 self.circle_rings_retirements_need_attention = false;
124 self.engine_peer_state_needs_repair = false;
125 self.key_interests_need_updating = false;
126
127 self.change_blocks = [NSMutableArray array];
128
129 self.waitForInitialSync_blocks = [NSMutableDictionary dictionary];
130 self.isInitialSyncing = false;
131
132 self.accountKeyIsTrusted = false;
133 self.accountKeyDerivationParamters = nil;
134
135 self.accountKey = NULL;
136 self.accountPrivateKey = NULL;
137 self.previousAccountKey = NULL;
138
139 self.saveBlock = nil;
140 }
141 return self;
142 }
143
144 - (void)dealloc {
145 if(self) {
146 CFReleaseNull(self->_accountKey);
147 CFReleaseNull(self->_accountPrivateKey);
148 CFReleaseNull(self->_previousAccountKey);
149 }
150 }
151
152 @synthesize accountKey = _accountKey;
153
154 - (void) setAccountKey: (SecKeyRef) key {
155 CFRetainAssign(self->_accountKey, key);
156 }
157
158 @synthesize previousAccountKey = _previousAccountKey;
159
160 - (void) setPreviousAccountKey: (SecKeyRef) key {
161 CFRetainAssign(self->_previousAccountKey, key);
162 }
163
164 @synthesize accountPrivateKey = _accountPrivateKey;
165
166 - (void) setAccountPrivateKey: (SecKeyRef) key {
167 CFRetainAssign(self->_accountPrivateKey, key);
168 }
169
170 // Syntactic sugar getters
171
172 - (BOOL) hasPeerInfo {
173 return self.fullPeerInfo != nil;
174 }
175
176 - (SOSPeerInfoRef) peerInfo {
177 return self.trust.peerInfo;
178 }
179
180 - (SOSFullPeerInfoRef) fullPeerInfo {
181 return self.trust.fullPeerInfo;
182 }
183
184 - (NSString*) peerID {
185 return self.trust.peerID;
186 }
187
188 -(bool) ensureFactoryCircles
189 {
190 if (!self){
191 return false;
192 }
193
194 if (!self.factory){
195 return false;
196 }
197
198 NSString* circle_name = (__bridge_transfer NSString*)SOSDataSourceFactoryCopyName(self.factory);
199
200 if (!circle_name){
201 return false;
202 }
203
204 CFReleaseSafe(SOSAccountEnsureCircle(self, (__bridge CFStringRef) circle_name, NULL));
205
206 return SOSAccountInflateTransports(self, (__bridge CFStringRef) circle_name, NULL);
207 }
208
209 -(void)ensureOctagonPeerKeys
210 {
211 #if OCTAGON
212 CKKSLockStateTracker *tracker = [CKKSViewManager manager].lockStateTracker;
213 if (tracker && tracker.isLocked == false) {
214 [self.trust ensureOctagonPeerKeys:self.circle_transport];
215 }
216 #endif
217 }
218
219 -(id) initWithGestalt:(CFDictionaryRef)newGestalt factory:(SOSDataSourceFactoryRef)f
220 {
221 self = [super init];
222 if(self){
223 self.queue = dispatch_queue_create("Account Queue", DISPATCH_QUEUE_SERIAL);
224
225 self.gestalt = [[NSDictionary alloc] initWithDictionary:(__bridge NSDictionary * _Nonnull)(newGestalt)];
226
227 SOSAccountTrustClassic *t = [[SOSAccountTrustClassic alloc] initWithRetirees:[NSMutableSet set] fpi:NULL circle:NULL departureCode:kSOSDepartureReasonError peerExpansion:[NSMutableDictionary dictionary]];
228
229 self.trust = t;
230 self.factory = f; // We adopt the factory. kthanksbai.
231
232 self.isListeningForSync = false;
233
234 self.accountPrivateKey = NULL;
235 self._password_tmp = NULL;
236 self.user_private_timer = NULL;
237 self.lock_notification_token = NOTIFY_TOKEN_INVALID;
238
239 self.change_blocks = [NSMutableArray array];
240 self.waitForInitialSync_blocks = NULL;
241
242 self.key_transport = nil;
243 self.circle_transport = NULL;
244 self.ck_storage = nil;
245 self.kvs_message_transport = nil;
246 self.ids_message_transport = nil;
247
248 self.circle_rings_retirements_need_attention = false;
249 self.engine_peer_state_needs_repair = false;
250 self.key_interests_need_updating = false;
251
252 self.backup_key =nil;
253 self.deviceID = nil;
254
255 self.waitForInitialSync_blocks = [NSMutableDictionary dictionary];
256 self.isInitialSyncing = false;
257 self.accountKeyIsTrusted = false;
258 self.accountKeyDerivationParamters = NULL;
259 self.accountKey = NULL;
260 self.previousAccountKey = NULL;
261
262 self.saveBlock = nil;
263 }
264 return self;
265 }
266
267 -(BOOL)isEqual:(id) object
268 {
269 if(![object isKindOfClass:[SOSAccount class]])
270 return NO;
271
272 SOSAccount* left = object;
273 return ([self.gestalt isEqual: left.gestalt] &&
274 CFEqualSafe(self.trust.trustedCircle, left.trust.trustedCircle) &&
275 [self.trust.expansion isEqual: left.trust.expansion] &&
276 CFEqualSafe(self.trust.fullPeerInfo, left.trust.fullPeerInfo));
277
278 }
279
280 - (void)userPublicKey:(void ((^))(BOOL trusted, NSData *spki, NSError *error))reply
281 {
282 dispatch_async(self.queue, ^{
283 if (!self.accountKeyIsTrusted || self.accountKey == NULL) {
284 NSDictionary *userinfo = @{
285 (id)kCFErrorDescriptionKey : @"User public key not trusted",
286 };
287 reply(self.accountKeyIsTrusted, NULL, [NSError errorWithDomain:(__bridge NSString *)kSOSErrorDomain code:kSOSErrorPublicKeyAbsent userInfo:userinfo]);
288 return;
289 }
290
291 NSData *data = CFBridgingRelease(SecKeyCopySubjectPublicKeyInfo(self.accountKey));
292 if (data == NULL) {
293 NSDictionary *userinfo = @{
294 (id)kCFErrorDescriptionKey : @"User public not available",
295 };
296 reply(self.accountKeyIsTrusted, NULL, [NSError errorWithDomain:(__bridge NSString *)kSOSErrorDomain code:kSOSErrorPublicKeyAbsent userInfo:userinfo]);
297 return;
298 }
299 reply(self.accountKeyIsTrusted, data, NULL);
300 });
301 }
302
303 - (void)kvsPerformanceCounters:(void(^)(NSDictionary <NSString *, NSNumber *> *))reply
304 {
305 /* Need to collect performance counters from all subsystems, not just circle transport, don't have counters yet though */
306 SOSCloudKeychainRequestPerfCounters(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFDictionaryRef returnedValues, CFErrorRef error)
307 {
308 reply((__bridge NSDictionary *)returnedValues);
309 });
310 }
311 - (void)idsPerformanceCounters:(void(^)(NSDictionary <NSString *, NSNumber *> *))reply
312 {
313 SOSCloudKeychainRetrieveCountersFromIDSProxy(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFDictionaryRef returnedValues, CFErrorRef error)
314 {
315 reply((__bridge NSDictionary *)returnedValues);
316 });
317 }
318
319 - (void)rateLimitingPerformanceCounters:(void(^)(NSDictionary <NSString *, NSString *> *))reply
320 {
321 CFErrorRef error = NULL;
322 CFDictionaryRef rateLimitingCounters = (CFDictionaryRef)SOSAccountGetValue(self, kSOSRateLimitingCounters, &error);
323 reply((__bridge NSDictionary*)rateLimitingCounters ? (__bridge NSDictionary*)rateLimitingCounters : [NSDictionary dictionary]);
324 }
325
326 - (void)stashedCredentialPublicKey:(void(^)(NSData *, NSError *error))reply
327 {
328 dispatch_async(self.queue, ^{
329 CFErrorRef error = NULL;
330
331 SecKeyRef user_private = SOSAccountCopyStashedUserPrivateKey(self, &error);
332 if (user_private == NULL) {
333 reply(NULL, (__bridge NSError *)error);
334 CFReleaseNull(error);
335 return;
336 }
337
338 NSData *publicKey = CFBridgingRelease(SecKeyCopySubjectPublicKeyInfo(user_private));
339 CFReleaseNull(user_private);
340 reply(publicKey, NULL);
341 });
342 }
343
344 - (void)assertStashedAccountCredential:(void(^)(BOOL result, NSError *error))complete
345 {
346 dispatch_async(self.queue, ^{
347 CFErrorRef error = NULL;
348 bool result = SOSAccountAssertStashedAccountCredential(self, &error);
349 complete(result, (__bridge NSError *)error);
350 CFReleaseNull(error);
351 });
352 }
353
354 static bool SyncKVSAndWait(CFErrorRef *error) {
355 dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
356
357 __block bool success = false;
358
359 secnoticeq("fresh", "EFP calling SOSCloudKeychainSynchronizeAndWait");
360
361 os_activity_initiate("CloudCircle EFRESH", OS_ACTIVITY_FLAG_DEFAULT, ^(void) {
362 SOSCloudKeychainSynchronizeAndWait(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(__unused CFDictionaryRef returnedValues, CFErrorRef sync_error) {
363 secnotice("fresh", "EFP returned, callback error: %@", sync_error);
364
365 success = (sync_error == NULL);
366 if (error) {
367 CFRetainAssign(*error, sync_error);
368 }
369
370 dispatch_semaphore_signal(wait_for);
371 });
372
373
374 dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER);
375 secnotice("fresh", "EFP complete: %s %@", success ? "success" : "failure", error ? *error : NULL);
376 });
377
378 return success;
379 }
380
381 static bool Flush(CFErrorRef *error) {
382 __block bool success = false;
383
384 dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
385 secnotice("flush", "Starting");
386
387 SOSCloudKeychainFlush(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFDictionaryRef returnedValues, CFErrorRef sync_error) {
388 success = (sync_error == NULL);
389 if (error) {
390 CFRetainAssign(*error, sync_error);
391 }
392
393 dispatch_semaphore_signal(wait_for);
394 });
395
396 dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER);
397
398 secnotice("flush", "Returned %s", success? "Success": "Failure");
399
400 return success;
401 }
402
403 - (bool)syncWaitAndFlush:(CFErrorRef *)error
404 {
405 secnotice("pairing", "sync and wait starting");
406
407 if (!SyncKVSAndWait(error)) {
408 secnotice("pairing", "failed sync and wait: %@", error ? *error : NULL);
409 return false;
410 }
411 if (!Flush(error)) {
412 secnotice("pairing", "failed flush: %@", error ? *error : NULL);
413 return false;
414 }
415 secnotice("pairing", "finished sync and wait");
416 return true;
417 }
418
419 - (void)validatedStashedAccountCredential:(void(^)(NSData *credential, NSError *error))complete
420 {
421 CFErrorRef syncerror = NULL;
422
423 if (![self syncWaitAndFlush:&syncerror]) {
424 complete(NULL, (__bridge NSError *)syncerror);
425 CFReleaseNull(syncerror);
426 return;
427 }
428
429 dispatch_async(self.queue, ^{
430 CFErrorRef error = NULL;
431 SecKeyRef key = NULL;
432 key = SOSAccountCopyStashedUserPrivateKey(self, &error);
433 if (key == NULL) {
434 secnotice("pairing", "no stashed credential");
435 complete(NULL, (__bridge NSError *)error);
436 CFReleaseNull(error);
437 return;
438 }
439
440 SecKeyRef publicKey = SecKeyCopyPublicKey(key);
441 if (publicKey) {
442 secnotice("pairing", "returning stash credential: %@", publicKey);
443 CFReleaseNull(publicKey);
444 }
445
446 NSData *keydata = CFBridgingRelease(SecKeyCopyExternalRepresentation(key, &error));
447 CFReleaseNull(key);
448 complete(keydata, (__bridge NSError *)error);
449 CFReleaseNull(error);
450 });
451 }
452
453 - (void)stashAccountCredential:(NSData *)credential complete:(void(^)(bool success, NSError *error))complete
454 {
455 CFErrorRef syncerror = NULL;
456
457 if (![self syncWaitAndFlush:&syncerror]) {
458 complete(NULL, (__bridge NSError *)syncerror);
459 CFReleaseNull(syncerror);
460 return;
461 }
462
463 [self performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
464 SecKeyRef accountPrivateKey = NULL;
465 CFErrorRef error = NULL;
466 NSDictionary *attributes = @{
467 (__bridge id)kSecAttrKeyClass : (__bridge id)kSecAttrKeyClassPrivate,
468 (__bridge id)kSecAttrKeyType : (__bridge id)kSecAttrKeyTypeEC,
469 };
470
471 accountPrivateKey = SecKeyCreateWithData((__bridge CFDataRef)credential, (__bridge CFDictionaryRef)attributes, &error);
472 if (accountPrivateKey == NULL) {
473 complete(false, (__bridge NSError *)error);
474 secnotice("pairing", "SecKeyCreateWithData failed: %@", error);
475 CFReleaseNull(error);
476 return;
477 }
478
479 if (!SOSAccountTryUserPrivateKey(self, accountPrivateKey, &error)) {
480 CFReleaseNull(accountPrivateKey);
481 complete(false, (__bridge NSError *)error);
482 secnotice("pairing", "SOSAccountTryUserPrivateKey failed: %@", error);
483 CFReleaseNull(error);
484 return;
485 }
486
487 secnotice("pairing", "SOSAccountTryUserPrivateKey succeeded");
488
489 CFReleaseNull(accountPrivateKey);
490 complete(true, NULL);
491 }];
492
493 // This makes getting the private key the same as Asserting the password - we read all the other things
494 // that we just expressed interest in.
495 CFErrorRef error = NULL;
496 if (!Flush(&error)) {
497 secnotice("pairing", "failed final flush: %@", error ? error : NULL);
498 return;
499 }
500 CFReleaseNull(error);
501 }
502
503 - (void)myPeerInfo:(void (^)(NSData *, NSError *))complete
504 {
505 __block CFErrorRef localError = NULL;
506 __block NSData *applicationBlob = NULL;
507 SecAKSDoWhileUserBagLocked(&localError, ^{
508 [self performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
509 SOSPeerInfoRef application = SOSAccountCopyApplication(txn.account, &localError);
510 if (application) {
511 applicationBlob = CFBridgingRelease(SOSPeerInfoCopyEncodedData(application, kCFAllocatorDefault, &localError));
512 CFReleaseNull(application);
513 }
514 }];
515 });
516 complete(applicationBlob, (__bridge NSError *)localError);
517 CFReleaseNull(localError);
518 }
519
520 - (void)circleJoiningBlob:(NSData *)applicant complete:(void (^)(NSData *blob, NSError *))complete
521 {
522 __block CFErrorRef localError = NULL;
523 __block NSData *blob = NULL;
524 SOSPeerInfoRef peer = SOSPeerInfoCreateFromData(NULL, &localError, (__bridge CFDataRef)applicant);
525 if (peer == NULL) {
526 complete(NULL, (__bridge NSError *)localError);
527 CFReleaseNull(localError);
528 return;
529 }
530 SecAKSDoWhileUserBagLocked(&localError, ^{
531 [self performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
532 blob = CFBridgingRelease(SOSAccountCopyCircleJoiningBlob(txn.account, peer, &localError));
533 }];
534 });
535
536 CFReleaseNull(peer);
537
538 complete(blob, (__bridge NSError *)localError);
539 CFReleaseNull(localError);
540 }
541
542 - (void)joinCircleWithBlob:(NSData *)blob version:(PiggyBackProtocolVersion)version complete:(void (^)(bool success, NSError *))complete
543 {
544 __block CFErrorRef localError = NULL;
545 __block bool res = false;
546
547 SecAKSDoWhileUserBagLocked(&localError, ^{
548 [self performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
549 res = SOSAccountJoinWithCircleJoiningBlob(txn.account, (__bridge CFDataRef)blob, version, &localError);
550 }];
551 });
552
553 complete(res, (__bridge NSError *)localError);
554 CFReleaseNull(localError);
555 }
556
557 - (void)initialSyncCredentials:(uint32_t)flags complete:(void (^)(NSArray *, NSError *))complete
558 {
559 CFErrorRef error = NULL;
560 uint32_t isflags = 0;
561
562 if (flags & SOSControlInitialSyncFlagTLK)
563 isflags |= SecServerInitialSyncCredentialFlagTLK;
564 if (flags & SOSControlInitialSyncFlagPCS)
565 isflags |= SecServerInitialSyncCredentialFlagPCS;
566 if (flags & SOSControlInitialSyncFlagPCSNonCurrent)
567 isflags |= SecServerInitialSyncCredentialFlagPCSNonCurrent;
568 if (flags & SOSControlInitialSyncFlagBluetoothMigration)
569 isflags |= SecServerInitialSyncCredentialFlagBluetoothMigration;
570
571
572 NSArray *array = CFBridgingRelease(_SecServerCopyInitialSyncCredentials(isflags, &error));
573 complete(array, (__bridge NSError *)error);
574 CFReleaseNull(error);
575 }
576
577 - (void)importInitialSyncCredentials:(NSArray *)items complete:(void (^)(bool success, NSError *))complete
578 {
579 CFErrorRef error = NULL;
580 bool res = _SecServerImportInitialSyncCredentials((__bridge CFArrayRef)items, &error);
581 complete(res, (__bridge NSError *)error);
582 CFReleaseNull(error);
583 }
584
585 - (void)triggerSync:(NSArray <NSString *> *)peers complete:(void(^)(bool success, NSError *))complete
586 {
587 __block CFErrorRef localError = NULL;
588 __block bool res = false;
589
590 secnotice("sync", "trigger a forced sync for %@", peers);
591
592 SecAKSDoWhileUserBagLocked(&localError, ^{
593 [self performTransaction:^(SOSAccountTransaction *txn) {
594 if ([peers count]) {
595 NSSet *peersSet = [NSSet setWithArray:peers];
596 CFMutableSetRef handledPeers = SOSAccountSyncWithPeers(txn, (__bridge CFSetRef)peersSet, &localError);
597 if (handledPeers && CFSetGetCount(handledPeers) == (CFIndex)[peersSet count]) {
598 res = true;
599 }
600 CFReleaseNull(handledPeers);
601 } else {
602 res = SOSAccountRequestSyncWithAllPeers(txn, &localError);
603 }
604 }];
605 });
606 complete(res, (__bridge NSError *)localError);
607 CFReleaseNull(localError);
608 }
609
610 - (void)getWatchdogParameters:(void (^)(NSDictionary* parameters, NSError* error))complete
611 {
612 // SecdWatchdog is only available in the secd/securityd - no other binary will contain that class
613 Class watchdogClass = NSClassFromString(@"SecdWatchdog");
614 if (watchdogClass) {
615 NSDictionary* parameters = [[watchdogClass watchdog] watchdogParameters];
616 complete(parameters, nil);
617 }
618 else {
619 complete(nil, [NSError errorWithDomain:@"com.apple.securityd.watchdog" code:1 userInfo:@{NSLocalizedDescriptionKey : @"failed to lookup SecdWatchdog class from ObjC runtime"}]);
620 }
621 }
622
623 - (void)setWatchdogParmeters:(NSDictionary*)parameters complete:(void (^)(NSError* error))complete
624 {
625 // SecdWatchdog is only available in the secd/securityd - no other binary will contain that class
626 NSError* error = nil;
627 Class watchdogClass = NSClassFromString(@"SecdWatchdog");
628 if (watchdogClass) {
629 [[watchdogClass watchdog] setWatchdogParameters:parameters error:&error];
630 complete(error);
631 }
632 else {
633 complete([NSError errorWithDomain:@"com.apple.securityd.watchdog" code:1 userInfo:@{NSLocalizedDescriptionKey : @"failed to lookup SecdWatchdog class from ObjC runtime"}]);
634 }
635 }
636
637 //
638 // MARK: Save Block
639 //
640
641 - (void) flattenToSaveBlock {
642 if (self.saveBlock) {
643 NSError* error = nil;
644 NSData* saveData = [self encodedData:&error];
645
646 (self.saveBlock)((__bridge CFDataRef) saveData, (__bridge CFErrorRef) error);
647 }
648 }
649
650 CFDictionaryRef SOSAccountCopyGestalt(SOSAccount* account) {
651 return CFDictionaryCreateCopy(kCFAllocatorDefault, (__bridge CFDictionaryRef)account.gestalt);
652 }
653
654 CFDictionaryRef SOSAccountCopyV2Dictionary(SOSAccount* account) {
655 CFDictionaryRef v2dict = SOSAccountGetValue(account, kSOSTestV2Settings, NULL);
656 return CFDictionaryCreateCopy(kCFAllocatorDefault, v2dict);
657 }
658
659 static bool SOSAccountUpdateDSID(SOSAccount* account, CFStringRef dsid){
660 SOSAccountSetValue(account, kSOSDSIDKey, dsid, NULL);
661 //send new DSID over account changed
662 [account.circle_transport kvsSendOfficialDSID:dsid err:NULL];
663 return true;
664 }
665
666 void SOSAccountAssertDSID(SOSAccount* account, CFStringRef dsid) {
667 CFStringRef accountDSID = SOSAccountGetValue(account, kSOSDSIDKey, NULL);
668 if(accountDSID == NULL) {
669 secdebug("updates", "Setting dsid, current dsid is empty for this account: %@", dsid);
670
671 SOSAccountUpdateDSID(account, dsid);
672 } else if(CFStringCompare(dsid, accountDSID, 0) != kCFCompareEqualTo) {
673 secnotice("updates", "Changing DSID from: %@ to %@", accountDSID, dsid);
674
675 //DSID has changed, blast the account!
676 SOSAccountSetToNew(account);
677
678 //update DSID to the new DSID
679 SOSAccountUpdateDSID(account, dsid);
680 } else {
681 secnotice("updates", "Not Changing DSID: %@ to %@", accountDSID, dsid);
682 }
683 }
684
685
686 void SOSAccountPendDisableViewSet(SOSAccount* account, CFSetRef disabledViews)
687 {
688 [account.trust valueUnionWith:kSOSPendingDisableViewsToBeSetKey valuesToUnion:disabledViews];
689 [account.trust valueSubtractFrom:kSOSPendingEnableViewsToBeSetKey valuesToSubtract:disabledViews];
690 }
691
692 SOSViewResultCode SOSAccountVirtualV0Behavior(SOSAccount* account, SOSViewActionCode actionCode) {
693 SOSViewResultCode retval = kSOSCCGeneralViewError;
694 // The V0 view switches on and off all on it's own, we allow people the delusion
695 // of control and status if it's what we're stuck at., otherwise error.
696 if (SOSAccountSyncingV0(account)) {
697 require_action_quiet(actionCode == kSOSCCViewDisable, errOut, CFSTR("Can't disable V0 view and it's on right now"));
698 retval = kSOSCCViewMember;
699 } else {
700 require_action_quiet(actionCode == kSOSCCViewEnable, errOut, CFSTR("Can't enable V0 and it's off right now"));
701 retval = kSOSCCViewNotMember;
702 }
703 errOut:
704 return retval;
705 }
706
707 SOSAccount* SOSAccountCreate(CFAllocatorRef allocator,
708 CFDictionaryRef gestalt,
709 SOSDataSourceFactoryRef factory) {
710
711 SOSAccount* a = [[SOSAccount alloc] initWithGestalt:gestalt factory:factory];
712 [a ensureFactoryCircles];
713 SOSAccountEnsureUUID(a);
714 secnotice("circleop", "Setting account.key_interests_need_updating to true in SOSAccountCreate");
715 a.key_interests_need_updating = true;
716
717 return a;
718 }
719
720 static OSStatus do_delete(CFDictionaryRef query) {
721 OSStatus result;
722
723 result = SecItemDelete(query);
724 if (result) {
725 secerror("SecItemDelete: %d", (int)result);
726 }
727 return result;
728 }
729
730 static int
731 do_keychain_delete_aks_bags()
732 {
733 OSStatus result;
734 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
735 kSecClass, kSecClassGenericPassword,
736 kSecAttrAccessGroup, CFSTR("com.apple.sbd"),
737 kSecAttrAccount, CFSTR("SecureBackupPublicKeybag"),
738 kSecAttrService, CFSTR("SecureBackupService"),
739 kSecAttrSynchronizable, kCFBooleanTrue,
740 kSecUseTombstones, kCFBooleanFalse,
741 NULL);
742
743 result = do_delete(item);
744 CFReleaseSafe(item);
745
746 return result;
747 }
748
749 static int
750 do_keychain_delete_identities()
751 {
752 OSStatus result;
753 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
754 kSecClass, kSecClassKey,
755 kSecAttrSynchronizable, kCFBooleanTrue,
756 kSecUseTombstones, kCFBooleanFalse,
757 kSecAttrAccessGroup, CFSTR("com.apple.security.sos"),
758 NULL);
759
760 result = do_delete(item);
761 CFReleaseSafe(item);
762
763 return result;
764 }
765
766 static int
767 do_keychain_delete_lakitu()
768 {
769 OSStatus result;
770 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
771 kSecClass, kSecClassGenericPassword,
772 kSecAttrSynchronizable, kCFBooleanTrue,
773 kSecUseTombstones, kCFBooleanFalse,
774 kSecAttrAccessGroup, CFSTR("com.apple.lakitu"),
775 kSecAttrAccount, CFSTR("EscrowServiceBypassToken"),
776 kSecAttrService, CFSTR("EscrowService"),
777 NULL);
778
779 result = do_delete(item);
780 CFReleaseSafe(item);
781
782 return result;
783 }
784
785 static int
786 do_keychain_delete_sbd()
787 {
788 OSStatus result;
789 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
790 kSecClass, kSecClassGenericPassword,
791 kSecAttrSynchronizable, kCFBooleanTrue,
792 kSecUseTombstones, kCFBooleanFalse,
793 kSecAttrAccessGroup, CFSTR("com.apple.sbd"),
794 NULL);
795
796 result = do_delete(item);
797 CFReleaseSafe(item);
798
799 return result;
800 }
801
802 void SOSAccountSetToNew(SOSAccount* a)
803 {
804 secnotice("accountChange", "Setting Account to New");
805 int result = 0;
806
807 /* remove all syncable items */
808 result = do_keychain_delete_aks_bags(); (void) result;
809 secdebug("set to new", "result for deleting aks bags: %d", result);
810
811 result = do_keychain_delete_identities(); (void) result;
812 secdebug("set to new", "result for deleting identities: %d", result);
813
814 result = do_keychain_delete_lakitu(); (void) result;
815 secdebug("set to new", "result for deleting lakitu: %d", result);
816
817 result = do_keychain_delete_sbd(); (void) result;
818 secdebug("set to new", "result for deleting sbd: %d", result);
819
820 a.accountKeyIsTrusted = false;
821
822 if (a.user_private_timer) {
823 dispatch_source_cancel(a.user_private_timer);
824 a.user_private_timer = NULL;
825 xpc_transaction_end();
826
827 }
828 if (a.lock_notification_token != NOTIFY_TOKEN_INVALID) {
829 notify_cancel(a.lock_notification_token);
830 a.lock_notification_token = NOTIFY_TOKEN_INVALID;
831 }
832
833 // keeping gestalt;
834 // keeping factory;
835 // Live Notification
836 // change_blocks;
837 // update_interest_block;
838 // update_block;
839 SOSUnregisterTransportKeyParameter(a.key_transport);
840 SOSUnregisterTransportMessage(a.ids_message_transport);
841 SOSUnregisterTransportMessage(a.kvs_message_transport);
842 SOSUnregisterTransportCircle(a.circle_transport);
843
844 a.circle_transport = NULL;
845 a.kvs_message_transport = NULL;
846 a.ids_message_transport = NULL;
847
848 a.trust = nil;
849 a.trust = [[SOSAccountTrustClassic alloc]initWithRetirees:[NSMutableSet set] fpi:NULL circle:NULL departureCode:kSOSDepartureReasonError peerExpansion:[NSMutableDictionary dictionary]];
850
851 [a ensureFactoryCircles]; // Does rings too
852
853 // By resetting our expansion dictionary we've reset our UUID, so we'll be notified properly
854 SOSAccountEnsureUUID(a);
855 secnotice("circleop", "Setting account.key_interests_need_updating to true in SOSAccountSetToNew");
856 a.key_interests_need_updating = true;
857 }
858
859 bool SOSAccountIsNew(SOSAccount* account, CFErrorRef *error){
860 bool result = false;
861 SOSAccountTrustClassic* trust = account.trust;
862 if(account.accountKeyIsTrusted != false || trust.departureCode != kSOSNeverAppliedToCircle ||
863 CFSetGetCount((__bridge CFSetRef)trust.retirees) != 0)
864 return result;
865
866 if(trust.retirees != nil)
867 return result;
868 if(trust.expansion != nil)
869 return result;
870
871 if(account.user_private_timer != NULL || account.lock_notification_token != NOTIFY_TOKEN_INVALID)
872 return result;
873
874 result = true;
875
876 return result;
877 }
878
879 dispatch_queue_t SOSAccountGetQueue(SOSAccount* account) {
880 return account.queue;
881 }
882
883 void SOSAccountSetUserPublicTrustedForTesting(SOSAccount* account){
884 account.accountKeyIsTrusted = true;
885 }
886
887 -(SOSCCStatus) getCircleStatus:(CFErrorRef*) error
888 {
889 SOSCCStatus circleStatus = [self.trust getCircleStatus:error];
890 if (!SOSAccountHasPublicKey(self, error)) {
891 if(circleStatus == kSOSCCInCircle) {
892 if(error) {
893 CFReleaseNull(*error);
894 SOSCreateError(kSOSErrorPublicKeyAbsent, CFSTR("Public Key isn't available, this peer is in the circle, but invalid. The iCloud Password must be provided to keychain syncing subsystem to repair this."), NULL, error);
895 }
896 }
897 circleStatus = kSOSCCError;
898 }
899 return circleStatus;
900 }
901
902 bool SOSAccountScanForRetired(SOSAccount* account, SOSCircleRef circle, CFErrorRef *error) {
903
904 SOSAccountTrustClassic *trust = account.trust;
905 NSMutableSet* retirees = trust.retirees;
906 SOSCircleForEachRetiredPeer(circle, ^(SOSPeerInfoRef peer) {
907 CFSetSetValue((__bridge CFMutableSetRef) retirees, peer);
908 CFErrorRef cleanupError = NULL;
909 if (![account.trust cleanupAfterPeer:account.kvs_message_transport circleTransport:account.circle_transport seconds:RETIREMENT_FINALIZATION_SECONDS circle:circle cleanupPeer:peer err:&cleanupError]) {
910 secnotice("retirement", "Error cleaning up after peer, probably orphaned some stuff in KVS: (%@) – moving on", cleanupError);
911 }
912 CFReleaseSafe(cleanupError);
913 });
914 return true;
915 }
916
917 SOSCircleRef SOSAccountCloneCircleWithRetirement(SOSAccount* account, SOSCircleRef starting_circle, CFErrorRef *error) {
918 SOSCircleRef new_circle = SOSCircleCopyCircle(NULL, starting_circle, error);
919 SOSPeerInfoRef me = account.peerInfo;
920 bool iAmApplicant = me && SOSCircleHasApplicant(new_circle, me, NULL);
921
922 SOSAccountTrustClassic *trust = account.trust;
923 NSMutableSet* retirees = trust.retirees;
924
925 if(!new_circle) return NULL;
926 __block bool workDone = false;
927 if (retirees) {
928 CFSetForEach((__bridge CFSetRef)retirees, ^(const void* value) {
929 SOSPeerInfoRef pi = (SOSPeerInfoRef) value;
930 if (isSOSPeerInfo(pi)) {
931 SOSCircleUpdatePeerInfo(new_circle, pi);
932 workDone = true;
933 }
934 });
935 }
936
937 if(workDone && SOSCircleCountPeers(new_circle) == 0) {
938 SecKeyRef userPrivKey = SOSAccountGetPrivateCredential(account, error);
939
940 if(iAmApplicant) {
941 if(userPrivKey) {
942 secnotice("resetToOffering", "Reset to offering with last retirement and me as applicant");
943 if(!SOSCircleResetToOffering(new_circle, userPrivKey, account.fullPeerInfo, error) ||
944 ![account.trust addiCloudIdentity:new_circle key:userPrivKey err:error]){
945 CFReleaseNull(new_circle);
946 return NULL;
947 }
948 } else {
949 // Do nothing. We can't resetToOffering without a userPrivKey. If we were to resetToEmpty
950 // we won't push the result later in handleUpdateCircle. If we leave the circle as it is
951 // we have a chance to set things right with a SetCreds/Join sequence. This will cause
952 // handleUpdateCircle to return false.
953 CFReleaseNull(new_circle);
954 return NULL;
955 }
956 } else {
957 // This case is when we aren't an applicant and the circle is retirement-empty.
958 secnotice("circleOps", "Reset to empty with last retirement");
959 SOSCircleResetToEmpty(new_circle, NULL);
960 }
961 }
962
963 return new_circle;
964 }
965
966 //
967 // MARK: Circle Membership change notificaion
968 //
969
970 void SOSAccountAddChangeBlock(SOSAccount* a, SOSAccountCircleMembershipChangeBlock changeBlock) {
971 SOSAccountCircleMembershipChangeBlock copy = changeBlock;
972 [a.change_blocks addObject:copy];
973 }
974
975 void SOSAccountRemoveChangeBlock(SOSAccount* a, SOSAccountCircleMembershipChangeBlock changeBlock) {
976 [a.change_blocks removeObject:changeBlock];
977 }
978
979 void SOSAccountPurgeIdentity(SOSAccount* account) {
980 SOSAccountTrustClassic *trust = account.trust;
981 SOSFullPeerInfoRef identity = trust.fullPeerInfo;
982
983 if (identity) {
984 // Purge private key but don't return error if we can't.
985 CFErrorRef purgeError = NULL;
986 if (!SOSFullPeerInfoPurgePersistentKey(identity, &purgeError)) {
987 secwarning("Couldn't purge persistent key for %@ [%@]", identity, purgeError);
988 }
989 CFReleaseNull(purgeError);
990
991 trust.fullPeerInfo = nil;
992 }
993 }
994
995 bool sosAccountLeaveCircle(SOSAccount* account, SOSCircleRef circle, CFErrorRef* error) {
996 SOSAccountTrustClassic *trust = account.trust;
997 SOSFullPeerInfoRef identity = trust.fullPeerInfo;
998 NSMutableSet* retirees = trust.retirees;
999
1000 SOSFullPeerInfoRef fpi = identity;
1001 if(!fpi) return false;
1002
1003 CFErrorRef localError = NULL;
1004
1005 bool retval = false;
1006
1007 SOSPeerInfoRef retire_peer = SOSFullPeerInfoPromoteToRetiredAndCopy(fpi, &localError);
1008 if (!retire_peer) {
1009 secerror("Create ticket failed for peer %@: %@", fpi, localError);
1010 } else {
1011 // See if we need to repost the circle we could either be an applicant or a peer already in the circle
1012 if(SOSCircleHasApplicant(circle, retire_peer, NULL)) {
1013 // Remove our application if we have one.
1014 SOSCircleWithdrawRequest(circle, retire_peer, NULL);
1015 } else if (SOSCircleHasPeer(circle, retire_peer, NULL)) {
1016 if (SOSCircleUpdatePeerInfo(circle, retire_peer)) {
1017 CFErrorRef cleanupError = NULL;
1018 if (![account.trust cleanupAfterPeer:account.kvs_message_transport circleTransport:account.circle_transport seconds:RETIREMENT_FINALIZATION_SECONDS circle:circle cleanupPeer:retire_peer err:&cleanupError]) {
1019 secerror("Error cleanup up after peer (%@): %@", retire_peer, cleanupError);
1020 }
1021 CFReleaseSafe(cleanupError);
1022 }
1023 }
1024
1025 // Store the retirement record locally.
1026 CFSetAddValue((__bridge CFMutableSetRef)retirees, retire_peer);
1027
1028 trust.retirees = retirees;
1029
1030 // Write retirement to Transport
1031 CFErrorRef postError = NULL;
1032 if(![account.circle_transport postRetirement:SOSCircleGetName(circle) peer:retire_peer err:&postError]){
1033 secwarning("Couldn't post retirement (%@)", postError);
1034 }
1035 if(![account.circle_transport flushChanges:&postError]){
1036 secwarning("Couldn't flush retirement data (%@)", postError);
1037 }
1038 CFReleaseNull(postError);
1039 }
1040
1041 SOSAccountPurgeIdentity(account);
1042
1043 retval = true;
1044
1045 CFReleaseNull(localError);
1046 CFReleaseNull(retire_peer);
1047 return retval;
1048 }
1049
1050 bool sosAccountLeaveRing(SOSAccount* account, SOSRingRef ring, CFErrorRef* error) {
1051 SOSAccountTrustClassic *trust = account.trust;
1052 SOSFullPeerInfoRef identity = trust.fullPeerInfo;
1053
1054 SOSFullPeerInfoRef fpi = identity;
1055 if(!fpi) return false;
1056 SOSPeerInfoRef pi = SOSFullPeerInfoGetPeerInfo(fpi);
1057 CFStringRef peerID = SOSPeerInfoGetPeerID(pi);
1058
1059 CFErrorRef localError = NULL;
1060
1061 bool retval = false;
1062 bool writeRing = false;
1063 bool writePeerInfo = false;
1064
1065 if(SOSRingHasPeerID(ring, peerID)) {
1066 writePeerInfo = true;
1067 }
1068
1069 if(writePeerInfo || writeRing) {
1070 SOSRingWithdraw(ring, NULL, fpi, error);
1071 }
1072
1073 if (writeRing) {
1074 CFDataRef ring_data = SOSRingCopyEncodedData(ring, error);
1075
1076 if (ring_data) {
1077 [account.circle_transport kvsRingPostRing:SOSRingGetName(ring) ring:ring_data err:NULL];
1078 }
1079 CFReleaseNull(ring_data);
1080 }
1081 retval = true;
1082 CFReleaseNull(localError);
1083 return retval;
1084 }
1085
1086 bool SOSAccountPostDebugScope(SOSAccount* account, CFTypeRef scope, CFErrorRef *error) {
1087 bool result = false;
1088 if (account.circle_transport) {
1089 result = [account.circle_transport kvssendDebugInfo:kSOSAccountDebugScope debug:scope err:error];
1090 }
1091 return result;
1092 }
1093
1094 /*
1095 NSUbiquitousKeyValueStoreInitialSyncChange is only posted if there is any
1096 local value that has been overwritten by a distant value. If there is no
1097 conflict between the local and the distant values when doing the initial
1098 sync (e.g. if the cloud has no data stored or the client has not stored
1099 any data yet), you'll never see that notification.
1100
1101 NSUbiquitousKeyValueStoreInitialSyncChange implies an initial round trip
1102 with server but initial round trip with server does not imply
1103 NSUbiquitousKeyValueStoreInitialSyncChange.
1104 */
1105
1106
1107 //
1108 // MARK: Status summary
1109 //
1110
1111
1112 CFStringRef SOSAccountGetSOSCCStatusString(SOSCCStatus status) {
1113 switch(status) {
1114 case kSOSCCInCircle: return CFSTR("kSOSCCInCircle");
1115 case kSOSCCNotInCircle: return CFSTR("kSOSCCNotInCircle");
1116 case kSOSCCRequestPending: return CFSTR("kSOSCCRequestPending");
1117 case kSOSCCCircleAbsent: return CFSTR("kSOSCCCircleAbsent");
1118 case kSOSCCError: return CFSTR("kSOSCCError");
1119 }
1120 return CFSTR("kSOSCCError");
1121 }
1122 SOSCCStatus SOSAccountGetSOSCCStatusFromString(CFStringRef status) {
1123 if(CFEqualSafe(status, CFSTR("kSOSCCInCircle"))) {
1124 return kSOSCCInCircle;
1125 } else if(CFEqualSafe(status, CFSTR("kSOSCCInCircle"))) {
1126 return kSOSCCInCircle;
1127 } else if(CFEqualSafe(status, CFSTR("kSOSCCNotInCircle"))) {
1128 return kSOSCCNotInCircle;
1129 } else if(CFEqualSafe(status, CFSTR("kSOSCCRequestPending"))) {
1130 return kSOSCCRequestPending;
1131 } else if(CFEqualSafe(status, CFSTR("kSOSCCCircleAbsent"))) {
1132 return kSOSCCCircleAbsent;
1133 } else if(CFEqualSafe(status, CFSTR("kSOSCCError"))) {
1134 return kSOSCCError;
1135 }
1136 return kSOSCCError;
1137 }
1138
1139 //
1140 // MARK: Account Reset Circles
1141 //
1142
1143 // This needs to be called within a [trust modifyCircle()] block
1144
1145
1146 bool SOSAccountRemoveIncompleteiCloudIdentities(SOSAccount* account, SOSCircleRef circle, SecKeyRef privKey, CFErrorRef *error) {
1147 bool retval = false;
1148
1149 SOSAccountTrustClassic *trust = account.trust;
1150 SOSFullPeerInfoRef identity = trust.fullPeerInfo;
1151
1152 CFMutableSetRef iCloud2Remove = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
1153
1154 SOSCircleForEachActivePeer(circle, ^(SOSPeerInfoRef peer) {
1155 if(SOSPeerInfoIsCloudIdentity(peer)) {
1156 SOSFullPeerInfoRef icfpi = SOSFullPeerInfoCreateCloudIdentity(kCFAllocatorDefault, peer, NULL);
1157 if(!icfpi) {
1158 CFSetAddValue(iCloud2Remove, peer);
1159 }
1160 CFReleaseNull(icfpi);
1161 }
1162 });
1163
1164 if(CFSetGetCount(iCloud2Remove) > 0) {
1165 retval = true;
1166 SOSCircleRemovePeers(circle, privKey, identity, iCloud2Remove, error);
1167 }
1168 CFReleaseNull(iCloud2Remove);
1169 return retval;
1170 }
1171
1172 //
1173 // MARK: start backups
1174 //
1175
1176
1177 bool SOSAccountEnsureInBackupRings(SOSAccount* account) {
1178 __block bool result = false;
1179 __block CFErrorRef error = NULL;
1180 secnotice("backup", "Ensuring in rings");
1181
1182 NSData *backupKey = nil;
1183
1184 if(!account.backup_key){
1185 result = true;
1186 return result;
1187 }
1188
1189 backupKey = (__bridge_transfer NSData*)SOSPeerInfoV2DictionaryCopyData(account.peerInfo, sBackupKeyKey);
1190
1191 bool updateBackupKey = ![backupKey isEqual:account.backup_key];
1192
1193 if(updateBackupKey) {
1194 result = SOSAccountUpdatePeerInfo(account, CFSTR("Backup public key"), &error, ^bool(SOSFullPeerInfoRef fpi, CFErrorRef *error) {
1195 return SOSFullPeerInfoUpdateBackupKey(fpi, (__bridge CFDataRef)(account.backup_key), error);
1196 });
1197 if (!result) {
1198 secnotice("backupkey", "Failed to setup backup public key: %@", error);
1199 CFReleaseNull(error);
1200 return result;
1201 }
1202 }
1203 if(!account.backup_key)
1204 {
1205 if (!result) {
1206 secnotice("backupkey", "Failed to setup backup public key: %@", error);
1207 }
1208 CFReleaseNull(error);
1209 return result;
1210 }
1211 if(!SOSBSKBIsGoodBackupPublic((__bridge CFDataRef)account.backup_key, &error)){
1212 if (!result) {
1213 secnotice("backupkey", "Failed to setup backup public key: %@", error);
1214 }
1215 CFReleaseNull(error);
1216 return result;
1217 }
1218
1219 CFDataRef recoveryKeyBackFromRing = SOSAccountCopyRecoveryPublic(kCFAllocatorDefault, account, &error);
1220
1221 if(updateBackupKey || recoveryKeyBackFromRing) {
1222 // It's a good key, we're going with it. Stop backing up the old way.
1223 CFErrorRef localError = NULL;
1224 if (!SOSDeleteV0Keybag(&localError)) {
1225 secerror("Failed to delete v0 keybag: %@", localError);
1226 }
1227 CFReleaseNull(localError);
1228
1229 result = true;
1230
1231 // Setup backups the new way.
1232 SOSAccountForEachBackupView(account, ^(const void *value) {
1233 CFStringRef viewName = (CFStringRef)value;
1234 if(updateBackupKey || (recoveryKeyBackFromRing && !SOSAccountRecoveryKeyIsInBackupAndCurrentInView(account, viewName))) {
1235 result &= SOSAccountNewBKSBForView(account, viewName, &error);
1236 }
1237 });
1238 }
1239
1240 if (!result) {
1241 secnotice("backupkey", "Failed to setup backup public key: %@", error);
1242 }
1243 CFReleaseNull(error);
1244 return result;
1245 }
1246
1247 //
1248 // MARK: Recovery Public Key Functions
1249 //
1250
1251 bool SOSAccountRegisterRecoveryPublicKey(SOSAccountTransaction* txn, CFDataRef recovery_key, CFErrorRef *error){
1252 bool retval = SOSAccountSetRecoveryKey(txn.account, recovery_key, error);
1253 if(retval) secnotice("recovery", "successfully registered recovery public key");
1254 else secnotice("recovery", "could not register recovery public key: %@", *error);
1255 SOSClearErrorIfTrue(retval, error);
1256 return retval;
1257 }
1258
1259 bool SOSAccountClearRecoveryPublicKey(SOSAccountTransaction* txn, CFDataRef recovery_key, CFErrorRef *error){
1260 bool retval = SOSAccountRemoveRecoveryKey(txn.account, error);
1261 if(retval) secnotice("recovery", "RK Cleared");
1262 else secnotice("recovery", "Couldn't clear RK(%@)", *error);
1263 SOSClearErrorIfTrue(retval, error);
1264 return retval;
1265 }
1266
1267 CFDataRef SOSAccountCopyRecoveryPublicKey(SOSAccountTransaction* txn, CFErrorRef *error){
1268 CFDataRef result = NULL;
1269 result = SOSAccountCopyRecoveryPublic(kCFAllocatorDefault, txn.account, error);
1270 if(!result) secnotice("recovery", "Could not retrieve the recovery public key from the ring: %@", *error);
1271
1272 if (!isData(result)) {
1273 CFReleaseNull(result);
1274 }
1275 SOSClearErrorIfTrue(result != NULL, error);
1276
1277 return result;
1278 }
1279
1280 //
1281 // MARK: Joining
1282 //
1283
1284
1285 static bool SOSAccountJoinCircle(SOSAccountTransaction* aTxn, SecKeyRef user_key,
1286 bool use_cloud_peer, CFErrorRef* error) {
1287 SOSAccount* account = aTxn.account;
1288 SOSAccountTrustClassic *trust = account.trust;
1289 __block bool result = false;
1290 __block SOSFullPeerInfoRef cloud_full_peer = NULL;
1291 require_action_quiet(trust.trustedCircle, fail, SOSCreateErrorWithFormat(kSOSErrorPeerNotFound, NULL, error, NULL, CFSTR("Don't have circle when joining???")));
1292 require_quiet([account.trust ensureFullPeerAvailable:(__bridge CFDictionaryRef)account.gestalt deviceID:(__bridge CFStringRef)account.deviceID backupKey:(__bridge CFDataRef)account.backup_key err:error], fail);
1293 SOSFullPeerInfoRef myCirclePeer = trust.fullPeerInfo;
1294 if (SOSCircleCountPeers(trust.trustedCircle) == 0 || SOSAccountGhostResultsInReset(account)) {
1295 secnotice("resetToOffering", "Resetting circle to offering since there are no peers");
1296 // this also clears initial sync data
1297 result = [account.trust resetCircleToOffering:aTxn userKey:user_key err:error];
1298 } else {
1299 SOSAccountSetValue(account, kSOSUnsyncedViewsKey, kCFBooleanTrue, NULL);
1300 if (use_cloud_peer) {
1301 cloud_full_peer = SOSCircleCopyiCloudFullPeerInfoRef(trust.trustedCircle, NULL);
1302 }
1303 [account.trust modifyCircle:account.circle_transport err:error action:^bool(SOSCircleRef circle) {
1304 result = SOSAccountAddEscrowToPeerInfo(account, myCirclePeer, error);
1305 result &= SOSCircleRequestAdmission(circle, user_key, myCirclePeer, error);
1306 trust.departureCode = kSOSNeverLeftCircle;
1307 if(result && cloud_full_peer) {
1308 CFErrorRef localError = NULL;
1309 CFStringRef cloudid = SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(cloud_full_peer));
1310 require_quiet(cloudid, finish);
1311 require_quiet(SOSCircleHasActivePeerWithID(circle, cloudid, &localError), finish);
1312 require_quiet(SOSCircleAcceptRequest(circle, user_key, cloud_full_peer, SOSFullPeerInfoGetPeerInfo(myCirclePeer), &localError), finish);
1313 finish:
1314 if (localError){
1315 secerror("Failed to join with cloud identity: %@", localError);
1316 CFReleaseNull(localError);
1317 }
1318 }
1319 return result;
1320 }];
1321 if (use_cloud_peer) {
1322 SOSAccountUpdateOutOfSyncViews(aTxn, SOSViewsGetAllCurrent());
1323 }
1324 }
1325 fail:
1326 CFReleaseNull(cloud_full_peer);
1327 return result;
1328 }
1329
1330 static bool SOSAccountJoinCircles_internal(SOSAccountTransaction* aTxn, bool use_cloud_identity, CFErrorRef* error) {
1331 SOSAccount* account = aTxn.account;
1332 SOSAccountTrustClassic *trust = account.trust;
1333 bool success = false;
1334
1335 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1336 require_quiet(user_key, done); // Fail if we don't get one.
1337
1338 require_action_quiet(trust.trustedCircle, done, SOSErrorCreate(kSOSErrorNoCircle, error, NULL, CFSTR("No circle to join")));
1339
1340 if (trust.fullPeerInfo != NULL) {
1341 SOSPeerInfoRef myPeer = trust.peerInfo;
1342 success = SOSCircleHasPeer(trust.trustedCircle, myPeer, NULL);
1343 require_quiet(!success, done);
1344
1345 SOSCircleRemoveRejectedPeer(trust.trustedCircle, myPeer, NULL); // If we were rejected we should remove it now.
1346
1347 if (!SOSCircleHasApplicant(trust.trustedCircle, myPeer, NULL)) {
1348 secerror("Resetting my peer (ID: %@) for circle '%@' during application", SOSPeerInfoGetPeerID(myPeer), SOSCircleGetName(trust.trustedCircle));
1349
1350 trust.fullPeerInfo = NULL;
1351 }
1352 }
1353
1354 success = SOSAccountJoinCircle(aTxn, user_key, use_cloud_identity, error);
1355
1356 require_quiet(success, done);
1357
1358 trust.departureCode = kSOSNeverLeftCircle;
1359
1360 done:
1361 return success;
1362 }
1363
1364 bool SOSAccountJoinCircles(SOSAccountTransaction* aTxn, CFErrorRef* error) {
1365 secnotice("circleOps", "Normal path circle join (SOSAccountJoinCircles)");
1366 return SOSAccountJoinCircles_internal(aTxn, false, error);
1367 }
1368
1369 CFStringRef SOSAccountCopyDeviceID(SOSAccount* account, CFErrorRef *error){
1370 CFStringRef result = NULL;
1371 SOSAccountTrustClassic *trust = account.trust;
1372
1373 require_action_quiet(trust.fullPeerInfo, fail, SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("No peer for me")));
1374
1375 result = SOSPeerInfoCopyDeviceID(trust.peerInfo);
1376
1377 fail:
1378 return result;
1379 }
1380
1381 bool SOSAccountSetMyDSID(SOSAccountTransaction* txn, CFStringRef IDS, CFErrorRef* error){
1382 bool result = true;
1383 SOSAccount* account = txn.account;
1384 SOSAccountTrustClassic *trust = account.trust;
1385
1386 secdebug("IDS Transport", "We are setting our device ID: %@", IDS);
1387 if(IDS != NULL && (CFStringGetLength(IDS) > 0)){
1388 if(!trust.fullPeerInfo){
1389 account.deviceID = [[NSString alloc] initWithString:(__bridge NSString * _Nonnull)(IDS)];
1390 SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("No peer for me"));
1391 return result;
1392 }
1393 result = [trust modifyCircle:account.circle_transport err:error action:^bool(SOSCircleRef circle) {
1394
1395 SOSFullPeerInfoUpdateDeviceID(trust.fullPeerInfo, IDS, error);
1396 SOSFullPeerInfoUpdateTransportType(trust.fullPeerInfo, SOSTransportMessageTypeKVS, error);
1397 SOSFullPeerInfoUpdateTransportPreference(trust.fullPeerInfo, kCFBooleanFalse, error);
1398 SOSFullPeerInfoUpdateTransportFragmentationPreference(trust.fullPeerInfo, kCFBooleanTrue, error);
1399 SOSFullPeerInfoUpdateTransportAckModelPreference(trust.fullPeerInfo, kCFBooleanTrue, error);
1400 return SOSCircleHasPeer(circle, trust.peerInfo, NULL);
1401 }];
1402 }
1403 else
1404 result = false;
1405
1406 // Initiate sync with all IDS peers, since we just learned we can talk that way.
1407 SOSAccountForEachCirclePeerExceptMe(account, ^(SOSPeerInfoRef peer) {
1408 if (SOSPeerInfoShouldUseIDSTransport(account.peerInfo, peer)) {
1409 [txn requestSyncWith:(__bridge NSString*) SOSPeerInfoGetPeerID(peer)];
1410 }
1411 });
1412
1413 account.deviceID = [[NSString alloc] initWithString:(__bridge NSString * _Nonnull)(IDS)];
1414 return result;
1415 }
1416
1417 bool SOSAccountSendIDSTestMessage(SOSAccount* account, CFStringRef message, CFErrorRef *error){
1418 bool result = true;
1419 //construct message dictionary, circle -> peerID -> message
1420
1421 CFMutableDictionaryRef peerToMessage = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1422
1423 CFStringRef operationString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), kIDSSendOneMessage);
1424 CFDictionaryRef rawMessage = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
1425 kIDSOperationType, operationString,
1426 kIDSMessageToSendKey, CFSTR("send IDS test message"),
1427 NULL);
1428
1429 SOSAccountForEachCirclePeerExceptMe(account, ^(SOSPeerInfoRef peer) {
1430 CFDictionaryAddValue(peerToMessage, SOSPeerInfoGetPeerID(peer), rawMessage);
1431 });
1432
1433 result = [account.ids_message_transport SOSTransportMessageSendMessages:account.ids_message_transport pm:peerToMessage err:error];
1434
1435 CFReleaseNull(peerToMessage);
1436 CFReleaseNull(operationString);
1437 CFReleaseNull(rawMessage);
1438 return result;
1439 }
1440
1441 bool SOSAccountStartPingTest(SOSAccount* account, CFStringRef message, CFErrorRef *error){
1442 bool result = false;
1443 //construct message dictionary, circle -> peerID -> message
1444 SOSAccountTrustClassic *trust = account.trust;
1445 if(account.ids_message_transport == NULL)
1446 account.ids_message_transport = [[SOSMessageIDS alloc] initWithAccount:account andName:(__bridge NSString *)(SOSCircleGetName(trust.trustedCircle))];
1447
1448 require_quiet(account.ids_message_transport, fail);
1449 CFMutableDictionaryRef peerToMessage = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1450
1451 CFStringRef operationString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), kIDSStartPingTestMessage);
1452 CFDictionaryRef rawMessage = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
1453 kIDSOperationType, operationString,
1454 kIDSMessageToSendKey, CFSTR("send IDS test message"),
1455 NULL);
1456
1457
1458 SOSAccountForEachCirclePeerExceptMe(account, ^(SOSPeerInfoRef peer) {
1459 CFDictionaryAddValue(peerToMessage, SOSPeerInfoGetPeerID(peer), rawMessage);
1460 });
1461
1462 result = [account.ids_message_transport SOSTransportMessageSendMessages:account.ids_message_transport pm:peerToMessage err:error];
1463
1464 CFReleaseNull(peerToMessage);
1465 CFReleaseNull(rawMessage);
1466 CFReleaseNull(operationString);
1467 fail:
1468 return result;
1469 }
1470
1471 bool SOSAccountRetrieveDeviceIDFromKeychainSyncingOverIDSProxy(SOSAccount* account, CFErrorRef *error){
1472 bool result = true;
1473
1474 __block bool success = true;
1475 __block CFErrorRef localError = NULL;
1476 dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
1477
1478 SOSCloudKeychainGetIDSDeviceID(^(CFDictionaryRef returnedValues, CFErrorRef sync_error){
1479 success = (sync_error == NULL);
1480 if (!success) {
1481 CFRetainAssign(localError, sync_error);
1482 }
1483
1484 dispatch_semaphore_signal(wait_for);
1485 });
1486
1487 dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER);
1488
1489 if(!success && localError != NULL && error != NULL){
1490 secerror("Could not ask KeychainSyncingOverIDSProxy for Device ID: %@", localError);
1491 *error = localError;
1492 result = false;
1493 }
1494 else{
1495 secdebug("IDS Transport", "Attempting to retrieve the IDS Device ID");
1496 }
1497 return result;
1498 }
1499
1500 bool SOSAccountJoinCirclesAfterRestore(SOSAccountTransaction* aTxn, CFErrorRef* error) {
1501 secnotice("circleOps", "Joining after restore (SOSAccountJoinCirclesAfterRestore)");
1502 return SOSAccountJoinCircles_internal(aTxn, true, error);
1503 }
1504
1505 bool SOSAccountRemovePeersFromCircle(SOSAccount* account, CFArrayRef peers, CFErrorRef* error)
1506 {
1507 bool result = false;
1508 CFMutableSetRef peersToRemove = NULL;
1509 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1510 if(!user_key){
1511 secnotice("circleOps", "Can't remove without userKey");
1512 return result;
1513 }
1514 SOSFullPeerInfoRef me_full = account.fullPeerInfo;
1515 SOSPeerInfoRef me = account.peerInfo;
1516 if(!(me_full && me))
1517 {
1518 secnotice("circleOps", "Can't remove without being active peer");
1519 SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("Can't remove without being active peer"));
1520 return result;
1521 }
1522
1523 result = true; // beyond this point failures would be rolled up in AccountModifyCircle.
1524
1525 peersToRemove = CFSetCreateMutableForSOSPeerInfosByIDWithArray(kCFAllocatorDefault, peers);
1526 if(!peersToRemove)
1527 {
1528 CFReleaseNull(peersToRemove);
1529 secnotice("circleOps", "No peerSet to remove");
1530 return result;
1531 }
1532
1533 // If we're one of the peers expected to leave - note that and then remove ourselves from the set (different handling).
1534 bool leaveCircle = CFSetContainsValue(peersToRemove, me);
1535 CFSetRemoveValue(peersToRemove, me);
1536
1537 result &= [account.trust modifyCircle:account.circle_transport err:error action:^(SOSCircleRef circle) {
1538 bool success = false;
1539
1540 if(CFSetGetCount(peersToRemove) != 0) {
1541 require_quiet(SOSCircleRemovePeers(circle, user_key, me_full, peersToRemove, error), done);
1542 success = SOSAccountGenerationSignatureUpdate(account, error);
1543 } else success = true;
1544
1545 if (success && leaveCircle) {
1546 secnotice("circleOps", "Leaving circle by client request (SOSAccountRemovePeersFromCircle)");
1547 success = sosAccountLeaveCircle(account, circle, error);
1548 }
1549
1550 done:
1551 return success;
1552
1553 }];
1554
1555 if(result) {
1556 CFStringSetPerformWithDescription(peersToRemove, ^(CFStringRef description) {
1557 secnotice("circleOps", "Removed Peers from circle %@", description);
1558 });
1559 }
1560
1561 CFReleaseNull(peersToRemove);
1562 return result;
1563 }
1564
1565
1566 bool SOSAccountBail(SOSAccount* account, uint64_t limit_in_seconds, CFErrorRef* error) {
1567 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1568 dispatch_group_t group = dispatch_group_create();
1569 SOSAccountTrustClassic *trust = account.trust;
1570 __block bool result = false;
1571 secnotice("circle", "Attempting to leave circle - best effort - in %llu seconds\n", limit_in_seconds);
1572 // Add a task to the group
1573 dispatch_group_async(group, queue, ^{
1574 [trust modifyCircle:account.circle_transport err:error action:^(SOSCircleRef circle) {
1575 secnotice("circleOps", "Leaving circle by client request (Bail)");
1576 return sosAccountLeaveCircle(account, circle, error);
1577 }];
1578 });
1579 dispatch_time_t milestone = dispatch_time(DISPATCH_TIME_NOW, limit_in_seconds * NSEC_PER_SEC);
1580 dispatch_group_wait(group, milestone);
1581
1582 trust.departureCode = kSOSWithdrewMembership;
1583
1584 return result;
1585 }
1586
1587
1588 //
1589 // MARK: Application
1590 //
1591
1592 static void for_each_applicant_in_each_circle(SOSAccount* account, CFArrayRef peer_infos,
1593 bool (^action)(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer)) {
1594 SOSAccountTrustClassic *trust = account.trust;
1595
1596 SOSPeerInfoRef me = trust.peerInfo;
1597 CFErrorRef peer_error = NULL;
1598 if (trust.trustedCircle && me &&
1599 SOSCircleHasPeer(trust.trustedCircle, me, &peer_error)) {
1600 [account.trust modifyCircle:account.circle_transport err:NULL action:^(SOSCircleRef circle) {
1601 __block bool modified = false;
1602 CFArrayForEach(peer_infos, ^(const void *value) {
1603 SOSPeerInfoRef peer = (SOSPeerInfoRef) value;
1604 if (isSOSPeerInfo(peer) && SOSCircleHasApplicant(circle, peer, NULL)) {
1605 if (action(circle, trust.fullPeerInfo, peer)) {
1606 modified = true;
1607 }
1608 }
1609 });
1610 return modified;
1611 }];
1612 }
1613 if (peer_error)
1614 secerror("Got error in SOSCircleHasPeer: %@", peer_error);
1615 CFReleaseSafe(peer_error); // TODO: We should be accumulating errors here.
1616 }
1617
1618 bool SOSAccountAcceptApplicants(SOSAccount* account, CFArrayRef applicants, CFErrorRef* error) {
1619 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1620 if (!user_key)
1621 return false;
1622
1623 __block bool success = true;
1624 __block int64_t num_peers = 0;
1625
1626 for_each_applicant_in_each_circle(account, applicants, ^(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer) {
1627 bool accepted = SOSCircleAcceptRequest(circle, user_key, myCirclePeer, peer, error);
1628 if (!accepted)
1629 success = false;
1630 else
1631 num_peers = MAX(num_peers, SOSCircleCountPeers(circle));
1632 return accepted;
1633 });
1634
1635 return success;
1636 }
1637
1638 bool SOSAccountRejectApplicants(SOSAccount* account, CFArrayRef applicants, CFErrorRef* error) {
1639 __block bool success = true;
1640 __block int64_t num_peers = 0;
1641
1642 for_each_applicant_in_each_circle(account, applicants, ^(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer) {
1643 bool rejected = SOSCircleRejectRequest(circle, myCirclePeer, peer, error);
1644 if (!rejected)
1645 success = false;
1646 else
1647 num_peers = MAX(num_peers, SOSCircleCountPeers(circle));
1648 return rejected;
1649 });
1650
1651 return success;
1652 }
1653
1654
1655 CFStringRef SOSAccountCopyIncompatibilityInfo(SOSAccount* account, CFErrorRef* error) {
1656 return CFSTR("We're compatible, go away");
1657 }
1658
1659 enum DepartureReason SOSAccountGetLastDepartureReason(SOSAccount* account, CFErrorRef* error) {
1660 SOSAccountTrustClassic *trust = account.trust;
1661 return trust.departureCode;
1662 }
1663
1664 void SOSAccountSetLastDepartureReason(SOSAccount* account, enum DepartureReason reason) {
1665 SOSAccountTrustClassic *trust = account.trust;
1666 trust.departureCode = reason;
1667 }
1668
1669
1670 CFArrayRef SOSAccountCopyGeneration(SOSAccount* account, CFErrorRef *error) {
1671 CFArrayRef result = NULL;
1672 CFNumberRef generation = NULL;
1673 SOSAccountTrustClassic *trust = account.trust;
1674
1675 require_quiet(SOSAccountHasPublicKey(account, error), fail);
1676 require_action_quiet(trust.trustedCircle, fail, SOSErrorCreate(kSOSErrorNoCircle, error, NULL, CFSTR("No circle")));
1677
1678 generation = (CFNumberRef)SOSCircleGetGeneration(trust.trustedCircle);
1679 result = CFArrayCreateForCFTypes(kCFAllocatorDefault, generation, NULL);
1680
1681 fail:
1682 return result;
1683 }
1684
1685 bool SOSValidateUserPublic(SOSAccount* account, CFErrorRef *error) {
1686 if (!SOSAccountHasPublicKey(account, error))
1687 return NULL;
1688
1689 return account.accountKeyIsTrusted;
1690 }
1691
1692 bool SOSAccountEnsurePeerRegistration(SOSAccount* account, CFErrorRef *error) {
1693 // TODO: this result is never set or used
1694 bool result = true;
1695 SOSAccountTrustClassic *trust = account.trust;
1696
1697 secnotice("updates", "Ensuring peer registration.");
1698
1699 if(!trust.trustedCircle || !trust.fullPeerInfo || !account.accountKeyIsTrusted)
1700 return result;
1701
1702 // If we are not in the circle, there is no point in setting up peers
1703 if(!SOSAccountIsMyPeerActive(account, NULL))
1704 return result;
1705
1706 // This code only uses the SOSFullPeerInfoRef for two things:
1707 // - Finding out if this device is in the trusted circle
1708 // - Using the peerID for this device to see if the current peer is "me"
1709 // - It is used indirectly by passing trust.fullPeerInfo to SOSEngineInitializePeerCoder
1710
1711 CFStringRef my_id = SOSPeerInfoGetPeerID(trust.peerInfo);
1712
1713 SOSCircleForEachValidSyncingPeer(trust.trustedCircle, account.accountKey, ^(SOSPeerInfoRef peer) {
1714 if (!SOSPeerInfoPeerIDEqual(peer, my_id)) {
1715 CFErrorRef localError = NULL;
1716
1717 SOSMessage *messageTransport = SOSPeerInfoHasDeviceID(peer) ? account.ids_message_transport : account.kvs_message_transport;
1718
1719 SOSEngineInitializePeerCoder((SOSEngineRef)[messageTransport SOSTransportMessageGetEngine], trust.fullPeerInfo, peer, &localError);
1720 if (localError)
1721 secnotice("updates", "can't initialize transport for peer %@ with %@ (%@)", peer, trust.fullPeerInfo, localError);
1722 CFReleaseSafe(localError);
1723 }
1724 });
1725
1726 //Initialize our device ID
1727 [account.ids_message_transport SOSTransportMessageIDSGetIDSDeviceID:account];
1728
1729 return result;
1730 }
1731
1732 //
1733 // Value manipulation
1734 //
1735
1736 CFTypeRef SOSAccountGetValue(SOSAccount* account, CFStringRef key, CFErrorRef *error) {
1737 SOSAccountTrustClassic *trust = account.trust;
1738 if (!trust.expansion) {
1739 return NULL;
1740 }
1741 return (__bridge CFTypeRef)([trust.expansion objectForKey:(__bridge NSString* _Nonnull)(key)]);
1742 }
1743
1744 bool SOSAccountAddEscrowRecords(SOSAccount* account, CFStringRef dsid, CFDictionaryRef record, CFErrorRef *error){
1745 CFMutableDictionaryRef escrowRecords = (CFMutableDictionaryRef)SOSAccountGetValue(account, kSOSEscrowRecord, error);
1746 CFMutableDictionaryRef escrowCopied = NULL;
1747 bool success = false;
1748
1749 if(isDictionary(escrowRecords) && escrowRecords != NULL)
1750 escrowCopied = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, CFDictionaryGetCount(escrowRecords), escrowRecords);
1751 else
1752 escrowCopied = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1753
1754 CFDictionaryAddValue(escrowCopied, dsid, record);
1755 SOSAccountSetValue(account, kSOSEscrowRecord, escrowCopied, error);
1756
1757 if(*error == NULL)
1758 success = true;
1759
1760 CFReleaseNull(escrowCopied);
1761
1762 return success;
1763
1764 }
1765
1766 bool SOSAccountAddEscrowToPeerInfo(SOSAccount* account, SOSFullPeerInfoRef myPeer, CFErrorRef *error){
1767 bool success = false;
1768
1769 CFDictionaryRef escrowRecords = SOSAccountGetValue(account, kSOSEscrowRecord, error);
1770 success = SOSFullPeerInfoReplaceEscrowRecords(myPeer, escrowRecords, error);
1771
1772 return success;
1773 }
1774
1775 bool SOSAccountCheckPeerAvailability(SOSAccount* account, CFErrorRef *error)
1776 {
1777 CFStringRef operationString = NULL;
1778 CFDictionaryRef rawMessage = NULL;
1779 CFMutableSetRef peers = NULL;
1780 CFMutableDictionaryRef peerList = NULL;
1781 char* message = NULL;
1782 bool result = false;
1783 SOSAccountTrustClassic* trust = account.trust;
1784 if(account.ids_message_transport == NULL)
1785 account.ids_message_transport = [[SOSMessageIDS alloc] initWithAccount:account andName:(__bridge NSString *)SOSCircleGetName(trust.trustedCircle)];
1786
1787 if(!account.ids_message_transport)
1788 return result;
1789
1790 //adding message type kIDSPeerAvailability so KeychainSyncingOverIDSProxy does not send this message as a keychain item
1791
1792 operationString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), kIDSPeerAvailability);
1793 rawMessage = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
1794 kIDSOperationType, operationString,
1795 kIDSMessageToSendKey, CFSTR("checking peers"),
1796 NULL);
1797
1798 peerList = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1799 SOSCircleRef circle = trust.trustedCircle;
1800
1801 //check each peer to make sure they have the right view set enabled
1802 CFSetRef mySubSet = SOSViewsGetV0SubviewSet();
1803 SOSCircleForEachValidPeer(circle, account.accountKey, ^(SOSPeerInfoRef peer) {
1804 if(!CFEqualSafe(peer, account.peerInfo)){
1805 CFMutableSetRef peerViews = SOSPeerInfoCopyEnabledViews(peer);
1806 CFSetRef intersectSets = CFSetCreateIntersection(kCFAllocatorDefault, mySubSet, peerViews);
1807 if(CFEqualSafe(intersectSets, mySubSet)){
1808 CFStringRef deviceID = SOSPeerInfoCopyDeviceID(peer);
1809 if(deviceID != NULL)
1810 CFDictionaryAddValue(peerList, SOSPeerInfoGetPeerID(peer), rawMessage);
1811 CFReleaseNull(deviceID);
1812 }
1813 CFReleaseNull(peerViews);
1814 CFReleaseNull(intersectSets);
1815 }
1816 });
1817
1818 require_quiet(CFDictionaryGetCount(peerList) > 0 , fail);
1819
1820 result = [account.ids_message_transport SOSTransportMessageSendMessages:account.ids_message_transport pm:peerList err:error];
1821
1822 fail:
1823 CFReleaseNull(rawMessage);
1824 CFReleaseNull(operationString);
1825 CFReleaseNull(peerList);
1826 CFReleaseNull(peers);
1827 free(message);
1828 return result;
1829 }
1830
1831
1832 void SOSAccountRecordRetiredPeersInCircle(SOSAccount* account) {
1833 if (![account.trust isInCircle:NULL])
1834 return;
1835 SOSAccountTrustClassic *trust = account.trust;
1836 [trust modifyCircle:account.circle_transport err:NULL action:^bool (SOSCircleRef circle) {
1837 __block bool updated = false;
1838 CFSetForEach((__bridge CFMutableSetRef)trust.retirees, ^(CFTypeRef element){
1839 SOSPeerInfoRef retiree = asSOSPeerInfo(element);
1840
1841 if (retiree && SOSCircleUpdatePeerInfo(circle, retiree)) {
1842 updated = true;
1843 secnotice("retirement", "Updated retired peer %@ in %@", retiree, circle);
1844 CFErrorRef cleanupError = NULL;
1845 if (![account.trust cleanupAfterPeer:account.kvs_message_transport circleTransport:account.circle_transport seconds:RETIREMENT_FINALIZATION_SECONDS circle:circle cleanupPeer:retiree err:&cleanupError])
1846 secerror("Error cleanup up after peer (%@): %@", retiree, cleanupError);
1847 CFReleaseSafe(cleanupError);
1848 }
1849 });
1850 return updated;
1851 }];
1852 }
1853
1854 static const uint64_t maxTimeToWaitInSeconds = 30ull * NSEC_PER_SEC;
1855
1856 static CFDictionaryRef SOSAccountGetObjectsFromCloud(dispatch_queue_t processQueue, CFErrorRef *error)
1857 {
1858 __block CFTypeRef object = NULL;
1859
1860 dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
1861 dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds);
1862
1863 CloudKeychainReplyBlock replyBlock =
1864 ^ (CFDictionaryRef returnedValues, CFErrorRef error)
1865 {
1866 object = returnedValues;
1867 if (object)
1868 CFRetain(object);
1869 if (error)
1870 {
1871 secerror("SOSCloudKeychainGetObjectsFromCloud returned error: %@", error);
1872 }
1873 dispatch_semaphore_signal(waitSemaphore);
1874 };
1875
1876 SOSCloudKeychainGetAllObjectsFromCloud(processQueue, replyBlock);
1877
1878 dispatch_semaphore_wait(waitSemaphore, finishTime);
1879 if (object && (CFGetTypeID(object) == CFNullGetTypeID())) // return a NULL instead of a CFNull
1880 {
1881 CFRelease(object);
1882 object = NULL;
1883 }
1884 return asDictionary(object, NULL); // don't propogate "NULL is not a dictionary" errors
1885 }
1886
1887
1888 static void SOSAccountRemoveKVSKeys(SOSAccount* account, NSArray* keysToRemove, dispatch_queue_t processQueue)
1889 {
1890 CFStringRef uuid = SOSAccountCopyUUID(account);
1891 dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
1892 dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds);
1893
1894 CloudKeychainReplyBlock replyBlock = ^ (CFDictionaryRef returnedValues, CFErrorRef error){
1895 if (error){
1896 secerror("SOSCloudKeychainRemoveKeys returned error: %@", error);
1897 }
1898 dispatch_semaphore_signal(waitSemaphore);
1899 };
1900
1901 SOSCloudKeychainRemoveKeys((__bridge CFArrayRef)(keysToRemove), uuid, processQueue, replyBlock);
1902 dispatch_semaphore_wait(waitSemaphore, finishTime);
1903 CFReleaseNull(uuid);
1904 }
1905
1906 static void SOSAccountWriteLastCleanupTimestampToKVS(SOSAccount* account)
1907 {
1908 NSMutableDictionary *writeTimestamp = [NSMutableDictionary dictionary];
1909
1910 CFMutableStringRef timeDescription = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, CFSTR("["));
1911 CFAbsoluteTime currentTimeAndDate = CFAbsoluteTimeGetCurrent();
1912
1913 withStringOfAbsoluteTime(currentTimeAndDate, ^(CFStringRef decription) {
1914 CFStringAppend(timeDescription, decription);
1915 });
1916 CFStringAppend(timeDescription, CFSTR("]"));
1917
1918 [writeTimestamp setObject:(__bridge NSString*)(timeDescription) forKey:(__bridge NSString*)kSOSKVSLastCleanupTimestampKey];
1919 CFReleaseNull(timeDescription);
1920
1921 dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
1922 dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds);
1923 dispatch_queue_t processQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1924
1925 CloudKeychainReplyBlock replyBlock = ^ (CFDictionaryRef returnedValues, CFErrorRef error){
1926 if (error){
1927 secerror("SOSCloudKeychainPutObjectsInCloud returned error: %@", error);
1928 }
1929 dispatch_semaphore_signal(waitSemaphore);
1930 };
1931
1932 SOSCloudKeychainPutObjectsInCloud((__bridge CFDictionaryRef)(writeTimestamp), processQueue, replyBlock);
1933 dispatch_semaphore_wait(waitSemaphore, finishTime);
1934 }
1935
1936 //Get all the key/values in KVS and remove old entries
1937 bool SOSAccountCleanupAllKVSKeys(SOSAccount* account, CFErrorRef* error)
1938 {
1939 dispatch_queue_t processQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1940
1941 NSDictionary *keysAndValues = (__bridge_transfer NSDictionary*)SOSAccountGetObjectsFromCloud(processQueue, error);
1942 NSMutableArray *peerIDs = [NSMutableArray array];
1943 NSMutableArray *keysToRemove = [NSMutableArray array];
1944
1945 CFArrayRef peers = SOSAccountCopyActiveValidPeers(account, error);
1946 CFArrayForEach(peers, ^(const void *value) {
1947 SOSPeerInfoRef peer = (SOSPeerInfoRef)value;
1948 NSString* peerID = (__bridge NSString*) SOSPeerInfoGetPeerID(peer);
1949
1950 //any peerid that is not ours gets added
1951 if(![[account.trust peerID] isEqualToString:peerID])
1952 [peerIDs addObject:peerID];
1953 });
1954 CFReleaseNull(peers);
1955
1956 [keysAndValues enumerateKeysAndObjectsUsingBlock:^(NSString * KVSKey, NSNumber * KVSValue, BOOL *stop) {
1957 __block bool keyMatchesPeerID = false;
1958
1959 //checks for full peer ids
1960 [peerIDs enumerateObjectsUsingBlock:^(id _Nonnull PeerID, NSUInteger idx, BOOL * _Nonnull stop) {
1961 //if key contains peerid of one active peer
1962 if([KVSKey containsString:PeerID]){
1963 secnotice("key-cleanup","key: %@", KVSKey);
1964 keyMatchesPeerID = true;
1965 }
1966 }];
1967 if((([KVSKey hasPrefix:@"ak"] || [KVSKey hasPrefix:@"-ak"]) && !keyMatchesPeerID)
1968 || [KVSKey hasPrefix:@"po"])
1969 [keysToRemove addObject:KVSKey];
1970 }];
1971
1972 secnotice("key-cleanup", "message keys that we should remove! %@", keysToRemove);
1973 secnotice("key-cleanup", "total keys: %lu, cleaning up %lu", (unsigned long)[keysAndValues count], (unsigned long)[keysToRemove count]);
1974
1975 SOSAccountRemoveKVSKeys(account, keysToRemove, processQueue);
1976
1977 //add last cleanup timestamp
1978 SOSAccountWriteLastCleanupTimestampToKVS(account);
1979 return true;
1980
1981 }
1982
1983 bool SOSAccountPopulateKVSWithBadKeys(SOSAccount* account, CFErrorRef* error) {
1984
1985 NSMutableDictionary *testKeysAndValues = [NSMutableDictionary dictionary];
1986 [testKeysAndValues setObject:@"deadbeef" forKey:@"-ak|asdfjkl;asdfjk;"];
1987 [testKeysAndValues setObject:@"foobar" forKey:@"ak|asdfasdfasdf:qwerqwerqwer"];
1988 [testKeysAndValues setObject:@"oldhistorycircle" forKey:@"poak|asdfasdfasdfasdf"];
1989 [testKeysAndValues setObject:@"oldhistorycircle" forKey:@"po|asdfasdfasdfasdfasdfasdf"];
1990 [testKeysAndValues setObject:@"oldhistorycircle" forKey:@"k>KeyParm"];
1991
1992 dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
1993 dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds);
1994 dispatch_queue_t processQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1995
1996 CloudKeychainReplyBlock replyBlock = ^ (CFDictionaryRef returnedValues, CFErrorRef error){
1997 if (error){
1998 secerror("SOSCloudKeychainPutObjectsInCloud returned error: %@", error);
1999 }
2000 dispatch_semaphore_signal(waitSemaphore);
2001 };
2002
2003 SOSCloudKeychainPutObjectsInCloud((__bridge CFDictionaryRef)(testKeysAndValues), processQueue, replyBlock);
2004 dispatch_semaphore_wait(waitSemaphore, finishTime);
2005
2006 return true;
2007 }
2008
2009 SOSPeerInfoRef SOSAccountCopyApplication(SOSAccount* account, CFErrorRef* error) {
2010 SOSPeerInfoRef applicant = NULL;
2011 SOSAccountTrustClassic *trust = account.trust;
2012 SecKeyRef userKey = SOSAccountGetPrivateCredential(account, error);
2013 if(!userKey) return false;
2014 if(![trust ensureFullPeerAvailable:(__bridge CFDictionaryRef)(account.gestalt) deviceID:(__bridge CFStringRef)(account.deviceID) backupKey:(__bridge CFDataRef)(account.backup_key) err:error])
2015 return applicant;
2016 if(!SOSFullPeerInfoPromoteToApplication(trust.fullPeerInfo, userKey, error))
2017 return applicant;
2018 applicant = SOSPeerInfoCreateCopy(kCFAllocatorDefault, trust.peerInfo, error);
2019
2020 return applicant;
2021 }
2022
2023
2024 static void
2025 AddStrippedResults(NSMutableArray *results, NSArray *keychainItems, NSMutableSet *seenUUID, bool authoriative)
2026 {
2027 [keychainItems enumerateObjectsUsingBlock:^(NSDictionary* keychainItem, NSUInteger idx, BOOL * _Nonnull stop) {
2028 NSString* parentUUID = keychainItem[(id)kSecAttrPath];
2029 NSString* viewUUID = keychainItem[(id)kSecAttrAccount];
2030 NSString *viewName = [keychainItem objectForKey:(id)kSecAttrServer];
2031
2032 if (parentUUID == NULL || viewUUID == NULL || viewName == NULL)
2033 return;
2034
2035 if([parentUUID isEqualToString:viewUUID] || authoriative){
2036
2037 /* check if we already have this entry */
2038 if ([seenUUID containsObject:viewUUID])
2039 return;
2040
2041 NSData* v_data = [keychainItem objectForKey:(id)kSecValueData];
2042 NSData *key = [[NSData alloc] initWithBase64EncodedData:v_data options:0];
2043
2044 if (key == NULL)
2045 return;
2046
2047 secnotice("piggy", "fetched TLK %@ with name %@", viewName, viewUUID);
2048
2049 NSMutableDictionary* strippedDown = [@{
2050 (id)kSecValueData : key,
2051 (id)kSecAttrServer : viewName,
2052 (id)kSecAttrAccount : viewUUID
2053 } mutableCopy];
2054 if (authoriative)
2055 strippedDown[@"auth"] = @YES;
2056
2057 [results addObject:strippedDown];
2058 [seenUUID addObject:viewUUID];
2059 }
2060 }];
2061 }
2062
2063 static void
2064 AddViewManagerResults(NSMutableArray *results, NSMutableSet *seenUUID)
2065 {
2066 #if OCTAGON
2067 CKKSViewManager* manager = [CKKSViewManager manager];
2068
2069 NSDictionary<NSString *,NSString *> *items = [manager activeTLKs];
2070
2071 for (NSString *view in items) {
2072 NSString *uuid = items[view];
2073 NSDictionary *query = @{
2074 (id)kSecClass : (id)kSecClassInternetPassword,
2075 (id)kSecAttrNoLegacy : @YES,
2076 (id)kSecAttrAccessGroup : @"com.apple.security.ckks",
2077 (id)kSecAttrAccount : uuid,
2078 (id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
2079 (id)kSecMatchLimit : (id)kSecMatchLimitAll,
2080 (id)kSecReturnAttributes: @YES,
2081 (id)kSecReturnData: @YES,
2082 };
2083 CFTypeRef result = NULL;
2084 if (SecItemCopyMatching((__bridge CFDictionaryRef)query, &result) == 0) {
2085 AddStrippedResults(results, (__bridge NSArray*)result, seenUUID, true);
2086 }
2087 CFReleaseNull(result);
2088 }
2089 #endif
2090 }
2091
2092
2093 NSMutableArray*
2094 SOSAccountGetAllTLKs(void)
2095 {
2096 CFTypeRef result = NULL;
2097 NSMutableArray* results = [NSMutableArray array];
2098 NSMutableSet *seenUUID = [NSMutableSet set];
2099
2100 // first use the TLK from the view manager
2101 AddViewManagerResults(results, seenUUID);
2102
2103 //try to grab tlk-piggy items
2104 NSDictionary* query = @{
2105 (id)kSecClass : (id)kSecClassInternetPassword,
2106 (id)kSecAttrNoLegacy : @YES,
2107 (id)kSecAttrAccessGroup : @"com.apple.security.ckks",
2108 (id)kSecAttrDescription: @"tlk",
2109 (id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
2110 (id)kSecMatchLimit : (id)kSecMatchLimitAll,
2111 (id)kSecReturnAttributes: @YES,
2112 (id)kSecReturnData: @YES,
2113 };
2114
2115 if (SecItemCopyMatching((__bridge CFDictionaryRef)query, &result) == 0) {
2116 AddStrippedResults(results, (__bridge NSArray*)result, seenUUID, false);
2117 }
2118 CFReleaseNull(result);
2119
2120 //try to grab tlk-piggy items
2121 query = @{
2122 (id)kSecClass : (id)kSecClassInternetPassword,
2123 (id)kSecAttrNoLegacy : @YES,
2124 (id)kSecAttrAccessGroup : @"com.apple.security.ckks",
2125 (id)kSecAttrDescription: @"tlk-piggy",
2126 (id)kSecAttrSynchronizable : (id)kSecAttrSynchronizableAny,
2127 (id)kSecMatchLimit : (id)kSecMatchLimitAll,
2128 (id)kSecReturnAttributes: @YES,
2129 (id)kSecReturnData: @YES,
2130 };
2131
2132 if (SecItemCopyMatching((__bridge CFDictionaryRef)query, &result) == 0) {
2133 AddStrippedResults(results, (__bridge NSArray*)result, seenUUID, false);
2134 }
2135 CFReleaseNull(result);
2136
2137 secnotice("piggy", "Found %d TLKs", (int)[results count]);
2138
2139 return results;
2140 }
2141
2142 static uint8_t* encode_tlk(kTLKTypes type, NSString *name, NSData *keychainData, NSData* uuid,
2143 const uint8_t *der, uint8_t *der_end)
2144 {
2145 if (type != kTLKUnknown) {
2146 return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
2147 piggy_encode_data(keychainData, der,
2148 piggy_encode_data(uuid, der,
2149 ccder_encode_uint64((uint64_t)type, der, der_end))));
2150 } else {
2151 return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
2152 piggy_encode_data(keychainData, der,
2153 piggy_encode_data(uuid, der,
2154 der_encode_string((__bridge CFStringRef)name, NULL, der, der_end))));
2155 }
2156 }
2157
2158 static uint8_t* piggy_encode_data(NSData* data,
2159 const uint8_t *der, uint8_t *der_end)
2160 {
2161 return ccder_encode_tl(CCDER_OCTET_STRING, data.length, der,
2162 ccder_encode_body(data.length, data.bytes, der, der_end));
2163
2164 }
2165
2166 static kTLKTypes
2167 name2type(NSString *view)
2168 {
2169 if ([view isEqualToString:@"Manatee"])
2170 return kTLKManatee;
2171 else if ([view isEqualToString:@"Engram"])
2172 return kTLKEngram;
2173 else if ([view isEqualToString:@"AutoUnlock"])
2174 return kTLKAutoUnlock;
2175 if ([view isEqualToString:@"Health"])
2176 return kTLKHealth;
2177 return kTLKUnknown;
2178 }
2179
2180 static unsigned
2181 rank_type(NSString *view)
2182 {
2183 if ([view isEqualToString:@"Manatee"])
2184 return 4;
2185 else if ([view isEqualToString:@"Engram"])
2186 return 3;
2187 else if ([view isEqualToString:@"AutoUnlock"])
2188 return 2;
2189 if ([view isEqualToString:@"Health"])
2190 return 1;
2191 return 0;
2192 }
2193
2194 static NSData *
2195 parse_uuid(NSString *uuidString)
2196 {
2197 NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:uuidString];
2198 uuid_t uuidblob;
2199 [uuid getUUIDBytes:uuidblob];
2200 return [NSData dataWithBytes:uuidblob length:sizeof(uuid_t)];
2201 }
2202 static size_t
2203 piggy_sizeof_data(NSData* data)
2204 {
2205 return ccder_sizeof(CCDER_OCTET_STRING, [data length]);
2206 }
2207
2208 static size_t sizeof_keychainitem(kTLKTypes type, NSString *name, NSData* keychainData, NSData* uuid) {
2209 if (type != kTLKUnknown) {
2210 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE,
2211 piggy_sizeof_data(keychainData) +
2212 piggy_sizeof_data(uuid) +
2213 ccder_sizeof_uint64(type));
2214 } else {
2215 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE,
2216 piggy_sizeof_data(keychainData) +
2217 piggy_sizeof_data(uuid) +
2218 der_sizeof_string((__bridge CFStringRef)name, NULL));
2219 }
2220 }
2221
2222 NSArray<NSDictionary*>*
2223 SOSAccountSortTLKS(NSArray<NSDictionary*>* tlks)
2224 {
2225 NSMutableArray<NSDictionary*>* sortedTLKs = [tlks mutableCopy];
2226
2227 [sortedTLKs sortUsingComparator:^NSComparisonResult(NSDictionary *obj1, NSDictionary *obj2) {
2228 unsigned rank1 = rank_type(obj1[(__bridge id)kSecAttrServer]);
2229 if (obj1[@"auth"] != NULL)
2230 rank1 += 1000;
2231 unsigned rank2 = rank_type(obj2[(__bridge id)kSecAttrServer]);
2232 if (obj2[@"auth"] != NULL)
2233 rank2 += 1000;
2234
2235 /*
2236 * Sort by rank (higher better), but prefer TLK that are authoriative (ie used by CKKSViewManager),
2237 * since we are sorting backward, the Ascending/Descending looks wrong below.
2238 */
2239 if (rank1 > rank2) {
2240 return NSOrderedAscending;
2241 } else if (rank1 < rank2) {
2242 return NSOrderedDescending;
2243 }
2244 return NSOrderedSame;
2245 }];
2246
2247 return sortedTLKs;
2248 }
2249
2250 static NSArray<NSData *> *
2251 build_tlks(NSArray<NSDictionary*>* tlks)
2252 {
2253 NSMutableArray *array = [NSMutableArray array];
2254 NSArray<NSDictionary*>* sortedTLKs = SOSAccountSortTLKS(tlks);
2255
2256 for (NSDictionary *item in sortedTLKs) {
2257 NSData* keychainData = item[(__bridge id)kSecValueData];
2258 NSString* name = item[(__bridge id)kSecAttrServer];
2259 NSString *uuidString = item[(__bridge id)kSecAttrAccount];
2260 NSData* uuid = parse_uuid(uuidString);
2261
2262 NSMutableData *tlk = [NSMutableData dataWithLength:sizeof_keychainitem(name2type(name), name, keychainData, uuid)];
2263
2264 unsigned char *der = [tlk mutableBytes];
2265 unsigned char *der_end = der + [tlk length];
2266
2267 if (encode_tlk(name2type(name), name, keychainData, uuid, der, der_end) == NULL)
2268 return NULL;
2269
2270 secnotice("piggy", "preparing TLK in order: %@: %@", name, uuidString);
2271
2272 [array addObject:tlk];
2273 }
2274 return array;
2275 }
2276
2277 static NSArray<NSData *> *
2278 build_identities(NSArray<NSData *>* identities)
2279 {
2280 NSMutableArray *array = [NSMutableArray array];
2281 for (NSData *item in identities) {
2282 NSMutableData *ident = [NSMutableData dataWithLength:ccder_sizeof_raw_octet_string([item length])];
2283
2284 unsigned char *der = [ident mutableBytes];
2285 unsigned char *der_end = der + [ident length];
2286
2287 ccder_encode_raw_octet_string([item length], [item bytes], der, der_end);
2288 [array addObject:ident];
2289 }
2290 return array;
2291 }
2292
2293
2294
2295 static unsigned char *
2296 encode_data_array(NSArray<NSData*>* data, unsigned char *der, unsigned char *der_end)
2297 {
2298 unsigned char *body_end = der_end;
2299 for (NSData *datum in data) {
2300 der_end = ccder_encode_body([datum length], [datum bytes], der, der_end);
2301 if (der_end == NULL)
2302 return NULL;
2303 }
2304 return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, body_end, der, der_end);
2305 }
2306
2307 static size_t sizeof_piggy(size_t identities_size, size_t tlk_size)
2308 {
2309 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE,
2310 ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, identities_size) +
2311 ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, tlk_size));
2312 }
2313
2314 static NSData *encode_piggy(size_t IdentitiesBudget,
2315 size_t TLKBudget,
2316 NSArray<NSData*>* identities,
2317 NSArray<NSDictionary*>* tlks)
2318 {
2319 NSArray<NSData *> *encodedTLKs = build_tlks(tlks);
2320 NSArray<NSData *> *encodedIdentities = build_identities(identities);
2321 NSMutableArray<NSData *> *budgetArray = [NSMutableArray array];
2322 NSMutableArray<NSData *> *identitiesArray = [NSMutableArray array];
2323 size_t payloadSize = 0, identitiesSize = 0;
2324 NSMutableData *result = NULL;
2325
2326 for (NSData *tlk in encodedTLKs) {
2327 if (TLKBudget - payloadSize < [tlk length])
2328 break;
2329 [budgetArray addObject:tlk];
2330 payloadSize += tlk.length;
2331 }
2332 secnotice("piggy", "sending %d tlks", (int)budgetArray.count);
2333
2334 for (NSData *ident in encodedIdentities) {
2335 if (IdentitiesBudget - identitiesSize < [ident length])
2336 break;
2337 [identitiesArray addObject:ident];
2338 identitiesSize += ident.length;
2339 }
2340 secnotice("piggy", "sending %d identities", (int)identitiesArray.count);
2341
2342
2343 size_t piggySize = sizeof_piggy(identitiesSize, payloadSize);
2344
2345 result = [NSMutableData dataWithLength:piggySize];
2346
2347 unsigned char *der = [result mutableBytes];
2348 unsigned char *der_end = der + [result length];
2349
2350 if (ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
2351 encode_data_array(identitiesArray, der,
2352 encode_data_array(budgetArray, der, der_end))) != [result mutableBytes])
2353 return NULL;
2354
2355 return result;
2356 }
2357
2358 static const size_t SOSCCIdentitiesBudget = 120;
2359 static const size_t SOSCCTLKBudget = 500;
2360
2361 NSData *
2362 SOSPiggyCreateInitialSyncData(NSArray<NSData*>* identities, NSArray<NSDictionary *>* tlks)
2363 {
2364 return encode_piggy(SOSCCIdentitiesBudget, SOSCCTLKBudget, identities, tlks);
2365 }
2366
2367 CF_RETURNS_RETAINED CFMutableArrayRef SOSAccountCopyiCloudIdentities(SOSAccount* account)
2368 {
2369 CFMutableArrayRef identities = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
2370
2371 SOSCircleForEachActivePeer(account.trust.trustedCircle, ^(SOSPeerInfoRef peer) {
2372 if(SOSPeerInfoIsCloudIdentity(peer)) {
2373 CFArrayAppendValue(identities, peer);
2374 }
2375 });
2376 return identities;
2377 }
2378
2379 CFDataRef SOSAccountCopyInitialSyncData(SOSAccount* account, CFErrorRef *error) {
2380 CFMutableArrayRef identities = SOSAccountCopyiCloudIdentities(account);
2381 secnotice("piggy", "identities: %@", identities);
2382
2383 NSMutableArray *encodedIdenities = [NSMutableArray array];
2384 CFIndex i, count = CFArrayGetCount(identities);
2385 for (i = 0; i < count; i++) {
2386 SOSPeerInfoRef fpi = (SOSPeerInfoRef)CFArrayGetValueAtIndex(identities, i);
2387 NSData *data = CFBridgingRelease(SOSPeerInfoCopyData(fpi, error));
2388 if (data)
2389 [encodedIdenities addObject:data];
2390 }
2391 CFRelease(identities);
2392
2393 NSMutableArray* tlks = SOSAccountGetAllTLKs();
2394
2395 return CFBridgingRetain(SOSPiggyCreateInitialSyncData(encodedIdenities, tlks));
2396 }
2397
2398 CFDataRef SOSAccountCopyCircleJoiningBlob(SOSAccount* account, SOSPeerInfoRef applicant, CFErrorRef *error) {
2399 SOSGenCountRef gencount = NULL;
2400 CFDataRef signature = NULL;
2401 SecKeyRef ourKey = NULL;
2402
2403 CFDataRef pbblob = NULL;
2404 SOSCircleRef prunedCircle = NULL;
2405
2406 secnotice("circleOps", "Making circle joining blob as sponsor (SOSAccountCopyCircleJoiningBlob)");
2407
2408 SecKeyRef userKey = SOSAccountGetTrustedPublicCredential(account, error);
2409 require_quiet(userKey, errOut);
2410
2411 require_action_quiet(applicant, errOut, SOSCreateError(kSOSErrorProcessingFailure, CFSTR("No applicant provided"), (error != NULL) ? *error : NULL, error));
2412 require_quiet(SOSPeerInfoApplicationVerify(applicant, userKey, error), errOut);
2413
2414 {
2415 SOSFullPeerInfoRef fpi = account.fullPeerInfo;
2416 ourKey = SOSFullPeerInfoCopyDeviceKey(fpi, error);
2417 require_quiet(ourKey, errOut);
2418 }
2419
2420 SOSCircleRef currentCircle = [account.trust getCircle:error];
2421 require_quiet(currentCircle, errOut);
2422
2423 prunedCircle = SOSCircleCopyCircle(NULL, currentCircle, error);
2424 require_quiet(prunedCircle, errOut);
2425 require_quiet(SOSCirclePreGenerationSign(prunedCircle, userKey, error), errOut);
2426
2427 gencount = SOSGenerationIncrementAndCreate(SOSCircleGetGeneration(prunedCircle));
2428
2429 signature = SOSCircleCopyNextGenSignatureWithPeerAdded(prunedCircle, applicant, ourKey, error);
2430 require_quiet(signature, errOut);
2431
2432 pbblob = SOSPiggyBackBlobCopyEncodedData(gencount, ourKey, signature, error);
2433
2434 errOut:
2435 CFReleaseNull(prunedCircle);
2436 CFReleaseNull(gencount);
2437 CFReleaseNull(signature);
2438 CFReleaseNull(ourKey);
2439
2440 if(!pbblob && error != NULL) {
2441 secnotice("circleOps", "Failed to make circle joining blob as sponsor %@", *error);
2442 }
2443
2444 return pbblob;
2445 }
2446
2447 bool SOSAccountJoinWithCircleJoiningBlob(SOSAccount* account, CFDataRef joiningBlob, PiggyBackProtocolVersion version, CFErrorRef *error) {
2448 bool retval = false;
2449 SecKeyRef userKey = NULL;
2450 SOSAccountTrustClassic *trust = account.trust;
2451 SOSGenCountRef gencount = NULL;
2452 CFDataRef signature = NULL;
2453 SecKeyRef pubKey = NULL;
2454 bool setInitialSyncTimeoutToV0 = false;
2455
2456 secnotice("circleOps", "Joining circles through piggy-back (SOSAccountCopyCircleJoiningBlob)");
2457
2458 if (!isData(joiningBlob))
2459 return false;
2460
2461 userKey = SOSAccountGetPrivateCredential(account, error);
2462 if(!userKey)
2463 return retval;
2464
2465 if (!SOSPiggyBackBlobCreateFromData(&gencount, &pubKey, &signature, joiningBlob, version, &setInitialSyncTimeoutToV0, error))
2466 return retval;
2467
2468 if(setInitialSyncTimeoutToV0){
2469 secnotice("piggy", "setting flag in account for piggybacking v0");
2470 SOSAccountSetValue(account, kSOSInitialSyncTimeoutV0, kCFBooleanTrue, NULL);
2471 }
2472 else{
2473 secnotice("piggy", "setting flag in account for piggybacking v0");
2474 SOSAccountClearValue(account, kSOSInitialSyncTimeoutV0, NULL);
2475 }
2476 SOSAccountSetValue(account, kSOSUnsyncedViewsKey, kCFBooleanTrue, NULL);
2477
2478 retval = [trust modifyCircle:account.circle_transport err:error action:^bool(SOSCircleRef copyOfCurrent) {
2479 return SOSCircleAcceptPeerFromHSA2(copyOfCurrent, userKey,
2480 gencount,
2481 pubKey,
2482 signature,
2483 trust.fullPeerInfo, error);;
2484
2485 }];
2486
2487 CFReleaseNull(gencount);
2488 CFReleaseNull(pubKey);
2489 CFReleaseNull(signature);
2490
2491 return retval;
2492 }
2493
2494 static char boolToChars(bool val, char truechar, char falsechar) {
2495 return val? truechar: falsechar;
2496 }
2497
2498 #define ACCOUNTLOGSTATE "accountLogState"
2499 void SOSAccountLogState(SOSAccount* account) {
2500 bool hasPubKey = account.accountKey != NULL;
2501 SOSAccountTrustClassic *trust = account.trust;
2502 bool pubTrusted = account.accountKeyIsTrusted;
2503 bool hasPriv = account.accountPrivateKey != NULL;
2504 SOSCCStatus stat = [account getCircleStatus:NULL];
2505
2506 CFStringRef userPubKeyID = (account.accountKey) ? SOSCopyIDOfKeyWithLength(account.accountKey, 8, NULL):
2507 CFStringCreateCopy(kCFAllocatorDefault, CFSTR("*No Key*"));
2508
2509 secnotice(ACCOUNTLOGSTATE, "Start");
2510
2511 secnotice(ACCOUNTLOGSTATE, "ACCOUNT: [keyStatus: %c%c%c hpub %@] [SOSCCStatus: %@]",
2512 boolToChars(hasPubKey, 'U', 'u'), boolToChars(pubTrusted, 'T', 't'), boolToChars(hasPriv, 'I', 'i'),
2513 userPubKeyID,
2514 SOSAccountGetSOSCCStatusString(stat)
2515 );
2516 CFReleaseNull(userPubKeyID);
2517 if(trust.trustedCircle) SOSCircleLogState(ACCOUNTLOGSTATE, trust.trustedCircle, account.accountKey, (__bridge CFStringRef)(account.peerID));
2518 else secnotice(ACCOUNTLOGSTATE, "ACCOUNT: No Circle");
2519 }
2520
2521 void SOSAccountLogViewState(SOSAccount* account) {
2522 bool isInCircle = [account.trust isInCircle:NULL];
2523 require_quiet(isInCircle, imOut);
2524 SOSPeerInfoRef mpi = account.peerInfo;
2525 bool isInitialComplete = SOSAccountHasCompletedInitialSync(account);
2526 bool isBackupComplete = SOSAccountHasCompletedRequiredBackupSync(account);
2527
2528 CFSetRef views = mpi ? SOSPeerInfoCopyEnabledViews(mpi) : NULL;
2529 CFStringSetPerformWithDescription(views, ^(CFStringRef description) {
2530 secnotice(ACCOUNTLOGSTATE, "Sync: %c%c PeerViews: %@",
2531 boolToChars(isInitialComplete, 'I', 'i'),
2532 boolToChars(isBackupComplete, 'B', 'b'),
2533 description);
2534 });
2535 CFReleaseNull(views);
2536 CFSetRef unsyncedViews = SOSAccountCopyOutstandingViews(account);
2537 CFStringSetPerformWithDescription(views, ^(CFStringRef description) {
2538 secnotice(ACCOUNTLOGSTATE, "outstanding views: %@", description);
2539 });
2540 CFReleaseNull(unsyncedViews);
2541
2542 imOut:
2543 secnotice(ACCOUNTLOGSTATE, "Finish");
2544
2545 return;
2546 }
2547
2548
2549 void SOSAccountSetTestSerialNumber(SOSAccount* account, CFStringRef serial) {
2550 if(!isString(serial)) return;
2551 CFMutableDictionaryRef newv2dict = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
2552 CFDictionarySetValue(newv2dict, sSerialNumberKey, serial);
2553 [account.trust updateV2Dictionary:account v2:newv2dict];
2554 }
2555
2556 void SOSAccountResetOTRNegotiationCoder(SOSAccount* account, CFStringRef peerid)
2557 {
2558 secnotice("otrtimer", "timer fired!");
2559 CFErrorRef error = NULL;
2560 SecADAddValueForScalarKey((__bridge CFStringRef) SecSOSAggdReattemptOTRNegotiation,1);
2561
2562 SOSEngineRef engine = SOSDataSourceFactoryGetEngineForDataSourceName(account.factory, SOSCircleGetName(account.trust.trustedCircle), NULL);
2563 SOSEngineWithPeerID(engine, peerid, &error, ^(SOSPeerRef peer, SOSCoderRef coder, SOSDataSourceRef dataSource, SOSTransactionRef txn, bool *forceSaveState) {
2564 if(SOSCoderIsCoderInAwaitingState(coder)){
2565 secnotice("otrtimer", "coder is in awaiting state, restarting coder");
2566 CFErrorRef localError = NULL;
2567 SOSCoderReset(coder);
2568 if(SOSCoderStart(coder, &localError) == kSOSCoderFailure){
2569 secerror("Attempt to recover coder failed to restart: %@", localError);
2570 }
2571 else{
2572 secnotice("otrtimer", "coder restarted!");
2573 SOSEngineSetCodersNeedSaving(engine, true);
2574 SOSPeerSetMustSendMessage(peer, true);
2575 SOSCCRequestSyncWithPeer(SOSPeerGetID(peer));
2576 }
2577 SOSPeerOTRTimerIncreaseOTRNegotiationRetryCount(account, (__bridge NSString*)SOSPeerGetID(peer));
2578 SOSPeerRemoveOTRTimerEntry(peer);
2579 SOSPeerOTRTimerRemoveRTTTimeoutForPeer(account, (__bridge NSString*)SOSPeerGetID(peer));
2580 SOSPeerOTRTimerRemoveLastSentMessageTimestamp(account, (__bridge NSString*)SOSPeerGetID(peer));
2581 }
2582 else{
2583 secnotice("otrtimer", "time fired but out of negotiation! Not restarting coder");
2584 }
2585 });
2586 if(error)
2587 {
2588 secnotice("otrtimer","error grabbing engine for peer id: %@, error:%@", peerid, error);
2589 }
2590 CFReleaseNull(error);
2591 }
2592
2593 void SOSAccountTimerFiredSendNextMessage(SOSAccountTransaction* txn, NSString* peerid, NSString* accessGroup)
2594 {
2595 __block SOSAccount* account = txn.account;
2596 CFErrorRef error = NULL;
2597
2598 SOSEngineRef engine = SOSDataSourceFactoryGetEngineForDataSourceName(txn.account.factory, SOSCircleGetName(account.trust.trustedCircle), NULL);
2599 SOSEngineWithPeerID(engine, (__bridge CFStringRef)peerid, &error, ^(SOSPeerRef peer, SOSCoderRef coder, SOSDataSourceRef dataSource, SOSTransactionRef txn, bool *forceSaveState) {
2600
2601 NSString *peer_id = (__bridge NSString*)SOSPeerGetID(peer);
2602 PeerRateLimiter *limiter = (__bridge PeerRateLimiter*)SOSPeerGetRateLimiter(peer);
2603 CFErrorRef error = NULL;
2604 NSData* message = [limiter.accessGroupToNextMessageToSend objectForKey:accessGroup];
2605
2606 if(message){
2607 secnotice("ratelimit","SOSPeerRateLimiter timer went off! sending:%@ \n to peer:%@", message, peer_id);
2608 bool sendResult = [account.ids_message_transport SOSTransportMessageSendMessage:account.ids_message_transport id:(__bridge CFStringRef)peer_id messageToSend:(__bridge CFDataRef)message err:&error];
2609
2610 if(!sendResult || error){
2611 secnotice("ratelimit", "could not send message: %@", error);
2612 }
2613 }
2614 [limiter.accessGroupRateLimitState setObject:[[NSNumber alloc]initWithLong:RateLimitStateCanSend] forKey:accessGroup];
2615 [limiter.accessGroupToTimer removeObjectForKey:accessGroup];
2616 [limiter.accessGroupToNextMessageToSend removeObjectForKey:accessGroup];
2617 });
2618
2619 if(error)
2620 {
2621 secnotice("otrtimer","error grabbing engine for peer id: %@, error:%@", peerid, error);
2622 }
2623 CFReleaseNull(error);
2624 }
2625
2626 @end
2627
2628