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 -(id) initWithGestalt:(CFDictionaryRef)newGestalt factory:(SOSDataSourceFactoryRef)f
382 self.queue = dispatch_queue_create("Account Queue", DISPATCH_QUEUE_SERIAL);
384 self.gestalt = [[NSDictionary alloc] initWithDictionary:(__bridge NSDictionary * _Nonnull)(newGestalt)];
386 SOSAccountTrustClassic *t = [[SOSAccountTrustClassic alloc] initWithRetirees:[NSMutableSet set] fpi:NULL circle:NULL departureCode:kSOSDepartureReasonError peerExpansion:[NSMutableDictionary dictionary]];
389 self.factory = f; // We adopt the factory. kthanksbai.
391 self.isListeningForSync = false;
393 self.accountPrivateKey = NULL;
394 self._password_tmp = NULL;
395 self.user_private_timer = NULL;
396 self.lock_notification_token = NOTIFY_TOKEN_INVALID;
398 self.change_blocks = [NSMutableArray array];
399 self.waitForInitialSync_blocks = NULL;
401 self.key_transport = nil;
402 self.circle_transport = NULL;
403 self.ck_storage = nil;
404 self.kvs_message_transport = nil;
405 self.ids_message_transport = nil;
407 self.circle_rings_retirements_need_attention = false;
408 self.engine_peer_state_needs_repair = false;
409 self.key_interests_need_updating = false;
411 self.backup_key =nil;
414 self.waitForInitialSync_blocks = [NSMutableDictionary dictionary];
415 self.isInitialSyncing = false;
416 self.accountKeyIsTrusted = false;
417 self.accountKeyDerivationParamters = NULL;
418 self.accountKey = NULL;
419 self.previousAccountKey = NULL;
421 self.saveBlock = nil;
423 self.listener = [NSXPCListener anonymousListener];
424 self.listener.delegate = self;
425 [self.listener resume];
430 -(BOOL)isEqual:(id) object
432 if(![object isKindOfClass:[SOSAccount class]])
435 SOSAccount* left = object;
436 return ([self.gestalt isEqual: left.gestalt] &&
437 CFEqualSafe(self.trust.trustedCircle, left.trust.trustedCircle) &&
438 [self.trust.expansion isEqual: left.trust.expansion] &&
439 CFEqualSafe(self.trust.fullPeerInfo, left.trust.fullPeerInfo));
443 - (void)userPublicKey:(void ((^))(BOOL trusted, NSData *spki, NSError *error))reply
445 dispatch_async(self.queue, ^{
446 if (!self.accountKeyIsTrusted || self.accountKey == NULL) {
447 NSDictionary *userinfo = @{
448 (id)kCFErrorDescriptionKey : @"User public key not trusted",
450 reply(self.accountKeyIsTrusted, NULL, [NSError errorWithDomain:(__bridge NSString *)kSOSErrorDomain code:kSOSErrorPublicKeyAbsent userInfo:userinfo]);
454 NSData *data = CFBridgingRelease(SecKeyCopySubjectPublicKeyInfo(self.accountKey));
456 NSDictionary *userinfo = @{
457 (id)kCFErrorDescriptionKey : @"User public not available",
459 reply(self.accountKeyIsTrusted, NULL, [NSError errorWithDomain:(__bridge NSString *)kSOSErrorDomain code:kSOSErrorPublicKeyAbsent userInfo:userinfo]);
462 reply(self.accountKeyIsTrusted, data, NULL);
466 - (void)kvsPerformanceCounters:(void(^)(NSDictionary <NSString *, NSNumber *> *))reply
468 /* Need to collect performance counters from all subsystems, not just circle transport, don't have counters yet though */
469 SOSCloudKeychainRequestPerfCounters(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFDictionaryRef returnedValues, CFErrorRef error)
471 reply((__bridge NSDictionary *)returnedValues);
474 - (void)idsPerformanceCounters:(void(^)(NSDictionary <NSString *, NSNumber *> *))reply
476 SOSCloudKeychainRetrieveCountersFromIDSProxy(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFDictionaryRef returnedValues, CFErrorRef error)
478 reply((__bridge NSDictionary *)returnedValues);
482 - (void)rateLimitingPerformanceCounters:(void(^)(NSDictionary <NSString *, NSString *> *))reply
484 CFErrorRef error = NULL;
485 CFDictionaryRef rateLimitingCounters = (CFDictionaryRef)SOSAccountGetValue(self, kSOSRateLimitingCounters, &error);
486 reply((__bridge NSDictionary*)rateLimitingCounters ? (__bridge NSDictionary*)rateLimitingCounters : [NSDictionary dictionary]);
489 - (void)stashedCredentialPublicKey:(void(^)(NSData *, NSError *error))reply
491 dispatch_async(self.queue, ^{
492 CFErrorRef error = NULL;
494 SecKeyRef user_private = SOSAccountCopyStashedUserPrivateKey(self, &error);
495 if (user_private == NULL) {
496 reply(NULL, (__bridge NSError *)error);
497 CFReleaseNull(error);
501 NSData *publicKey = CFBridgingRelease(SecKeyCopySubjectPublicKeyInfo(user_private));
502 CFReleaseNull(user_private);
503 reply(publicKey, NULL);
507 - (void)assertStashedAccountCredential:(void(^)(BOOL result, NSError *error))complete
509 dispatch_async(self.queue, ^{
510 CFErrorRef error = NULL;
511 bool result = SOSAccountAssertStashedAccountCredential(self, &error);
512 complete(result, (__bridge NSError *)error);
513 CFReleaseNull(error);
517 static bool SyncKVSAndWait(CFErrorRef *error) {
518 dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
520 __block bool success = false;
522 secnoticeq("fresh", "EFP calling SOSCloudKeychainSynchronizeAndWait");
524 os_activity_initiate("CloudCircle EFRESH", OS_ACTIVITY_FLAG_DEFAULT, ^(void) {
525 SOSCloudKeychainSynchronizeAndWait(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(__unused CFDictionaryRef returnedValues, CFErrorRef sync_error) {
526 secnotice("fresh", "EFP returned, callback error: %@", sync_error);
528 success = (sync_error == NULL);
530 CFRetainAssign(*error, sync_error);
533 dispatch_semaphore_signal(wait_for);
537 dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER);
538 secnotice("fresh", "EFP complete: %s %@", success ? "success" : "failure", error ? *error : NULL);
544 static bool Flush(CFErrorRef *error) {
545 __block bool success = false;
547 dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
548 secnotice("flush", "Starting");
550 SOSCloudKeychainFlush(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFDictionaryRef returnedValues, CFErrorRef sync_error) {
551 success = (sync_error == NULL);
553 CFRetainAssign(*error, sync_error);
556 dispatch_semaphore_signal(wait_for);
559 dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER);
561 secnotice("flush", "Returned %s", success? "Success": "Failure");
566 - (bool)syncWaitAndFlush:(CFErrorRef *)error
568 secnotice("pairing", "sync and wait starting");
570 if (!SyncKVSAndWait(error)) {
571 secnotice("pairing", "failed sync and wait: %@", error ? *error : NULL);
575 secnotice("pairing", "failed flush: %@", error ? *error : NULL);
578 secnotice("pairing", "finished sync and wait");
582 - (void)validatedStashedAccountCredential:(void(^)(NSData *credential, NSError *error))complete
584 CFErrorRef syncerror = NULL;
586 if (![self syncWaitAndFlush:&syncerror]) {
587 complete(NULL, (__bridge NSError *)syncerror);
588 CFReleaseNull(syncerror);
592 dispatch_async(self.queue, ^{
593 CFErrorRef error = NULL;
594 SecKeyRef key = NULL;
595 key = SOSAccountCopyStashedUserPrivateKey(self, &error);
597 secnotice("pairing", "no stashed credential");
598 complete(NULL, (__bridge NSError *)error);
599 CFReleaseNull(error);
603 SecKeyRef publicKey = SecKeyCopyPublicKey(key);
605 secnotice("pairing", "returning stash credential: %@", publicKey);
606 CFReleaseNull(publicKey);
609 NSData *keydata = CFBridgingRelease(SecKeyCopyExternalRepresentation(key, &error));
611 complete(keydata, (__bridge NSError *)error);
612 CFReleaseNull(error);
616 - (void)stashAccountCredential:(NSData *)credential complete:(void(^)(bool success, NSError *error))complete
618 CFErrorRef syncerror = NULL;
620 if (![self syncWaitAndFlush:&syncerror]) {
621 complete(NULL, (__bridge NSError *)syncerror);
622 CFReleaseNull(syncerror);
626 [self performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
627 SecKeyRef accountPrivateKey = NULL;
628 CFErrorRef error = NULL;
629 NSDictionary *attributes = @{
630 (__bridge id)kSecAttrKeyClass : (__bridge id)kSecAttrKeyClassPrivate,
631 (__bridge id)kSecAttrKeyType : (__bridge id)kSecAttrKeyTypeEC,
634 accountPrivateKey = SecKeyCreateWithData((__bridge CFDataRef)credential, (__bridge CFDictionaryRef)attributes, &error);
635 if (accountPrivateKey == NULL) {
636 complete(false, (__bridge NSError *)error);
637 secnotice("pairing", "SecKeyCreateWithData failed: %@", error);
638 CFReleaseNull(error);
642 if (!SOSAccountTryUserPrivateKey(self, accountPrivateKey, &error)) {
643 CFReleaseNull(accountPrivateKey);
644 complete(false, (__bridge NSError *)error);
645 secnotice("pairing", "SOSAccountTryUserPrivateKey failed: %@", error);
646 CFReleaseNull(error);
650 secnotice("pairing", "SOSAccountTryUserPrivateKey succeeded");
652 CFReleaseNull(accountPrivateKey);
653 complete(true, NULL);
657 - (void)myPeerInfo:(void (^)(NSData *, NSError *))complete
659 __block CFErrorRef localError = NULL;
660 __block NSData *applicationBlob = NULL;
661 SecAKSDoWhileUserBagLocked(&localError, ^{
662 [self performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
663 SOSPeerInfoRef application = SOSAccountCopyApplication(txn.account, &localError);
665 applicationBlob = CFBridgingRelease(SOSPeerInfoCopyEncodedData(application, kCFAllocatorDefault, &localError));
666 CFReleaseNull(application);
670 complete(applicationBlob, (__bridge NSError *)localError);
671 CFReleaseNull(localError);
674 - (void)circleJoiningBlob:(NSData *)applicant complete:(void (^)(NSData *blob, NSError *))complete
676 __block CFErrorRef localError = NULL;
677 __block NSData *blob = NULL;
678 SOSPeerInfoRef peer = SOSPeerInfoCreateFromData(NULL, &localError, (__bridge CFDataRef)applicant);
680 complete(NULL, (__bridge NSError *)localError);
681 CFReleaseNull(localError);
684 SecAKSDoWhileUserBagLocked(&localError, ^{
685 [self performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
686 blob = CFBridgingRelease(SOSAccountCopyCircleJoiningBlob(txn.account, peer, &localError));
692 complete(blob, (__bridge NSError *)localError);
693 CFReleaseNull(localError);
696 - (void)joinCircleWithBlob:(NSData *)blob version:(PiggyBackProtocolVersion)version complete:(void (^)(bool success, NSError *))complete
698 __block CFErrorRef localError = NULL;
699 __block bool res = false;
701 SecAKSDoWhileUserBagLocked(&localError, ^{
702 [self performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
703 res = SOSAccountJoinWithCircleJoiningBlob(txn.account, (__bridge CFDataRef)blob, version, &localError);
707 complete(res, (__bridge NSError *)localError);
708 CFReleaseNull(localError);
711 - (void)initialSyncCredentials:(uint32_t)flags complete:(void (^)(NSArray *, NSError *))complete
713 CFErrorRef error = NULL;
714 uint32_t isflags = 0;
716 if (flags & SOSControlInitialSyncFlagTLK)
717 isflags |= SecServerInitialSyncCredentialFlagTLK;
718 if (flags & SOSControlInitialSyncFlagPCS)
719 isflags |= SecServerInitialSyncCredentialFlagPCS;
720 if (flags & SOSControlInitialSyncFlagPCSNonCurrent)
721 isflags |= SecServerInitialSyncCredentialFlagPCSNonCurrent;
723 NSArray *array = CFBridgingRelease(_SecServerCopyInitialSyncCredentials(isflags, &error));
724 complete(array, (__bridge NSError *)error);
725 CFReleaseNull(error);
728 - (void)importInitialSyncCredentials:(NSArray *)items complete:(void (^)(bool success, NSError *))complete
730 CFErrorRef error = NULL;
731 bool res = _SecServerImportInitialSyncCredentials((__bridge CFArrayRef)items, &error);
732 complete(res, (__bridge NSError *)error);
733 CFReleaseNull(error);
736 - (void)triggerSync:(NSArray <NSString *> *)peers complete:(void(^)(bool success, NSError *))complete
738 __block CFErrorRef localError = NULL;
739 __block bool res = false;
741 secnotice("sync", "trigger a forced sync for %@", peers);
743 SecAKSDoWhileUserBagLocked(&localError, ^{
744 [self performTransaction:^(SOSAccountTransaction *txn) {
746 NSSet *peersSet = [NSSet setWithArray:peers];
747 CFMutableSetRef handledPeers = SOSAccountSyncWithPeers(txn, (__bridge CFSetRef)peersSet, &localError);
748 if (handledPeers && CFSetGetCount(handledPeers) == (CFIndex)[peersSet count]) {
751 CFReleaseNull(handledPeers);
753 res = SOSAccountRequestSyncWithAllPeers(txn, &localError);
757 complete(res, (__bridge NSError *)localError);
758 CFReleaseNull(localError);
761 - (void)getWatchdogParameters:(void (^)(NSDictionary* parameters, NSError* error))complete
763 // SecdWatchdog is only available in the secd/securityd - no other binary will contain that class
764 Class watchdogClass = NSClassFromString(@"SecdWatchdog");
766 NSDictionary* parameters = [[watchdogClass watchdog] watchdogParameters];
767 complete(parameters, nil);
770 complete(nil, [NSError errorWithDomain:@"com.apple.securityd.watchdog" code:1 userInfo:@{NSLocalizedDescriptionKey : @"failed to lookup SecdWatchdog class from ObjC runtime"}]);
774 - (void)setWatchdogParmeters:(NSDictionary*)parameters complete:(void (^)(NSError* error))complete
776 // SecdWatchdog is only available in the secd/securityd - no other binary will contain that class
777 NSError* error = nil;
778 Class watchdogClass = NSClassFromString(@"SecdWatchdog");
780 [[watchdogClass watchdog] setWatchdogParameters:parameters error:&error];
784 complete([NSError errorWithDomain:@"com.apple.securityd.watchdog" code:1 userInfo:@{NSLocalizedDescriptionKey : @"failed to lookup SecdWatchdog class from ObjC runtime"}]);
793 - (void) flattenToSaveBlock {
794 if (self.saveBlock) {
795 NSError* error = nil;
796 NSData* saveData = [self encodedData:&error];
798 (self.saveBlock)((__bridge CFDataRef) saveData, (__bridge CFErrorRef) error);
802 CFDictionaryRef SOSAccountCopyGestalt(SOSAccount* account) {
803 return CFDictionaryCreateCopy(kCFAllocatorDefault, (__bridge CFDictionaryRef)account.gestalt);
806 CFDictionaryRef SOSAccountCopyV2Dictionary(SOSAccount* account) {
807 CFDictionaryRef v2dict = SOSAccountGetValue(account, kSOSTestV2Settings, NULL);
808 return CFDictionaryCreateCopy(kCFAllocatorDefault, v2dict);
811 static bool SOSAccountUpdateDSID(SOSAccount* account, CFStringRef dsid){
812 SOSAccountSetValue(account, kSOSDSIDKey, dsid, NULL);
813 //send new DSID over account changed
814 [account.circle_transport kvsSendOfficialDSID:dsid err:NULL];
818 void SOSAccountAssertDSID(SOSAccount* account, CFStringRef dsid) {
819 CFStringRef accountDSID = SOSAccountGetValue(account, kSOSDSIDKey, NULL);
820 if(accountDSID == NULL) {
821 secdebug("updates", "Setting dsid, current dsid is empty for this account: %@", dsid);
823 SOSAccountUpdateDSID(account, dsid);
824 } else if(CFStringCompare(dsid, accountDSID, 0) != kCFCompareEqualTo) {
825 secnotice("updates", "Changing DSID from: %@ to %@", accountDSID, dsid);
827 //DSID has changed, blast the account!
828 SOSAccountSetToNew(account);
830 //update DSID to the new DSID
831 SOSAccountUpdateDSID(account, dsid);
833 secnotice("updates", "Not Changing DSID: %@ to %@", accountDSID, dsid);
838 void SOSAccountPendDisableViewSet(SOSAccount* account, CFSetRef disabledViews)
840 [account.trust valueUnionWith:kSOSPendingDisableViewsToBeSetKey valuesToUnion:disabledViews];
841 [account.trust valueSubtractFrom:kSOSPendingEnableViewsToBeSetKey valuesToSubtract:disabledViews];
844 SOSViewResultCode SOSAccountVirtualV0Behavior(SOSAccount* account, SOSViewActionCode actionCode) {
845 SOSViewResultCode retval = kSOSCCGeneralViewError;
846 // The V0 view switches on and off all on it's own, we allow people the delusion
847 // of control and status if it's what we're stuck at., otherwise error.
848 if (SOSAccountSyncingV0(account)) {
849 require_action_quiet(actionCode == kSOSCCViewDisable, errOut, CFSTR("Can't disable V0 view and it's on right now"));
850 retval = kSOSCCViewMember;
852 require_action_quiet(actionCode == kSOSCCViewEnable, errOut, CFSTR("Can't enable V0 and it's off right now"));
853 retval = kSOSCCViewNotMember;
859 SOSAccount* SOSAccountCreate(CFAllocatorRef allocator,
860 CFDictionaryRef gestalt,
861 SOSDataSourceFactoryRef factory) {
863 SOSAccount* a = [[SOSAccount alloc] initWithGestalt:gestalt factory:factory];
864 [a ensureFactoryCircles];
865 SOSAccountEnsureUUID(a);
866 a.key_interests_need_updating = true;
871 static OSStatus do_delete(CFDictionaryRef query) {
874 result = SecItemDelete(query);
876 secerror("SecItemDelete: %d", (int)result);
882 do_keychain_delete_aks_bags()
885 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
886 kSecClass, kSecClassGenericPassword,
887 kSecAttrAccessGroup, CFSTR("com.apple.sbd"),
888 kSecAttrAccount, CFSTR("SecureBackupPublicKeybag"),
889 kSecAttrService, CFSTR("SecureBackupService"),
890 kSecAttrSynchronizable, kCFBooleanTrue,
891 kSecUseTombstones, kCFBooleanFalse,
894 result = do_delete(item);
901 do_keychain_delete_identities()
904 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
905 kSecClass, kSecClassKey,
906 kSecAttrSynchronizable, kCFBooleanTrue,
907 kSecUseTombstones, kCFBooleanFalse,
908 kSecAttrAccessGroup, CFSTR("com.apple.security.sos"),
911 result = do_delete(item);
918 do_keychain_delete_lakitu()
921 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
922 kSecClass, kSecClassGenericPassword,
923 kSecAttrSynchronizable, kCFBooleanTrue,
924 kSecUseTombstones, kCFBooleanFalse,
925 kSecAttrAccessGroup, CFSTR("com.apple.lakitu"),
926 kSecAttrAccount, CFSTR("EscrowServiceBypassToken"),
927 kSecAttrService, CFSTR("EscrowService"),
930 result = do_delete(item);
937 do_keychain_delete_sbd()
940 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
941 kSecClass, kSecClassGenericPassword,
942 kSecAttrSynchronizable, kCFBooleanTrue,
943 kSecUseTombstones, kCFBooleanFalse,
944 kSecAttrAccessGroup, CFSTR("com.apple.sbd"),
947 result = do_delete(item);
953 void SOSAccountSetToNew(SOSAccount* a)
955 secnotice("accountChange", "Setting Account to New");
958 /* remove all syncable items */
959 result = do_keychain_delete_aks_bags(); (void) result;
960 secdebug("set to new", "result for deleting aks bags: %d", result);
962 result = do_keychain_delete_identities(); (void) result;
963 secdebug("set to new", "result for deleting identities: %d", result);
965 result = do_keychain_delete_lakitu(); (void) result;
966 secdebug("set to new", "result for deleting lakitu: %d", result);
968 result = do_keychain_delete_sbd(); (void) result;
969 secdebug("set to new", "result for deleting sbd: %d", result);
971 a.accountKeyIsTrusted = false;
973 if (a.user_private_timer) {
974 dispatch_source_cancel(a.user_private_timer);
975 a.user_private_timer = NULL;
976 xpc_transaction_end();
979 if (a.lock_notification_token != NOTIFY_TOKEN_INVALID) {
980 notify_cancel(a.lock_notification_token);
981 a.lock_notification_token = NOTIFY_TOKEN_INVALID;
988 // update_interest_block;
990 SOSUnregisterTransportKeyParameter(a.key_transport);
991 SOSUnregisterTransportMessage(a.ids_message_transport);
992 SOSUnregisterTransportMessage(a.kvs_message_transport);
993 SOSUnregisterTransportCircle(a.circle_transport);
995 a.circle_transport = NULL;
996 a.kvs_message_transport = NULL;
997 a.ids_message_transport = NULL;
1000 a.trust = [[SOSAccountTrustClassic alloc]initWithRetirees:[NSMutableSet set] fpi:NULL circle:NULL departureCode:kSOSDepartureReasonError peerExpansion:[NSMutableDictionary dictionary]];
1002 [a ensureFactoryCircles]; // Does rings too
1004 // By resetting our expansion dictionary we've reset our UUID, so we'll be notified properly
1005 SOSAccountEnsureUUID(a);
1007 a.key_interests_need_updating = true;
1010 bool SOSAccountIsNew(SOSAccount* account, CFErrorRef *error){
1011 bool result = false;
1012 SOSAccountTrustClassic* trust = account.trust;
1013 if(account.accountKeyIsTrusted != false || trust.departureCode != kSOSNeverAppliedToCircle ||
1014 CFSetGetCount((__bridge CFSetRef)trust.retirees) != 0)
1017 if(trust.retirees != nil)
1019 if(trust.expansion != nil)
1022 if(account.user_private_timer != NULL || account.lock_notification_token != NOTIFY_TOKEN_INVALID)
1030 CFStringRef SOSAccountCreateCompactDescription(SOSAccount* a) {
1032 CFStringRef gestaltDescription = CFDictionaryCopySuperCompactDescription((__bridge CFDictionaryRef)(a.gestalt));
1034 CFStringRef result = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@"), gestaltDescription);
1036 CFReleaseNull(gestaltDescription);
1040 dispatch_queue_t SOSAccountGetQueue(SOSAccount* account) {
1041 return account.queue;
1044 void SOSAccountSetUserPublicTrustedForTesting(SOSAccount* account){
1045 account.accountKeyIsTrusted = true;
1048 -(SOSCCStatus) getCircleStatus:(CFErrorRef*) error
1050 SOSCCStatus circleStatus = kSOSCCError;
1051 if (SOSAccountHasPublicKey(self, error)) {
1052 circleStatus = [self.trust getCircleStatus:error];
1054 return circleStatus;
1056 bool SOSAccountScanForRetired(SOSAccount* account, SOSCircleRef circle, CFErrorRef *error) {
1058 SOSAccountTrustClassic *trust = account.trust;
1059 NSMutableSet* retirees = trust.retirees;
1060 SOSCircleForEachRetiredPeer(circle, ^(SOSPeerInfoRef peer) {
1061 CFSetSetValue((__bridge CFMutableSetRef) retirees, peer);
1062 CFErrorRef cleanupError = NULL;
1063 if (![account.trust cleanupAfterPeer:account.kvs_message_transport circleTransport:account.circle_transport seconds:RETIREMENT_FINALIZATION_SECONDS circle:circle cleanupPeer:peer err:&cleanupError]) {
1064 secnotice("retirement", "Error cleaning up after peer, probably orphaned some stuff in KVS: (%@) – moving on", cleanupError);
1066 CFReleaseSafe(cleanupError);
1071 SOSCircleRef SOSAccountCloneCircleWithRetirement(SOSAccount* account, SOSCircleRef starting_circle, CFErrorRef *error) {
1072 SOSCircleRef new_circle = SOSCircleCopyCircle(NULL, starting_circle, error);
1073 SOSPeerInfoRef me = account.peerInfo;
1074 bool iAmApplicant = me && SOSCircleHasApplicant(new_circle, me, NULL);
1076 SOSAccountTrustClassic *trust = account.trust;
1077 NSMutableSet* retirees = trust.retirees;
1079 if(!new_circle) return NULL;
1080 __block bool workDone = false;
1082 CFSetForEach((__bridge CFSetRef)retirees, ^(const void* value) {
1083 SOSPeerInfoRef pi = (SOSPeerInfoRef) value;
1084 if (isSOSPeerInfo(pi)) {
1085 SOSCircleUpdatePeerInfo(new_circle, pi);
1091 if(workDone && SOSCircleCountPeers(new_circle) == 0) {
1092 SecKeyRef userPrivKey = SOSAccountGetPrivateCredential(account, error);
1096 secnotice("resetToOffering", "Reset to offering with last retirement and me as applicant");
1097 if(!SOSCircleResetToOffering(new_circle, userPrivKey, account.fullPeerInfo, error) ||
1098 ![account.trust addiCloudIdentity:new_circle key:userPrivKey err:error]){
1099 CFReleaseNull(new_circle);
1103 // Do nothing. We can't resetToOffering without a userPrivKey. If we were to resetToEmpty
1104 // we won't push the result later in handleUpdateCircle. If we leave the circle as it is
1105 // we have a chance to set things right with a SetCreds/Join sequence. This will cause
1106 // handleUpdateCircle to return false.
1107 CFReleaseNull(new_circle);
1111 // This case is when we aren't an applicant and the circle is retirement-empty.
1112 secnotice("resetToEmpty", "Reset to empty with last retirement");
1113 SOSCircleResetToEmpty(new_circle, NULL);
1121 // MARK: Circle Membership change notificaion
1124 void SOSAccountAddChangeBlock(SOSAccount* a, SOSAccountCircleMembershipChangeBlock changeBlock) {
1125 SOSAccountCircleMembershipChangeBlock copy = changeBlock;
1126 [a.change_blocks addObject:copy];
1129 void SOSAccountRemoveChangeBlock(SOSAccount* a, SOSAccountCircleMembershipChangeBlock changeBlock) {
1130 [a.change_blocks removeObject:changeBlock];
1133 void SOSAccountPurgeIdentity(SOSAccount* account) {
1134 SOSAccountTrustClassic *trust = account.trust;
1135 SOSFullPeerInfoRef identity = trust.fullPeerInfo;
1138 // Purge private key but don't return error if we can't.
1139 CFErrorRef purgeError = NULL;
1140 if (!SOSFullPeerInfoPurgePersistentKey(identity, &purgeError)) {
1141 secwarning("Couldn't purge persistent key for %@ [%@]", identity, purgeError);
1143 CFReleaseNull(purgeError);
1145 trust.fullPeerInfo = nil;
1149 bool sosAccountLeaveCircle(SOSAccount* account, SOSCircleRef circle, CFErrorRef* error) {
1150 SOSAccountTrustClassic *trust = account.trust;
1151 SOSFullPeerInfoRef identity = trust.fullPeerInfo;
1152 NSMutableSet* retirees = trust.retirees;
1154 SOSFullPeerInfoRef fpi = identity;
1155 if(!fpi) return false;
1157 CFErrorRef localError = NULL;
1159 bool retval = false;
1161 SOSPeerInfoRef retire_peer = SOSFullPeerInfoPromoteToRetiredAndCopy(fpi, &localError);
1163 secerror("Create ticket failed for peer %@: %@", fpi, localError);
1165 // See if we need to repost the circle we could either be an applicant or a peer already in the circle
1166 if(SOSCircleHasApplicant(circle, retire_peer, NULL)) {
1167 // Remove our application if we have one.
1168 SOSCircleWithdrawRequest(circle, retire_peer, NULL);
1169 } else if (SOSCircleHasPeer(circle, retire_peer, NULL)) {
1170 if (SOSCircleUpdatePeerInfo(circle, retire_peer)) {
1171 CFErrorRef cleanupError = NULL;
1172 if (![account.trust cleanupAfterPeer:account.kvs_message_transport circleTransport:account.circle_transport seconds:RETIREMENT_FINALIZATION_SECONDS circle:circle cleanupPeer:retire_peer err:&cleanupError]) {
1173 secerror("Error cleanup up after peer (%@): %@", retire_peer, cleanupError);
1175 CFReleaseSafe(cleanupError);
1179 // Store the retirement record locally.
1180 CFSetAddValue((__bridge CFMutableSetRef)retirees, retire_peer);
1182 trust.retirees = retirees;
1184 // Write retirement to Transport
1185 CFErrorRef postError = NULL;
1186 if(![account.circle_transport postRetirement:SOSCircleGetName(circle) peer:retire_peer err:&postError]){
1187 secwarning("Couldn't post retirement (%@)", postError);
1189 if(![account.circle_transport flushChanges:&postError]){
1190 secwarning("Couldn't flush retirement data (%@)", postError);
1192 CFReleaseNull(postError);
1195 SOSAccountPurgeIdentity(account);
1199 CFReleaseNull(localError);
1200 CFReleaseNull(retire_peer);
1204 bool sosAccountLeaveRing(SOSAccount* account, SOSRingRef ring, CFErrorRef* error) {
1205 SOSAccountTrustClassic *trust = account.trust;
1206 SOSFullPeerInfoRef identity = trust.fullPeerInfo;
1208 SOSFullPeerInfoRef fpi = identity;
1209 if(!fpi) return false;
1210 SOSPeerInfoRef pi = SOSFullPeerInfoGetPeerInfo(fpi);
1211 CFStringRef peerID = SOSPeerInfoGetPeerID(pi);
1213 CFErrorRef localError = NULL;
1215 bool retval = false;
1216 bool writeRing = false;
1217 bool writePeerInfo = false;
1219 if(SOSRingHasPeerID(ring, peerID)) {
1220 writePeerInfo = true;
1223 if(writePeerInfo || writeRing) {
1224 SOSRingWithdraw(ring, NULL, fpi, error);
1228 CFDataRef ring_data = SOSRingCopyEncodedData(ring, error);
1231 [account.circle_transport kvsRingPostRing:SOSRingGetName(ring) ring:ring_data err:NULL];
1233 CFReleaseNull(ring_data);
1236 CFReleaseNull(localError);
1240 bool SOSAccountPostDebugScope(SOSAccount* account, CFTypeRef scope, CFErrorRef *error) {
1241 bool result = false;
1242 if (account.circle_transport) {
1243 result = [account.circle_transport kvssendDebugInfo:kSOSAccountDebugScope debug:scope err:error];
1249 NSUbiquitousKeyValueStoreInitialSyncChange is only posted if there is any
1250 local value that has been overwritten by a distant value. If there is no
1251 conflict between the local and the distant values when doing the initial
1252 sync (e.g. if the cloud has no data stored or the client has not stored
1253 any data yet), you'll never see that notification.
1255 NSUbiquitousKeyValueStoreInitialSyncChange implies an initial round trip
1256 with server but initial round trip with server does not imply
1257 NSUbiquitousKeyValueStoreInitialSyncChange.
1262 // MARK: Status summary
1266 CFStringRef SOSAccountGetSOSCCStatusString(SOSCCStatus status) {
1268 case kSOSCCInCircle: return CFSTR("kSOSCCInCircle");
1269 case kSOSCCNotInCircle: return CFSTR("kSOSCCNotInCircle");
1270 case kSOSCCRequestPending: return CFSTR("kSOSCCRequestPending");
1271 case kSOSCCCircleAbsent: return CFSTR("kSOSCCCircleAbsent");
1272 case kSOSCCError: return CFSTR("kSOSCCError");
1274 return CFSTR("kSOSCCError");
1276 SOSCCStatus SOSAccountGetSOSCCStatusFromString(CFStringRef status) {
1277 if(CFEqualSafe(status, CFSTR("kSOSCCInCircle"))) {
1278 return kSOSCCInCircle;
1279 } else if(CFEqualSafe(status, CFSTR("kSOSCCInCircle"))) {
1280 return kSOSCCInCircle;
1281 } else if(CFEqualSafe(status, CFSTR("kSOSCCNotInCircle"))) {
1282 return kSOSCCNotInCircle;
1283 } else if(CFEqualSafe(status, CFSTR("kSOSCCRequestPending"))) {
1284 return kSOSCCRequestPending;
1285 } else if(CFEqualSafe(status, CFSTR("kSOSCCCircleAbsent"))) {
1286 return kSOSCCCircleAbsent;
1287 } else if(CFEqualSafe(status, CFSTR("kSOSCCError"))) {
1294 // MARK: Account Reset Circles
1297 // This needs to be called within a [trust modifyCircle()] block
1300 bool SOSAccountRemoveIncompleteiCloudIdentities(SOSAccount* account, SOSCircleRef circle, SecKeyRef privKey, CFErrorRef *error) {
1301 bool retval = false;
1303 SOSAccountTrustClassic *trust = account.trust;
1304 SOSFullPeerInfoRef identity = trust.fullPeerInfo;
1306 CFMutableSetRef iCloud2Remove = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
1308 SOSCircleForEachActivePeer(circle, ^(SOSPeerInfoRef peer) {
1309 if(SOSPeerInfoIsCloudIdentity(peer)) {
1310 SOSFullPeerInfoRef icfpi = SOSFullPeerInfoCreateCloudIdentity(kCFAllocatorDefault, peer, NULL);
1312 CFSetAddValue(iCloud2Remove, peer);
1314 CFReleaseNull(icfpi);
1318 if(CFSetGetCount(iCloud2Remove) > 0) {
1320 SOSCircleRemovePeers(circle, privKey, identity, iCloud2Remove, error);
1322 CFReleaseNull(iCloud2Remove);
1327 // MARK: start backups
1331 bool SOSAccountEnsureInBackupRings(SOSAccount* account) {
1332 __block bool result = false;
1333 __block CFErrorRef error = NULL;
1334 secnotice("backup", "Ensuring in rings");
1336 NSData *backupKey = nil;
1338 if(!account.backup_key){
1343 backupKey = (__bridge_transfer NSData*)SOSPeerInfoV2DictionaryCopyData(account.peerInfo, sBackupKeyKey);
1345 bool updateBackupKey = ![backupKey isEqual:account.backup_key];
1347 if(updateBackupKey) {
1348 result = SOSAccountUpdatePeerInfo(account, CFSTR("Backup public key"), &error, ^bool(SOSFullPeerInfoRef fpi, CFErrorRef *error) {
1349 return SOSFullPeerInfoUpdateBackupKey(fpi, (__bridge CFDataRef)(account.backup_key), error);
1352 secnotice("backupkey", "Failed to setup backup public key: %@", error ? (CFTypeRef) error : (CFTypeRef) CFSTR("No error space provided"));
1356 if(!account.backup_key)
1359 secnotice("backupkey", "Failed to setup backup public key: %@", error ? (CFTypeRef) error : (CFTypeRef) CFSTR("No error space provided"));
1363 if(!SOSBSKBIsGoodBackupPublic((__bridge CFDataRef)account.backup_key, &error)){
1365 secnotice("backupkey", "Failed to setup backup public key: %@", error ? (CFTypeRef) error : (CFTypeRef) CFSTR("No error space provided"));
1370 CFDataRef recoveryKeyBackFromRing = SOSAccountCopyRecoveryPublic(kCFAllocatorDefault, account, &error);
1372 if(updateBackupKey || recoveryKeyBackFromRing) {
1373 // It's a good key, we're going with it. Stop backing up the old way.
1374 CFErrorRef localError = NULL;
1375 if (!SOSDeleteV0Keybag(&localError)) {
1376 secerror("Failed to delete v0 keybag: %@", localError);
1378 CFReleaseNull(localError);
1382 // Setup backups the new way.
1383 SOSAccountForEachBackupView(account, ^(const void *value) {
1384 CFStringRef viewName = (CFStringRef)value;
1385 if(updateBackupKey || (recoveryKeyBackFromRing && !SOSAccountRecoveryKeyIsInBackupAndCurrentInView(account, viewName))) {
1386 result &= SOSAccountNewBKSBForView(account, viewName, &error);
1392 secnotice("backupkey", "Failed to setup backup public key: %@", error ? (CFTypeRef) error : (CFTypeRef) CFSTR("No error space provided"));
1398 // MARK: Recovery Public Key Functions
1401 bool SOSAccountRegisterRecoveryPublicKey(SOSAccountTransaction* txn, CFDataRef recovery_key, CFErrorRef *error){
1402 bool retval = SOSAccountSetRecoveryKey(txn.account, recovery_key, error);
1403 if(retval) secnotice("recovery", "successfully registered recovery public key");
1404 else secnotice("recovery", "could not register recovery public key: %@", *error);
1405 SOSClearErrorIfTrue(retval, error);
1409 bool SOSAccountClearRecoveryPublicKey(SOSAccountTransaction* txn, CFDataRef recovery_key, CFErrorRef *error){
1410 bool retval = SOSAccountRemoveRecoveryKey(txn.account, error);
1411 if(retval) secnotice("recovery", "RK Cleared");
1412 else secnotice("recovery", "Couldn't clear RK(%@)", *error);
1413 SOSClearErrorIfTrue(retval, error);
1417 CFDataRef SOSAccountCopyRecoveryPublicKey(SOSAccountTransaction* txn, CFErrorRef *error){
1418 CFDataRef result = NULL;
1419 result = SOSAccountCopyRecoveryPublic(kCFAllocatorDefault, txn.account, error);
1420 if(!result) secnotice("recovery", "Could not retrieve the recovery public key from the ring: %@", *error);
1422 if (!isData(result)) {
1423 CFReleaseNull(result);
1425 SOSClearErrorIfTrue(result != NULL, error);
1435 static bool SOSAccountJoinCircle(SOSAccountTransaction* aTxn, SecKeyRef user_key,
1436 bool use_cloud_peer, CFErrorRef* error) {
1437 SOSAccount* account = aTxn.account;
1438 SOSAccountTrustClassic *trust = account.trust;
1439 __block bool result = false;
1440 __block SOSFullPeerInfoRef cloud_full_peer = NULL;
1441 require_action_quiet(trust.trustedCircle, fail, SOSCreateErrorWithFormat(kSOSErrorPeerNotFound, NULL, error, NULL, CFSTR("Don't have circle when joining???")));
1442 require_quiet([account.trust ensureFullPeerAvailable:(__bridge CFDictionaryRef)account.gestalt deviceID:(__bridge CFStringRef)account.deviceID backupKey:(__bridge CFDataRef)account.backup_key err:error], fail);
1443 SOSFullPeerInfoRef myCirclePeer = trust.fullPeerInfo;
1444 if (SOSCircleCountPeers(trust.trustedCircle) == 0 || SOSAccountGhostResultsInReset(account)) {
1445 secnotice("resetToOffering", "Resetting circle to offering since there are no peers");
1446 // this also clears initial sync data
1447 result = [account.trust resetCircleToOffering:aTxn userKey:user_key err:error];
1449 SOSAccountSetValue(account, kSOSUnsyncedViewsKey, kCFBooleanTrue, NULL);
1450 if (use_cloud_peer) {
1451 cloud_full_peer = SOSCircleCopyiCloudFullPeerInfoRef(trust.trustedCircle, NULL);
1453 [account.trust modifyCircle:account.circle_transport err:error action:^bool(SOSCircleRef circle) {
1454 result = SOSAccountAddEscrowToPeerInfo(account, myCirclePeer, error);
1455 result &= SOSCircleRequestAdmission(circle, user_key, myCirclePeer, error);
1456 trust.departureCode = kSOSNeverLeftCircle;
1457 if(result && cloud_full_peer) {
1458 CFErrorRef localError = NULL;
1459 CFStringRef cloudid = SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(cloud_full_peer));
1460 require_quiet(cloudid, finish);
1461 require_quiet(SOSCircleHasActivePeerWithID(circle, cloudid, &localError), finish);
1462 require_quiet(SOSCircleAcceptRequest(circle, user_key, cloud_full_peer, SOSFullPeerInfoGetPeerInfo(myCirclePeer), &localError), finish);
1465 secerror("Failed to join with cloud identity: %@", localError);
1466 CFReleaseNull(localError);
1471 if (use_cloud_peer) {
1472 SOSAccountUpdateOutOfSyncViews(aTxn, SOSViewsGetAllCurrent());
1476 CFReleaseNull(cloud_full_peer);
1480 static bool SOSAccountJoinCircles_internal(SOSAccountTransaction* aTxn, bool use_cloud_identity, CFErrorRef* error) {
1481 SOSAccount* account = aTxn.account;
1482 SOSAccountTrustClassic *trust = account.trust;
1483 bool success = false;
1485 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1486 require_quiet(user_key, done); // Fail if we don't get one.
1488 require_action_quiet(trust.trustedCircle, done, SOSErrorCreate(kSOSErrorNoCircle, error, NULL, CFSTR("No circle to join")));
1490 if (trust.fullPeerInfo != NULL) {
1491 SOSPeerInfoRef myPeer = trust.peerInfo;
1492 success = SOSCircleHasPeer(trust.trustedCircle, myPeer, NULL);
1493 require_quiet(!success, done);
1495 SOSCircleRemoveRejectedPeer(trust.trustedCircle, myPeer, NULL); // If we were rejected we should remove it now.
1497 if (!SOSCircleHasApplicant(trust.trustedCircle, myPeer, NULL)) {
1498 secerror("Resetting my peer (ID: %@) for circle '%@' during application", SOSPeerInfoGetPeerID(myPeer), SOSCircleGetName(trust.trustedCircle));
1500 trust.fullPeerInfo = NULL;
1504 success = SOSAccountJoinCircle(aTxn, user_key, use_cloud_identity, error);
1506 require_quiet(success, done);
1508 trust.departureCode = kSOSNeverLeftCircle;
1514 bool SOSAccountJoinCircles(SOSAccountTransaction* aTxn, CFErrorRef* error) {
1515 secnotice("circleJoin", "Normal path circle join (SOSAccountJoinCircles)");
1516 return SOSAccountJoinCircles_internal(aTxn, false, error);
1519 CFStringRef SOSAccountCopyDeviceID(SOSAccount* account, CFErrorRef *error){
1520 CFStringRef result = NULL;
1521 SOSAccountTrustClassic *trust = account.trust;
1523 require_action_quiet(trust.fullPeerInfo, fail, SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("No peer for me")));
1525 result = SOSPeerInfoCopyDeviceID(trust.peerInfo);
1531 bool SOSAccountSetMyDSID(SOSAccountTransaction* txn, CFStringRef IDS, CFErrorRef* error){
1533 SOSAccount* account = txn.account;
1534 SOSAccountTrustClassic *trust = account.trust;
1536 secdebug("IDS Transport", "We are setting our device ID: %@", IDS);
1537 if(IDS != NULL && (CFStringGetLength(IDS) > 0)){
1538 if(!trust.fullPeerInfo){
1539 account.deviceID = [[NSString alloc] initWithString:(__bridge NSString * _Nonnull)(IDS)];
1540 SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("No peer for me"));
1543 result = [trust modifyCircle:account.circle_transport err:error action:^bool(SOSCircleRef circle) {
1545 SOSFullPeerInfoUpdateDeviceID(trust.fullPeerInfo, IDS, error);
1546 SOSFullPeerInfoUpdateTransportType(trust.fullPeerInfo, SOSTransportMessageTypeIDSV2, error);
1547 SOSFullPeerInfoUpdateTransportPreference(trust.fullPeerInfo, kCFBooleanFalse, error);
1548 SOSFullPeerInfoUpdateTransportFragmentationPreference(trust.fullPeerInfo, kCFBooleanTrue, error);
1549 SOSFullPeerInfoUpdateTransportAckModelPreference(trust.fullPeerInfo, kCFBooleanTrue, error);
1550 return SOSCircleHasPeer(circle, trust.peerInfo, NULL);
1556 // Initiate sync with all IDS peers, since we just learned we can talk that way.
1557 SOSAccountForEachCirclePeerExceptMe(account, ^(SOSPeerInfoRef peer) {
1558 if (SOSPeerInfoShouldUseIDSTransport(account.peerInfo, peer)) {
1559 [txn requestSyncWith:(__bridge NSString*) SOSPeerInfoGetPeerID(peer)];
1563 account.deviceID = [[NSString alloc] initWithString:(__bridge NSString * _Nonnull)(IDS)];
1567 bool SOSAccountSendIDSTestMessage(SOSAccount* account, CFStringRef message, CFErrorRef *error){
1569 //construct message dictionary, circle -> peerID -> message
1571 CFMutableDictionaryRef peerToMessage = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1573 CFStringRef operationString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), kIDSSendOneMessage);
1574 CFDictionaryRef rawMessage = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
1575 kIDSOperationType, operationString,
1576 kIDSMessageToSendKey, CFSTR("send IDS test message"),
1579 SOSAccountForEachCirclePeerExceptMe(account, ^(SOSPeerInfoRef peer) {
1580 CFDictionaryAddValue(peerToMessage, SOSPeerInfoGetPeerID(peer), rawMessage);
1583 result = [account.ids_message_transport SOSTransportMessageSendMessages:account.ids_message_transport pm:peerToMessage err:error];
1585 CFReleaseNull(peerToMessage);
1586 CFReleaseNull(operationString);
1587 CFReleaseNull(rawMessage);
1591 bool SOSAccountStartPingTest(SOSAccount* account, CFStringRef message, CFErrorRef *error){
1592 bool result = false;
1593 //construct message dictionary, circle -> peerID -> message
1594 SOSAccountTrustClassic *trust = account.trust;
1595 if(account.ids_message_transport == NULL)
1596 account.ids_message_transport = [[SOSMessageIDS alloc] initWithAccount:account andName:(__bridge NSString *)(SOSCircleGetName(trust.trustedCircle))];
1598 require_quiet(account.ids_message_transport, fail);
1599 CFMutableDictionaryRef peerToMessage = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1601 CFStringRef operationString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), kIDSStartPingTestMessage);
1602 CFDictionaryRef rawMessage = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
1603 kIDSOperationType, operationString,
1604 kIDSMessageToSendKey, CFSTR("send IDS test message"),
1608 SOSAccountForEachCirclePeerExceptMe(account, ^(SOSPeerInfoRef peer) {
1609 CFDictionaryAddValue(peerToMessage, SOSPeerInfoGetPeerID(peer), rawMessage);
1612 result = [account.ids_message_transport SOSTransportMessageSendMessages:account.ids_message_transport pm:peerToMessage err:error];
1614 CFReleaseNull(peerToMessage);
1615 CFReleaseNull(rawMessage);
1616 CFReleaseNull(operationString);
1621 bool SOSAccountRetrieveDeviceIDFromKeychainSyncingOverIDSProxy(SOSAccount* account, CFErrorRef *error){
1624 __block bool success = true;
1625 __block CFErrorRef localError = NULL;
1626 dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
1628 SOSCloudKeychainGetIDSDeviceID(^(CFDictionaryRef returnedValues, CFErrorRef sync_error){
1629 success = (sync_error == NULL);
1631 CFRetainAssign(localError, sync_error);
1634 dispatch_semaphore_signal(wait_for);
1637 dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER);
1639 if(!success && localError != NULL && error != NULL){
1640 secerror("Could not ask KeychainSyncingOverIDSProxy for Device ID: %@", localError);
1641 *error = localError;
1645 secdebug("IDS Transport", "Attempting to retrieve the IDS Device ID");
1650 bool SOSAccountJoinCirclesAfterRestore(SOSAccountTransaction* aTxn, CFErrorRef* error) {
1651 secnotice("circleJoin", "Joining after restore (SOSAccountJoinCirclesAfterRestore)");
1652 return SOSAccountJoinCircles_internal(aTxn, true, error);
1655 bool SOSAccountRemovePeersFromCircle(SOSAccount* account, CFArrayRef peers, CFErrorRef* error)
1657 bool result = false;
1658 CFMutableSetRef peersToRemove = NULL;
1659 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1661 secnotice("removePeers", "Can't remove without userKey");
1664 SOSFullPeerInfoRef me_full = account.fullPeerInfo;
1665 SOSPeerInfoRef me = account.peerInfo;
1666 if(!(me_full && me))
1668 secnotice("removePeers", "Can't remove without being active peer");
1669 SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("Can't remove without being active peer"));
1673 result = true; // beyond this point failures would be rolled up in AccountModifyCircle.
1675 peersToRemove = CFSetCreateMutableForSOSPeerInfosByIDWithArray(kCFAllocatorDefault, peers);
1678 CFReleaseNull(peersToRemove);
1679 secnotice("removePeers", "No peerSet to remove");
1683 // If we're one of the peers expected to leave - note that and then remove ourselves from the set (different handling).
1684 bool leaveCircle = CFSetContainsValue(peersToRemove, me);
1685 CFSetRemoveValue(peersToRemove, me);
1687 result &= [account.trust modifyCircle:account.circle_transport err:error action:^(SOSCircleRef circle) {
1688 bool success = false;
1690 if(CFSetGetCount(peersToRemove) != 0) {
1691 require_quiet(SOSCircleRemovePeers(circle, user_key, me_full, peersToRemove, error), done);
1692 success = SOSAccountGenerationSignatureUpdate(account, error);
1693 } else success = true;
1695 if (success && leaveCircle) {
1696 secnotice("leaveCircle", "Leaving circle by client request");
1697 success = sosAccountLeaveCircle(account, circle, error);
1705 CFReleaseNull(peersToRemove);
1710 bool SOSAccountBail(SOSAccount* account, uint64_t limit_in_seconds, CFErrorRef* error) {
1711 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1712 dispatch_group_t group = dispatch_group_create();
1713 SOSAccountTrustClassic *trust = account.trust;
1714 __block bool result = false;
1715 secnotice("circle", "Attempting to leave circle - best effort - in %llu seconds\n", limit_in_seconds);
1716 // Add a task to the group
1717 dispatch_group_async(group, queue, ^{
1718 [trust modifyCircle:account.circle_transport err:error action:^(SOSCircleRef circle) {
1719 secnotice("leaveCircle", "Leaving circle by client request");
1720 return sosAccountLeaveCircle(account, circle, error);
1723 dispatch_time_t milestone = dispatch_time(DISPATCH_TIME_NOW, limit_in_seconds * NSEC_PER_SEC);
1724 dispatch_group_wait(group, milestone);
1726 trust.departureCode = kSOSWithdrewMembership;
1733 // MARK: Application
1736 static void for_each_applicant_in_each_circle(SOSAccount* account, CFArrayRef peer_infos,
1737 bool (^action)(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer)) {
1738 SOSAccountTrustClassic *trust = account.trust;
1740 SOSPeerInfoRef me = trust.peerInfo;
1741 CFErrorRef peer_error = NULL;
1742 if (trust.trustedCircle && me &&
1743 SOSCircleHasPeer(trust.trustedCircle, me, &peer_error)) {
1744 [account.trust modifyCircle:account.circle_transport err:NULL action:^(SOSCircleRef circle) {
1745 __block bool modified = false;
1746 CFArrayForEach(peer_infos, ^(const void *value) {
1747 SOSPeerInfoRef peer = (SOSPeerInfoRef) value;
1748 if (isSOSPeerInfo(peer) && SOSCircleHasApplicant(circle, peer, NULL)) {
1749 if (action(circle, trust.fullPeerInfo, peer)) {
1758 secerror("Got error in SOSCircleHasPeer: %@", peer_error);
1759 CFReleaseSafe(peer_error); // TODO: We should be accumulating errors here.
1762 bool SOSAccountAcceptApplicants(SOSAccount* account, CFArrayRef applicants, CFErrorRef* error) {
1763 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1767 __block bool success = true;
1768 __block int64_t num_peers = 0;
1770 for_each_applicant_in_each_circle(account, applicants, ^(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer) {
1771 bool accepted = SOSCircleAcceptRequest(circle, user_key, myCirclePeer, peer, error);
1775 num_peers = MAX(num_peers, SOSCircleCountPeers(circle));
1782 bool SOSAccountRejectApplicants(SOSAccount* account, CFArrayRef applicants, CFErrorRef* error) {
1783 __block bool success = true;
1784 __block int64_t num_peers = 0;
1786 for_each_applicant_in_each_circle(account, applicants, ^(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer) {
1787 bool rejected = SOSCircleRejectRequest(circle, myCirclePeer, peer, error);
1791 num_peers = MAX(num_peers, SOSCircleCountPeers(circle));
1799 CFStringRef SOSAccountCopyIncompatibilityInfo(SOSAccount* account, CFErrorRef* error) {
1800 return CFSTR("We're compatible, go away");
1803 enum DepartureReason SOSAccountGetLastDepartureReason(SOSAccount* account, CFErrorRef* error) {
1804 SOSAccountTrustClassic *trust = account.trust;
1805 return trust.departureCode;
1808 void SOSAccountSetLastDepartureReason(SOSAccount* account, enum DepartureReason reason) {
1809 SOSAccountTrustClassic *trust = account.trust;
1810 trust.departureCode = reason;
1814 CFArrayRef SOSAccountCopyGeneration(SOSAccount* account, CFErrorRef *error) {
1815 CFArrayRef result = NULL;
1816 CFNumberRef generation = NULL;
1817 SOSAccountTrustClassic *trust = account.trust;
1819 require_quiet(SOSAccountHasPublicKey(account, error), fail);
1820 require_action_quiet(trust.trustedCircle, fail, SOSErrorCreate(kSOSErrorNoCircle, error, NULL, CFSTR("No circle")));
1822 generation = (CFNumberRef)SOSCircleGetGeneration(trust.trustedCircle);
1823 result = CFArrayCreateForCFTypes(kCFAllocatorDefault, generation, NULL);
1829 bool SOSValidateUserPublic(SOSAccount* account, CFErrorRef *error) {
1830 if (!SOSAccountHasPublicKey(account, error))
1833 return account.accountKeyIsTrusted;
1836 bool SOSAccountEnsurePeerRegistration(SOSAccount* account, CFErrorRef *error) {
1837 // TODO: this result is never set or used
1839 SOSAccountTrustClassic *trust = account.trust;
1841 secnotice("updates", "Ensuring peer registration.");
1843 if(!trust.trustedCircle || !trust.fullPeerInfo || !account.accountKeyIsTrusted)
1846 // If we are not in the circle, there is no point in setting up peers
1847 if(!SOSAccountIsMyPeerActive(account, NULL))
1850 // This code only uses the SOSFullPeerInfoRef for two things:
1851 // - Finding out if this device is in the trusted circle
1852 // - Using the peerID for this device to see if the current peer is "me"
1853 // - It is used indirectly by passing trust.fullPeerInfo to SOSEngineInitializePeerCoder
1855 CFStringRef my_id = SOSPeerInfoGetPeerID(trust.peerInfo);
1857 SOSCircleForEachValidSyncingPeer(trust.trustedCircle, account.accountKey, ^(SOSPeerInfoRef peer) {
1858 if (!SOSPeerInfoPeerIDEqual(peer, my_id)) {
1859 CFErrorRef localError = NULL;
1861 SOSMessage *messageTransport = SOSPeerInfoHasDeviceID(peer) ? account.ids_message_transport : account.kvs_message_transport;
1863 SOSEngineInitializePeerCoder((SOSEngineRef)[messageTransport SOSTransportMessageGetEngine], trust.fullPeerInfo, peer, &localError);
1865 secnotice("updates", "can't initialize transport for peer %@ with %@ (%@)", peer, trust.fullPeerInfo, localError);
1866 CFReleaseSafe(localError);
1870 //Initialize our device ID
1871 [account.ids_message_transport SOSTransportMessageIDSGetIDSDeviceID:account];
1877 // Value manipulation
1880 CFTypeRef SOSAccountGetValue(SOSAccount* account, CFStringRef key, CFErrorRef *error) {
1881 SOSAccountTrustClassic *trust = account.trust;
1882 if (!trust.expansion) {
1885 return (__bridge CFTypeRef)([trust.expansion objectForKey:(__bridge NSString* _Nonnull)(key)]);
1888 bool SOSAccountAddEscrowRecords(SOSAccount* account, CFStringRef dsid, CFDictionaryRef record, CFErrorRef *error){
1889 CFMutableDictionaryRef escrowRecords = (CFMutableDictionaryRef)SOSAccountGetValue(account, kSOSEscrowRecord, error);
1890 CFMutableDictionaryRef escrowCopied = NULL;
1891 bool success = false;
1893 if(isDictionary(escrowRecords) && escrowRecords != NULL)
1894 escrowCopied = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, CFDictionaryGetCount(escrowRecords), escrowRecords);
1896 escrowCopied = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1898 CFDictionaryAddValue(escrowCopied, dsid, record);
1899 SOSAccountSetValue(account, kSOSEscrowRecord, escrowCopied, error);
1904 CFReleaseNull(escrowCopied);
1910 bool SOSAccountAddEscrowToPeerInfo(SOSAccount* account, SOSFullPeerInfoRef myPeer, CFErrorRef *error){
1911 bool success = false;
1913 CFDictionaryRef escrowRecords = SOSAccountGetValue(account, kSOSEscrowRecord, error);
1914 success = SOSFullPeerInfoReplaceEscrowRecords(myPeer, escrowRecords, error);
1919 bool SOSAccountCheckPeerAvailability(SOSAccount* account, CFErrorRef *error)
1921 CFStringRef operationString = NULL;
1922 CFDictionaryRef rawMessage = NULL;
1923 CFMutableSetRef peers = NULL;
1924 CFMutableDictionaryRef peerList = NULL;
1925 char* message = NULL;
1926 bool result = false;
1927 SOSAccountTrustClassic* trust = account.trust;
1928 if(account.ids_message_transport == NULL)
1929 account.ids_message_transport = [[SOSMessageIDS alloc] initWithAccount:account andName:(__bridge NSString *)SOSCircleGetName(trust.trustedCircle)];
1931 if(!account.ids_message_transport)
1934 //adding message type kIDSPeerAvailability so KeychainSyncingOverIDSProxy does not send this message as a keychain item
1936 operationString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), kIDSPeerAvailability);
1937 rawMessage = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
1938 kIDSOperationType, operationString,
1939 kIDSMessageToSendKey, CFSTR("checking peers"),
1942 peerList = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1943 SOSCircleRef circle = trust.trustedCircle;
1945 //check each peer to make sure they have the right view set enabled
1946 CFSetRef mySubSet = SOSViewsGetV0SubviewSet();
1947 SOSCircleForEachValidPeer(circle, account.accountKey, ^(SOSPeerInfoRef peer) {
1948 if(!CFEqualSafe(peer, account.peerInfo)){
1949 CFMutableSetRef peerViews = SOSPeerInfoCopyEnabledViews(peer);
1950 CFSetRef intersectSets = CFSetCreateIntersection(kCFAllocatorDefault, mySubSet, peerViews);
1951 if(CFEqualSafe(intersectSets, mySubSet)){
1952 CFStringRef deviceID = SOSPeerInfoCopyDeviceID(peer);
1953 if(deviceID != NULL)
1954 CFDictionaryAddValue(peerList, SOSPeerInfoGetPeerID(peer), rawMessage);
1955 CFReleaseNull(deviceID);
1957 CFReleaseNull(peerViews);
1958 CFReleaseNull(intersectSets);
1962 require_quiet(CFDictionaryGetCount(peerList) > 0 , fail);
1964 result = [account.ids_message_transport SOSTransportMessageSendMessages:account.ids_message_transport pm:peerList err:error];
1967 CFReleaseNull(rawMessage);
1968 CFReleaseNull(operationString);
1969 CFReleaseNull(peerList);
1970 CFReleaseNull(peers);
1976 void SOSAccountRecordRetiredPeersInCircle(SOSAccount* account) {
1977 if (![account.trust isInCircle:NULL])
1979 SOSAccountTrustClassic *trust = account.trust;
1980 [trust modifyCircle:account.circle_transport err:NULL action:^bool (SOSCircleRef circle) {
1981 __block bool updated = false;
1982 CFSetForEach((__bridge CFMutableSetRef)trust.retirees, ^(CFTypeRef element){
1983 SOSPeerInfoRef retiree = asSOSPeerInfo(element);
1985 if (retiree && SOSCircleUpdatePeerInfo(circle, retiree)) {
1987 secnotice("retirement", "Updated retired peer %@ in %@", retiree, circle);
1988 CFErrorRef cleanupError = NULL;
1989 if (![account.trust cleanupAfterPeer:account.kvs_message_transport circleTransport:account.circle_transport seconds:RETIREMENT_FINALIZATION_SECONDS circle:circle cleanupPeer:retiree err:&cleanupError])
1990 secerror("Error cleanup up after peer (%@): %@", retiree, cleanupError);
1991 CFReleaseSafe(cleanupError);
1998 static const uint64_t maxTimeToWaitInSeconds = 30ull * NSEC_PER_SEC;
2000 static CFDictionaryRef SOSAccountGetObjectsFromCloud(dispatch_queue_t processQueue, CFErrorRef *error)
2002 __block CFTypeRef object = NULL;
2004 dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
2005 dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds);
2007 CloudKeychainReplyBlock replyBlock =
2008 ^ (CFDictionaryRef returnedValues, CFErrorRef error)
2010 secnotice("key-cleanup", "SOSCloudKeychainGetObjectsFromCloud returned: %@", returnedValues);
2011 object = returnedValues;
2016 secerror("SOSCloudKeychainGetObjectsFromCloud returned error: %@", error);
2018 secnotice("key-cleanup", "SOSCloudKeychainGetObjectsFromCloud block exit: %@", object);
2019 dispatch_semaphore_signal(waitSemaphore);
2022 SOSCloudKeychainGetAllObjectsFromCloud(processQueue, replyBlock);
2024 dispatch_semaphore_wait(waitSemaphore, finishTime);
2025 if (object && (CFGetTypeID(object) == CFNullGetTypeID())) // return a NULL instead of a CFNull
2030 secnotice("key-cleanup", "returned: %@", object);
2031 return asDictionary(object, error);
2035 static void SOSAccountRemoveKVSKeys(SOSAccount* account, NSArray* keysToRemove, dispatch_queue_t processQueue)
2037 CFStringRef uuid = SOSAccountCopyUUID(account);
2038 dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
2039 dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds);
2041 CloudKeychainReplyBlock replyBlock = ^ (CFDictionaryRef returnedValues, CFErrorRef error){
2043 secerror("SOSCloudKeychainRemoveKeys returned error: %@", error);
2045 dispatch_semaphore_signal(waitSemaphore);
2048 SOSCloudKeychainRemoveKeys((__bridge CFArrayRef)(keysToRemove), uuid, processQueue, replyBlock);
2049 dispatch_semaphore_wait(waitSemaphore, finishTime);
2053 static void SOSAccountWriteLastCleanupTimestampToKVS(SOSAccount* account)
2055 NSMutableDictionary *writeTimestamp = [NSMutableDictionary dictionary];
2057 CFMutableStringRef timeDescription = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, CFSTR("["));
2058 CFAbsoluteTime currentTimeAndDate = CFAbsoluteTimeGetCurrent();
2060 withStringOfAbsoluteTime(currentTimeAndDate, ^(CFStringRef decription) {
2061 CFStringAppend(timeDescription, decription);
2063 CFStringAppend(timeDescription, CFSTR("]"));
2065 [writeTimestamp setObject:(__bridge NSString*)(timeDescription) forKey:(__bridge NSString*)kSOSKVSLastCleanupTimestampKey];
2067 dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
2068 dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds);
2069 dispatch_queue_t processQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
2071 CloudKeychainReplyBlock replyBlock = ^ (CFDictionaryRef returnedValues, CFErrorRef error){
2073 secerror("SOSCloudKeychainPutObjectsInCloud returned error: %@", error);
2075 dispatch_semaphore_signal(waitSemaphore);
2078 SOSCloudKeychainPutObjectsInCloud((__bridge CFDictionaryRef)(writeTimestamp), processQueue, replyBlock);
2079 dispatch_semaphore_wait(waitSemaphore, finishTime);
2082 //Get all the key/values in KVS and remove old entries
2083 bool SOSAccountCleanupAllKVSKeys(SOSAccount* account, CFErrorRef* error)
2085 dispatch_queue_t processQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
2087 NSDictionary *keysAndValues = (__bridge_transfer NSDictionary*)SOSAccountGetObjectsFromCloud(processQueue, error);
2088 NSMutableArray *peerIDs = [NSMutableArray array];
2089 NSMutableArray *keysToRemove = [NSMutableArray array];
2091 CFArrayForEach(SOSAccountCopyActiveValidPeers(account, error), ^(const void *value) {
2092 SOSPeerInfoRef peer = (SOSPeerInfoRef)value;
2093 NSString* peerID = (__bridge NSString*) SOSPeerInfoGetPeerID(peer);
2095 //any peerid that is not ours gets added
2096 if(![[account.trust peerID] isEqualToString:peerID])
2097 [peerIDs addObject:peerID];
2100 [keysAndValues enumerateKeysAndObjectsUsingBlock:^(NSString * KVSKey, NSNumber * KVSValue, BOOL *stop) {
2101 __block bool keyMatchesPeerID = false;
2103 //checks for full peer ids
2104 [peerIDs enumerateObjectsUsingBlock:^(id _Nonnull PeerID, NSUInteger idx, BOOL * _Nonnull stop) {
2105 //if key contains peerid of one active peer
2106 if([KVSKey containsString:PeerID]){
2107 secnotice("key-cleanup","key: %@", KVSKey);
2108 keyMatchesPeerID = true;
2111 if((([KVSKey hasPrefix:@"ak"] || [KVSKey hasPrefix:@"-ak"]) && !keyMatchesPeerID)
2112 || [KVSKey hasPrefix:@"po"])
2113 [keysToRemove addObject:KVSKey];
2116 secnotice("key-cleanup", "message keys that we should remove! %@", keysToRemove);
2117 secnotice("key-cleanup", "total keys: %lu, cleaning up %lu", (unsigned long)[keysAndValues count], (unsigned long)[keysToRemove count]);
2119 SOSAccountRemoveKVSKeys(account, keysToRemove, processQueue);
2121 //add last cleanup timestamp
2122 SOSAccountWriteLastCleanupTimestampToKVS(account);
2127 bool SOSAccountPopulateKVSWithBadKeys(SOSAccount* account, CFErrorRef* error) {
2129 NSMutableDictionary *testKeysAndValues = [NSMutableDictionary dictionary];
2130 [testKeysAndValues setObject:@"deadbeef" forKey:@"-ak|asdfjkl;asdfjk;"];
2131 [testKeysAndValues setObject:@"foobar" forKey:@"ak|asdfasdfasdf:qwerqwerqwer"];
2132 [testKeysAndValues setObject:@"oldhistorycircle" forKey:@"poak|asdfasdfasdfasdf"];
2133 [testKeysAndValues setObject:@"oldhistorycircle" forKey:@"po|asdfasdfasdfasdfasdfasdf"];
2134 [testKeysAndValues setObject:@"oldhistorycircle" forKey:@"k>KeyParm"];
2136 dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
2137 dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds);
2138 dispatch_queue_t processQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
2140 CloudKeychainReplyBlock replyBlock = ^ (CFDictionaryRef returnedValues, CFErrorRef error){
2142 secerror("SOSCloudKeychainPutObjectsInCloud returned error: %@", error);
2144 dispatch_semaphore_signal(waitSemaphore);
2147 SOSCloudKeychainPutObjectsInCloud((__bridge CFDictionaryRef)(testKeysAndValues), processQueue, replyBlock);
2148 dispatch_semaphore_wait(waitSemaphore, finishTime);
2153 SOSPeerInfoRef SOSAccountCopyApplication(SOSAccount* account, CFErrorRef* error) {
2154 SOSPeerInfoRef applicant = NULL;
2155 SOSAccountTrustClassic *trust = account.trust;
2156 SecKeyRef userKey = SOSAccountGetPrivateCredential(account, error);
2157 if(!userKey) return false;
2158 if(![trust ensureFullPeerAvailable:(__bridge CFDictionaryRef)(account.gestalt) deviceID:(__bridge CFStringRef)(account.deviceID) backupKey:(__bridge CFDataRef)(account.backup_key) err:error])
2160 if(!SOSFullPeerInfoPromoteToApplication(trust.fullPeerInfo, userKey, error))
2162 applicant = SOSPeerInfoCreateCopy(kCFAllocatorDefault, trust.peerInfo, error);
2169 AddStrippedResults(NSMutableArray *results, NSArray *keychainItems, NSMutableSet *seenUUID, bool authoriative)
2171 [keychainItems enumerateObjectsUsingBlock:^(NSDictionary* keychainItem, NSUInteger idx, BOOL * _Nonnull stop) {
2172 NSString* parentUUID = keychainItem[(id)kSecAttrPath];
2173 NSString* viewUUID = keychainItem[(id)kSecAttrAccount];
2174 NSString *viewName = [keychainItem objectForKey:(id)kSecAttrServer];
2176 if (parentUUID == NULL || viewUUID == NULL || viewName == NULL)
2179 if([parentUUID isEqualToString:viewUUID] || authoriative){
2181 /* check if we already have this entry */
2182 if ([seenUUID containsObject:viewUUID])
2185 NSData* v_data = [keychainItem objectForKey:(id)kSecValueData];
2186 NSData *key = [[NSData alloc] initWithBase64EncodedData:v_data options:0];
2191 secnotice("piggy", "fetched TLK %@ with name %@", viewName, viewUUID);
2193 NSMutableDictionary* strippedDown = [@{
2194 (id)kSecValueData : key,
2195 (id)kSecAttrServer : viewName,
2196 (id)kSecAttrAccount : viewUUID
2199 strippedDown[@"auth"] = @YES;
2201 [results addObject:strippedDown];
2202 [seenUUID addObject:viewUUID];
2208 AddViewManagerResults(NSMutableArray *results, NSMutableSet *seenUUID)
2211 CKKSViewManager* manager = [CKKSViewManager manager];
2213 NSDictionary<NSString *,NSString *> *items = [manager activeTLKs];
2215 for (NSString *view in items) {
2216 NSString *uuid = items[view];
2217 NSDictionary *query = @{
2218 (id)kSecClass : (id)kSecClassInternetPassword,
2219 (id)kSecAttrNoLegacy : @YES,
2220 (id)kSecAttrAccessGroup : @"com.apple.security.ckks",
2221 (id)kSecAttrAccount : uuid,
2222 (id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
2223 (id)kSecMatchLimit : (id)kSecMatchLimitAll,
2224 (id)kSecReturnAttributes: @YES,
2225 (id)kSecReturnData: @YES,
2227 CFTypeRef result = NULL;
2228 if (SecItemCopyMatching((__bridge CFDictionaryRef)query, &result) == 0) {
2229 AddStrippedResults(results, (__bridge NSArray*)result, seenUUID, true);
2231 CFReleaseNull(result);
2238 SOSAccountGetAllTLKs(void)
2240 CFTypeRef result = NULL;
2241 NSMutableArray* results = [NSMutableArray array];
2242 NSMutableSet *seenUUID = [NSMutableSet set];
2244 // first use the TLK from the view manager
2245 AddViewManagerResults(results, seenUUID);
2247 //try to grab tlk-piggy items
2248 NSDictionary* query = @{
2249 (id)kSecClass : (id)kSecClassInternetPassword,
2250 (id)kSecAttrNoLegacy : @YES,
2251 (id)kSecAttrAccessGroup : @"com.apple.security.ckks",
2252 (id)kSecAttrDescription: @"tlk",
2253 (id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
2254 (id)kSecMatchLimit : (id)kSecMatchLimitAll,
2255 (id)kSecReturnAttributes: @YES,
2256 (id)kSecReturnData: @YES,
2259 if (SecItemCopyMatching((__bridge CFDictionaryRef)query, &result) == 0) {
2260 AddStrippedResults(results, (__bridge NSArray*)result, seenUUID, false);
2262 CFReleaseNull(result);
2264 //try to grab tlk-piggy items
2266 (id)kSecClass : (id)kSecClassInternetPassword,
2267 (id)kSecAttrNoLegacy : @YES,
2268 (id)kSecAttrAccessGroup : @"com.apple.security.ckks",
2269 (id)kSecAttrDescription: @"tlk-piggy",
2270 (id)kSecAttrSynchronizable : (id)kSecAttrSynchronizableAny,
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 secnotice("piggy", "Found %d TLKs", (int)[results count]);
2286 static uint8_t* encode_tlk(kTLKTypes type, NSString *name, NSData *keychainData, NSData* uuid,
2287 const uint8_t *der, uint8_t *der_end)
2289 if (type != kTLKUnknown) {
2290 return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
2291 piggy_encode_data(keychainData, der,
2292 piggy_encode_data(uuid, der,
2293 ccder_encode_uint64((uint64_t)type, der, der_end))));
2295 return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
2296 piggy_encode_data(keychainData, der,
2297 piggy_encode_data(uuid, der,
2298 der_encode_string((__bridge CFStringRef)name, NULL, der, der_end))));
2302 static uint8_t* piggy_encode_data(NSData* data,
2303 const uint8_t *der, uint8_t *der_end)
2305 return ccder_encode_tl(CCDER_OCTET_STRING, data.length, der,
2306 ccder_encode_body(data.length, data.bytes, der, der_end));
2311 name2type(NSString *view)
2313 if ([view isEqualToString:@"Manatee"])
2315 else if ([view isEqualToString:@"Engram"])
2317 else if ([view isEqualToString:@"AutoUnlock"])
2318 return kTLKAutoUnlock;
2319 if ([view isEqualToString:@"Health"])
2325 rank_type(NSString *view)
2327 if ([view isEqualToString:@"Manatee"])
2329 else if ([view isEqualToString:@"Engram"])
2331 else if ([view isEqualToString:@"AutoUnlock"])
2333 if ([view isEqualToString:@"Health"])
2339 parse_uuid(NSString *uuidString)
2341 NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:uuidString];
2343 [uuid getUUIDBytes:uuidblob];
2344 return [NSData dataWithBytes:uuidblob length:sizeof(uuid_t)];
2347 piggy_sizeof_data(NSData* data)
2349 return ccder_sizeof(CCDER_OCTET_STRING, [data length]);
2352 static size_t sizeof_keychainitem(kTLKTypes type, NSString *name, NSData* keychainData, NSData* uuid) {
2353 if (type != kTLKUnknown) {
2354 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE,
2355 piggy_sizeof_data(keychainData) +
2356 piggy_sizeof_data(uuid) +
2357 ccder_sizeof_uint64(type));
2359 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE,
2360 piggy_sizeof_data(keychainData) +
2361 piggy_sizeof_data(uuid) +
2362 der_sizeof_string((__bridge CFStringRef)name, NULL));
2366 NSArray<NSDictionary*>*
2367 SOSAccountSortTLKS(NSArray<NSDictionary*>* tlks)
2369 NSMutableArray<NSDictionary*>* sortedTLKs = [tlks mutableCopy];
2371 [sortedTLKs sortUsingComparator:^NSComparisonResult(NSDictionary *obj1, NSDictionary *obj2) {
2372 unsigned rank1 = rank_type(obj1[(__bridge id)kSecAttrServer]);
2373 if (obj1[@"auth"] != NULL)
2375 unsigned rank2 = rank_type(obj2[(__bridge id)kSecAttrServer]);
2376 if (obj2[@"auth"] != NULL)
2380 * Sort by rank (higher better), but prefer TLK that are authoriative (ie used by CKKSViewManager),
2381 * since we are sorting backward, the Ascending/Descending looks wrong below.
2383 if (rank1 > rank2) {
2384 return NSOrderedAscending;
2385 } else if (rank1 < rank2) {
2386 return NSOrderedDescending;
2388 return NSOrderedSame;
2394 static NSArray<NSData *> *
2395 build_tlks(NSArray<NSDictionary*>* tlks)
2397 NSMutableArray *array = [NSMutableArray array];
2398 NSArray<NSDictionary*>* sortedTLKs = SOSAccountSortTLKS(tlks);
2400 for (NSDictionary *item in sortedTLKs) {
2401 NSData* keychainData = item[(__bridge id)kSecValueData];
2402 NSString* name = item[(__bridge id)kSecAttrServer];
2403 NSString *uuidString = item[(__bridge id)kSecAttrAccount];
2404 NSData* uuid = parse_uuid(uuidString);
2406 NSMutableData *tlk = [NSMutableData dataWithLength:sizeof_keychainitem(name2type(name), name, keychainData, uuid)];
2408 unsigned char *der = [tlk mutableBytes];
2409 unsigned char *der_end = der + [tlk length];
2411 if (encode_tlk(name2type(name), name, keychainData, uuid, der, der_end) == NULL)
2414 secnotice("piggy", "preparing TLK in order: %@: %@", name, uuidString);
2416 [array addObject:tlk];
2421 static NSArray<NSData *> *
2422 build_identities(NSArray<NSData *>* identities)
2424 NSMutableArray *array = [NSMutableArray array];
2425 for (NSData *item in identities) {
2426 NSMutableData *ident = [NSMutableData dataWithLength:ccder_sizeof_raw_octet_string([item length])];
2428 unsigned char *der = [ident mutableBytes];
2429 unsigned char *der_end = der + [ident length];
2431 ccder_encode_raw_octet_string([item length], [item bytes], der, der_end);
2432 [array addObject:ident];
2439 static unsigned char *
2440 encode_data_array(NSArray<NSData*>* data, unsigned char *der, unsigned char *der_end)
2442 unsigned char *body_end = der_end;
2443 for (NSData *datum in data) {
2444 der_end = ccder_encode_body([datum length], [datum bytes], der, der_end);
2445 if (der_end == NULL)
2448 return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, body_end, der, der_end);
2451 static size_t sizeof_piggy(size_t identities_size, size_t tlk_size)
2453 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE,
2454 ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, identities_size) +
2455 ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, tlk_size));
2458 static NSData *encode_piggy(size_t IdentitiesBudget,
2460 NSArray<NSData*>* identities,
2461 NSArray<NSDictionary*>* tlks)
2463 NSArray<NSData *> *encodedTLKs = build_tlks(tlks);
2464 NSArray<NSData *> *encodedIdentities = build_identities(identities);
2465 NSMutableArray<NSData *> *budgetArray = [NSMutableArray array];
2466 NSMutableArray<NSData *> *identitiesArray = [NSMutableArray array];
2467 size_t payloadSize = 0, identitiesSize = 0;
2468 NSMutableData *result = NULL;
2470 for (NSData *tlk in encodedTLKs) {
2471 if (TLKBudget - payloadSize < [tlk length])
2473 [budgetArray addObject:tlk];
2474 payloadSize += tlk.length;
2476 secnotice("piggy", "sending %d tlks", (int)budgetArray.count);
2478 for (NSData *ident in encodedIdentities) {
2479 if (IdentitiesBudget - identitiesSize < [ident length])
2481 [identitiesArray addObject:ident];
2482 identitiesSize += ident.length;
2484 secnotice("piggy", "sending %d identities", (int)identitiesArray.count);
2487 size_t piggySize = sizeof_piggy(identitiesSize, payloadSize);
2489 result = [NSMutableData dataWithLength:piggySize];
2491 unsigned char *der = [result mutableBytes];
2492 unsigned char *der_end = der + [result length];
2494 if (ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
2495 encode_data_array(identitiesArray, der,
2496 encode_data_array(budgetArray, der, der_end))) != [result mutableBytes])
2502 static const size_t SOSCCIdentitiesBudget = 120;
2503 static const size_t SOSCCTLKBudget = 500;
2506 SOSPiggyCreateInitialSyncData(NSArray<NSData*>* identities, NSArray<NSDictionary *>* tlks)
2508 return encode_piggy(SOSCCIdentitiesBudget, SOSCCTLKBudget, identities, tlks);
2511 CFMutableArrayRef SOSAccountCopyiCloudIdentities(SOSAccount* account)
2513 CFMutableArrayRef identities = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
2515 SOSCircleForEachActivePeer(account.trust.trustedCircle, ^(SOSPeerInfoRef peer) {
2516 if(SOSPeerInfoIsCloudIdentity(peer)) {
2517 CFArrayAppendValue(identities, peer);
2523 CFDataRef SOSAccountCopyInitialSyncData(SOSAccount* account, CFErrorRef *error) {
2524 CFMutableArrayRef identities = SOSAccountCopyiCloudIdentities(account);
2525 secnotice("piggy", "identities: %@", identities);
2527 NSMutableArray *encodedIdenities = [NSMutableArray array];
2528 CFIndex i, count = CFArrayGetCount(identities);
2529 for (i = 0; i < count; i++) {
2530 SOSPeerInfoRef fpi = (SOSPeerInfoRef)CFArrayGetValueAtIndex(identities, i);
2531 NSData *data = CFBridgingRelease(SOSPeerInfoCopyData(fpi, error));
2533 [encodedIdenities addObject:data];
2535 CFRelease(identities);
2537 NSMutableArray* tlks = SOSAccountGetAllTLKs();
2539 return CFBridgingRetain(SOSPiggyCreateInitialSyncData(encodedIdenities, tlks));
2542 CFDataRef SOSAccountCopyCircleJoiningBlob(SOSAccount* account, SOSPeerInfoRef applicant, CFErrorRef *error) {
2543 SOSGenCountRef gencount = NULL;
2544 CFDataRef signature = NULL;
2545 SecKeyRef ourKey = NULL;
2547 CFDataRef pbblob = NULL;
2549 secnotice("circleJoin", "Making circle joining blob as sponsor (SOSAccountCopyCircleJoiningBlob)");
2551 SecKeyRef userKey = SOSAccountGetTrustedPublicCredential(account, error);
2552 require_quiet(userKey, errOut);
2554 require_action_quiet(applicant, errOut, SOSCreateError(kSOSErrorProcessingFailure, CFSTR("No applicant provided"), (error != NULL) ? *error : NULL, error));
2555 require_quiet(SOSPeerInfoApplicationVerify(applicant, userKey, error), errOut);
2558 SOSFullPeerInfoRef fpi = account.fullPeerInfo;
2559 ourKey = SOSFullPeerInfoCopyDeviceKey(fpi, error);
2560 require_quiet(ourKey, errOut);
2563 SOSCircleRef currentCircle = [account.trust getCircle:error];
2564 require_quiet(currentCircle, errOut);
2566 SOSCircleRef prunedCircle = SOSCircleCopyCircle(NULL, currentCircle, error);
2567 require_quiet(prunedCircle, errOut);
2568 require_quiet(SOSCirclePreGenerationSign(prunedCircle, userKey, error), errOut);
2570 gencount = SOSGenerationIncrementAndCreate(SOSCircleGetGeneration(prunedCircle));
2572 signature = SOSCircleCopyNextGenSignatureWithPeerAdded(prunedCircle, applicant, ourKey, error);
2573 require_quiet(signature, errOut);
2575 pbblob = SOSPiggyBackBlobCopyEncodedData(gencount, ourKey, signature, error);
2578 CFReleaseNull(gencount);
2579 CFReleaseNull(signature);
2580 CFReleaseNull(ourKey);
2582 if(!pbblob && error != NULL) {
2583 secnotice("circleJoin", "Failed to make circle joining blob as sponsor %@", *error);
2589 bool SOSAccountJoinWithCircleJoiningBlob(SOSAccount* account, CFDataRef joiningBlob, PiggyBackProtocolVersion version, CFErrorRef *error) {
2590 bool retval = false;
2591 SecKeyRef userKey = NULL;
2592 SOSAccountTrustClassic *trust = account.trust;
2593 SOSGenCountRef gencount = NULL;
2594 CFDataRef signature = NULL;
2595 SecKeyRef pubKey = NULL;
2596 bool setInitialSyncTimeoutToV0 = false;
2598 secnotice("circleJoin", "Joining circles through piggy-back (SOSAccountCopyCircleJoiningBlob)");
2600 if (!isData(joiningBlob))
2603 userKey = SOSAccountGetPrivateCredential(account, error);
2607 if (!SOSPiggyBackBlobCreateFromData(&gencount, &pubKey, &signature, joiningBlob, version, &setInitialSyncTimeoutToV0, error))
2610 if(setInitialSyncTimeoutToV0){
2611 secnotice("piggy", "setting flag in account for piggybacking v0");
2612 SOSAccountSetValue(account, kSOSInitialSyncTimeoutV0, kCFBooleanTrue, NULL);
2615 secnotice("piggy", "setting flag in account for piggybacking v0");
2616 SOSAccountClearValue(account, kSOSInitialSyncTimeoutV0, NULL);
2618 SOSAccountSetValue(account, kSOSUnsyncedViewsKey, kCFBooleanTrue, NULL);
2620 retval = [trust modifyCircle:account.circle_transport err:error action:^bool(SOSCircleRef copyOfCurrent) {
2621 return SOSCircleAcceptPeerFromHSA2(copyOfCurrent, userKey,
2625 trust.fullPeerInfo, error);;
2629 CFReleaseNull(gencount);
2630 CFReleaseNull(pubKey);
2631 CFReleaseNull(signature);
2636 static char boolToChars(bool val, char truechar, char falsechar) {
2637 return val? truechar: falsechar;
2640 #define ACCOUNTLOGSTATE "accountLogState"
2641 void SOSAccountLogState(SOSAccount* account) {
2642 bool hasPubKey = account.accountKey != NULL;
2643 SOSAccountTrustClassic *trust = account.trust;
2644 bool pubTrusted = account.accountKeyIsTrusted;
2645 bool hasPriv = account.accountPrivateKey != NULL;
2646 SOSCCStatus stat = [account getCircleStatus:NULL];
2648 CFStringRef userPubKeyID = (account.accountKey) ? SOSCopyIDOfKeyWithLength(account.accountKey, 8, NULL):
2649 CFStringCreateCopy(kCFAllocatorDefault, CFSTR("*No Key*"));
2651 secnotice(ACCOUNTLOGSTATE, "Start");
2653 secnotice(ACCOUNTLOGSTATE, "ACCOUNT: [keyStatus: %c%c%c hpub %@] [SOSCCStatus: %@]",
2654 boolToChars(hasPubKey, 'U', 'u'), boolToChars(pubTrusted, 'T', 't'), boolToChars(hasPriv, 'I', 'i'),
2656 SOSAccountGetSOSCCStatusString(stat)
2658 CFReleaseNull(userPubKeyID);
2659 if(trust.trustedCircle) SOSCircleLogState(ACCOUNTLOGSTATE, trust.trustedCircle, account.accountKey, (__bridge CFStringRef)(account.peerID));
2660 else secnotice(ACCOUNTLOGSTATE, "ACCOUNT: No Circle");
2663 void SOSAccountLogViewState(SOSAccount* account) {
2664 bool isInCircle = [account.trust isInCircle:NULL];
2665 require_quiet(isInCircle, imOut);
2666 SOSPeerInfoRef mpi = account.peerInfo;
2667 bool isInitialComplete = SOSAccountHasCompletedInitialSync(account);
2668 bool isBackupComplete = SOSAccountHasCompletedRequiredBackupSync(account);
2670 CFSetRef views = mpi ? SOSPeerInfoCopyEnabledViews(mpi) : NULL;
2671 CFStringSetPerformWithDescription(views, ^(CFStringRef description) {
2672 secnotice(ACCOUNTLOGSTATE, "Sync: %c%c PeerViews: %@",
2673 boolToChars(isInitialComplete, 'I', 'i'),
2674 boolToChars(isBackupComplete, 'B', 'b'),
2677 CFReleaseNull(views);
2678 CFSetRef unsyncedViews = SOSAccountCopyOutstandingViews(account);
2679 CFStringSetPerformWithDescription(views, ^(CFStringRef description) {
2680 secnotice(ACCOUNTLOGSTATE, "outstanding views: %@", description);
2682 CFReleaseNull(unsyncedViews);
2685 secnotice(ACCOUNTLOGSTATE, "Finish");
2691 void SOSAccountSetTestSerialNumber(SOSAccount* account, CFStringRef serial) {
2692 if(!isString(serial)) return;
2693 CFMutableDictionaryRef newv2dict = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
2694 CFDictionarySetValue(newv2dict, sSerialNumberKey, serial);
2695 [account.trust updateV2Dictionary:account v2:newv2dict];
2698 void SOSAccountResetOTRNegotiationCoder(SOSAccountTransaction* txn, CFStringRef peerid)
2700 secnotice("otrtimer", "timer fired!");
2701 CFErrorRef error = NULL;
2702 SecADAddValueForScalarKey((__bridge CFStringRef) SecSOSAggdReattemptOTRNegotiation,1);
2703 __block SOSAccount* account = txn.account;
2705 SOSEngineRef engine = SOSDataSourceFactoryGetEngineForDataSourceName(account.factory, SOSCircleGetName(account.trust.trustedCircle), NULL);
2706 SOSEngineWithPeerID(engine, peerid, &error, ^(SOSPeerRef peer, SOSCoderRef coder, SOSDataSourceRef dataSource, SOSTransactionRef txn, bool *forceSaveState) {
2707 if(SOSCoderIsCoderInAwaitingState(coder)){
2708 secnotice("otrtimer", "coder is in awaiting state, restarting coder");
2709 CFErrorRef localError = NULL;
2710 SOSCoderReset(coder);
2711 if(SOSCoderStart(coder, &localError) == kSOSCoderFailure){
2712 secerror("Attempt to recover coder failed to restart: %@", localError);
2715 secnotice("otrtimer", "coder restarted!");
2716 SOSEngineSetCodersNeedSaving(engine, true);
2717 SOSPeerSetMustSendMessage(peer, true);
2718 SOSCCRequestSyncWithPeer(SOSPeerGetID(peer));
2720 SOSPeerOTRTimerIncreaseOTRNegotiationRetryCount(account, (__bridge NSString*)SOSPeerGetID(peer));
2721 SOSPeerRemoveOTRTimerEntry(peer);
2722 SOSPeerOTRTimerRemoveRTTTimeoutForPeer(account, (__bridge NSString*)SOSPeerGetID(peer));
2723 SOSPeerOTRTimerRemoveLastSentMessageTimestamp(account, (__bridge NSString*)SOSPeerGetID(peer));
2726 secnotice("otrtimer", "time fired but out of negotiation! Not restarting coder");
2731 secnotice("otrtimer","error grabbing engine for peer id: %@, error:%@", peerid, error);
2733 CFReleaseNull(error);
2736 void SOSAccountTimerFiredSendNextMessage(SOSAccountTransaction* txn, NSString* peerid, NSString* accessGroup)
2738 __block SOSAccount* account = txn.account;
2739 CFErrorRef error = NULL;
2741 SOSEngineRef engine = SOSDataSourceFactoryGetEngineForDataSourceName(txn.account.factory, SOSCircleGetName(account.trust.trustedCircle), NULL);
2742 SOSEngineWithPeerID(engine, (__bridge CFStringRef)peerid, &error, ^(SOSPeerRef peer, SOSCoderRef coder, SOSDataSourceRef dataSource, SOSTransactionRef txn, bool *forceSaveState) {
2744 NSString *peer_id = (__bridge NSString*)SOSPeerGetID(peer);
2745 PeerRateLimiter *limiter = (__bridge PeerRateLimiter*)SOSPeerGetRateLimiter(peer);
2746 CFErrorRef error = NULL;
2747 NSData* message = [limiter.accessGroupToNextMessageToSend objectForKey:accessGroup];
2750 secnotice("ratelimit","SOSPeerRateLimiter timer went off! sending:%@ \n to peer:%@", message, peer_id);
2751 bool sendResult = [account.ids_message_transport SOSTransportMessageSendMessage:account.ids_message_transport id:(__bridge CFStringRef)peer_id messageToSend:(__bridge CFDataRef)message err:&error];
2753 if(!sendResult || error){
2754 secnotice("ratelimit", "could not send message: %@", error);
2757 [limiter.accessGroupRateLimitState setObject:[[NSNumber alloc]initWithLong:RateLimitStateCanSend] forKey:accessGroup];
2758 [limiter.accessGroupToTimer removeObjectForKey:accessGroup];
2759 [limiter.accessGroupToNextMessageToSend removeObjectForKey:accessGroup];
2764 secnotice("otrtimer","error grabbing engine for peer id: %@, error:%@", peerid, error);
2766 CFReleaseNull(error);