2 * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
6 * SOSAccount.c - Implementation of the secure object syncing account.
7 * An account contains a SOSCircle for each protection domain synced.
10 #import <Foundation/Foundation.h>
11 #import <Foundation/NSXPCConnection.h>
12 #import <Foundation/NSXPCConnection_Private.h>
15 #include <Security/SecureObjectSync/SOSAccount.h>
16 #include <Security/SecureObjectSync/SOSPeerInfo.h>
17 #include <Security/SecureObjectSync/SOSPeerInfoV2.h>
18 #include <Security/SecureObjectSync/SOSPeerInfoCollections.h>
19 #import "Security/SecureObjectSync/SOSTransportCircle.h"
20 #import "Security/SecureObjectSync/SOSTransportCircleKVS.h"
21 #include <Security/SecureObjectSync/SOSTransportMessage.h>
22 #include <Security/SecureObjectSync/SOSTransportMessageKVS.h>
23 #include <Security/SecureObjectSync/SOSTransportMessageIDS.h>
24 #include <Security/SecureObjectSync/SOSTransportKeyParameter.h>
25 #include <Security/SecureObjectSync/SOSKVSKeys.h>
26 #include <Security/SecureObjectSync/SOSTransport.h>
27 #include <Security/SecureObjectSync/SOSPeerCoder.h>
28 #include <Security/SecureObjectSync/SOSInternal.h>
29 #include <Security/SecureObjectSync/SOSRing.h>
30 #include <Security/SecureObjectSync/SOSRingUtils.h>
31 #include <Security/SecureObjectSync/SOSRingRecovery.h>
32 #include <Security/SecureObjectSync/SOSPeerInfoSecurityProperties.h>
33 #include <Security/SecureObjectSync/SOSAccountTransaction.h>
34 #include <Security/SecureObjectSync/SOSAccountGhost.h>
35 #include <Security/SecureObjectSync/SOSPiggyback.h>
36 #include <Security/SecureObjectSync/SOSControlHelper.h>
38 #import <Security/SecureObjectSync/SOSAccountTrust.h>
39 #import <Security/SecureObjectSync/SOSAccountTrustClassic.h>
40 #import "Security/SecureObjectSync/SOSAccountTrustClassic+Circle.h"
41 #import "Security/SecureObjectSync/SOSAccountTrustClassic+Expansion.h"
42 #import "Security/SecureObjectSync/SOSAccountTrustClassic+Identity.h"
43 #import "Security/SecureObjectSync/SOSAccountTrustClassic+Retirement.h"
44 #import "Security/SecureObjectSync/SOSPeerOTRTimer.h"
45 #import "Security/SecureObjectSync/SOSPeerRateLimiter.h"
46 #import "Security/SecureObjectSync/SOSTypes.h"
47 #import "keychain/ckks/CKKSViewManager.h"
49 #include <Security/SecItemInternal.h>
50 #include <Security/SecEntitlements.h>
51 #include <SOSCircle/CKBridge/SOSCloudKeychainClient.h>
53 #include <SecItemServer.h>
55 #import "SecdWatchdog.h"
57 #include <utilities/SecCFWrappers.h>
58 #include <utilities/SecCFError.h>
59 #include <utilities/SecADWrapper.h>
61 #include <os/activity.h>
62 #include <os/state_private.h>
64 #include <utilities/SecCoreCrypto.h>
66 #include <utilities/der_plist.h>
67 #include <utilities/der_plist_internal.h>
68 #include <corecrypto/ccder.h>
70 const CFStringRef kSOSDSIDKey = CFSTR("AccountDSID");
71 const CFStringRef kSOSEscrowRecord = CFSTR("EscrowRecord");
72 const CFStringRef kSOSUnsyncedViewsKey = CFSTR("unsynced");
73 const CFStringRef kSOSInitialSyncTimeoutV0 = CFSTR("initialsynctimeout");
74 const CFStringRef kSOSPendingEnableViewsToBeSetKey = CFSTR("pendingEnableViews");
75 const CFStringRef kSOSPendingDisableViewsToBeSetKey = CFSTR("pendingDisableViews");
76 const CFStringRef kSOSTestV2Settings = CFSTR("v2dictionary");
77 const CFStringRef kSOSRecoveryKey = CFSTR("RecoveryKey");
78 const CFStringRef kSOSRecoveryRing = CFSTR("RecoveryRing");
79 const CFStringRef kSOSAccountUUID = CFSTR("UUID");
80 const CFStringRef kSOSRateLimitingCounters = CFSTR("RateLimitCounters");
81 const CFStringRef kSOSAccountPeerNegotiationTimeouts = CFSTR("PeerNegotiationTimeouts"); //Dictionary<CFStringRef, CFNumberRef>
82 const CFStringRef kSOSAccountPeerLastSentTimestamp = CFSTR("kSOSAccountPeerLastSentTimestamp"); //Dictionary<CFStringRef, CFDateRef>
83 const CFStringRef kSOSAccountRenegotiationRetryCount = CFSTR("NegotiationRetryCount");
84 const CFStringRef kOTRConfigVersion = CFSTR("OTRConfigVersion");
85 NSString* const SecSOSAggdReattemptOTRNegotiation = @"com.apple.security.sos.otrretry";
87 const uint64_t max_packet_size_over_idms = 500;
90 #define kPublicKeyNotAvailable "com.apple.security.publickeynotavailable"
92 #define DATE_LENGTH 25
93 const CFStringRef kSOSAccountDebugScope = CFSTR("Scope");
95 @interface SOSAccount () <NSXPCListenerDelegate, SOSControlProtocol>
96 @property (retain,nonnull) NSXPCListener *listener;
100 @interface SOSClient : NSObject <SOSControlProtocol>
101 @property (weak) NSXPCConnection * connection;
102 @property (strong) SOSAccount * account;
104 - (instancetype)initWithConnection:(NSXPCConnection *)connection account:(SOSAccount *)account;
107 @implementation SOSClient
109 @synthesize account = _account;
110 @synthesize connection = _connection;
112 - (instancetype)initWithConnection:(NSXPCConnection *)connection account:(SOSAccount *)account
114 if ((self = [super init])) {
115 _connection = connection;
121 - (bool)checkEntitlement:(NSString *)entitlement
123 NSXPCConnection *strongConnection = _connection;
125 NSNumber *num = [strongConnection valueForEntitlement:entitlement];
126 if (![num isKindOfClass:[NSNumber class]] || ![num boolValue]) {
127 secinfo("sos", "Client pid: %d doesn't have entitlement: %@",
128 [strongConnection processIdentifier], entitlement);
134 - (void)userPublicKey:(void ((^))(BOOL trusted, NSData *spki, NSError *error))reply
136 [self.account userPublicKey:reply];
139 - (void)kvsPerformanceCounters:(void(^)(NSDictionary <NSString *, NSNumber *> *))reply
141 [self.account kvsPerformanceCounters:reply];
144 - (void)idsPerformanceCounters:(void(^)(NSDictionary <NSString *, NSNumber *> *))reply
146 [self.account idsPerformanceCounters:reply];
149 - (void)rateLimitingPerformanceCounters:(void(^)(NSDictionary <NSString *, NSString *> *))reply
151 [self.account rateLimitingPerformanceCounters:reply];
154 - (void)stashedCredentialPublicKey:(void(^)(NSData *, NSError *error))reply
156 [self.account stashedCredentialPublicKey:reply];
159 - (void)assertStashedAccountCredential:(void(^)(BOOL result, NSError *error))reply
161 [self.account assertStashedAccountCredential:reply];
164 - (void)validatedStashedAccountCredential:(void(^)(NSData *credential, NSError *error))complete
166 [self.account validatedStashedAccountCredential:complete];
169 - (void)stashAccountCredential:(NSData *)credential complete:(void(^)(bool success, NSError *error))complete
171 [self.account stashAccountCredential:credential complete:complete];
174 - (void)myPeerInfo:(void (^)(NSData *, NSError *))complete
176 [self.account myPeerInfo:complete];
179 - (void)circleJoiningBlob:(NSData *)applicant complete:(void (^)(NSData *blob, NSError *))complete
181 [self.account circleJoiningBlob:applicant complete:complete];
184 - (void)joinCircleWithBlob:(NSData *)blob version:(PiggyBackProtocolVersion)version complete:(void (^)(bool success, NSError *))complete
186 [self.account joinCircleWithBlob:blob version:version complete:complete];
189 - (void)initialSyncCredentials:(uint32_t)flags complete:(void (^)(NSArray *, NSError *))complete
191 if (![self checkEntitlement:(__bridge NSString *)kSecEntitlementKeychainInitialSync]) {
192 complete(@[], [NSError errorWithDomain:(__bridge NSString *)kSOSErrorDomain code:kSOSEntitlementMissing userInfo:NULL]);
196 [self.account initialSyncCredentials:flags complete:complete];
199 - (void)importInitialSyncCredentials:(NSArray *)items complete:(void (^)(bool success, NSError *))complete
201 if (![self checkEntitlement:(__bridge NSString *)kSecEntitlementKeychainInitialSync]) {
202 complete(false, [NSError errorWithDomain:(__bridge NSString *)kSOSErrorDomain code:kSOSEntitlementMissing userInfo:NULL]);
206 [self.account importInitialSyncCredentials:items complete:complete];
209 - (void)triggerSync:(NSArray <NSString *> *)peers complete:(void(^)(bool success, NSError *))complete
211 if (![self checkEntitlement:(__bridge NSString *)kSecEntitlementKeychainCloudCircle]) {
212 complete(false, [NSError errorWithDomain:(__bridge NSString *)kSOSErrorDomain code:kSOSEntitlementMissing userInfo:NULL]);
216 [self.account triggerSync:peers complete:complete];
219 - (void)getWatchdogParameters:(void (^)(NSDictionary* parameters, NSError* error))complete
221 [self.account getWatchdogParameters:complete];
224 - (void)setWatchdogParmeters:(NSDictionary*)parameters complete:(void (^)(NSError* error))complete
226 [self.account setWatchdogParmeters:parameters complete:complete];
232 @implementation SOSAccount
234 // Auto synthesis for most fields works great.
235 // A few CF fields need retention work when assigning.
241 self.gestalt = [NSMutableDictionary dictionary];
242 self.backup_key = nil;
245 self.trust = [SOSAccountTrustClassic trustClassic];
248 self.user_private_timer = NULL;
251 self._password_tmp = nil;
252 self.isListeningForSync = false;
253 self.lock_notification_token = -1;
255 self.key_transport = nil;
256 self.circle_transport = NULL;
257 self.kvs_message_transport = nil;
258 self.ids_message_transport = nil;
259 self.ck_storage = nil;
261 self.circle_rings_retirements_need_attention = false;
262 self.engine_peer_state_needs_repair = false;
263 self.key_interests_need_updating = false;
265 self.change_blocks = [NSMutableArray array];
267 self.waitForInitialSync_blocks = [NSMutableDictionary dictionary];
268 self.isInitialSyncing = false;
270 self.accountKeyIsTrusted = false;
271 self.accountKeyDerivationParamters = nil;
273 self.accountKey = NULL;
274 self.accountPrivateKey = NULL;
275 self.previousAccountKey = NULL;
277 self.saveBlock = nil;
279 self.listener = [NSXPCListener anonymousListener];
280 self.listener.delegate = self;
281 [self.listener resume];
287 // All the CF objects stored here need clearing (implicitly releasing them).
288 self.accountKey = NULL;
289 self.accountPrivateKey = NULL;
290 self.previousAccountKey = NULL;
294 @synthesize accountKey = _accountKey;
296 - (void) setAccountKey: (SecKeyRef) key {
297 CFRetainAssign(self->_accountKey, key);
300 @synthesize previousAccountKey = _previousAccountKey;
302 - (void) setPreviousAccountKey: (SecKeyRef) key {
303 CFRetainAssign(self->_previousAccountKey, key);
306 @synthesize accountPrivateKey = _accountPrivateKey;
308 - (void) setAccountPrivateKey: (SecKeyRef) key {
309 CFRetainAssign(self->_accountPrivateKey, key);
312 // Syntactic sugar getters
314 - (BOOL) hasPeerInfo {
315 return self.fullPeerInfo != nil;
318 - (SOSPeerInfoRef) peerInfo {
319 return self.trust.peerInfo;
322 - (SOSFullPeerInfoRef) fullPeerInfo {
323 return self.trust.fullPeerInfo;
326 - (NSString*) peerID {
327 return self.trust.peerID;
331 - (xpc_endpoint_t)xpcControlEndpoint {
332 return [self.listener.endpoint _endpoint];
335 - (BOOL)listener:(__unused NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection
337 NSNumber *num = [newConnection valueForEntitlement:(__bridge NSString *)kSecEntitlementKeychainCloudCircle];
338 if (![num isKindOfClass:[NSNumber class]] || ![num boolValue]) {
339 secinfo("sos", "Client pid: %d doesn't have entitlement: %@",
340 [newConnection processIdentifier], kSecEntitlementKeychainCloudCircle);
345 SOSClient *sosClient = [[SOSClient alloc] initWithConnection:newConnection account:self];
347 newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(SOSControlProtocol)];
348 _SOSControlSetupInterface(newConnection.exportedInterface);
349 newConnection.exportedObject = sosClient;
351 [newConnection resume];
357 -(bool) ensureFactoryCircles
367 NSString* circle_name = (__bridge_transfer NSString*)SOSDataSourceFactoryCopyName(self.factory);
373 SOSAccountEnsureCircle(self, (__bridge CFStringRef) circle_name, NULL);
375 return SOSAccountInflateTransports(self, (__bridge CFStringRef) circle_name, NULL);
378 -(void)ensureOctagonPeerKeys
380 [self.trust ensureOctagonPeerKeys:self.circle_transport];
383 -(id) initWithGestalt:(CFDictionaryRef)newGestalt factory:(SOSDataSourceFactoryRef)f
387 self.queue = dispatch_queue_create("Account Queue", DISPATCH_QUEUE_SERIAL);
389 self.gestalt = [[NSDictionary alloc] initWithDictionary:(__bridge NSDictionary * _Nonnull)(newGestalt)];
391 SOSAccountTrustClassic *t = [[SOSAccountTrustClassic alloc] initWithRetirees:[NSMutableSet set] fpi:NULL circle:NULL departureCode:kSOSDepartureReasonError peerExpansion:[NSMutableDictionary dictionary]];
394 self.factory = f; // We adopt the factory. kthanksbai.
396 self.isListeningForSync = false;
398 self.accountPrivateKey = NULL;
399 self._password_tmp = NULL;
400 self.user_private_timer = NULL;
401 self.lock_notification_token = NOTIFY_TOKEN_INVALID;
403 self.change_blocks = [NSMutableArray array];
404 self.waitForInitialSync_blocks = NULL;
406 self.key_transport = nil;
407 self.circle_transport = NULL;
408 self.ck_storage = nil;
409 self.kvs_message_transport = nil;
410 self.ids_message_transport = nil;
412 self.circle_rings_retirements_need_attention = false;
413 self.engine_peer_state_needs_repair = false;
414 self.key_interests_need_updating = false;
416 self.backup_key =nil;
419 self.waitForInitialSync_blocks = [NSMutableDictionary dictionary];
420 self.isInitialSyncing = false;
421 self.accountKeyIsTrusted = false;
422 self.accountKeyDerivationParamters = NULL;
423 self.accountKey = NULL;
424 self.previousAccountKey = NULL;
426 self.saveBlock = nil;
428 self.listener = [NSXPCListener anonymousListener];
429 self.listener.delegate = self;
430 [self.listener resume];
435 -(BOOL)isEqual:(id) object
437 if(![object isKindOfClass:[SOSAccount class]])
440 SOSAccount* left = object;
441 return ([self.gestalt isEqual: left.gestalt] &&
442 CFEqualSafe(self.trust.trustedCircle, left.trust.trustedCircle) &&
443 [self.trust.expansion isEqual: left.trust.expansion] &&
444 CFEqualSafe(self.trust.fullPeerInfo, left.trust.fullPeerInfo));
448 - (void)userPublicKey:(void ((^))(BOOL trusted, NSData *spki, NSError *error))reply
450 dispatch_async(self.queue, ^{
451 if (!self.accountKeyIsTrusted || self.accountKey == NULL) {
452 NSDictionary *userinfo = @{
453 (id)kCFErrorDescriptionKey : @"User public key not trusted",
455 reply(self.accountKeyIsTrusted, NULL, [NSError errorWithDomain:(__bridge NSString *)kSOSErrorDomain code:kSOSErrorPublicKeyAbsent userInfo:userinfo]);
459 NSData *data = CFBridgingRelease(SecKeyCopySubjectPublicKeyInfo(self.accountKey));
461 NSDictionary *userinfo = @{
462 (id)kCFErrorDescriptionKey : @"User public not available",
464 reply(self.accountKeyIsTrusted, NULL, [NSError errorWithDomain:(__bridge NSString *)kSOSErrorDomain code:kSOSErrorPublicKeyAbsent userInfo:userinfo]);
467 reply(self.accountKeyIsTrusted, data, NULL);
471 - (void)kvsPerformanceCounters:(void(^)(NSDictionary <NSString *, NSNumber *> *))reply
473 /* Need to collect performance counters from all subsystems, not just circle transport, don't have counters yet though */
474 SOSCloudKeychainRequestPerfCounters(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFDictionaryRef returnedValues, CFErrorRef error)
476 reply((__bridge NSDictionary *)returnedValues);
479 - (void)idsPerformanceCounters:(void(^)(NSDictionary <NSString *, NSNumber *> *))reply
481 SOSCloudKeychainRetrieveCountersFromIDSProxy(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFDictionaryRef returnedValues, CFErrorRef error)
483 reply((__bridge NSDictionary *)returnedValues);
487 - (void)rateLimitingPerformanceCounters:(void(^)(NSDictionary <NSString *, NSString *> *))reply
489 CFErrorRef error = NULL;
490 CFDictionaryRef rateLimitingCounters = (CFDictionaryRef)SOSAccountGetValue(self, kSOSRateLimitingCounters, &error);
491 reply((__bridge NSDictionary*)rateLimitingCounters ? (__bridge NSDictionary*)rateLimitingCounters : [NSDictionary dictionary]);
494 - (void)stashedCredentialPublicKey:(void(^)(NSData *, NSError *error))reply
496 dispatch_async(self.queue, ^{
497 CFErrorRef error = NULL;
499 SecKeyRef user_private = SOSAccountCopyStashedUserPrivateKey(self, &error);
500 if (user_private == NULL) {
501 reply(NULL, (__bridge NSError *)error);
502 CFReleaseNull(error);
506 NSData *publicKey = CFBridgingRelease(SecKeyCopySubjectPublicKeyInfo(user_private));
507 CFReleaseNull(user_private);
508 reply(publicKey, NULL);
512 - (void)assertStashedAccountCredential:(void(^)(BOOL result, NSError *error))complete
514 dispatch_async(self.queue, ^{
515 CFErrorRef error = NULL;
516 bool result = SOSAccountAssertStashedAccountCredential(self, &error);
517 complete(result, (__bridge NSError *)error);
518 CFReleaseNull(error);
522 static bool SyncKVSAndWait(CFErrorRef *error) {
523 dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
525 __block bool success = false;
527 secnoticeq("fresh", "EFP calling SOSCloudKeychainSynchronizeAndWait");
529 os_activity_initiate("CloudCircle EFRESH", OS_ACTIVITY_FLAG_DEFAULT, ^(void) {
530 SOSCloudKeychainSynchronizeAndWait(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(__unused CFDictionaryRef returnedValues, CFErrorRef sync_error) {
531 secnotice("fresh", "EFP returned, callback error: %@", sync_error);
533 success = (sync_error == NULL);
535 CFRetainAssign(*error, sync_error);
538 dispatch_semaphore_signal(wait_for);
542 dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER);
543 secnotice("fresh", "EFP complete: %s %@", success ? "success" : "failure", error ? *error : NULL);
549 static bool Flush(CFErrorRef *error) {
550 __block bool success = false;
552 dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
553 secnotice("flush", "Starting");
555 SOSCloudKeychainFlush(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFDictionaryRef returnedValues, CFErrorRef sync_error) {
556 success = (sync_error == NULL);
558 CFRetainAssign(*error, sync_error);
561 dispatch_semaphore_signal(wait_for);
564 dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER);
566 secnotice("flush", "Returned %s", success? "Success": "Failure");
571 - (bool)syncWaitAndFlush:(CFErrorRef *)error
573 secnotice("pairing", "sync and wait starting");
575 if (!SyncKVSAndWait(error)) {
576 secnotice("pairing", "failed sync and wait: %@", error ? *error : NULL);
580 secnotice("pairing", "failed flush: %@", error ? *error : NULL);
583 secnotice("pairing", "finished sync and wait");
587 - (void)validatedStashedAccountCredential:(void(^)(NSData *credential, NSError *error))complete
589 CFErrorRef syncerror = NULL;
591 if (![self syncWaitAndFlush:&syncerror]) {
592 complete(NULL, (__bridge NSError *)syncerror);
593 CFReleaseNull(syncerror);
597 dispatch_async(self.queue, ^{
598 CFErrorRef error = NULL;
599 SecKeyRef key = NULL;
600 key = SOSAccountCopyStashedUserPrivateKey(self, &error);
602 secnotice("pairing", "no stashed credential");
603 complete(NULL, (__bridge NSError *)error);
604 CFReleaseNull(error);
608 SecKeyRef publicKey = SecKeyCopyPublicKey(key);
610 secnotice("pairing", "returning stash credential: %@", publicKey);
611 CFReleaseNull(publicKey);
614 NSData *keydata = CFBridgingRelease(SecKeyCopyExternalRepresentation(key, &error));
616 complete(keydata, (__bridge NSError *)error);
617 CFReleaseNull(error);
621 - (void)stashAccountCredential:(NSData *)credential complete:(void(^)(bool success, NSError *error))complete
623 CFErrorRef syncerror = NULL;
625 if (![self syncWaitAndFlush:&syncerror]) {
626 complete(NULL, (__bridge NSError *)syncerror);
627 CFReleaseNull(syncerror);
631 [self performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
632 SecKeyRef accountPrivateKey = NULL;
633 CFErrorRef error = NULL;
634 NSDictionary *attributes = @{
635 (__bridge id)kSecAttrKeyClass : (__bridge id)kSecAttrKeyClassPrivate,
636 (__bridge id)kSecAttrKeyType : (__bridge id)kSecAttrKeyTypeEC,
639 accountPrivateKey = SecKeyCreateWithData((__bridge CFDataRef)credential, (__bridge CFDictionaryRef)attributes, &error);
640 if (accountPrivateKey == NULL) {
641 complete(false, (__bridge NSError *)error);
642 secnotice("pairing", "SecKeyCreateWithData failed: %@", error);
643 CFReleaseNull(error);
647 if (!SOSAccountTryUserPrivateKey(self, accountPrivateKey, &error)) {
648 CFReleaseNull(accountPrivateKey);
649 complete(false, (__bridge NSError *)error);
650 secnotice("pairing", "SOSAccountTryUserPrivateKey failed: %@", error);
651 CFReleaseNull(error);
655 secnotice("pairing", "SOSAccountTryUserPrivateKey succeeded");
657 CFReleaseNull(accountPrivateKey);
658 complete(true, NULL);
661 // This makes getting the private key the same as Asserting the password - we read all the other things
662 // that we just expressed interest in.
663 CFErrorRef error = NULL;
664 if (!Flush(&error)) {
665 secnotice("pairing", "failed final flush: %@", error ? error : NULL);
668 CFReleaseNull(error);
671 - (void)myPeerInfo:(void (^)(NSData *, NSError *))complete
673 __block CFErrorRef localError = NULL;
674 __block NSData *applicationBlob = NULL;
675 SecAKSDoWhileUserBagLocked(&localError, ^{
676 [self performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
677 SOSPeerInfoRef application = SOSAccountCopyApplication(txn.account, &localError);
679 applicationBlob = CFBridgingRelease(SOSPeerInfoCopyEncodedData(application, kCFAllocatorDefault, &localError));
680 CFReleaseNull(application);
684 complete(applicationBlob, (__bridge NSError *)localError);
685 CFReleaseNull(localError);
688 - (void)circleJoiningBlob:(NSData *)applicant complete:(void (^)(NSData *blob, NSError *))complete
690 __block CFErrorRef localError = NULL;
691 __block NSData *blob = NULL;
692 SOSPeerInfoRef peer = SOSPeerInfoCreateFromData(NULL, &localError, (__bridge CFDataRef)applicant);
694 complete(NULL, (__bridge NSError *)localError);
695 CFReleaseNull(localError);
698 SecAKSDoWhileUserBagLocked(&localError, ^{
699 [self performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
700 blob = CFBridgingRelease(SOSAccountCopyCircleJoiningBlob(txn.account, peer, &localError));
706 complete(blob, (__bridge NSError *)localError);
707 CFReleaseNull(localError);
710 - (void)joinCircleWithBlob:(NSData *)blob version:(PiggyBackProtocolVersion)version complete:(void (^)(bool success, NSError *))complete
712 __block CFErrorRef localError = NULL;
713 __block bool res = false;
715 SecAKSDoWhileUserBagLocked(&localError, ^{
716 [self performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
717 res = SOSAccountJoinWithCircleJoiningBlob(txn.account, (__bridge CFDataRef)blob, version, &localError);
721 complete(res, (__bridge NSError *)localError);
722 CFReleaseNull(localError);
725 - (void)initialSyncCredentials:(uint32_t)flags complete:(void (^)(NSArray *, NSError *))complete
727 CFErrorRef error = NULL;
728 uint32_t isflags = 0;
730 if (flags & SOSControlInitialSyncFlagTLK)
731 isflags |= SecServerInitialSyncCredentialFlagTLK;
732 if (flags & SOSControlInitialSyncFlagPCS)
733 isflags |= SecServerInitialSyncCredentialFlagPCS;
734 if (flags & SOSControlInitialSyncFlagPCSNonCurrent)
735 isflags |= SecServerInitialSyncCredentialFlagPCSNonCurrent;
736 if (flags & SOSControlInitialSyncFlagBluetoothMigration)
737 isflags |= SecServerInitialSyncCredentialFlagBluetoothMigration;
740 NSArray *array = CFBridgingRelease(_SecServerCopyInitialSyncCredentials(isflags, &error));
741 complete(array, (__bridge NSError *)error);
742 CFReleaseNull(error);
745 - (void)importInitialSyncCredentials:(NSArray *)items complete:(void (^)(bool success, NSError *))complete
747 CFErrorRef error = NULL;
748 bool res = _SecServerImportInitialSyncCredentials((__bridge CFArrayRef)items, &error);
749 complete(res, (__bridge NSError *)error);
750 CFReleaseNull(error);
753 - (void)triggerSync:(NSArray <NSString *> *)peers complete:(void(^)(bool success, NSError *))complete
755 __block CFErrorRef localError = NULL;
756 __block bool res = false;
758 secnotice("sync", "trigger a forced sync for %@", peers);
760 SecAKSDoWhileUserBagLocked(&localError, ^{
761 [self performTransaction:^(SOSAccountTransaction *txn) {
763 NSSet *peersSet = [NSSet setWithArray:peers];
764 CFMutableSetRef handledPeers = SOSAccountSyncWithPeers(txn, (__bridge CFSetRef)peersSet, &localError);
765 if (handledPeers && CFSetGetCount(handledPeers) == (CFIndex)[peersSet count]) {
768 CFReleaseNull(handledPeers);
770 res = SOSAccountRequestSyncWithAllPeers(txn, &localError);
774 complete(res, (__bridge NSError *)localError);
775 CFReleaseNull(localError);
778 - (void)getWatchdogParameters:(void (^)(NSDictionary* parameters, NSError* error))complete
780 // SecdWatchdog is only available in the secd/securityd - no other binary will contain that class
781 Class watchdogClass = NSClassFromString(@"SecdWatchdog");
783 NSDictionary* parameters = [[watchdogClass watchdog] watchdogParameters];
784 complete(parameters, nil);
787 complete(nil, [NSError errorWithDomain:@"com.apple.securityd.watchdog" code:1 userInfo:@{NSLocalizedDescriptionKey : @"failed to lookup SecdWatchdog class from ObjC runtime"}]);
791 - (void)setWatchdogParmeters:(NSDictionary*)parameters complete:(void (^)(NSError* error))complete
793 // SecdWatchdog is only available in the secd/securityd - no other binary will contain that class
794 NSError* error = nil;
795 Class watchdogClass = NSClassFromString(@"SecdWatchdog");
797 [[watchdogClass watchdog] setWatchdogParameters:parameters error:&error];
801 complete([NSError errorWithDomain:@"com.apple.securityd.watchdog" code:1 userInfo:@{NSLocalizedDescriptionKey : @"failed to lookup SecdWatchdog class from ObjC runtime"}]);
810 - (void) flattenToSaveBlock {
811 if (self.saveBlock) {
812 NSError* error = nil;
813 NSData* saveData = [self encodedData:&error];
815 (self.saveBlock)((__bridge CFDataRef) saveData, (__bridge CFErrorRef) error);
819 CFDictionaryRef SOSAccountCopyGestalt(SOSAccount* account) {
820 return CFDictionaryCreateCopy(kCFAllocatorDefault, (__bridge CFDictionaryRef)account.gestalt);
823 CFDictionaryRef SOSAccountCopyV2Dictionary(SOSAccount* account) {
824 CFDictionaryRef v2dict = SOSAccountGetValue(account, kSOSTestV2Settings, NULL);
825 return CFDictionaryCreateCopy(kCFAllocatorDefault, v2dict);
828 static bool SOSAccountUpdateDSID(SOSAccount* account, CFStringRef dsid){
829 SOSAccountSetValue(account, kSOSDSIDKey, dsid, NULL);
830 //send new DSID over account changed
831 [account.circle_transport kvsSendOfficialDSID:dsid err:NULL];
835 void SOSAccountAssertDSID(SOSAccount* account, CFStringRef dsid) {
836 CFStringRef accountDSID = SOSAccountGetValue(account, kSOSDSIDKey, NULL);
837 if(accountDSID == NULL) {
838 secdebug("updates", "Setting dsid, current dsid is empty for this account: %@", dsid);
840 SOSAccountUpdateDSID(account, dsid);
841 } else if(CFStringCompare(dsid, accountDSID, 0) != kCFCompareEqualTo) {
842 secnotice("updates", "Changing DSID from: %@ to %@", accountDSID, dsid);
844 //DSID has changed, blast the account!
845 SOSAccountSetToNew(account);
847 //update DSID to the new DSID
848 SOSAccountUpdateDSID(account, dsid);
850 secnotice("updates", "Not Changing DSID: %@ to %@", accountDSID, dsid);
855 void SOSAccountPendDisableViewSet(SOSAccount* account, CFSetRef disabledViews)
857 [account.trust valueUnionWith:kSOSPendingDisableViewsToBeSetKey valuesToUnion:disabledViews];
858 [account.trust valueSubtractFrom:kSOSPendingEnableViewsToBeSetKey valuesToSubtract:disabledViews];
861 SOSViewResultCode SOSAccountVirtualV0Behavior(SOSAccount* account, SOSViewActionCode actionCode) {
862 SOSViewResultCode retval = kSOSCCGeneralViewError;
863 // The V0 view switches on and off all on it's own, we allow people the delusion
864 // of control and status if it's what we're stuck at., otherwise error.
865 if (SOSAccountSyncingV0(account)) {
866 require_action_quiet(actionCode == kSOSCCViewDisable, errOut, CFSTR("Can't disable V0 view and it's on right now"));
867 retval = kSOSCCViewMember;
869 require_action_quiet(actionCode == kSOSCCViewEnable, errOut, CFSTR("Can't enable V0 and it's off right now"));
870 retval = kSOSCCViewNotMember;
876 SOSAccount* SOSAccountCreate(CFAllocatorRef allocator,
877 CFDictionaryRef gestalt,
878 SOSDataSourceFactoryRef factory) {
880 SOSAccount* a = [[SOSAccount alloc] initWithGestalt:gestalt factory:factory];
881 [a ensureFactoryCircles];
882 SOSAccountEnsureUUID(a);
883 a.key_interests_need_updating = true;
888 static OSStatus do_delete(CFDictionaryRef query) {
891 result = SecItemDelete(query);
893 secerror("SecItemDelete: %d", (int)result);
899 do_keychain_delete_aks_bags()
902 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
903 kSecClass, kSecClassGenericPassword,
904 kSecAttrAccessGroup, CFSTR("com.apple.sbd"),
905 kSecAttrAccount, CFSTR("SecureBackupPublicKeybag"),
906 kSecAttrService, CFSTR("SecureBackupService"),
907 kSecAttrSynchronizable, kCFBooleanTrue,
908 kSecUseTombstones, kCFBooleanFalse,
911 result = do_delete(item);
918 do_keychain_delete_identities()
921 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
922 kSecClass, kSecClassKey,
923 kSecAttrSynchronizable, kCFBooleanTrue,
924 kSecUseTombstones, kCFBooleanFalse,
925 kSecAttrAccessGroup, CFSTR("com.apple.security.sos"),
928 result = do_delete(item);
935 do_keychain_delete_lakitu()
938 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
939 kSecClass, kSecClassGenericPassword,
940 kSecAttrSynchronizable, kCFBooleanTrue,
941 kSecUseTombstones, kCFBooleanFalse,
942 kSecAttrAccessGroup, CFSTR("com.apple.lakitu"),
943 kSecAttrAccount, CFSTR("EscrowServiceBypassToken"),
944 kSecAttrService, CFSTR("EscrowService"),
947 result = do_delete(item);
954 do_keychain_delete_sbd()
957 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
958 kSecClass, kSecClassGenericPassword,
959 kSecAttrSynchronizable, kCFBooleanTrue,
960 kSecUseTombstones, kCFBooleanFalse,
961 kSecAttrAccessGroup, CFSTR("com.apple.sbd"),
964 result = do_delete(item);
970 void SOSAccountSetToNew(SOSAccount* a)
972 secnotice("accountChange", "Setting Account to New");
975 /* remove all syncable items */
976 result = do_keychain_delete_aks_bags(); (void) result;
977 secdebug("set to new", "result for deleting aks bags: %d", result);
979 result = do_keychain_delete_identities(); (void) result;
980 secdebug("set to new", "result for deleting identities: %d", result);
982 result = do_keychain_delete_lakitu(); (void) result;
983 secdebug("set to new", "result for deleting lakitu: %d", result);
985 result = do_keychain_delete_sbd(); (void) result;
986 secdebug("set to new", "result for deleting sbd: %d", result);
988 a.accountKeyIsTrusted = false;
990 if (a.user_private_timer) {
991 dispatch_source_cancel(a.user_private_timer);
992 a.user_private_timer = NULL;
993 xpc_transaction_end();
996 if (a.lock_notification_token != NOTIFY_TOKEN_INVALID) {
997 notify_cancel(a.lock_notification_token);
998 a.lock_notification_token = NOTIFY_TOKEN_INVALID;
1003 // Live Notification
1005 // update_interest_block;
1007 SOSUnregisterTransportKeyParameter(a.key_transport);
1008 SOSUnregisterTransportMessage(a.ids_message_transport);
1009 SOSUnregisterTransportMessage(a.kvs_message_transport);
1010 SOSUnregisterTransportCircle(a.circle_transport);
1012 a.circle_transport = NULL;
1013 a.kvs_message_transport = NULL;
1014 a.ids_message_transport = NULL;
1017 a.trust = [[SOSAccountTrustClassic alloc]initWithRetirees:[NSMutableSet set] fpi:NULL circle:NULL departureCode:kSOSDepartureReasonError peerExpansion:[NSMutableDictionary dictionary]];
1019 [a ensureFactoryCircles]; // Does rings too
1021 // By resetting our expansion dictionary we've reset our UUID, so we'll be notified properly
1022 SOSAccountEnsureUUID(a);
1024 a.key_interests_need_updating = true;
1027 bool SOSAccountIsNew(SOSAccount* account, CFErrorRef *error){
1028 bool result = false;
1029 SOSAccountTrustClassic* trust = account.trust;
1030 if(account.accountKeyIsTrusted != false || trust.departureCode != kSOSNeverAppliedToCircle ||
1031 CFSetGetCount((__bridge CFSetRef)trust.retirees) != 0)
1034 if(trust.retirees != nil)
1036 if(trust.expansion != nil)
1039 if(account.user_private_timer != NULL || account.lock_notification_token != NOTIFY_TOKEN_INVALID)
1047 CFStringRef SOSAccountCreateCompactDescription(SOSAccount* a) {
1049 CFStringRef gestaltDescription = CFDictionaryCopySuperCompactDescription((__bridge CFDictionaryRef)(a.gestalt));
1051 CFStringRef result = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@"), gestaltDescription);
1053 CFReleaseNull(gestaltDescription);
1057 dispatch_queue_t SOSAccountGetQueue(SOSAccount* account) {
1058 return account.queue;
1061 void SOSAccountSetUserPublicTrustedForTesting(SOSAccount* account){
1062 account.accountKeyIsTrusted = true;
1065 -(SOSCCStatus) getCircleStatus:(CFErrorRef*) error
1067 SOSCCStatus circleStatus = kSOSCCError;
1068 if (SOSAccountHasPublicKey(self, error)) {
1069 circleStatus = [self.trust getCircleStatus:error];
1071 return circleStatus;
1073 bool SOSAccountScanForRetired(SOSAccount* account, SOSCircleRef circle, CFErrorRef *error) {
1075 SOSAccountTrustClassic *trust = account.trust;
1076 NSMutableSet* retirees = trust.retirees;
1077 SOSCircleForEachRetiredPeer(circle, ^(SOSPeerInfoRef peer) {
1078 CFSetSetValue((__bridge CFMutableSetRef) retirees, peer);
1079 CFErrorRef cleanupError = NULL;
1080 if (![account.trust cleanupAfterPeer:account.kvs_message_transport circleTransport:account.circle_transport seconds:RETIREMENT_FINALIZATION_SECONDS circle:circle cleanupPeer:peer err:&cleanupError]) {
1081 secnotice("retirement", "Error cleaning up after peer, probably orphaned some stuff in KVS: (%@) – moving on", cleanupError);
1083 CFReleaseSafe(cleanupError);
1088 SOSCircleRef SOSAccountCloneCircleWithRetirement(SOSAccount* account, SOSCircleRef starting_circle, CFErrorRef *error) {
1089 SOSCircleRef new_circle = SOSCircleCopyCircle(NULL, starting_circle, error);
1090 SOSPeerInfoRef me = account.peerInfo;
1091 bool iAmApplicant = me && SOSCircleHasApplicant(new_circle, me, NULL);
1093 SOSAccountTrustClassic *trust = account.trust;
1094 NSMutableSet* retirees = trust.retirees;
1096 if(!new_circle) return NULL;
1097 __block bool workDone = false;
1099 CFSetForEach((__bridge CFSetRef)retirees, ^(const void* value) {
1100 SOSPeerInfoRef pi = (SOSPeerInfoRef) value;
1101 if (isSOSPeerInfo(pi)) {
1102 SOSCircleUpdatePeerInfo(new_circle, pi);
1108 if(workDone && SOSCircleCountPeers(new_circle) == 0) {
1109 SecKeyRef userPrivKey = SOSAccountGetPrivateCredential(account, error);
1113 secnotice("resetToOffering", "Reset to offering with last retirement and me as applicant");
1114 if(!SOSCircleResetToOffering(new_circle, userPrivKey, account.fullPeerInfo, error) ||
1115 ![account.trust addiCloudIdentity:new_circle key:userPrivKey err:error]){
1116 CFReleaseNull(new_circle);
1120 // Do nothing. We can't resetToOffering without a userPrivKey. If we were to resetToEmpty
1121 // we won't push the result later in handleUpdateCircle. If we leave the circle as it is
1122 // we have a chance to set things right with a SetCreds/Join sequence. This will cause
1123 // handleUpdateCircle to return false.
1124 CFReleaseNull(new_circle);
1128 // This case is when we aren't an applicant and the circle is retirement-empty.
1129 secnotice("resetToEmpty", "Reset to empty with last retirement");
1130 SOSCircleResetToEmpty(new_circle, NULL);
1138 // MARK: Circle Membership change notificaion
1141 void SOSAccountAddChangeBlock(SOSAccount* a, SOSAccountCircleMembershipChangeBlock changeBlock) {
1142 SOSAccountCircleMembershipChangeBlock copy = changeBlock;
1143 [a.change_blocks addObject:copy];
1146 void SOSAccountRemoveChangeBlock(SOSAccount* a, SOSAccountCircleMembershipChangeBlock changeBlock) {
1147 [a.change_blocks removeObject:changeBlock];
1150 void SOSAccountPurgeIdentity(SOSAccount* account) {
1151 SOSAccountTrustClassic *trust = account.trust;
1152 SOSFullPeerInfoRef identity = trust.fullPeerInfo;
1155 // Purge private key but don't return error if we can't.
1156 CFErrorRef purgeError = NULL;
1157 if (!SOSFullPeerInfoPurgePersistentKey(identity, &purgeError)) {
1158 secwarning("Couldn't purge persistent key for %@ [%@]", identity, purgeError);
1160 CFReleaseNull(purgeError);
1162 trust.fullPeerInfo = nil;
1166 bool sosAccountLeaveCircle(SOSAccount* account, SOSCircleRef circle, CFErrorRef* error) {
1167 SOSAccountTrustClassic *trust = account.trust;
1168 SOSFullPeerInfoRef identity = trust.fullPeerInfo;
1169 NSMutableSet* retirees = trust.retirees;
1171 SOSFullPeerInfoRef fpi = identity;
1172 if(!fpi) return false;
1174 CFErrorRef localError = NULL;
1176 bool retval = false;
1178 SOSPeerInfoRef retire_peer = SOSFullPeerInfoPromoteToRetiredAndCopy(fpi, &localError);
1180 secerror("Create ticket failed for peer %@: %@", fpi, localError);
1182 // See if we need to repost the circle we could either be an applicant or a peer already in the circle
1183 if(SOSCircleHasApplicant(circle, retire_peer, NULL)) {
1184 // Remove our application if we have one.
1185 SOSCircleWithdrawRequest(circle, retire_peer, NULL);
1186 } else if (SOSCircleHasPeer(circle, retire_peer, NULL)) {
1187 if (SOSCircleUpdatePeerInfo(circle, retire_peer)) {
1188 CFErrorRef cleanupError = NULL;
1189 if (![account.trust cleanupAfterPeer:account.kvs_message_transport circleTransport:account.circle_transport seconds:RETIREMENT_FINALIZATION_SECONDS circle:circle cleanupPeer:retire_peer err:&cleanupError]) {
1190 secerror("Error cleanup up after peer (%@): %@", retire_peer, cleanupError);
1192 CFReleaseSafe(cleanupError);
1196 // Store the retirement record locally.
1197 CFSetAddValue((__bridge CFMutableSetRef)retirees, retire_peer);
1199 trust.retirees = retirees;
1201 // Write retirement to Transport
1202 CFErrorRef postError = NULL;
1203 if(![account.circle_transport postRetirement:SOSCircleGetName(circle) peer:retire_peer err:&postError]){
1204 secwarning("Couldn't post retirement (%@)", postError);
1206 if(![account.circle_transport flushChanges:&postError]){
1207 secwarning("Couldn't flush retirement data (%@)", postError);
1209 CFReleaseNull(postError);
1212 SOSAccountPurgeIdentity(account);
1216 CFReleaseNull(localError);
1217 CFReleaseNull(retire_peer);
1221 bool sosAccountLeaveRing(SOSAccount* account, SOSRingRef ring, CFErrorRef* error) {
1222 SOSAccountTrustClassic *trust = account.trust;
1223 SOSFullPeerInfoRef identity = trust.fullPeerInfo;
1225 SOSFullPeerInfoRef fpi = identity;
1226 if(!fpi) return false;
1227 SOSPeerInfoRef pi = SOSFullPeerInfoGetPeerInfo(fpi);
1228 CFStringRef peerID = SOSPeerInfoGetPeerID(pi);
1230 CFErrorRef localError = NULL;
1232 bool retval = false;
1233 bool writeRing = false;
1234 bool writePeerInfo = false;
1236 if(SOSRingHasPeerID(ring, peerID)) {
1237 writePeerInfo = true;
1240 if(writePeerInfo || writeRing) {
1241 SOSRingWithdraw(ring, NULL, fpi, error);
1245 CFDataRef ring_data = SOSRingCopyEncodedData(ring, error);
1248 [account.circle_transport kvsRingPostRing:SOSRingGetName(ring) ring:ring_data err:NULL];
1250 CFReleaseNull(ring_data);
1253 CFReleaseNull(localError);
1257 bool SOSAccountPostDebugScope(SOSAccount* account, CFTypeRef scope, CFErrorRef *error) {
1258 bool result = false;
1259 if (account.circle_transport) {
1260 result = [account.circle_transport kvssendDebugInfo:kSOSAccountDebugScope debug:scope err:error];
1266 NSUbiquitousKeyValueStoreInitialSyncChange is only posted if there is any
1267 local value that has been overwritten by a distant value. If there is no
1268 conflict between the local and the distant values when doing the initial
1269 sync (e.g. if the cloud has no data stored or the client has not stored
1270 any data yet), you'll never see that notification.
1272 NSUbiquitousKeyValueStoreInitialSyncChange implies an initial round trip
1273 with server but initial round trip with server does not imply
1274 NSUbiquitousKeyValueStoreInitialSyncChange.
1279 // MARK: Status summary
1283 CFStringRef SOSAccountGetSOSCCStatusString(SOSCCStatus status) {
1285 case kSOSCCInCircle: return CFSTR("kSOSCCInCircle");
1286 case kSOSCCNotInCircle: return CFSTR("kSOSCCNotInCircle");
1287 case kSOSCCRequestPending: return CFSTR("kSOSCCRequestPending");
1288 case kSOSCCCircleAbsent: return CFSTR("kSOSCCCircleAbsent");
1289 case kSOSCCError: return CFSTR("kSOSCCError");
1291 return CFSTR("kSOSCCError");
1293 SOSCCStatus SOSAccountGetSOSCCStatusFromString(CFStringRef status) {
1294 if(CFEqualSafe(status, CFSTR("kSOSCCInCircle"))) {
1295 return kSOSCCInCircle;
1296 } else if(CFEqualSafe(status, CFSTR("kSOSCCInCircle"))) {
1297 return kSOSCCInCircle;
1298 } else if(CFEqualSafe(status, CFSTR("kSOSCCNotInCircle"))) {
1299 return kSOSCCNotInCircle;
1300 } else if(CFEqualSafe(status, CFSTR("kSOSCCRequestPending"))) {
1301 return kSOSCCRequestPending;
1302 } else if(CFEqualSafe(status, CFSTR("kSOSCCCircleAbsent"))) {
1303 return kSOSCCCircleAbsent;
1304 } else if(CFEqualSafe(status, CFSTR("kSOSCCError"))) {
1311 // MARK: Account Reset Circles
1314 // This needs to be called within a [trust modifyCircle()] block
1317 bool SOSAccountRemoveIncompleteiCloudIdentities(SOSAccount* account, SOSCircleRef circle, SecKeyRef privKey, CFErrorRef *error) {
1318 bool retval = false;
1320 SOSAccountTrustClassic *trust = account.trust;
1321 SOSFullPeerInfoRef identity = trust.fullPeerInfo;
1323 CFMutableSetRef iCloud2Remove = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
1325 SOSCircleForEachActivePeer(circle, ^(SOSPeerInfoRef peer) {
1326 if(SOSPeerInfoIsCloudIdentity(peer)) {
1327 SOSFullPeerInfoRef icfpi = SOSFullPeerInfoCreateCloudIdentity(kCFAllocatorDefault, peer, NULL);
1329 CFSetAddValue(iCloud2Remove, peer);
1331 CFReleaseNull(icfpi);
1335 if(CFSetGetCount(iCloud2Remove) > 0) {
1337 SOSCircleRemovePeers(circle, privKey, identity, iCloud2Remove, error);
1339 CFReleaseNull(iCloud2Remove);
1344 // MARK: start backups
1348 bool SOSAccountEnsureInBackupRings(SOSAccount* account) {
1349 __block bool result = false;
1350 __block CFErrorRef error = NULL;
1351 secnotice("backup", "Ensuring in rings");
1353 NSData *backupKey = nil;
1355 if(!account.backup_key){
1360 backupKey = (__bridge_transfer NSData*)SOSPeerInfoV2DictionaryCopyData(account.peerInfo, sBackupKeyKey);
1362 bool updateBackupKey = ![backupKey isEqual:account.backup_key];
1364 if(updateBackupKey) {
1365 result = SOSAccountUpdatePeerInfo(account, CFSTR("Backup public key"), &error, ^bool(SOSFullPeerInfoRef fpi, CFErrorRef *error) {
1366 return SOSFullPeerInfoUpdateBackupKey(fpi, (__bridge CFDataRef)(account.backup_key), error);
1369 secnotice("backupkey", "Failed to setup backup public key: %@", error ? (CFTypeRef) error : (CFTypeRef) CFSTR("No error space provided"));
1373 if(!account.backup_key)
1376 secnotice("backupkey", "Failed to setup backup public key: %@", error ? (CFTypeRef) error : (CFTypeRef) CFSTR("No error space provided"));
1380 if(!SOSBSKBIsGoodBackupPublic((__bridge CFDataRef)account.backup_key, &error)){
1382 secnotice("backupkey", "Failed to setup backup public key: %@", error ? (CFTypeRef) error : (CFTypeRef) CFSTR("No error space provided"));
1387 CFDataRef recoveryKeyBackFromRing = SOSAccountCopyRecoveryPublic(kCFAllocatorDefault, account, &error);
1389 if(updateBackupKey || recoveryKeyBackFromRing) {
1390 // It's a good key, we're going with it. Stop backing up the old way.
1391 CFErrorRef localError = NULL;
1392 if (!SOSDeleteV0Keybag(&localError)) {
1393 secerror("Failed to delete v0 keybag: %@", localError);
1395 CFReleaseNull(localError);
1399 // Setup backups the new way.
1400 SOSAccountForEachBackupView(account, ^(const void *value) {
1401 CFStringRef viewName = (CFStringRef)value;
1402 if(updateBackupKey || (recoveryKeyBackFromRing && !SOSAccountRecoveryKeyIsInBackupAndCurrentInView(account, viewName))) {
1403 result &= SOSAccountNewBKSBForView(account, viewName, &error);
1409 secnotice("backupkey", "Failed to setup backup public key: %@", error ? (CFTypeRef) error : (CFTypeRef) CFSTR("No error space provided"));
1415 // MARK: Recovery Public Key Functions
1418 bool SOSAccountRegisterRecoveryPublicKey(SOSAccountTransaction* txn, CFDataRef recovery_key, CFErrorRef *error){
1419 bool retval = SOSAccountSetRecoveryKey(txn.account, recovery_key, error);
1420 if(retval) secnotice("recovery", "successfully registered recovery public key");
1421 else secnotice("recovery", "could not register recovery public key: %@", *error);
1422 SOSClearErrorIfTrue(retval, error);
1426 bool SOSAccountClearRecoveryPublicKey(SOSAccountTransaction* txn, CFDataRef recovery_key, CFErrorRef *error){
1427 bool retval = SOSAccountRemoveRecoveryKey(txn.account, error);
1428 if(retval) secnotice("recovery", "RK Cleared");
1429 else secnotice("recovery", "Couldn't clear RK(%@)", *error);
1430 SOSClearErrorIfTrue(retval, error);
1434 CFDataRef SOSAccountCopyRecoveryPublicKey(SOSAccountTransaction* txn, CFErrorRef *error){
1435 CFDataRef result = NULL;
1436 result = SOSAccountCopyRecoveryPublic(kCFAllocatorDefault, txn.account, error);
1437 if(!result) secnotice("recovery", "Could not retrieve the recovery public key from the ring: %@", *error);
1439 if (!isData(result)) {
1440 CFReleaseNull(result);
1442 SOSClearErrorIfTrue(result != NULL, error);
1452 static bool SOSAccountJoinCircle(SOSAccountTransaction* aTxn, SecKeyRef user_key,
1453 bool use_cloud_peer, CFErrorRef* error) {
1454 SOSAccount* account = aTxn.account;
1455 SOSAccountTrustClassic *trust = account.trust;
1456 __block bool result = false;
1457 __block SOSFullPeerInfoRef cloud_full_peer = NULL;
1458 require_action_quiet(trust.trustedCircle, fail, SOSCreateErrorWithFormat(kSOSErrorPeerNotFound, NULL, error, NULL, CFSTR("Don't have circle when joining???")));
1459 require_quiet([account.trust ensureFullPeerAvailable:(__bridge CFDictionaryRef)account.gestalt deviceID:(__bridge CFStringRef)account.deviceID backupKey:(__bridge CFDataRef)account.backup_key err:error], fail);
1460 SOSFullPeerInfoRef myCirclePeer = trust.fullPeerInfo;
1461 if (SOSCircleCountPeers(trust.trustedCircle) == 0 || SOSAccountGhostResultsInReset(account)) {
1462 secnotice("resetToOffering", "Resetting circle to offering since there are no peers");
1463 // this also clears initial sync data
1464 result = [account.trust resetCircleToOffering:aTxn userKey:user_key err:error];
1466 SOSAccountSetValue(account, kSOSUnsyncedViewsKey, kCFBooleanTrue, NULL);
1467 if (use_cloud_peer) {
1468 cloud_full_peer = SOSCircleCopyiCloudFullPeerInfoRef(trust.trustedCircle, NULL);
1470 [account.trust modifyCircle:account.circle_transport err:error action:^bool(SOSCircleRef circle) {
1471 result = SOSAccountAddEscrowToPeerInfo(account, myCirclePeer, error);
1472 result &= SOSCircleRequestAdmission(circle, user_key, myCirclePeer, error);
1473 trust.departureCode = kSOSNeverLeftCircle;
1474 if(result && cloud_full_peer) {
1475 CFErrorRef localError = NULL;
1476 CFStringRef cloudid = SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(cloud_full_peer));
1477 require_quiet(cloudid, finish);
1478 require_quiet(SOSCircleHasActivePeerWithID(circle, cloudid, &localError), finish);
1479 require_quiet(SOSCircleAcceptRequest(circle, user_key, cloud_full_peer, SOSFullPeerInfoGetPeerInfo(myCirclePeer), &localError), finish);
1482 secerror("Failed to join with cloud identity: %@", localError);
1483 CFReleaseNull(localError);
1488 if (use_cloud_peer) {
1489 SOSAccountUpdateOutOfSyncViews(aTxn, SOSViewsGetAllCurrent());
1493 CFReleaseNull(cloud_full_peer);
1497 static bool SOSAccountJoinCircles_internal(SOSAccountTransaction* aTxn, bool use_cloud_identity, CFErrorRef* error) {
1498 SOSAccount* account = aTxn.account;
1499 SOSAccountTrustClassic *trust = account.trust;
1500 bool success = false;
1502 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1503 require_quiet(user_key, done); // Fail if we don't get one.
1505 require_action_quiet(trust.trustedCircle, done, SOSErrorCreate(kSOSErrorNoCircle, error, NULL, CFSTR("No circle to join")));
1507 if (trust.fullPeerInfo != NULL) {
1508 SOSPeerInfoRef myPeer = trust.peerInfo;
1509 success = SOSCircleHasPeer(trust.trustedCircle, myPeer, NULL);
1510 require_quiet(!success, done);
1512 SOSCircleRemoveRejectedPeer(trust.trustedCircle, myPeer, NULL); // If we were rejected we should remove it now.
1514 if (!SOSCircleHasApplicant(trust.trustedCircle, myPeer, NULL)) {
1515 secerror("Resetting my peer (ID: %@) for circle '%@' during application", SOSPeerInfoGetPeerID(myPeer), SOSCircleGetName(trust.trustedCircle));
1517 trust.fullPeerInfo = NULL;
1521 success = SOSAccountJoinCircle(aTxn, user_key, use_cloud_identity, error);
1523 require_quiet(success, done);
1525 trust.departureCode = kSOSNeverLeftCircle;
1531 bool SOSAccountJoinCircles(SOSAccountTransaction* aTxn, CFErrorRef* error) {
1532 secnotice("circleJoin", "Normal path circle join (SOSAccountJoinCircles)");
1533 return SOSAccountJoinCircles_internal(aTxn, false, error);
1536 CFStringRef SOSAccountCopyDeviceID(SOSAccount* account, CFErrorRef *error){
1537 CFStringRef result = NULL;
1538 SOSAccountTrustClassic *trust = account.trust;
1540 require_action_quiet(trust.fullPeerInfo, fail, SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("No peer for me")));
1542 result = SOSPeerInfoCopyDeviceID(trust.peerInfo);
1548 bool SOSAccountSetMyDSID(SOSAccountTransaction* txn, CFStringRef IDS, CFErrorRef* error){
1550 SOSAccount* account = txn.account;
1551 SOSAccountTrustClassic *trust = account.trust;
1553 secdebug("IDS Transport", "We are setting our device ID: %@", IDS);
1554 if(IDS != NULL && (CFStringGetLength(IDS) > 0)){
1555 if(!trust.fullPeerInfo){
1556 account.deviceID = [[NSString alloc] initWithString:(__bridge NSString * _Nonnull)(IDS)];
1557 SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("No peer for me"));
1560 result = [trust modifyCircle:account.circle_transport err:error action:^bool(SOSCircleRef circle) {
1562 SOSFullPeerInfoUpdateDeviceID(trust.fullPeerInfo, IDS, error);
1563 SOSFullPeerInfoUpdateTransportType(trust.fullPeerInfo, SOSTransportMessageTypeIDSV2, error);
1564 SOSFullPeerInfoUpdateTransportPreference(trust.fullPeerInfo, kCFBooleanFalse, error);
1565 SOSFullPeerInfoUpdateTransportFragmentationPreference(trust.fullPeerInfo, kCFBooleanTrue, error);
1566 SOSFullPeerInfoUpdateTransportAckModelPreference(trust.fullPeerInfo, kCFBooleanTrue, error);
1567 return SOSCircleHasPeer(circle, trust.peerInfo, NULL);
1573 // Initiate sync with all IDS peers, since we just learned we can talk that way.
1574 SOSAccountForEachCirclePeerExceptMe(account, ^(SOSPeerInfoRef peer) {
1575 if (SOSPeerInfoShouldUseIDSTransport(account.peerInfo, peer)) {
1576 [txn requestSyncWith:(__bridge NSString*) SOSPeerInfoGetPeerID(peer)];
1580 account.deviceID = [[NSString alloc] initWithString:(__bridge NSString * _Nonnull)(IDS)];
1584 bool SOSAccountSendIDSTestMessage(SOSAccount* account, CFStringRef message, CFErrorRef *error){
1586 //construct message dictionary, circle -> peerID -> message
1588 CFMutableDictionaryRef peerToMessage = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1590 CFStringRef operationString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), kIDSSendOneMessage);
1591 CFDictionaryRef rawMessage = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
1592 kIDSOperationType, operationString,
1593 kIDSMessageToSendKey, CFSTR("send IDS test message"),
1596 SOSAccountForEachCirclePeerExceptMe(account, ^(SOSPeerInfoRef peer) {
1597 CFDictionaryAddValue(peerToMessage, SOSPeerInfoGetPeerID(peer), rawMessage);
1600 result = [account.ids_message_transport SOSTransportMessageSendMessages:account.ids_message_transport pm:peerToMessage err:error];
1602 CFReleaseNull(peerToMessage);
1603 CFReleaseNull(operationString);
1604 CFReleaseNull(rawMessage);
1608 bool SOSAccountStartPingTest(SOSAccount* account, CFStringRef message, CFErrorRef *error){
1609 bool result = false;
1610 //construct message dictionary, circle -> peerID -> message
1611 SOSAccountTrustClassic *trust = account.trust;
1612 if(account.ids_message_transport == NULL)
1613 account.ids_message_transport = [[SOSMessageIDS alloc] initWithAccount:account andName:(__bridge NSString *)(SOSCircleGetName(trust.trustedCircle))];
1615 require_quiet(account.ids_message_transport, fail);
1616 CFMutableDictionaryRef peerToMessage = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1618 CFStringRef operationString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), kIDSStartPingTestMessage);
1619 CFDictionaryRef rawMessage = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
1620 kIDSOperationType, operationString,
1621 kIDSMessageToSendKey, CFSTR("send IDS test message"),
1625 SOSAccountForEachCirclePeerExceptMe(account, ^(SOSPeerInfoRef peer) {
1626 CFDictionaryAddValue(peerToMessage, SOSPeerInfoGetPeerID(peer), rawMessage);
1629 result = [account.ids_message_transport SOSTransportMessageSendMessages:account.ids_message_transport pm:peerToMessage err:error];
1631 CFReleaseNull(peerToMessage);
1632 CFReleaseNull(rawMessage);
1633 CFReleaseNull(operationString);
1638 bool SOSAccountRetrieveDeviceIDFromKeychainSyncingOverIDSProxy(SOSAccount* account, CFErrorRef *error){
1641 __block bool success = true;
1642 __block CFErrorRef localError = NULL;
1643 dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
1645 SOSCloudKeychainGetIDSDeviceID(^(CFDictionaryRef returnedValues, CFErrorRef sync_error){
1646 success = (sync_error == NULL);
1648 CFRetainAssign(localError, sync_error);
1651 dispatch_semaphore_signal(wait_for);
1654 dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER);
1656 if(!success && localError != NULL && error != NULL){
1657 secerror("Could not ask KeychainSyncingOverIDSProxy for Device ID: %@", localError);
1658 *error = localError;
1662 secdebug("IDS Transport", "Attempting to retrieve the IDS Device ID");
1667 bool SOSAccountJoinCirclesAfterRestore(SOSAccountTransaction* aTxn, CFErrorRef* error) {
1668 secnotice("circleJoin", "Joining after restore (SOSAccountJoinCirclesAfterRestore)");
1669 return SOSAccountJoinCircles_internal(aTxn, true, error);
1672 bool SOSAccountRemovePeersFromCircle(SOSAccount* account, CFArrayRef peers, CFErrorRef* error)
1674 bool result = false;
1675 CFMutableSetRef peersToRemove = NULL;
1676 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1678 secnotice("removePeers", "Can't remove without userKey");
1681 SOSFullPeerInfoRef me_full = account.fullPeerInfo;
1682 SOSPeerInfoRef me = account.peerInfo;
1683 if(!(me_full && me))
1685 secnotice("removePeers", "Can't remove without being active peer");
1686 SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("Can't remove without being active peer"));
1690 result = true; // beyond this point failures would be rolled up in AccountModifyCircle.
1692 peersToRemove = CFSetCreateMutableForSOSPeerInfosByIDWithArray(kCFAllocatorDefault, peers);
1695 CFReleaseNull(peersToRemove);
1696 secnotice("removePeers", "No peerSet to remove");
1700 // If we're one of the peers expected to leave - note that and then remove ourselves from the set (different handling).
1701 bool leaveCircle = CFSetContainsValue(peersToRemove, me);
1702 CFSetRemoveValue(peersToRemove, me);
1704 result &= [account.trust modifyCircle:account.circle_transport err:error action:^(SOSCircleRef circle) {
1705 bool success = false;
1707 if(CFSetGetCount(peersToRemove) != 0) {
1708 require_quiet(SOSCircleRemovePeers(circle, user_key, me_full, peersToRemove, error), done);
1709 success = SOSAccountGenerationSignatureUpdate(account, error);
1710 } else success = true;
1712 if (success && leaveCircle) {
1713 secnotice("leaveCircle", "Leaving circle by client request");
1714 success = sosAccountLeaveCircle(account, circle, error);
1722 CFReleaseNull(peersToRemove);
1727 bool SOSAccountBail(SOSAccount* account, uint64_t limit_in_seconds, CFErrorRef* error) {
1728 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1729 dispatch_group_t group = dispatch_group_create();
1730 SOSAccountTrustClassic *trust = account.trust;
1731 __block bool result = false;
1732 secnotice("circle", "Attempting to leave circle - best effort - in %llu seconds\n", limit_in_seconds);
1733 // Add a task to the group
1734 dispatch_group_async(group, queue, ^{
1735 [trust modifyCircle:account.circle_transport err:error action:^(SOSCircleRef circle) {
1736 secnotice("leaveCircle", "Leaving circle by client request");
1737 return sosAccountLeaveCircle(account, circle, error);
1740 dispatch_time_t milestone = dispatch_time(DISPATCH_TIME_NOW, limit_in_seconds * NSEC_PER_SEC);
1741 dispatch_group_wait(group, milestone);
1743 trust.departureCode = kSOSWithdrewMembership;
1750 // MARK: Application
1753 static void for_each_applicant_in_each_circle(SOSAccount* account, CFArrayRef peer_infos,
1754 bool (^action)(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer)) {
1755 SOSAccountTrustClassic *trust = account.trust;
1757 SOSPeerInfoRef me = trust.peerInfo;
1758 CFErrorRef peer_error = NULL;
1759 if (trust.trustedCircle && me &&
1760 SOSCircleHasPeer(trust.trustedCircle, me, &peer_error)) {
1761 [account.trust modifyCircle:account.circle_transport err:NULL action:^(SOSCircleRef circle) {
1762 __block bool modified = false;
1763 CFArrayForEach(peer_infos, ^(const void *value) {
1764 SOSPeerInfoRef peer = (SOSPeerInfoRef) value;
1765 if (isSOSPeerInfo(peer) && SOSCircleHasApplicant(circle, peer, NULL)) {
1766 if (action(circle, trust.fullPeerInfo, peer)) {
1775 secerror("Got error in SOSCircleHasPeer: %@", peer_error);
1776 CFReleaseSafe(peer_error); // TODO: We should be accumulating errors here.
1779 bool SOSAccountAcceptApplicants(SOSAccount* account, CFArrayRef applicants, CFErrorRef* error) {
1780 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1784 __block bool success = true;
1785 __block int64_t num_peers = 0;
1787 for_each_applicant_in_each_circle(account, applicants, ^(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer) {
1788 bool accepted = SOSCircleAcceptRequest(circle, user_key, myCirclePeer, peer, error);
1792 num_peers = MAX(num_peers, SOSCircleCountPeers(circle));
1799 bool SOSAccountRejectApplicants(SOSAccount* account, CFArrayRef applicants, CFErrorRef* error) {
1800 __block bool success = true;
1801 __block int64_t num_peers = 0;
1803 for_each_applicant_in_each_circle(account, applicants, ^(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer) {
1804 bool rejected = SOSCircleRejectRequest(circle, myCirclePeer, peer, error);
1808 num_peers = MAX(num_peers, SOSCircleCountPeers(circle));
1816 CFStringRef SOSAccountCopyIncompatibilityInfo(SOSAccount* account, CFErrorRef* error) {
1817 return CFSTR("We're compatible, go away");
1820 enum DepartureReason SOSAccountGetLastDepartureReason(SOSAccount* account, CFErrorRef* error) {
1821 SOSAccountTrustClassic *trust = account.trust;
1822 return trust.departureCode;
1825 void SOSAccountSetLastDepartureReason(SOSAccount* account, enum DepartureReason reason) {
1826 SOSAccountTrustClassic *trust = account.trust;
1827 trust.departureCode = reason;
1831 CFArrayRef SOSAccountCopyGeneration(SOSAccount* account, CFErrorRef *error) {
1832 CFArrayRef result = NULL;
1833 CFNumberRef generation = NULL;
1834 SOSAccountTrustClassic *trust = account.trust;
1836 require_quiet(SOSAccountHasPublicKey(account, error), fail);
1837 require_action_quiet(trust.trustedCircle, fail, SOSErrorCreate(kSOSErrorNoCircle, error, NULL, CFSTR("No circle")));
1839 generation = (CFNumberRef)SOSCircleGetGeneration(trust.trustedCircle);
1840 result = CFArrayCreateForCFTypes(kCFAllocatorDefault, generation, NULL);
1846 bool SOSValidateUserPublic(SOSAccount* account, CFErrorRef *error) {
1847 if (!SOSAccountHasPublicKey(account, error))
1850 return account.accountKeyIsTrusted;
1853 bool SOSAccountEnsurePeerRegistration(SOSAccount* account, CFErrorRef *error) {
1854 // TODO: this result is never set or used
1856 SOSAccountTrustClassic *trust = account.trust;
1858 secnotice("updates", "Ensuring peer registration.");
1860 if(!trust.trustedCircle || !trust.fullPeerInfo || !account.accountKeyIsTrusted)
1863 // If we are not in the circle, there is no point in setting up peers
1864 if(!SOSAccountIsMyPeerActive(account, NULL))
1867 // This code only uses the SOSFullPeerInfoRef for two things:
1868 // - Finding out if this device is in the trusted circle
1869 // - Using the peerID for this device to see if the current peer is "me"
1870 // - It is used indirectly by passing trust.fullPeerInfo to SOSEngineInitializePeerCoder
1872 CFStringRef my_id = SOSPeerInfoGetPeerID(trust.peerInfo);
1874 SOSCircleForEachValidSyncingPeer(trust.trustedCircle, account.accountKey, ^(SOSPeerInfoRef peer) {
1875 if (!SOSPeerInfoPeerIDEqual(peer, my_id)) {
1876 CFErrorRef localError = NULL;
1878 SOSMessage *messageTransport = SOSPeerInfoHasDeviceID(peer) ? account.ids_message_transport : account.kvs_message_transport;
1880 SOSEngineInitializePeerCoder((SOSEngineRef)[messageTransport SOSTransportMessageGetEngine], trust.fullPeerInfo, peer, &localError);
1882 secnotice("updates", "can't initialize transport for peer %@ with %@ (%@)", peer, trust.fullPeerInfo, localError);
1883 CFReleaseSafe(localError);
1887 //Initialize our device ID
1888 [account.ids_message_transport SOSTransportMessageIDSGetIDSDeviceID:account];
1894 // Value manipulation
1897 CFTypeRef SOSAccountGetValue(SOSAccount* account, CFStringRef key, CFErrorRef *error) {
1898 SOSAccountTrustClassic *trust = account.trust;
1899 if (!trust.expansion) {
1902 return (__bridge CFTypeRef)([trust.expansion objectForKey:(__bridge NSString* _Nonnull)(key)]);
1905 bool SOSAccountAddEscrowRecords(SOSAccount* account, CFStringRef dsid, CFDictionaryRef record, CFErrorRef *error){
1906 CFMutableDictionaryRef escrowRecords = (CFMutableDictionaryRef)SOSAccountGetValue(account, kSOSEscrowRecord, error);
1907 CFMutableDictionaryRef escrowCopied = NULL;
1908 bool success = false;
1910 if(isDictionary(escrowRecords) && escrowRecords != NULL)
1911 escrowCopied = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, CFDictionaryGetCount(escrowRecords), escrowRecords);
1913 escrowCopied = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1915 CFDictionaryAddValue(escrowCopied, dsid, record);
1916 SOSAccountSetValue(account, kSOSEscrowRecord, escrowCopied, error);
1921 CFReleaseNull(escrowCopied);
1927 bool SOSAccountAddEscrowToPeerInfo(SOSAccount* account, SOSFullPeerInfoRef myPeer, CFErrorRef *error){
1928 bool success = false;
1930 CFDictionaryRef escrowRecords = SOSAccountGetValue(account, kSOSEscrowRecord, error);
1931 success = SOSFullPeerInfoReplaceEscrowRecords(myPeer, escrowRecords, error);
1936 bool SOSAccountCheckPeerAvailability(SOSAccount* account, CFErrorRef *error)
1938 CFStringRef operationString = NULL;
1939 CFDictionaryRef rawMessage = NULL;
1940 CFMutableSetRef peers = NULL;
1941 CFMutableDictionaryRef peerList = NULL;
1942 char* message = NULL;
1943 bool result = false;
1944 SOSAccountTrustClassic* trust = account.trust;
1945 if(account.ids_message_transport == NULL)
1946 account.ids_message_transport = [[SOSMessageIDS alloc] initWithAccount:account andName:(__bridge NSString *)SOSCircleGetName(trust.trustedCircle)];
1948 if(!account.ids_message_transport)
1951 //adding message type kIDSPeerAvailability so KeychainSyncingOverIDSProxy does not send this message as a keychain item
1953 operationString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), kIDSPeerAvailability);
1954 rawMessage = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
1955 kIDSOperationType, operationString,
1956 kIDSMessageToSendKey, CFSTR("checking peers"),
1959 peerList = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1960 SOSCircleRef circle = trust.trustedCircle;
1962 //check each peer to make sure they have the right view set enabled
1963 CFSetRef mySubSet = SOSViewsGetV0SubviewSet();
1964 SOSCircleForEachValidPeer(circle, account.accountKey, ^(SOSPeerInfoRef peer) {
1965 if(!CFEqualSafe(peer, account.peerInfo)){
1966 CFMutableSetRef peerViews = SOSPeerInfoCopyEnabledViews(peer);
1967 CFSetRef intersectSets = CFSetCreateIntersection(kCFAllocatorDefault, mySubSet, peerViews);
1968 if(CFEqualSafe(intersectSets, mySubSet)){
1969 CFStringRef deviceID = SOSPeerInfoCopyDeviceID(peer);
1970 if(deviceID != NULL)
1971 CFDictionaryAddValue(peerList, SOSPeerInfoGetPeerID(peer), rawMessage);
1972 CFReleaseNull(deviceID);
1974 CFReleaseNull(peerViews);
1975 CFReleaseNull(intersectSets);
1979 require_quiet(CFDictionaryGetCount(peerList) > 0 , fail);
1981 result = [account.ids_message_transport SOSTransportMessageSendMessages:account.ids_message_transport pm:peerList err:error];
1984 CFReleaseNull(rawMessage);
1985 CFReleaseNull(operationString);
1986 CFReleaseNull(peerList);
1987 CFReleaseNull(peers);
1993 void SOSAccountRecordRetiredPeersInCircle(SOSAccount* account) {
1994 if (![account.trust isInCircle:NULL])
1996 SOSAccountTrustClassic *trust = account.trust;
1997 [trust modifyCircle:account.circle_transport err:NULL action:^bool (SOSCircleRef circle) {
1998 __block bool updated = false;
1999 CFSetForEach((__bridge CFMutableSetRef)trust.retirees, ^(CFTypeRef element){
2000 SOSPeerInfoRef retiree = asSOSPeerInfo(element);
2002 if (retiree && SOSCircleUpdatePeerInfo(circle, retiree)) {
2004 secnotice("retirement", "Updated retired peer %@ in %@", retiree, circle);
2005 CFErrorRef cleanupError = NULL;
2006 if (![account.trust cleanupAfterPeer:account.kvs_message_transport circleTransport:account.circle_transport seconds:RETIREMENT_FINALIZATION_SECONDS circle:circle cleanupPeer:retiree err:&cleanupError])
2007 secerror("Error cleanup up after peer (%@): %@", retiree, cleanupError);
2008 CFReleaseSafe(cleanupError);
2015 static const uint64_t maxTimeToWaitInSeconds = 30ull * NSEC_PER_SEC;
2017 static CFDictionaryRef SOSAccountGetObjectsFromCloud(dispatch_queue_t processQueue, CFErrorRef *error)
2019 __block CFTypeRef object = NULL;
2021 dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
2022 dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds);
2024 CloudKeychainReplyBlock replyBlock =
2025 ^ (CFDictionaryRef returnedValues, CFErrorRef error)
2027 secnotice("key-cleanup", "SOSCloudKeychainGetObjectsFromCloud returned: %@", returnedValues);
2028 object = returnedValues;
2033 secerror("SOSCloudKeychainGetObjectsFromCloud returned error: %@", error);
2035 secnotice("key-cleanup", "SOSCloudKeychainGetObjectsFromCloud block exit: %@", object);
2036 dispatch_semaphore_signal(waitSemaphore);
2039 SOSCloudKeychainGetAllObjectsFromCloud(processQueue, replyBlock);
2041 dispatch_semaphore_wait(waitSemaphore, finishTime);
2042 if (object && (CFGetTypeID(object) == CFNullGetTypeID())) // return a NULL instead of a CFNull
2047 secnotice("key-cleanup", "returned: %@", object);
2048 return asDictionary(object, error);
2052 static void SOSAccountRemoveKVSKeys(SOSAccount* account, NSArray* keysToRemove, dispatch_queue_t processQueue)
2054 CFStringRef uuid = SOSAccountCopyUUID(account);
2055 dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
2056 dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds);
2058 CloudKeychainReplyBlock replyBlock = ^ (CFDictionaryRef returnedValues, CFErrorRef error){
2060 secerror("SOSCloudKeychainRemoveKeys returned error: %@", error);
2062 dispatch_semaphore_signal(waitSemaphore);
2065 SOSCloudKeychainRemoveKeys((__bridge CFArrayRef)(keysToRemove), uuid, processQueue, replyBlock);
2066 dispatch_semaphore_wait(waitSemaphore, finishTime);
2070 static void SOSAccountWriteLastCleanupTimestampToKVS(SOSAccount* account)
2072 NSMutableDictionary *writeTimestamp = [NSMutableDictionary dictionary];
2074 CFMutableStringRef timeDescription = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, CFSTR("["));
2075 CFAbsoluteTime currentTimeAndDate = CFAbsoluteTimeGetCurrent();
2077 withStringOfAbsoluteTime(currentTimeAndDate, ^(CFStringRef decription) {
2078 CFStringAppend(timeDescription, decription);
2080 CFStringAppend(timeDescription, CFSTR("]"));
2082 [writeTimestamp setObject:(__bridge NSString*)(timeDescription) forKey:(__bridge NSString*)kSOSKVSLastCleanupTimestampKey];
2084 dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
2085 dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds);
2086 dispatch_queue_t processQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
2088 CloudKeychainReplyBlock replyBlock = ^ (CFDictionaryRef returnedValues, CFErrorRef error){
2090 secerror("SOSCloudKeychainPutObjectsInCloud returned error: %@", error);
2092 dispatch_semaphore_signal(waitSemaphore);
2095 SOSCloudKeychainPutObjectsInCloud((__bridge CFDictionaryRef)(writeTimestamp), processQueue, replyBlock);
2096 dispatch_semaphore_wait(waitSemaphore, finishTime);
2099 //Get all the key/values in KVS and remove old entries
2100 bool SOSAccountCleanupAllKVSKeys(SOSAccount* account, CFErrorRef* error)
2102 dispatch_queue_t processQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
2104 NSDictionary *keysAndValues = (__bridge_transfer NSDictionary*)SOSAccountGetObjectsFromCloud(processQueue, error);
2105 NSMutableArray *peerIDs = [NSMutableArray array];
2106 NSMutableArray *keysToRemove = [NSMutableArray array];
2108 CFArrayForEach(SOSAccountCopyActiveValidPeers(account, error), ^(const void *value) {
2109 SOSPeerInfoRef peer = (SOSPeerInfoRef)value;
2110 NSString* peerID = (__bridge NSString*) SOSPeerInfoGetPeerID(peer);
2112 //any peerid that is not ours gets added
2113 if(![[account.trust peerID] isEqualToString:peerID])
2114 [peerIDs addObject:peerID];
2117 [keysAndValues enumerateKeysAndObjectsUsingBlock:^(NSString * KVSKey, NSNumber * KVSValue, BOOL *stop) {
2118 __block bool keyMatchesPeerID = false;
2120 //checks for full peer ids
2121 [peerIDs enumerateObjectsUsingBlock:^(id _Nonnull PeerID, NSUInteger idx, BOOL * _Nonnull stop) {
2122 //if key contains peerid of one active peer
2123 if([KVSKey containsString:PeerID]){
2124 secnotice("key-cleanup","key: %@", KVSKey);
2125 keyMatchesPeerID = true;
2128 if((([KVSKey hasPrefix:@"ak"] || [KVSKey hasPrefix:@"-ak"]) && !keyMatchesPeerID)
2129 || [KVSKey hasPrefix:@"po"])
2130 [keysToRemove addObject:KVSKey];
2133 secnotice("key-cleanup", "message keys that we should remove! %@", keysToRemove);
2134 secnotice("key-cleanup", "total keys: %lu, cleaning up %lu", (unsigned long)[keysAndValues count], (unsigned long)[keysToRemove count]);
2136 SOSAccountRemoveKVSKeys(account, keysToRemove, processQueue);
2138 //add last cleanup timestamp
2139 SOSAccountWriteLastCleanupTimestampToKVS(account);
2144 bool SOSAccountPopulateKVSWithBadKeys(SOSAccount* account, CFErrorRef* error) {
2146 NSMutableDictionary *testKeysAndValues = [NSMutableDictionary dictionary];
2147 [testKeysAndValues setObject:@"deadbeef" forKey:@"-ak|asdfjkl;asdfjk;"];
2148 [testKeysAndValues setObject:@"foobar" forKey:@"ak|asdfasdfasdf:qwerqwerqwer"];
2149 [testKeysAndValues setObject:@"oldhistorycircle" forKey:@"poak|asdfasdfasdfasdf"];
2150 [testKeysAndValues setObject:@"oldhistorycircle" forKey:@"po|asdfasdfasdfasdfasdfasdf"];
2151 [testKeysAndValues setObject:@"oldhistorycircle" forKey:@"k>KeyParm"];
2153 dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
2154 dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds);
2155 dispatch_queue_t processQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
2157 CloudKeychainReplyBlock replyBlock = ^ (CFDictionaryRef returnedValues, CFErrorRef error){
2159 secerror("SOSCloudKeychainPutObjectsInCloud returned error: %@", error);
2161 dispatch_semaphore_signal(waitSemaphore);
2164 SOSCloudKeychainPutObjectsInCloud((__bridge CFDictionaryRef)(testKeysAndValues), processQueue, replyBlock);
2165 dispatch_semaphore_wait(waitSemaphore, finishTime);
2170 SOSPeerInfoRef SOSAccountCopyApplication(SOSAccount* account, CFErrorRef* error) {
2171 SOSPeerInfoRef applicant = NULL;
2172 SOSAccountTrustClassic *trust = account.trust;
2173 SecKeyRef userKey = SOSAccountGetPrivateCredential(account, error);
2174 if(!userKey) return false;
2175 if(![trust ensureFullPeerAvailable:(__bridge CFDictionaryRef)(account.gestalt) deviceID:(__bridge CFStringRef)(account.deviceID) backupKey:(__bridge CFDataRef)(account.backup_key) err:error])
2177 if(!SOSFullPeerInfoPromoteToApplication(trust.fullPeerInfo, userKey, error))
2179 applicant = SOSPeerInfoCreateCopy(kCFAllocatorDefault, trust.peerInfo, error);
2186 AddStrippedResults(NSMutableArray *results, NSArray *keychainItems, NSMutableSet *seenUUID, bool authoriative)
2188 [keychainItems enumerateObjectsUsingBlock:^(NSDictionary* keychainItem, NSUInteger idx, BOOL * _Nonnull stop) {
2189 NSString* parentUUID = keychainItem[(id)kSecAttrPath];
2190 NSString* viewUUID = keychainItem[(id)kSecAttrAccount];
2191 NSString *viewName = [keychainItem objectForKey:(id)kSecAttrServer];
2193 if (parentUUID == NULL || viewUUID == NULL || viewName == NULL)
2196 if([parentUUID isEqualToString:viewUUID] || authoriative){
2198 /* check if we already have this entry */
2199 if ([seenUUID containsObject:viewUUID])
2202 NSData* v_data = [keychainItem objectForKey:(id)kSecValueData];
2203 NSData *key = [[NSData alloc] initWithBase64EncodedData:v_data options:0];
2208 secnotice("piggy", "fetched TLK %@ with name %@", viewName, viewUUID);
2210 NSMutableDictionary* strippedDown = [@{
2211 (id)kSecValueData : key,
2212 (id)kSecAttrServer : viewName,
2213 (id)kSecAttrAccount : viewUUID
2216 strippedDown[@"auth"] = @YES;
2218 [results addObject:strippedDown];
2219 [seenUUID addObject:viewUUID];
2225 AddViewManagerResults(NSMutableArray *results, NSMutableSet *seenUUID)
2228 CKKSViewManager* manager = [CKKSViewManager manager];
2230 NSDictionary<NSString *,NSString *> *items = [manager activeTLKs];
2232 for (NSString *view in items) {
2233 NSString *uuid = items[view];
2234 NSDictionary *query = @{
2235 (id)kSecClass : (id)kSecClassInternetPassword,
2236 (id)kSecAttrNoLegacy : @YES,
2237 (id)kSecAttrAccessGroup : @"com.apple.security.ckks",
2238 (id)kSecAttrAccount : uuid,
2239 (id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
2240 (id)kSecMatchLimit : (id)kSecMatchLimitAll,
2241 (id)kSecReturnAttributes: @YES,
2242 (id)kSecReturnData: @YES,
2244 CFTypeRef result = NULL;
2245 if (SecItemCopyMatching((__bridge CFDictionaryRef)query, &result) == 0) {
2246 AddStrippedResults(results, (__bridge NSArray*)result, seenUUID, true);
2248 CFReleaseNull(result);
2255 SOSAccountGetAllTLKs(void)
2257 CFTypeRef result = NULL;
2258 NSMutableArray* results = [NSMutableArray array];
2259 NSMutableSet *seenUUID = [NSMutableSet set];
2261 // first use the TLK from the view manager
2262 AddViewManagerResults(results, seenUUID);
2264 //try to grab tlk-piggy items
2265 NSDictionary* query = @{
2266 (id)kSecClass : (id)kSecClassInternetPassword,
2267 (id)kSecAttrNoLegacy : @YES,
2268 (id)kSecAttrAccessGroup : @"com.apple.security.ckks",
2269 (id)kSecAttrDescription: @"tlk",
2270 (id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
2271 (id)kSecMatchLimit : (id)kSecMatchLimitAll,
2272 (id)kSecReturnAttributes: @YES,
2273 (id)kSecReturnData: @YES,
2276 if (SecItemCopyMatching((__bridge CFDictionaryRef)query, &result) == 0) {
2277 AddStrippedResults(results, (__bridge NSArray*)result, seenUUID, false);
2279 CFReleaseNull(result);
2281 //try to grab tlk-piggy items
2283 (id)kSecClass : (id)kSecClassInternetPassword,
2284 (id)kSecAttrNoLegacy : @YES,
2285 (id)kSecAttrAccessGroup : @"com.apple.security.ckks",
2286 (id)kSecAttrDescription: @"tlk-piggy",
2287 (id)kSecAttrSynchronizable : (id)kSecAttrSynchronizableAny,
2288 (id)kSecMatchLimit : (id)kSecMatchLimitAll,
2289 (id)kSecReturnAttributes: @YES,
2290 (id)kSecReturnData: @YES,
2293 if (SecItemCopyMatching((__bridge CFDictionaryRef)query, &result) == 0) {
2294 AddStrippedResults(results, (__bridge NSArray*)result, seenUUID, false);
2296 CFReleaseNull(result);
2298 secnotice("piggy", "Found %d TLKs", (int)[results count]);
2303 static uint8_t* encode_tlk(kTLKTypes type, NSString *name, NSData *keychainData, NSData* uuid,
2304 const uint8_t *der, uint8_t *der_end)
2306 if (type != kTLKUnknown) {
2307 return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
2308 piggy_encode_data(keychainData, der,
2309 piggy_encode_data(uuid, der,
2310 ccder_encode_uint64((uint64_t)type, der, der_end))));
2312 return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
2313 piggy_encode_data(keychainData, der,
2314 piggy_encode_data(uuid, der,
2315 der_encode_string((__bridge CFStringRef)name, NULL, der, der_end))));
2319 static uint8_t* piggy_encode_data(NSData* data,
2320 const uint8_t *der, uint8_t *der_end)
2322 return ccder_encode_tl(CCDER_OCTET_STRING, data.length, der,
2323 ccder_encode_body(data.length, data.bytes, der, der_end));
2328 name2type(NSString *view)
2330 if ([view isEqualToString:@"Manatee"])
2332 else if ([view isEqualToString:@"Engram"])
2334 else if ([view isEqualToString:@"AutoUnlock"])
2335 return kTLKAutoUnlock;
2336 if ([view isEqualToString:@"Health"])
2342 rank_type(NSString *view)
2344 if ([view isEqualToString:@"Manatee"])
2346 else if ([view isEqualToString:@"Engram"])
2348 else if ([view isEqualToString:@"AutoUnlock"])
2350 if ([view isEqualToString:@"Health"])
2356 parse_uuid(NSString *uuidString)
2358 NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:uuidString];
2360 [uuid getUUIDBytes:uuidblob];
2361 return [NSData dataWithBytes:uuidblob length:sizeof(uuid_t)];
2364 piggy_sizeof_data(NSData* data)
2366 return ccder_sizeof(CCDER_OCTET_STRING, [data length]);
2369 static size_t sizeof_keychainitem(kTLKTypes type, NSString *name, NSData* keychainData, NSData* uuid) {
2370 if (type != kTLKUnknown) {
2371 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE,
2372 piggy_sizeof_data(keychainData) +
2373 piggy_sizeof_data(uuid) +
2374 ccder_sizeof_uint64(type));
2376 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE,
2377 piggy_sizeof_data(keychainData) +
2378 piggy_sizeof_data(uuid) +
2379 der_sizeof_string((__bridge CFStringRef)name, NULL));
2383 NSArray<NSDictionary*>*
2384 SOSAccountSortTLKS(NSArray<NSDictionary*>* tlks)
2386 NSMutableArray<NSDictionary*>* sortedTLKs = [tlks mutableCopy];
2388 [sortedTLKs sortUsingComparator:^NSComparisonResult(NSDictionary *obj1, NSDictionary *obj2) {
2389 unsigned rank1 = rank_type(obj1[(__bridge id)kSecAttrServer]);
2390 if (obj1[@"auth"] != NULL)
2392 unsigned rank2 = rank_type(obj2[(__bridge id)kSecAttrServer]);
2393 if (obj2[@"auth"] != NULL)
2397 * Sort by rank (higher better), but prefer TLK that are authoriative (ie used by CKKSViewManager),
2398 * since we are sorting backward, the Ascending/Descending looks wrong below.
2400 if (rank1 > rank2) {
2401 return NSOrderedAscending;
2402 } else if (rank1 < rank2) {
2403 return NSOrderedDescending;
2405 return NSOrderedSame;
2411 static NSArray<NSData *> *
2412 build_tlks(NSArray<NSDictionary*>* tlks)
2414 NSMutableArray *array = [NSMutableArray array];
2415 NSArray<NSDictionary*>* sortedTLKs = SOSAccountSortTLKS(tlks);
2417 for (NSDictionary *item in sortedTLKs) {
2418 NSData* keychainData = item[(__bridge id)kSecValueData];
2419 NSString* name = item[(__bridge id)kSecAttrServer];
2420 NSString *uuidString = item[(__bridge id)kSecAttrAccount];
2421 NSData* uuid = parse_uuid(uuidString);
2423 NSMutableData *tlk = [NSMutableData dataWithLength:sizeof_keychainitem(name2type(name), name, keychainData, uuid)];
2425 unsigned char *der = [tlk mutableBytes];
2426 unsigned char *der_end = der + [tlk length];
2428 if (encode_tlk(name2type(name), name, keychainData, uuid, der, der_end) == NULL)
2431 secnotice("piggy", "preparing TLK in order: %@: %@", name, uuidString);
2433 [array addObject:tlk];
2438 static NSArray<NSData *> *
2439 build_identities(NSArray<NSData *>* identities)
2441 NSMutableArray *array = [NSMutableArray array];
2442 for (NSData *item in identities) {
2443 NSMutableData *ident = [NSMutableData dataWithLength:ccder_sizeof_raw_octet_string([item length])];
2445 unsigned char *der = [ident mutableBytes];
2446 unsigned char *der_end = der + [ident length];
2448 ccder_encode_raw_octet_string([item length], [item bytes], der, der_end);
2449 [array addObject:ident];
2456 static unsigned char *
2457 encode_data_array(NSArray<NSData*>* data, unsigned char *der, unsigned char *der_end)
2459 unsigned char *body_end = der_end;
2460 for (NSData *datum in data) {
2461 der_end = ccder_encode_body([datum length], [datum bytes], der, der_end);
2462 if (der_end == NULL)
2465 return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, body_end, der, der_end);
2468 static size_t sizeof_piggy(size_t identities_size, size_t tlk_size)
2470 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE,
2471 ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, identities_size) +
2472 ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, tlk_size));
2475 static NSData *encode_piggy(size_t IdentitiesBudget,
2477 NSArray<NSData*>* identities,
2478 NSArray<NSDictionary*>* tlks)
2480 NSArray<NSData *> *encodedTLKs = build_tlks(tlks);
2481 NSArray<NSData *> *encodedIdentities = build_identities(identities);
2482 NSMutableArray<NSData *> *budgetArray = [NSMutableArray array];
2483 NSMutableArray<NSData *> *identitiesArray = [NSMutableArray array];
2484 size_t payloadSize = 0, identitiesSize = 0;
2485 NSMutableData *result = NULL;
2487 for (NSData *tlk in encodedTLKs) {
2488 if (TLKBudget - payloadSize < [tlk length])
2490 [budgetArray addObject:tlk];
2491 payloadSize += tlk.length;
2493 secnotice("piggy", "sending %d tlks", (int)budgetArray.count);
2495 for (NSData *ident in encodedIdentities) {
2496 if (IdentitiesBudget - identitiesSize < [ident length])
2498 [identitiesArray addObject:ident];
2499 identitiesSize += ident.length;
2501 secnotice("piggy", "sending %d identities", (int)identitiesArray.count);
2504 size_t piggySize = sizeof_piggy(identitiesSize, payloadSize);
2506 result = [NSMutableData dataWithLength:piggySize];
2508 unsigned char *der = [result mutableBytes];
2509 unsigned char *der_end = der + [result length];
2511 if (ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
2512 encode_data_array(identitiesArray, der,
2513 encode_data_array(budgetArray, der, der_end))) != [result mutableBytes])
2519 static const size_t SOSCCIdentitiesBudget = 120;
2520 static const size_t SOSCCTLKBudget = 500;
2523 SOSPiggyCreateInitialSyncData(NSArray<NSData*>* identities, NSArray<NSDictionary *>* tlks)
2525 return encode_piggy(SOSCCIdentitiesBudget, SOSCCTLKBudget, identities, tlks);
2528 CFMutableArrayRef SOSAccountCopyiCloudIdentities(SOSAccount* account)
2530 CFMutableArrayRef identities = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
2532 SOSCircleForEachActivePeer(account.trust.trustedCircle, ^(SOSPeerInfoRef peer) {
2533 if(SOSPeerInfoIsCloudIdentity(peer)) {
2534 CFArrayAppendValue(identities, peer);
2540 CFDataRef SOSAccountCopyInitialSyncData(SOSAccount* account, CFErrorRef *error) {
2541 CFMutableArrayRef identities = SOSAccountCopyiCloudIdentities(account);
2542 secnotice("piggy", "identities: %@", identities);
2544 NSMutableArray *encodedIdenities = [NSMutableArray array];
2545 CFIndex i, count = CFArrayGetCount(identities);
2546 for (i = 0; i < count; i++) {
2547 SOSPeerInfoRef fpi = (SOSPeerInfoRef)CFArrayGetValueAtIndex(identities, i);
2548 NSData *data = CFBridgingRelease(SOSPeerInfoCopyData(fpi, error));
2550 [encodedIdenities addObject:data];
2552 CFRelease(identities);
2554 NSMutableArray* tlks = SOSAccountGetAllTLKs();
2556 return CFBridgingRetain(SOSPiggyCreateInitialSyncData(encodedIdenities, tlks));
2559 CFDataRef SOSAccountCopyCircleJoiningBlob(SOSAccount* account, SOSPeerInfoRef applicant, CFErrorRef *error) {
2560 SOSGenCountRef gencount = NULL;
2561 CFDataRef signature = NULL;
2562 SecKeyRef ourKey = NULL;
2564 CFDataRef pbblob = NULL;
2566 secnotice("circleJoin", "Making circle joining blob as sponsor (SOSAccountCopyCircleJoiningBlob)");
2568 SecKeyRef userKey = SOSAccountGetTrustedPublicCredential(account, error);
2569 require_quiet(userKey, errOut);
2571 require_action_quiet(applicant, errOut, SOSCreateError(kSOSErrorProcessingFailure, CFSTR("No applicant provided"), (error != NULL) ? *error : NULL, error));
2572 require_quiet(SOSPeerInfoApplicationVerify(applicant, userKey, error), errOut);
2575 SOSFullPeerInfoRef fpi = account.fullPeerInfo;
2576 ourKey = SOSFullPeerInfoCopyDeviceKey(fpi, error);
2577 require_quiet(ourKey, errOut);
2580 SOSCircleRef currentCircle = [account.trust getCircle:error];
2581 require_quiet(currentCircle, errOut);
2583 SOSCircleRef prunedCircle = SOSCircleCopyCircle(NULL, currentCircle, error);
2584 require_quiet(prunedCircle, errOut);
2585 require_quiet(SOSCirclePreGenerationSign(prunedCircle, userKey, error), errOut);
2587 gencount = SOSGenerationIncrementAndCreate(SOSCircleGetGeneration(prunedCircle));
2589 signature = SOSCircleCopyNextGenSignatureWithPeerAdded(prunedCircle, applicant, ourKey, error);
2590 require_quiet(signature, errOut);
2592 pbblob = SOSPiggyBackBlobCopyEncodedData(gencount, ourKey, signature, error);
2595 CFReleaseNull(gencount);
2596 CFReleaseNull(signature);
2597 CFReleaseNull(ourKey);
2599 if(!pbblob && error != NULL) {
2600 secnotice("circleJoin", "Failed to make circle joining blob as sponsor %@", *error);
2606 bool SOSAccountJoinWithCircleJoiningBlob(SOSAccount* account, CFDataRef joiningBlob, PiggyBackProtocolVersion version, CFErrorRef *error) {
2607 bool retval = false;
2608 SecKeyRef userKey = NULL;
2609 SOSAccountTrustClassic *trust = account.trust;
2610 SOSGenCountRef gencount = NULL;
2611 CFDataRef signature = NULL;
2612 SecKeyRef pubKey = NULL;
2613 bool setInitialSyncTimeoutToV0 = false;
2615 secnotice("circleJoin", "Joining circles through piggy-back (SOSAccountCopyCircleJoiningBlob)");
2617 if (!isData(joiningBlob))
2620 userKey = SOSAccountGetPrivateCredential(account, error);
2624 if (!SOSPiggyBackBlobCreateFromData(&gencount, &pubKey, &signature, joiningBlob, version, &setInitialSyncTimeoutToV0, error))
2627 if(setInitialSyncTimeoutToV0){
2628 secnotice("piggy", "setting flag in account for piggybacking v0");
2629 SOSAccountSetValue(account, kSOSInitialSyncTimeoutV0, kCFBooleanTrue, NULL);
2632 secnotice("piggy", "setting flag in account for piggybacking v0");
2633 SOSAccountClearValue(account, kSOSInitialSyncTimeoutV0, NULL);
2635 SOSAccountSetValue(account, kSOSUnsyncedViewsKey, kCFBooleanTrue, NULL);
2637 retval = [trust modifyCircle:account.circle_transport err:error action:^bool(SOSCircleRef copyOfCurrent) {
2638 return SOSCircleAcceptPeerFromHSA2(copyOfCurrent, userKey,
2642 trust.fullPeerInfo, error);;
2646 CFReleaseNull(gencount);
2647 CFReleaseNull(pubKey);
2648 CFReleaseNull(signature);
2653 static char boolToChars(bool val, char truechar, char falsechar) {
2654 return val? truechar: falsechar;
2657 #define ACCOUNTLOGSTATE "accountLogState"
2658 void SOSAccountLogState(SOSAccount* account) {
2659 bool hasPubKey = account.accountKey != NULL;
2660 SOSAccountTrustClassic *trust = account.trust;
2661 bool pubTrusted = account.accountKeyIsTrusted;
2662 bool hasPriv = account.accountPrivateKey != NULL;
2663 SOSCCStatus stat = [account getCircleStatus:NULL];
2665 CFStringRef userPubKeyID = (account.accountKey) ? SOSCopyIDOfKeyWithLength(account.accountKey, 8, NULL):
2666 CFStringCreateCopy(kCFAllocatorDefault, CFSTR("*No Key*"));
2668 secnotice(ACCOUNTLOGSTATE, "Start");
2670 secnotice(ACCOUNTLOGSTATE, "ACCOUNT: [keyStatus: %c%c%c hpub %@] [SOSCCStatus: %@]",
2671 boolToChars(hasPubKey, 'U', 'u'), boolToChars(pubTrusted, 'T', 't'), boolToChars(hasPriv, 'I', 'i'),
2673 SOSAccountGetSOSCCStatusString(stat)
2675 CFReleaseNull(userPubKeyID);
2676 if(trust.trustedCircle) SOSCircleLogState(ACCOUNTLOGSTATE, trust.trustedCircle, account.accountKey, (__bridge CFStringRef)(account.peerID));
2677 else secnotice(ACCOUNTLOGSTATE, "ACCOUNT: No Circle");
2680 void SOSAccountLogViewState(SOSAccount* account) {
2681 bool isInCircle = [account.trust isInCircle:NULL];
2682 require_quiet(isInCircle, imOut);
2683 SOSPeerInfoRef mpi = account.peerInfo;
2684 bool isInitialComplete = SOSAccountHasCompletedInitialSync(account);
2685 bool isBackupComplete = SOSAccountHasCompletedRequiredBackupSync(account);
2687 CFSetRef views = mpi ? SOSPeerInfoCopyEnabledViews(mpi) : NULL;
2688 CFStringSetPerformWithDescription(views, ^(CFStringRef description) {
2689 secnotice(ACCOUNTLOGSTATE, "Sync: %c%c PeerViews: %@",
2690 boolToChars(isInitialComplete, 'I', 'i'),
2691 boolToChars(isBackupComplete, 'B', 'b'),
2694 CFReleaseNull(views);
2695 CFSetRef unsyncedViews = SOSAccountCopyOutstandingViews(account);
2696 CFStringSetPerformWithDescription(views, ^(CFStringRef description) {
2697 secnotice(ACCOUNTLOGSTATE, "outstanding views: %@", description);
2699 CFReleaseNull(unsyncedViews);
2702 secnotice(ACCOUNTLOGSTATE, "Finish");
2708 void SOSAccountSetTestSerialNumber(SOSAccount* account, CFStringRef serial) {
2709 if(!isString(serial)) return;
2710 CFMutableDictionaryRef newv2dict = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
2711 CFDictionarySetValue(newv2dict, sSerialNumberKey, serial);
2712 [account.trust updateV2Dictionary:account v2:newv2dict];
2715 void SOSAccountResetOTRNegotiationCoder(SOSAccountTransaction* txn, CFStringRef peerid)
2717 secnotice("otrtimer", "timer fired!");
2718 CFErrorRef error = NULL;
2719 SecADAddValueForScalarKey((__bridge CFStringRef) SecSOSAggdReattemptOTRNegotiation,1);
2720 __block SOSAccount* account = txn.account;
2722 SOSEngineRef engine = SOSDataSourceFactoryGetEngineForDataSourceName(account.factory, SOSCircleGetName(account.trust.trustedCircle), NULL);
2723 SOSEngineWithPeerID(engine, peerid, &error, ^(SOSPeerRef peer, SOSCoderRef coder, SOSDataSourceRef dataSource, SOSTransactionRef txn, bool *forceSaveState) {
2724 if(SOSCoderIsCoderInAwaitingState(coder)){
2725 secnotice("otrtimer", "coder is in awaiting state, restarting coder");
2726 CFErrorRef localError = NULL;
2727 SOSCoderReset(coder);
2728 if(SOSCoderStart(coder, &localError) == kSOSCoderFailure){
2729 secerror("Attempt to recover coder failed to restart: %@", localError);
2732 secnotice("otrtimer", "coder restarted!");
2733 SOSEngineSetCodersNeedSaving(engine, true);
2734 SOSPeerSetMustSendMessage(peer, true);
2735 SOSCCRequestSyncWithPeer(SOSPeerGetID(peer));
2737 SOSPeerOTRTimerIncreaseOTRNegotiationRetryCount(account, (__bridge NSString*)SOSPeerGetID(peer));
2738 SOSPeerRemoveOTRTimerEntry(peer);
2739 SOSPeerOTRTimerRemoveRTTTimeoutForPeer(account, (__bridge NSString*)SOSPeerGetID(peer));
2740 SOSPeerOTRTimerRemoveLastSentMessageTimestamp(account, (__bridge NSString*)SOSPeerGetID(peer));
2743 secnotice("otrtimer", "time fired but out of negotiation! Not restarting coder");
2748 secnotice("otrtimer","error grabbing engine for peer id: %@, error:%@", peerid, error);
2750 CFReleaseNull(error);
2753 void SOSAccountTimerFiredSendNextMessage(SOSAccountTransaction* txn, NSString* peerid, NSString* accessGroup)
2755 __block SOSAccount* account = txn.account;
2756 CFErrorRef error = NULL;
2758 SOSEngineRef engine = SOSDataSourceFactoryGetEngineForDataSourceName(txn.account.factory, SOSCircleGetName(account.trust.trustedCircle), NULL);
2759 SOSEngineWithPeerID(engine, (__bridge CFStringRef)peerid, &error, ^(SOSPeerRef peer, SOSCoderRef coder, SOSDataSourceRef dataSource, SOSTransactionRef txn, bool *forceSaveState) {
2761 NSString *peer_id = (__bridge NSString*)SOSPeerGetID(peer);
2762 PeerRateLimiter *limiter = (__bridge PeerRateLimiter*)SOSPeerGetRateLimiter(peer);
2763 CFErrorRef error = NULL;
2764 NSData* message = [limiter.accessGroupToNextMessageToSend objectForKey:accessGroup];
2767 secnotice("ratelimit","SOSPeerRateLimiter timer went off! sending:%@ \n to peer:%@", message, peer_id);
2768 bool sendResult = [account.ids_message_transport SOSTransportMessageSendMessage:account.ids_message_transport id:(__bridge CFStringRef)peer_id messageToSend:(__bridge CFDataRef)message err:&error];
2770 if(!sendResult || error){
2771 secnotice("ratelimit", "could not send message: %@", error);
2774 [limiter.accessGroupRateLimitState setObject:[[NSNumber alloc]initWithLong:RateLimitStateCanSend] forKey:accessGroup];
2775 [limiter.accessGroupToTimer removeObjectForKey:accessGroup];
2776 [limiter.accessGroupToNextMessageToSend removeObjectForKey:accessGroup];
2781 secnotice("otrtimer","error grabbing engine for peer id: %@, error:%@", peerid, error);
2783 CFReleaseNull(error);