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;
722 if (flags & SOSControlInitialSyncFlagBluetoothMigration)
723 isflags |= SecServerInitialSyncCredentialFlagBluetoothMigration;
726 NSArray *array = CFBridgingRelease(_SecServerCopyInitialSyncCredentials(isflags, &error));
727 complete(array, (__bridge NSError *)error);
728 CFReleaseNull(error);
731 - (void)importInitialSyncCredentials:(NSArray *)items complete:(void (^)(bool success, NSError *))complete
733 CFErrorRef error = NULL;
734 bool res = _SecServerImportInitialSyncCredentials((__bridge CFArrayRef)items, &error);
735 complete(res, (__bridge NSError *)error);
736 CFReleaseNull(error);
739 - (void)triggerSync:(NSArray <NSString *> *)peers complete:(void(^)(bool success, NSError *))complete
741 __block CFErrorRef localError = NULL;
742 __block bool res = false;
744 secnotice("sync", "trigger a forced sync for %@", peers);
746 SecAKSDoWhileUserBagLocked(&localError, ^{
747 [self performTransaction:^(SOSAccountTransaction *txn) {
749 NSSet *peersSet = [NSSet setWithArray:peers];
750 CFMutableSetRef handledPeers = SOSAccountSyncWithPeers(txn, (__bridge CFSetRef)peersSet, &localError);
751 if (handledPeers && CFSetGetCount(handledPeers) == (CFIndex)[peersSet count]) {
754 CFReleaseNull(handledPeers);
756 res = SOSAccountRequestSyncWithAllPeers(txn, &localError);
760 complete(res, (__bridge NSError *)localError);
761 CFReleaseNull(localError);
764 - (void)getWatchdogParameters:(void (^)(NSDictionary* parameters, NSError* error))complete
766 // SecdWatchdog is only available in the secd/securityd - no other binary will contain that class
767 Class watchdogClass = NSClassFromString(@"SecdWatchdog");
769 NSDictionary* parameters = [[watchdogClass watchdog] watchdogParameters];
770 complete(parameters, nil);
773 complete(nil, [NSError errorWithDomain:@"com.apple.securityd.watchdog" code:1 userInfo:@{NSLocalizedDescriptionKey : @"failed to lookup SecdWatchdog class from ObjC runtime"}]);
777 - (void)setWatchdogParmeters:(NSDictionary*)parameters complete:(void (^)(NSError* error))complete
779 // SecdWatchdog is only available in the secd/securityd - no other binary will contain that class
780 NSError* error = nil;
781 Class watchdogClass = NSClassFromString(@"SecdWatchdog");
783 [[watchdogClass watchdog] setWatchdogParameters:parameters error:&error];
787 complete([NSError errorWithDomain:@"com.apple.securityd.watchdog" code:1 userInfo:@{NSLocalizedDescriptionKey : @"failed to lookup SecdWatchdog class from ObjC runtime"}]);
796 - (void) flattenToSaveBlock {
797 if (self.saveBlock) {
798 NSError* error = nil;
799 NSData* saveData = [self encodedData:&error];
801 (self.saveBlock)((__bridge CFDataRef) saveData, (__bridge CFErrorRef) error);
805 CFDictionaryRef SOSAccountCopyGestalt(SOSAccount* account) {
806 return CFDictionaryCreateCopy(kCFAllocatorDefault, (__bridge CFDictionaryRef)account.gestalt);
809 CFDictionaryRef SOSAccountCopyV2Dictionary(SOSAccount* account) {
810 CFDictionaryRef v2dict = SOSAccountGetValue(account, kSOSTestV2Settings, NULL);
811 return CFDictionaryCreateCopy(kCFAllocatorDefault, v2dict);
814 static bool SOSAccountUpdateDSID(SOSAccount* account, CFStringRef dsid){
815 SOSAccountSetValue(account, kSOSDSIDKey, dsid, NULL);
816 //send new DSID over account changed
817 [account.circle_transport kvsSendOfficialDSID:dsid err:NULL];
821 void SOSAccountAssertDSID(SOSAccount* account, CFStringRef dsid) {
822 CFStringRef accountDSID = SOSAccountGetValue(account, kSOSDSIDKey, NULL);
823 if(accountDSID == NULL) {
824 secdebug("updates", "Setting dsid, current dsid is empty for this account: %@", dsid);
826 SOSAccountUpdateDSID(account, dsid);
827 } else if(CFStringCompare(dsid, accountDSID, 0) != kCFCompareEqualTo) {
828 secnotice("updates", "Changing DSID from: %@ to %@", accountDSID, dsid);
830 //DSID has changed, blast the account!
831 SOSAccountSetToNew(account);
833 //update DSID to the new DSID
834 SOSAccountUpdateDSID(account, dsid);
836 secnotice("updates", "Not Changing DSID: %@ to %@", accountDSID, dsid);
841 void SOSAccountPendDisableViewSet(SOSAccount* account, CFSetRef disabledViews)
843 [account.trust valueUnionWith:kSOSPendingDisableViewsToBeSetKey valuesToUnion:disabledViews];
844 [account.trust valueSubtractFrom:kSOSPendingEnableViewsToBeSetKey valuesToSubtract:disabledViews];
847 SOSViewResultCode SOSAccountVirtualV0Behavior(SOSAccount* account, SOSViewActionCode actionCode) {
848 SOSViewResultCode retval = kSOSCCGeneralViewError;
849 // The V0 view switches on and off all on it's own, we allow people the delusion
850 // of control and status if it's what we're stuck at., otherwise error.
851 if (SOSAccountSyncingV0(account)) {
852 require_action_quiet(actionCode == kSOSCCViewDisable, errOut, CFSTR("Can't disable V0 view and it's on right now"));
853 retval = kSOSCCViewMember;
855 require_action_quiet(actionCode == kSOSCCViewEnable, errOut, CFSTR("Can't enable V0 and it's off right now"));
856 retval = kSOSCCViewNotMember;
862 SOSAccount* SOSAccountCreate(CFAllocatorRef allocator,
863 CFDictionaryRef gestalt,
864 SOSDataSourceFactoryRef factory) {
866 SOSAccount* a = [[SOSAccount alloc] initWithGestalt:gestalt factory:factory];
867 [a ensureFactoryCircles];
868 SOSAccountEnsureUUID(a);
869 a.key_interests_need_updating = true;
874 static OSStatus do_delete(CFDictionaryRef query) {
877 result = SecItemDelete(query);
879 secerror("SecItemDelete: %d", (int)result);
885 do_keychain_delete_aks_bags()
888 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
889 kSecClass, kSecClassGenericPassword,
890 kSecAttrAccessGroup, CFSTR("com.apple.sbd"),
891 kSecAttrAccount, CFSTR("SecureBackupPublicKeybag"),
892 kSecAttrService, CFSTR("SecureBackupService"),
893 kSecAttrSynchronizable, kCFBooleanTrue,
894 kSecUseTombstones, kCFBooleanFalse,
897 result = do_delete(item);
904 do_keychain_delete_identities()
907 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
908 kSecClass, kSecClassKey,
909 kSecAttrSynchronizable, kCFBooleanTrue,
910 kSecUseTombstones, kCFBooleanFalse,
911 kSecAttrAccessGroup, CFSTR("com.apple.security.sos"),
914 result = do_delete(item);
921 do_keychain_delete_lakitu()
924 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
925 kSecClass, kSecClassGenericPassword,
926 kSecAttrSynchronizable, kCFBooleanTrue,
927 kSecUseTombstones, kCFBooleanFalse,
928 kSecAttrAccessGroup, CFSTR("com.apple.lakitu"),
929 kSecAttrAccount, CFSTR("EscrowServiceBypassToken"),
930 kSecAttrService, CFSTR("EscrowService"),
933 result = do_delete(item);
940 do_keychain_delete_sbd()
943 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
944 kSecClass, kSecClassGenericPassword,
945 kSecAttrSynchronizable, kCFBooleanTrue,
946 kSecUseTombstones, kCFBooleanFalse,
947 kSecAttrAccessGroup, CFSTR("com.apple.sbd"),
950 result = do_delete(item);
956 void SOSAccountSetToNew(SOSAccount* a)
958 secnotice("accountChange", "Setting Account to New");
961 /* remove all syncable items */
962 result = do_keychain_delete_aks_bags(); (void) result;
963 secdebug("set to new", "result for deleting aks bags: %d", result);
965 result = do_keychain_delete_identities(); (void) result;
966 secdebug("set to new", "result for deleting identities: %d", result);
968 result = do_keychain_delete_lakitu(); (void) result;
969 secdebug("set to new", "result for deleting lakitu: %d", result);
971 result = do_keychain_delete_sbd(); (void) result;
972 secdebug("set to new", "result for deleting sbd: %d", result);
974 a.accountKeyIsTrusted = false;
976 if (a.user_private_timer) {
977 dispatch_source_cancel(a.user_private_timer);
978 a.user_private_timer = NULL;
979 xpc_transaction_end();
982 if (a.lock_notification_token != NOTIFY_TOKEN_INVALID) {
983 notify_cancel(a.lock_notification_token);
984 a.lock_notification_token = NOTIFY_TOKEN_INVALID;
991 // update_interest_block;
993 SOSUnregisterTransportKeyParameter(a.key_transport);
994 SOSUnregisterTransportMessage(a.ids_message_transport);
995 SOSUnregisterTransportMessage(a.kvs_message_transport);
996 SOSUnregisterTransportCircle(a.circle_transport);
998 a.circle_transport = NULL;
999 a.kvs_message_transport = NULL;
1000 a.ids_message_transport = NULL;
1003 a.trust = [[SOSAccountTrustClassic alloc]initWithRetirees:[NSMutableSet set] fpi:NULL circle:NULL departureCode:kSOSDepartureReasonError peerExpansion:[NSMutableDictionary dictionary]];
1005 [a ensureFactoryCircles]; // Does rings too
1007 // By resetting our expansion dictionary we've reset our UUID, so we'll be notified properly
1008 SOSAccountEnsureUUID(a);
1010 a.key_interests_need_updating = true;
1013 bool SOSAccountIsNew(SOSAccount* account, CFErrorRef *error){
1014 bool result = false;
1015 SOSAccountTrustClassic* trust = account.trust;
1016 if(account.accountKeyIsTrusted != false || trust.departureCode != kSOSNeverAppliedToCircle ||
1017 CFSetGetCount((__bridge CFSetRef)trust.retirees) != 0)
1020 if(trust.retirees != nil)
1022 if(trust.expansion != nil)
1025 if(account.user_private_timer != NULL || account.lock_notification_token != NOTIFY_TOKEN_INVALID)
1033 CFStringRef SOSAccountCreateCompactDescription(SOSAccount* a) {
1035 CFStringRef gestaltDescription = CFDictionaryCopySuperCompactDescription((__bridge CFDictionaryRef)(a.gestalt));
1037 CFStringRef result = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@"), gestaltDescription);
1039 CFReleaseNull(gestaltDescription);
1043 dispatch_queue_t SOSAccountGetQueue(SOSAccount* account) {
1044 return account.queue;
1047 void SOSAccountSetUserPublicTrustedForTesting(SOSAccount* account){
1048 account.accountKeyIsTrusted = true;
1051 -(SOSCCStatus) getCircleStatus:(CFErrorRef*) error
1053 SOSCCStatus circleStatus = kSOSCCError;
1054 if (SOSAccountHasPublicKey(self, error)) {
1055 circleStatus = [self.trust getCircleStatus:error];
1057 return circleStatus;
1059 bool SOSAccountScanForRetired(SOSAccount* account, SOSCircleRef circle, CFErrorRef *error) {
1061 SOSAccountTrustClassic *trust = account.trust;
1062 NSMutableSet* retirees = trust.retirees;
1063 SOSCircleForEachRetiredPeer(circle, ^(SOSPeerInfoRef peer) {
1064 CFSetSetValue((__bridge CFMutableSetRef) retirees, peer);
1065 CFErrorRef cleanupError = NULL;
1066 if (![account.trust cleanupAfterPeer:account.kvs_message_transport circleTransport:account.circle_transport seconds:RETIREMENT_FINALIZATION_SECONDS circle:circle cleanupPeer:peer err:&cleanupError]) {
1067 secnotice("retirement", "Error cleaning up after peer, probably orphaned some stuff in KVS: (%@) – moving on", cleanupError);
1069 CFReleaseSafe(cleanupError);
1074 SOSCircleRef SOSAccountCloneCircleWithRetirement(SOSAccount* account, SOSCircleRef starting_circle, CFErrorRef *error) {
1075 SOSCircleRef new_circle = SOSCircleCopyCircle(NULL, starting_circle, error);
1076 SOSPeerInfoRef me = account.peerInfo;
1077 bool iAmApplicant = me && SOSCircleHasApplicant(new_circle, me, NULL);
1079 SOSAccountTrustClassic *trust = account.trust;
1080 NSMutableSet* retirees = trust.retirees;
1082 if(!new_circle) return NULL;
1083 __block bool workDone = false;
1085 CFSetForEach((__bridge CFSetRef)retirees, ^(const void* value) {
1086 SOSPeerInfoRef pi = (SOSPeerInfoRef) value;
1087 if (isSOSPeerInfo(pi)) {
1088 SOSCircleUpdatePeerInfo(new_circle, pi);
1094 if(workDone && SOSCircleCountPeers(new_circle) == 0) {
1095 SecKeyRef userPrivKey = SOSAccountGetPrivateCredential(account, error);
1099 secnotice("resetToOffering", "Reset to offering with last retirement and me as applicant");
1100 if(!SOSCircleResetToOffering(new_circle, userPrivKey, account.fullPeerInfo, error) ||
1101 ![account.trust addiCloudIdentity:new_circle key:userPrivKey err:error]){
1102 CFReleaseNull(new_circle);
1106 // Do nothing. We can't resetToOffering without a userPrivKey. If we were to resetToEmpty
1107 // we won't push the result later in handleUpdateCircle. If we leave the circle as it is
1108 // we have a chance to set things right with a SetCreds/Join sequence. This will cause
1109 // handleUpdateCircle to return false.
1110 CFReleaseNull(new_circle);
1114 // This case is when we aren't an applicant and the circle is retirement-empty.
1115 secnotice("resetToEmpty", "Reset to empty with last retirement");
1116 SOSCircleResetToEmpty(new_circle, NULL);
1124 // MARK: Circle Membership change notificaion
1127 void SOSAccountAddChangeBlock(SOSAccount* a, SOSAccountCircleMembershipChangeBlock changeBlock) {
1128 SOSAccountCircleMembershipChangeBlock copy = changeBlock;
1129 [a.change_blocks addObject:copy];
1132 void SOSAccountRemoveChangeBlock(SOSAccount* a, SOSAccountCircleMembershipChangeBlock changeBlock) {
1133 [a.change_blocks removeObject:changeBlock];
1136 void SOSAccountPurgeIdentity(SOSAccount* account) {
1137 SOSAccountTrustClassic *trust = account.trust;
1138 SOSFullPeerInfoRef identity = trust.fullPeerInfo;
1141 // Purge private key but don't return error if we can't.
1142 CFErrorRef purgeError = NULL;
1143 if (!SOSFullPeerInfoPurgePersistentKey(identity, &purgeError)) {
1144 secwarning("Couldn't purge persistent key for %@ [%@]", identity, purgeError);
1146 CFReleaseNull(purgeError);
1148 trust.fullPeerInfo = nil;
1152 bool sosAccountLeaveCircle(SOSAccount* account, SOSCircleRef circle, CFErrorRef* error) {
1153 SOSAccountTrustClassic *trust = account.trust;
1154 SOSFullPeerInfoRef identity = trust.fullPeerInfo;
1155 NSMutableSet* retirees = trust.retirees;
1157 SOSFullPeerInfoRef fpi = identity;
1158 if(!fpi) return false;
1160 CFErrorRef localError = NULL;
1162 bool retval = false;
1164 SOSPeerInfoRef retire_peer = SOSFullPeerInfoPromoteToRetiredAndCopy(fpi, &localError);
1166 secerror("Create ticket failed for peer %@: %@", fpi, localError);
1168 // See if we need to repost the circle we could either be an applicant or a peer already in the circle
1169 if(SOSCircleHasApplicant(circle, retire_peer, NULL)) {
1170 // Remove our application if we have one.
1171 SOSCircleWithdrawRequest(circle, retire_peer, NULL);
1172 } else if (SOSCircleHasPeer(circle, retire_peer, NULL)) {
1173 if (SOSCircleUpdatePeerInfo(circle, retire_peer)) {
1174 CFErrorRef cleanupError = NULL;
1175 if (![account.trust cleanupAfterPeer:account.kvs_message_transport circleTransport:account.circle_transport seconds:RETIREMENT_FINALIZATION_SECONDS circle:circle cleanupPeer:retire_peer err:&cleanupError]) {
1176 secerror("Error cleanup up after peer (%@): %@", retire_peer, cleanupError);
1178 CFReleaseSafe(cleanupError);
1182 // Store the retirement record locally.
1183 CFSetAddValue((__bridge CFMutableSetRef)retirees, retire_peer);
1185 trust.retirees = retirees;
1187 // Write retirement to Transport
1188 CFErrorRef postError = NULL;
1189 if(![account.circle_transport postRetirement:SOSCircleGetName(circle) peer:retire_peer err:&postError]){
1190 secwarning("Couldn't post retirement (%@)", postError);
1192 if(![account.circle_transport flushChanges:&postError]){
1193 secwarning("Couldn't flush retirement data (%@)", postError);
1195 CFReleaseNull(postError);
1198 SOSAccountPurgeIdentity(account);
1202 CFReleaseNull(localError);
1203 CFReleaseNull(retire_peer);
1207 bool sosAccountLeaveRing(SOSAccount* account, SOSRingRef ring, CFErrorRef* error) {
1208 SOSAccountTrustClassic *trust = account.trust;
1209 SOSFullPeerInfoRef identity = trust.fullPeerInfo;
1211 SOSFullPeerInfoRef fpi = identity;
1212 if(!fpi) return false;
1213 SOSPeerInfoRef pi = SOSFullPeerInfoGetPeerInfo(fpi);
1214 CFStringRef peerID = SOSPeerInfoGetPeerID(pi);
1216 CFErrorRef localError = NULL;
1218 bool retval = false;
1219 bool writeRing = false;
1220 bool writePeerInfo = false;
1222 if(SOSRingHasPeerID(ring, peerID)) {
1223 writePeerInfo = true;
1226 if(writePeerInfo || writeRing) {
1227 SOSRingWithdraw(ring, NULL, fpi, error);
1231 CFDataRef ring_data = SOSRingCopyEncodedData(ring, error);
1234 [account.circle_transport kvsRingPostRing:SOSRingGetName(ring) ring:ring_data err:NULL];
1236 CFReleaseNull(ring_data);
1239 CFReleaseNull(localError);
1243 bool SOSAccountPostDebugScope(SOSAccount* account, CFTypeRef scope, CFErrorRef *error) {
1244 bool result = false;
1245 if (account.circle_transport) {
1246 result = [account.circle_transport kvssendDebugInfo:kSOSAccountDebugScope debug:scope err:error];
1252 NSUbiquitousKeyValueStoreInitialSyncChange is only posted if there is any
1253 local value that has been overwritten by a distant value. If there is no
1254 conflict between the local and the distant values when doing the initial
1255 sync (e.g. if the cloud has no data stored or the client has not stored
1256 any data yet), you'll never see that notification.
1258 NSUbiquitousKeyValueStoreInitialSyncChange implies an initial round trip
1259 with server but initial round trip with server does not imply
1260 NSUbiquitousKeyValueStoreInitialSyncChange.
1265 // MARK: Status summary
1269 CFStringRef SOSAccountGetSOSCCStatusString(SOSCCStatus status) {
1271 case kSOSCCInCircle: return CFSTR("kSOSCCInCircle");
1272 case kSOSCCNotInCircle: return CFSTR("kSOSCCNotInCircle");
1273 case kSOSCCRequestPending: return CFSTR("kSOSCCRequestPending");
1274 case kSOSCCCircleAbsent: return CFSTR("kSOSCCCircleAbsent");
1275 case kSOSCCError: return CFSTR("kSOSCCError");
1277 return CFSTR("kSOSCCError");
1279 SOSCCStatus SOSAccountGetSOSCCStatusFromString(CFStringRef status) {
1280 if(CFEqualSafe(status, CFSTR("kSOSCCInCircle"))) {
1281 return kSOSCCInCircle;
1282 } else if(CFEqualSafe(status, CFSTR("kSOSCCInCircle"))) {
1283 return kSOSCCInCircle;
1284 } else if(CFEqualSafe(status, CFSTR("kSOSCCNotInCircle"))) {
1285 return kSOSCCNotInCircle;
1286 } else if(CFEqualSafe(status, CFSTR("kSOSCCRequestPending"))) {
1287 return kSOSCCRequestPending;
1288 } else if(CFEqualSafe(status, CFSTR("kSOSCCCircleAbsent"))) {
1289 return kSOSCCCircleAbsent;
1290 } else if(CFEqualSafe(status, CFSTR("kSOSCCError"))) {
1297 // MARK: Account Reset Circles
1300 // This needs to be called within a [trust modifyCircle()] block
1303 bool SOSAccountRemoveIncompleteiCloudIdentities(SOSAccount* account, SOSCircleRef circle, SecKeyRef privKey, CFErrorRef *error) {
1304 bool retval = false;
1306 SOSAccountTrustClassic *trust = account.trust;
1307 SOSFullPeerInfoRef identity = trust.fullPeerInfo;
1309 CFMutableSetRef iCloud2Remove = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
1311 SOSCircleForEachActivePeer(circle, ^(SOSPeerInfoRef peer) {
1312 if(SOSPeerInfoIsCloudIdentity(peer)) {
1313 SOSFullPeerInfoRef icfpi = SOSFullPeerInfoCreateCloudIdentity(kCFAllocatorDefault, peer, NULL);
1315 CFSetAddValue(iCloud2Remove, peer);
1317 CFReleaseNull(icfpi);
1321 if(CFSetGetCount(iCloud2Remove) > 0) {
1323 SOSCircleRemovePeers(circle, privKey, identity, iCloud2Remove, error);
1325 CFReleaseNull(iCloud2Remove);
1330 // MARK: start backups
1334 bool SOSAccountEnsureInBackupRings(SOSAccount* account) {
1335 __block bool result = false;
1336 __block CFErrorRef error = NULL;
1337 secnotice("backup", "Ensuring in rings");
1339 NSData *backupKey = nil;
1341 if(!account.backup_key){
1346 backupKey = (__bridge_transfer NSData*)SOSPeerInfoV2DictionaryCopyData(account.peerInfo, sBackupKeyKey);
1348 bool updateBackupKey = ![backupKey isEqual:account.backup_key];
1350 if(updateBackupKey) {
1351 result = SOSAccountUpdatePeerInfo(account, CFSTR("Backup public key"), &error, ^bool(SOSFullPeerInfoRef fpi, CFErrorRef *error) {
1352 return SOSFullPeerInfoUpdateBackupKey(fpi, (__bridge CFDataRef)(account.backup_key), error);
1355 secnotice("backupkey", "Failed to setup backup public key: %@", error ? (CFTypeRef) error : (CFTypeRef) CFSTR("No error space provided"));
1359 if(!account.backup_key)
1362 secnotice("backupkey", "Failed to setup backup public key: %@", error ? (CFTypeRef) error : (CFTypeRef) CFSTR("No error space provided"));
1366 if(!SOSBSKBIsGoodBackupPublic((__bridge CFDataRef)account.backup_key, &error)){
1368 secnotice("backupkey", "Failed to setup backup public key: %@", error ? (CFTypeRef) error : (CFTypeRef) CFSTR("No error space provided"));
1373 CFDataRef recoveryKeyBackFromRing = SOSAccountCopyRecoveryPublic(kCFAllocatorDefault, account, &error);
1375 if(updateBackupKey || recoveryKeyBackFromRing) {
1376 // It's a good key, we're going with it. Stop backing up the old way.
1377 CFErrorRef localError = NULL;
1378 if (!SOSDeleteV0Keybag(&localError)) {
1379 secerror("Failed to delete v0 keybag: %@", localError);
1381 CFReleaseNull(localError);
1385 // Setup backups the new way.
1386 SOSAccountForEachBackupView(account, ^(const void *value) {
1387 CFStringRef viewName = (CFStringRef)value;
1388 if(updateBackupKey || (recoveryKeyBackFromRing && !SOSAccountRecoveryKeyIsInBackupAndCurrentInView(account, viewName))) {
1389 result &= SOSAccountNewBKSBForView(account, viewName, &error);
1395 secnotice("backupkey", "Failed to setup backup public key: %@", error ? (CFTypeRef) error : (CFTypeRef) CFSTR("No error space provided"));
1401 // MARK: Recovery Public Key Functions
1404 bool SOSAccountRegisterRecoveryPublicKey(SOSAccountTransaction* txn, CFDataRef recovery_key, CFErrorRef *error){
1405 bool retval = SOSAccountSetRecoveryKey(txn.account, recovery_key, error);
1406 if(retval) secnotice("recovery", "successfully registered recovery public key");
1407 else secnotice("recovery", "could not register recovery public key: %@", *error);
1408 SOSClearErrorIfTrue(retval, error);
1412 bool SOSAccountClearRecoveryPublicKey(SOSAccountTransaction* txn, CFDataRef recovery_key, CFErrorRef *error){
1413 bool retval = SOSAccountRemoveRecoveryKey(txn.account, error);
1414 if(retval) secnotice("recovery", "RK Cleared");
1415 else secnotice("recovery", "Couldn't clear RK(%@)", *error);
1416 SOSClearErrorIfTrue(retval, error);
1420 CFDataRef SOSAccountCopyRecoveryPublicKey(SOSAccountTransaction* txn, CFErrorRef *error){
1421 CFDataRef result = NULL;
1422 result = SOSAccountCopyRecoveryPublic(kCFAllocatorDefault, txn.account, error);
1423 if(!result) secnotice("recovery", "Could not retrieve the recovery public key from the ring: %@", *error);
1425 if (!isData(result)) {
1426 CFReleaseNull(result);
1428 SOSClearErrorIfTrue(result != NULL, error);
1438 static bool SOSAccountJoinCircle(SOSAccountTransaction* aTxn, SecKeyRef user_key,
1439 bool use_cloud_peer, CFErrorRef* error) {
1440 SOSAccount* account = aTxn.account;
1441 SOSAccountTrustClassic *trust = account.trust;
1442 __block bool result = false;
1443 __block SOSFullPeerInfoRef cloud_full_peer = NULL;
1444 require_action_quiet(trust.trustedCircle, fail, SOSCreateErrorWithFormat(kSOSErrorPeerNotFound, NULL, error, NULL, CFSTR("Don't have circle when joining???")));
1445 require_quiet([account.trust ensureFullPeerAvailable:(__bridge CFDictionaryRef)account.gestalt deviceID:(__bridge CFStringRef)account.deviceID backupKey:(__bridge CFDataRef)account.backup_key err:error], fail);
1446 SOSFullPeerInfoRef myCirclePeer = trust.fullPeerInfo;
1447 if (SOSCircleCountPeers(trust.trustedCircle) == 0 || SOSAccountGhostResultsInReset(account)) {
1448 secnotice("resetToOffering", "Resetting circle to offering since there are no peers");
1449 // this also clears initial sync data
1450 result = [account.trust resetCircleToOffering:aTxn userKey:user_key err:error];
1452 SOSAccountSetValue(account, kSOSUnsyncedViewsKey, kCFBooleanTrue, NULL);
1453 if (use_cloud_peer) {
1454 cloud_full_peer = SOSCircleCopyiCloudFullPeerInfoRef(trust.trustedCircle, NULL);
1456 [account.trust modifyCircle:account.circle_transport err:error action:^bool(SOSCircleRef circle) {
1457 result = SOSAccountAddEscrowToPeerInfo(account, myCirclePeer, error);
1458 result &= SOSCircleRequestAdmission(circle, user_key, myCirclePeer, error);
1459 trust.departureCode = kSOSNeverLeftCircle;
1460 if(result && cloud_full_peer) {
1461 CFErrorRef localError = NULL;
1462 CFStringRef cloudid = SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(cloud_full_peer));
1463 require_quiet(cloudid, finish);
1464 require_quiet(SOSCircleHasActivePeerWithID(circle, cloudid, &localError), finish);
1465 require_quiet(SOSCircleAcceptRequest(circle, user_key, cloud_full_peer, SOSFullPeerInfoGetPeerInfo(myCirclePeer), &localError), finish);
1468 secerror("Failed to join with cloud identity: %@", localError);
1469 CFReleaseNull(localError);
1474 if (use_cloud_peer) {
1475 SOSAccountUpdateOutOfSyncViews(aTxn, SOSViewsGetAllCurrent());
1479 CFReleaseNull(cloud_full_peer);
1483 static bool SOSAccountJoinCircles_internal(SOSAccountTransaction* aTxn, bool use_cloud_identity, CFErrorRef* error) {
1484 SOSAccount* account = aTxn.account;
1485 SOSAccountTrustClassic *trust = account.trust;
1486 bool success = false;
1488 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1489 require_quiet(user_key, done); // Fail if we don't get one.
1491 require_action_quiet(trust.trustedCircle, done, SOSErrorCreate(kSOSErrorNoCircle, error, NULL, CFSTR("No circle to join")));
1493 if (trust.fullPeerInfo != NULL) {
1494 SOSPeerInfoRef myPeer = trust.peerInfo;
1495 success = SOSCircleHasPeer(trust.trustedCircle, myPeer, NULL);
1496 require_quiet(!success, done);
1498 SOSCircleRemoveRejectedPeer(trust.trustedCircle, myPeer, NULL); // If we were rejected we should remove it now.
1500 if (!SOSCircleHasApplicant(trust.trustedCircle, myPeer, NULL)) {
1501 secerror("Resetting my peer (ID: %@) for circle '%@' during application", SOSPeerInfoGetPeerID(myPeer), SOSCircleGetName(trust.trustedCircle));
1503 trust.fullPeerInfo = NULL;
1507 success = SOSAccountJoinCircle(aTxn, user_key, use_cloud_identity, error);
1509 require_quiet(success, done);
1511 trust.departureCode = kSOSNeverLeftCircle;
1517 bool SOSAccountJoinCircles(SOSAccountTransaction* aTxn, CFErrorRef* error) {
1518 secnotice("circleJoin", "Normal path circle join (SOSAccountJoinCircles)");
1519 return SOSAccountJoinCircles_internal(aTxn, false, error);
1522 CFStringRef SOSAccountCopyDeviceID(SOSAccount* account, CFErrorRef *error){
1523 CFStringRef result = NULL;
1524 SOSAccountTrustClassic *trust = account.trust;
1526 require_action_quiet(trust.fullPeerInfo, fail, SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("No peer for me")));
1528 result = SOSPeerInfoCopyDeviceID(trust.peerInfo);
1534 bool SOSAccountSetMyDSID(SOSAccountTransaction* txn, CFStringRef IDS, CFErrorRef* error){
1536 SOSAccount* account = txn.account;
1537 SOSAccountTrustClassic *trust = account.trust;
1539 secdebug("IDS Transport", "We are setting our device ID: %@", IDS);
1540 if(IDS != NULL && (CFStringGetLength(IDS) > 0)){
1541 if(!trust.fullPeerInfo){
1542 account.deviceID = [[NSString alloc] initWithString:(__bridge NSString * _Nonnull)(IDS)];
1543 SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("No peer for me"));
1546 result = [trust modifyCircle:account.circle_transport err:error action:^bool(SOSCircleRef circle) {
1548 SOSFullPeerInfoUpdateDeviceID(trust.fullPeerInfo, IDS, error);
1549 SOSFullPeerInfoUpdateTransportType(trust.fullPeerInfo, SOSTransportMessageTypeIDSV2, error);
1550 SOSFullPeerInfoUpdateTransportPreference(trust.fullPeerInfo, kCFBooleanFalse, error);
1551 SOSFullPeerInfoUpdateTransportFragmentationPreference(trust.fullPeerInfo, kCFBooleanTrue, error);
1552 SOSFullPeerInfoUpdateTransportAckModelPreference(trust.fullPeerInfo, kCFBooleanTrue, error);
1553 return SOSCircleHasPeer(circle, trust.peerInfo, NULL);
1559 // Initiate sync with all IDS peers, since we just learned we can talk that way.
1560 SOSAccountForEachCirclePeerExceptMe(account, ^(SOSPeerInfoRef peer) {
1561 if (SOSPeerInfoShouldUseIDSTransport(account.peerInfo, peer)) {
1562 [txn requestSyncWith:(__bridge NSString*) SOSPeerInfoGetPeerID(peer)];
1566 account.deviceID = [[NSString alloc] initWithString:(__bridge NSString * _Nonnull)(IDS)];
1570 bool SOSAccountSendIDSTestMessage(SOSAccount* account, CFStringRef message, CFErrorRef *error){
1572 //construct message dictionary, circle -> peerID -> message
1574 CFMutableDictionaryRef peerToMessage = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1576 CFStringRef operationString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), kIDSSendOneMessage);
1577 CFDictionaryRef rawMessage = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
1578 kIDSOperationType, operationString,
1579 kIDSMessageToSendKey, CFSTR("send IDS test message"),
1582 SOSAccountForEachCirclePeerExceptMe(account, ^(SOSPeerInfoRef peer) {
1583 CFDictionaryAddValue(peerToMessage, SOSPeerInfoGetPeerID(peer), rawMessage);
1586 result = [account.ids_message_transport SOSTransportMessageSendMessages:account.ids_message_transport pm:peerToMessage err:error];
1588 CFReleaseNull(peerToMessage);
1589 CFReleaseNull(operationString);
1590 CFReleaseNull(rawMessage);
1594 bool SOSAccountStartPingTest(SOSAccount* account, CFStringRef message, CFErrorRef *error){
1595 bool result = false;
1596 //construct message dictionary, circle -> peerID -> message
1597 SOSAccountTrustClassic *trust = account.trust;
1598 if(account.ids_message_transport == NULL)
1599 account.ids_message_transport = [[SOSMessageIDS alloc] initWithAccount:account andName:(__bridge NSString *)(SOSCircleGetName(trust.trustedCircle))];
1601 require_quiet(account.ids_message_transport, fail);
1602 CFMutableDictionaryRef peerToMessage = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1604 CFStringRef operationString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), kIDSStartPingTestMessage);
1605 CFDictionaryRef rawMessage = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
1606 kIDSOperationType, operationString,
1607 kIDSMessageToSendKey, CFSTR("send IDS test message"),
1611 SOSAccountForEachCirclePeerExceptMe(account, ^(SOSPeerInfoRef peer) {
1612 CFDictionaryAddValue(peerToMessage, SOSPeerInfoGetPeerID(peer), rawMessage);
1615 result = [account.ids_message_transport SOSTransportMessageSendMessages:account.ids_message_transport pm:peerToMessage err:error];
1617 CFReleaseNull(peerToMessage);
1618 CFReleaseNull(rawMessage);
1619 CFReleaseNull(operationString);
1624 bool SOSAccountRetrieveDeviceIDFromKeychainSyncingOverIDSProxy(SOSAccount* account, CFErrorRef *error){
1627 __block bool success = true;
1628 __block CFErrorRef localError = NULL;
1629 dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
1631 SOSCloudKeychainGetIDSDeviceID(^(CFDictionaryRef returnedValues, CFErrorRef sync_error){
1632 success = (sync_error == NULL);
1634 CFRetainAssign(localError, sync_error);
1637 dispatch_semaphore_signal(wait_for);
1640 dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER);
1642 if(!success && localError != NULL && error != NULL){
1643 secerror("Could not ask KeychainSyncingOverIDSProxy for Device ID: %@", localError);
1644 *error = localError;
1648 secdebug("IDS Transport", "Attempting to retrieve the IDS Device ID");
1653 bool SOSAccountJoinCirclesAfterRestore(SOSAccountTransaction* aTxn, CFErrorRef* error) {
1654 secnotice("circleJoin", "Joining after restore (SOSAccountJoinCirclesAfterRestore)");
1655 return SOSAccountJoinCircles_internal(aTxn, true, error);
1658 bool SOSAccountRemovePeersFromCircle(SOSAccount* account, CFArrayRef peers, CFErrorRef* error)
1660 bool result = false;
1661 CFMutableSetRef peersToRemove = NULL;
1662 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1664 secnotice("removePeers", "Can't remove without userKey");
1667 SOSFullPeerInfoRef me_full = account.fullPeerInfo;
1668 SOSPeerInfoRef me = account.peerInfo;
1669 if(!(me_full && me))
1671 secnotice("removePeers", "Can't remove without being active peer");
1672 SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("Can't remove without being active peer"));
1676 result = true; // beyond this point failures would be rolled up in AccountModifyCircle.
1678 peersToRemove = CFSetCreateMutableForSOSPeerInfosByIDWithArray(kCFAllocatorDefault, peers);
1681 CFReleaseNull(peersToRemove);
1682 secnotice("removePeers", "No peerSet to remove");
1686 // If we're one of the peers expected to leave - note that and then remove ourselves from the set (different handling).
1687 bool leaveCircle = CFSetContainsValue(peersToRemove, me);
1688 CFSetRemoveValue(peersToRemove, me);
1690 result &= [account.trust modifyCircle:account.circle_transport err:error action:^(SOSCircleRef circle) {
1691 bool success = false;
1693 if(CFSetGetCount(peersToRemove) != 0) {
1694 require_quiet(SOSCircleRemovePeers(circle, user_key, me_full, peersToRemove, error), done);
1695 success = SOSAccountGenerationSignatureUpdate(account, error);
1696 } else success = true;
1698 if (success && leaveCircle) {
1699 secnotice("leaveCircle", "Leaving circle by client request");
1700 success = sosAccountLeaveCircle(account, circle, error);
1708 CFReleaseNull(peersToRemove);
1713 bool SOSAccountBail(SOSAccount* account, uint64_t limit_in_seconds, CFErrorRef* error) {
1714 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1715 dispatch_group_t group = dispatch_group_create();
1716 SOSAccountTrustClassic *trust = account.trust;
1717 __block bool result = false;
1718 secnotice("circle", "Attempting to leave circle - best effort - in %llu seconds\n", limit_in_seconds);
1719 // Add a task to the group
1720 dispatch_group_async(group, queue, ^{
1721 [trust modifyCircle:account.circle_transport err:error action:^(SOSCircleRef circle) {
1722 secnotice("leaveCircle", "Leaving circle by client request");
1723 return sosAccountLeaveCircle(account, circle, error);
1726 dispatch_time_t milestone = dispatch_time(DISPATCH_TIME_NOW, limit_in_seconds * NSEC_PER_SEC);
1727 dispatch_group_wait(group, milestone);
1729 trust.departureCode = kSOSWithdrewMembership;
1736 // MARK: Application
1739 static void for_each_applicant_in_each_circle(SOSAccount* account, CFArrayRef peer_infos,
1740 bool (^action)(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer)) {
1741 SOSAccountTrustClassic *trust = account.trust;
1743 SOSPeerInfoRef me = trust.peerInfo;
1744 CFErrorRef peer_error = NULL;
1745 if (trust.trustedCircle && me &&
1746 SOSCircleHasPeer(trust.trustedCircle, me, &peer_error)) {
1747 [account.trust modifyCircle:account.circle_transport err:NULL action:^(SOSCircleRef circle) {
1748 __block bool modified = false;
1749 CFArrayForEach(peer_infos, ^(const void *value) {
1750 SOSPeerInfoRef peer = (SOSPeerInfoRef) value;
1751 if (isSOSPeerInfo(peer) && SOSCircleHasApplicant(circle, peer, NULL)) {
1752 if (action(circle, trust.fullPeerInfo, peer)) {
1761 secerror("Got error in SOSCircleHasPeer: %@", peer_error);
1762 CFReleaseSafe(peer_error); // TODO: We should be accumulating errors here.
1765 bool SOSAccountAcceptApplicants(SOSAccount* account, CFArrayRef applicants, CFErrorRef* error) {
1766 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1770 __block bool success = true;
1771 __block int64_t num_peers = 0;
1773 for_each_applicant_in_each_circle(account, applicants, ^(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer) {
1774 bool accepted = SOSCircleAcceptRequest(circle, user_key, myCirclePeer, peer, error);
1778 num_peers = MAX(num_peers, SOSCircleCountPeers(circle));
1785 bool SOSAccountRejectApplicants(SOSAccount* account, CFArrayRef applicants, CFErrorRef* error) {
1786 __block bool success = true;
1787 __block int64_t num_peers = 0;
1789 for_each_applicant_in_each_circle(account, applicants, ^(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer) {
1790 bool rejected = SOSCircleRejectRequest(circle, myCirclePeer, peer, error);
1794 num_peers = MAX(num_peers, SOSCircleCountPeers(circle));
1802 CFStringRef SOSAccountCopyIncompatibilityInfo(SOSAccount* account, CFErrorRef* error) {
1803 return CFSTR("We're compatible, go away");
1806 enum DepartureReason SOSAccountGetLastDepartureReason(SOSAccount* account, CFErrorRef* error) {
1807 SOSAccountTrustClassic *trust = account.trust;
1808 return trust.departureCode;
1811 void SOSAccountSetLastDepartureReason(SOSAccount* account, enum DepartureReason reason) {
1812 SOSAccountTrustClassic *trust = account.trust;
1813 trust.departureCode = reason;
1817 CFArrayRef SOSAccountCopyGeneration(SOSAccount* account, CFErrorRef *error) {
1818 CFArrayRef result = NULL;
1819 CFNumberRef generation = NULL;
1820 SOSAccountTrustClassic *trust = account.trust;
1822 require_quiet(SOSAccountHasPublicKey(account, error), fail);
1823 require_action_quiet(trust.trustedCircle, fail, SOSErrorCreate(kSOSErrorNoCircle, error, NULL, CFSTR("No circle")));
1825 generation = (CFNumberRef)SOSCircleGetGeneration(trust.trustedCircle);
1826 result = CFArrayCreateForCFTypes(kCFAllocatorDefault, generation, NULL);
1832 bool SOSValidateUserPublic(SOSAccount* account, CFErrorRef *error) {
1833 if (!SOSAccountHasPublicKey(account, error))
1836 return account.accountKeyIsTrusted;
1839 bool SOSAccountEnsurePeerRegistration(SOSAccount* account, CFErrorRef *error) {
1840 // TODO: this result is never set or used
1842 SOSAccountTrustClassic *trust = account.trust;
1844 secnotice("updates", "Ensuring peer registration.");
1846 if(!trust.trustedCircle || !trust.fullPeerInfo || !account.accountKeyIsTrusted)
1849 // If we are not in the circle, there is no point in setting up peers
1850 if(!SOSAccountIsMyPeerActive(account, NULL))
1853 // This code only uses the SOSFullPeerInfoRef for two things:
1854 // - Finding out if this device is in the trusted circle
1855 // - Using the peerID for this device to see if the current peer is "me"
1856 // - It is used indirectly by passing trust.fullPeerInfo to SOSEngineInitializePeerCoder
1858 CFStringRef my_id = SOSPeerInfoGetPeerID(trust.peerInfo);
1860 SOSCircleForEachValidSyncingPeer(trust.trustedCircle, account.accountKey, ^(SOSPeerInfoRef peer) {
1861 if (!SOSPeerInfoPeerIDEqual(peer, my_id)) {
1862 CFErrorRef localError = NULL;
1864 SOSMessage *messageTransport = SOSPeerInfoHasDeviceID(peer) ? account.ids_message_transport : account.kvs_message_transport;
1866 SOSEngineInitializePeerCoder((SOSEngineRef)[messageTransport SOSTransportMessageGetEngine], trust.fullPeerInfo, peer, &localError);
1868 secnotice("updates", "can't initialize transport for peer %@ with %@ (%@)", peer, trust.fullPeerInfo, localError);
1869 CFReleaseSafe(localError);
1873 //Initialize our device ID
1874 [account.ids_message_transport SOSTransportMessageIDSGetIDSDeviceID:account];
1880 // Value manipulation
1883 CFTypeRef SOSAccountGetValue(SOSAccount* account, CFStringRef key, CFErrorRef *error) {
1884 SOSAccountTrustClassic *trust = account.trust;
1885 if (!trust.expansion) {
1888 return (__bridge CFTypeRef)([trust.expansion objectForKey:(__bridge NSString* _Nonnull)(key)]);
1891 bool SOSAccountAddEscrowRecords(SOSAccount* account, CFStringRef dsid, CFDictionaryRef record, CFErrorRef *error){
1892 CFMutableDictionaryRef escrowRecords = (CFMutableDictionaryRef)SOSAccountGetValue(account, kSOSEscrowRecord, error);
1893 CFMutableDictionaryRef escrowCopied = NULL;
1894 bool success = false;
1896 if(isDictionary(escrowRecords) && escrowRecords != NULL)
1897 escrowCopied = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, CFDictionaryGetCount(escrowRecords), escrowRecords);
1899 escrowCopied = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1901 CFDictionaryAddValue(escrowCopied, dsid, record);
1902 SOSAccountSetValue(account, kSOSEscrowRecord, escrowCopied, error);
1907 CFReleaseNull(escrowCopied);
1913 bool SOSAccountAddEscrowToPeerInfo(SOSAccount* account, SOSFullPeerInfoRef myPeer, CFErrorRef *error){
1914 bool success = false;
1916 CFDictionaryRef escrowRecords = SOSAccountGetValue(account, kSOSEscrowRecord, error);
1917 success = SOSFullPeerInfoReplaceEscrowRecords(myPeer, escrowRecords, error);
1922 bool SOSAccountCheckPeerAvailability(SOSAccount* account, CFErrorRef *error)
1924 CFStringRef operationString = NULL;
1925 CFDictionaryRef rawMessage = NULL;
1926 CFMutableSetRef peers = NULL;
1927 CFMutableDictionaryRef peerList = NULL;
1928 char* message = NULL;
1929 bool result = false;
1930 SOSAccountTrustClassic* trust = account.trust;
1931 if(account.ids_message_transport == NULL)
1932 account.ids_message_transport = [[SOSMessageIDS alloc] initWithAccount:account andName:(__bridge NSString *)SOSCircleGetName(trust.trustedCircle)];
1934 if(!account.ids_message_transport)
1937 //adding message type kIDSPeerAvailability so KeychainSyncingOverIDSProxy does not send this message as a keychain item
1939 operationString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), kIDSPeerAvailability);
1940 rawMessage = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
1941 kIDSOperationType, operationString,
1942 kIDSMessageToSendKey, CFSTR("checking peers"),
1945 peerList = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1946 SOSCircleRef circle = trust.trustedCircle;
1948 //check each peer to make sure they have the right view set enabled
1949 CFSetRef mySubSet = SOSViewsGetV0SubviewSet();
1950 SOSCircleForEachValidPeer(circle, account.accountKey, ^(SOSPeerInfoRef peer) {
1951 if(!CFEqualSafe(peer, account.peerInfo)){
1952 CFMutableSetRef peerViews = SOSPeerInfoCopyEnabledViews(peer);
1953 CFSetRef intersectSets = CFSetCreateIntersection(kCFAllocatorDefault, mySubSet, peerViews);
1954 if(CFEqualSafe(intersectSets, mySubSet)){
1955 CFStringRef deviceID = SOSPeerInfoCopyDeviceID(peer);
1956 if(deviceID != NULL)
1957 CFDictionaryAddValue(peerList, SOSPeerInfoGetPeerID(peer), rawMessage);
1958 CFReleaseNull(deviceID);
1960 CFReleaseNull(peerViews);
1961 CFReleaseNull(intersectSets);
1965 require_quiet(CFDictionaryGetCount(peerList) > 0 , fail);
1967 result = [account.ids_message_transport SOSTransportMessageSendMessages:account.ids_message_transport pm:peerList err:error];
1970 CFReleaseNull(rawMessage);
1971 CFReleaseNull(operationString);
1972 CFReleaseNull(peerList);
1973 CFReleaseNull(peers);
1979 void SOSAccountRecordRetiredPeersInCircle(SOSAccount* account) {
1980 if (![account.trust isInCircle:NULL])
1982 SOSAccountTrustClassic *trust = account.trust;
1983 [trust modifyCircle:account.circle_transport err:NULL action:^bool (SOSCircleRef circle) {
1984 __block bool updated = false;
1985 CFSetForEach((__bridge CFMutableSetRef)trust.retirees, ^(CFTypeRef element){
1986 SOSPeerInfoRef retiree = asSOSPeerInfo(element);
1988 if (retiree && SOSCircleUpdatePeerInfo(circle, retiree)) {
1990 secnotice("retirement", "Updated retired peer %@ in %@", retiree, circle);
1991 CFErrorRef cleanupError = NULL;
1992 if (![account.trust cleanupAfterPeer:account.kvs_message_transport circleTransport:account.circle_transport seconds:RETIREMENT_FINALIZATION_SECONDS circle:circle cleanupPeer:retiree err:&cleanupError])
1993 secerror("Error cleanup up after peer (%@): %@", retiree, cleanupError);
1994 CFReleaseSafe(cleanupError);
2001 static const uint64_t maxTimeToWaitInSeconds = 30ull * NSEC_PER_SEC;
2003 static CFDictionaryRef SOSAccountGetObjectsFromCloud(dispatch_queue_t processQueue, CFErrorRef *error)
2005 __block CFTypeRef object = NULL;
2007 dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
2008 dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds);
2010 CloudKeychainReplyBlock replyBlock =
2011 ^ (CFDictionaryRef returnedValues, CFErrorRef error)
2013 secnotice("key-cleanup", "SOSCloudKeychainGetObjectsFromCloud returned: %@", returnedValues);
2014 object = returnedValues;
2019 secerror("SOSCloudKeychainGetObjectsFromCloud returned error: %@", error);
2021 secnotice("key-cleanup", "SOSCloudKeychainGetObjectsFromCloud block exit: %@", object);
2022 dispatch_semaphore_signal(waitSemaphore);
2025 SOSCloudKeychainGetAllObjectsFromCloud(processQueue, replyBlock);
2027 dispatch_semaphore_wait(waitSemaphore, finishTime);
2028 if (object && (CFGetTypeID(object) == CFNullGetTypeID())) // return a NULL instead of a CFNull
2033 secnotice("key-cleanup", "returned: %@", object);
2034 return asDictionary(object, error);
2038 static void SOSAccountRemoveKVSKeys(SOSAccount* account, NSArray* keysToRemove, dispatch_queue_t processQueue)
2040 CFStringRef uuid = SOSAccountCopyUUID(account);
2041 dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
2042 dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds);
2044 CloudKeychainReplyBlock replyBlock = ^ (CFDictionaryRef returnedValues, CFErrorRef error){
2046 secerror("SOSCloudKeychainRemoveKeys returned error: %@", error);
2048 dispatch_semaphore_signal(waitSemaphore);
2051 SOSCloudKeychainRemoveKeys((__bridge CFArrayRef)(keysToRemove), uuid, processQueue, replyBlock);
2052 dispatch_semaphore_wait(waitSemaphore, finishTime);
2056 static void SOSAccountWriteLastCleanupTimestampToKVS(SOSAccount* account)
2058 NSMutableDictionary *writeTimestamp = [NSMutableDictionary dictionary];
2060 CFMutableStringRef timeDescription = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, CFSTR("["));
2061 CFAbsoluteTime currentTimeAndDate = CFAbsoluteTimeGetCurrent();
2063 withStringOfAbsoluteTime(currentTimeAndDate, ^(CFStringRef decription) {
2064 CFStringAppend(timeDescription, decription);
2066 CFStringAppend(timeDescription, CFSTR("]"));
2068 [writeTimestamp setObject:(__bridge NSString*)(timeDescription) forKey:(__bridge NSString*)kSOSKVSLastCleanupTimestampKey];
2070 dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
2071 dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds);
2072 dispatch_queue_t processQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
2074 CloudKeychainReplyBlock replyBlock = ^ (CFDictionaryRef returnedValues, CFErrorRef error){
2076 secerror("SOSCloudKeychainPutObjectsInCloud returned error: %@", error);
2078 dispatch_semaphore_signal(waitSemaphore);
2081 SOSCloudKeychainPutObjectsInCloud((__bridge CFDictionaryRef)(writeTimestamp), processQueue, replyBlock);
2082 dispatch_semaphore_wait(waitSemaphore, finishTime);
2085 //Get all the key/values in KVS and remove old entries
2086 bool SOSAccountCleanupAllKVSKeys(SOSAccount* account, CFErrorRef* error)
2088 dispatch_queue_t processQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
2090 NSDictionary *keysAndValues = (__bridge_transfer NSDictionary*)SOSAccountGetObjectsFromCloud(processQueue, error);
2091 NSMutableArray *peerIDs = [NSMutableArray array];
2092 NSMutableArray *keysToRemove = [NSMutableArray array];
2094 CFArrayForEach(SOSAccountCopyActiveValidPeers(account, error), ^(const void *value) {
2095 SOSPeerInfoRef peer = (SOSPeerInfoRef)value;
2096 NSString* peerID = (__bridge NSString*) SOSPeerInfoGetPeerID(peer);
2098 //any peerid that is not ours gets added
2099 if(![[account.trust peerID] isEqualToString:peerID])
2100 [peerIDs addObject:peerID];
2103 [keysAndValues enumerateKeysAndObjectsUsingBlock:^(NSString * KVSKey, NSNumber * KVSValue, BOOL *stop) {
2104 __block bool keyMatchesPeerID = false;
2106 //checks for full peer ids
2107 [peerIDs enumerateObjectsUsingBlock:^(id _Nonnull PeerID, NSUInteger idx, BOOL * _Nonnull stop) {
2108 //if key contains peerid of one active peer
2109 if([KVSKey containsString:PeerID]){
2110 secnotice("key-cleanup","key: %@", KVSKey);
2111 keyMatchesPeerID = true;
2114 if((([KVSKey hasPrefix:@"ak"] || [KVSKey hasPrefix:@"-ak"]) && !keyMatchesPeerID)
2115 || [KVSKey hasPrefix:@"po"])
2116 [keysToRemove addObject:KVSKey];
2119 secnotice("key-cleanup", "message keys that we should remove! %@", keysToRemove);
2120 secnotice("key-cleanup", "total keys: %lu, cleaning up %lu", (unsigned long)[keysAndValues count], (unsigned long)[keysToRemove count]);
2122 SOSAccountRemoveKVSKeys(account, keysToRemove, processQueue);
2124 //add last cleanup timestamp
2125 SOSAccountWriteLastCleanupTimestampToKVS(account);
2130 bool SOSAccountPopulateKVSWithBadKeys(SOSAccount* account, CFErrorRef* error) {
2132 NSMutableDictionary *testKeysAndValues = [NSMutableDictionary dictionary];
2133 [testKeysAndValues setObject:@"deadbeef" forKey:@"-ak|asdfjkl;asdfjk;"];
2134 [testKeysAndValues setObject:@"foobar" forKey:@"ak|asdfasdfasdf:qwerqwerqwer"];
2135 [testKeysAndValues setObject:@"oldhistorycircle" forKey:@"poak|asdfasdfasdfasdf"];
2136 [testKeysAndValues setObject:@"oldhistorycircle" forKey:@"po|asdfasdfasdfasdfasdfasdf"];
2137 [testKeysAndValues setObject:@"oldhistorycircle" forKey:@"k>KeyParm"];
2139 dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
2140 dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds);
2141 dispatch_queue_t processQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
2143 CloudKeychainReplyBlock replyBlock = ^ (CFDictionaryRef returnedValues, CFErrorRef error){
2145 secerror("SOSCloudKeychainPutObjectsInCloud returned error: %@", error);
2147 dispatch_semaphore_signal(waitSemaphore);
2150 SOSCloudKeychainPutObjectsInCloud((__bridge CFDictionaryRef)(testKeysAndValues), processQueue, replyBlock);
2151 dispatch_semaphore_wait(waitSemaphore, finishTime);
2156 SOSPeerInfoRef SOSAccountCopyApplication(SOSAccount* account, CFErrorRef* error) {
2157 SOSPeerInfoRef applicant = NULL;
2158 SOSAccountTrustClassic *trust = account.trust;
2159 SecKeyRef userKey = SOSAccountGetPrivateCredential(account, error);
2160 if(!userKey) return false;
2161 if(![trust ensureFullPeerAvailable:(__bridge CFDictionaryRef)(account.gestalt) deviceID:(__bridge CFStringRef)(account.deviceID) backupKey:(__bridge CFDataRef)(account.backup_key) err:error])
2163 if(!SOSFullPeerInfoPromoteToApplication(trust.fullPeerInfo, userKey, error))
2165 applicant = SOSPeerInfoCreateCopy(kCFAllocatorDefault, trust.peerInfo, error);
2172 AddStrippedResults(NSMutableArray *results, NSArray *keychainItems, NSMutableSet *seenUUID, bool authoriative)
2174 [keychainItems enumerateObjectsUsingBlock:^(NSDictionary* keychainItem, NSUInteger idx, BOOL * _Nonnull stop) {
2175 NSString* parentUUID = keychainItem[(id)kSecAttrPath];
2176 NSString* viewUUID = keychainItem[(id)kSecAttrAccount];
2177 NSString *viewName = [keychainItem objectForKey:(id)kSecAttrServer];
2179 if (parentUUID == NULL || viewUUID == NULL || viewName == NULL)
2182 if([parentUUID isEqualToString:viewUUID] || authoriative){
2184 /* check if we already have this entry */
2185 if ([seenUUID containsObject:viewUUID])
2188 NSData* v_data = [keychainItem objectForKey:(id)kSecValueData];
2189 NSData *key = [[NSData alloc] initWithBase64EncodedData:v_data options:0];
2194 secnotice("piggy", "fetched TLK %@ with name %@", viewName, viewUUID);
2196 NSMutableDictionary* strippedDown = [@{
2197 (id)kSecValueData : key,
2198 (id)kSecAttrServer : viewName,
2199 (id)kSecAttrAccount : viewUUID
2202 strippedDown[@"auth"] = @YES;
2204 [results addObject:strippedDown];
2205 [seenUUID addObject:viewUUID];
2211 AddViewManagerResults(NSMutableArray *results, NSMutableSet *seenUUID)
2214 CKKSViewManager* manager = [CKKSViewManager manager];
2216 NSDictionary<NSString *,NSString *> *items = [manager activeTLKs];
2218 for (NSString *view in items) {
2219 NSString *uuid = items[view];
2220 NSDictionary *query = @{
2221 (id)kSecClass : (id)kSecClassInternetPassword,
2222 (id)kSecAttrNoLegacy : @YES,
2223 (id)kSecAttrAccessGroup : @"com.apple.security.ckks",
2224 (id)kSecAttrAccount : uuid,
2225 (id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
2226 (id)kSecMatchLimit : (id)kSecMatchLimitAll,
2227 (id)kSecReturnAttributes: @YES,
2228 (id)kSecReturnData: @YES,
2230 CFTypeRef result = NULL;
2231 if (SecItemCopyMatching((__bridge CFDictionaryRef)query, &result) == 0) {
2232 AddStrippedResults(results, (__bridge NSArray*)result, seenUUID, true);
2234 CFReleaseNull(result);
2241 SOSAccountGetAllTLKs(void)
2243 CFTypeRef result = NULL;
2244 NSMutableArray* results = [NSMutableArray array];
2245 NSMutableSet *seenUUID = [NSMutableSet set];
2247 // first use the TLK from the view manager
2248 AddViewManagerResults(results, seenUUID);
2250 //try to grab tlk-piggy items
2251 NSDictionary* query = @{
2252 (id)kSecClass : (id)kSecClassInternetPassword,
2253 (id)kSecAttrNoLegacy : @YES,
2254 (id)kSecAttrAccessGroup : @"com.apple.security.ckks",
2255 (id)kSecAttrDescription: @"tlk",
2256 (id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
2257 (id)kSecMatchLimit : (id)kSecMatchLimitAll,
2258 (id)kSecReturnAttributes: @YES,
2259 (id)kSecReturnData: @YES,
2262 if (SecItemCopyMatching((__bridge CFDictionaryRef)query, &result) == 0) {
2263 AddStrippedResults(results, (__bridge NSArray*)result, seenUUID, false);
2265 CFReleaseNull(result);
2267 //try to grab tlk-piggy items
2269 (id)kSecClass : (id)kSecClassInternetPassword,
2270 (id)kSecAttrNoLegacy : @YES,
2271 (id)kSecAttrAccessGroup : @"com.apple.security.ckks",
2272 (id)kSecAttrDescription: @"tlk-piggy",
2273 (id)kSecAttrSynchronizable : (id)kSecAttrSynchronizableAny,
2274 (id)kSecMatchLimit : (id)kSecMatchLimitAll,
2275 (id)kSecReturnAttributes: @YES,
2276 (id)kSecReturnData: @YES,
2279 if (SecItemCopyMatching((__bridge CFDictionaryRef)query, &result) == 0) {
2280 AddStrippedResults(results, (__bridge NSArray*)result, seenUUID, false);
2282 CFReleaseNull(result);
2284 secnotice("piggy", "Found %d TLKs", (int)[results count]);
2289 static uint8_t* encode_tlk(kTLKTypes type, NSString *name, NSData *keychainData, NSData* uuid,
2290 const uint8_t *der, uint8_t *der_end)
2292 if (type != kTLKUnknown) {
2293 return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
2294 piggy_encode_data(keychainData, der,
2295 piggy_encode_data(uuid, der,
2296 ccder_encode_uint64((uint64_t)type, der, der_end))));
2298 return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
2299 piggy_encode_data(keychainData, der,
2300 piggy_encode_data(uuid, der,
2301 der_encode_string((__bridge CFStringRef)name, NULL, der, der_end))));
2305 static uint8_t* piggy_encode_data(NSData* data,
2306 const uint8_t *der, uint8_t *der_end)
2308 return ccder_encode_tl(CCDER_OCTET_STRING, data.length, der,
2309 ccder_encode_body(data.length, data.bytes, der, der_end));
2314 name2type(NSString *view)
2316 if ([view isEqualToString:@"Manatee"])
2318 else if ([view isEqualToString:@"Engram"])
2320 else if ([view isEqualToString:@"AutoUnlock"])
2321 return kTLKAutoUnlock;
2322 if ([view isEqualToString:@"Health"])
2328 rank_type(NSString *view)
2330 if ([view isEqualToString:@"Manatee"])
2332 else if ([view isEqualToString:@"Engram"])
2334 else if ([view isEqualToString:@"AutoUnlock"])
2336 if ([view isEqualToString:@"Health"])
2342 parse_uuid(NSString *uuidString)
2344 NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:uuidString];
2346 [uuid getUUIDBytes:uuidblob];
2347 return [NSData dataWithBytes:uuidblob length:sizeof(uuid_t)];
2350 piggy_sizeof_data(NSData* data)
2352 return ccder_sizeof(CCDER_OCTET_STRING, [data length]);
2355 static size_t sizeof_keychainitem(kTLKTypes type, NSString *name, NSData* keychainData, NSData* uuid) {
2356 if (type != kTLKUnknown) {
2357 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE,
2358 piggy_sizeof_data(keychainData) +
2359 piggy_sizeof_data(uuid) +
2360 ccder_sizeof_uint64(type));
2362 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE,
2363 piggy_sizeof_data(keychainData) +
2364 piggy_sizeof_data(uuid) +
2365 der_sizeof_string((__bridge CFStringRef)name, NULL));
2369 NSArray<NSDictionary*>*
2370 SOSAccountSortTLKS(NSArray<NSDictionary*>* tlks)
2372 NSMutableArray<NSDictionary*>* sortedTLKs = [tlks mutableCopy];
2374 [sortedTLKs sortUsingComparator:^NSComparisonResult(NSDictionary *obj1, NSDictionary *obj2) {
2375 unsigned rank1 = rank_type(obj1[(__bridge id)kSecAttrServer]);
2376 if (obj1[@"auth"] != NULL)
2378 unsigned rank2 = rank_type(obj2[(__bridge id)kSecAttrServer]);
2379 if (obj2[@"auth"] != NULL)
2383 * Sort by rank (higher better), but prefer TLK that are authoriative (ie used by CKKSViewManager),
2384 * since we are sorting backward, the Ascending/Descending looks wrong below.
2386 if (rank1 > rank2) {
2387 return NSOrderedAscending;
2388 } else if (rank1 < rank2) {
2389 return NSOrderedDescending;
2391 return NSOrderedSame;
2397 static NSArray<NSData *> *
2398 build_tlks(NSArray<NSDictionary*>* tlks)
2400 NSMutableArray *array = [NSMutableArray array];
2401 NSArray<NSDictionary*>* sortedTLKs = SOSAccountSortTLKS(tlks);
2403 for (NSDictionary *item in sortedTLKs) {
2404 NSData* keychainData = item[(__bridge id)kSecValueData];
2405 NSString* name = item[(__bridge id)kSecAttrServer];
2406 NSString *uuidString = item[(__bridge id)kSecAttrAccount];
2407 NSData* uuid = parse_uuid(uuidString);
2409 NSMutableData *tlk = [NSMutableData dataWithLength:sizeof_keychainitem(name2type(name), name, keychainData, uuid)];
2411 unsigned char *der = [tlk mutableBytes];
2412 unsigned char *der_end = der + [tlk length];
2414 if (encode_tlk(name2type(name), name, keychainData, uuid, der, der_end) == NULL)
2417 secnotice("piggy", "preparing TLK in order: %@: %@", name, uuidString);
2419 [array addObject:tlk];
2424 static NSArray<NSData *> *
2425 build_identities(NSArray<NSData *>* identities)
2427 NSMutableArray *array = [NSMutableArray array];
2428 for (NSData *item in identities) {
2429 NSMutableData *ident = [NSMutableData dataWithLength:ccder_sizeof_raw_octet_string([item length])];
2431 unsigned char *der = [ident mutableBytes];
2432 unsigned char *der_end = der + [ident length];
2434 ccder_encode_raw_octet_string([item length], [item bytes], der, der_end);
2435 [array addObject:ident];
2442 static unsigned char *
2443 encode_data_array(NSArray<NSData*>* data, unsigned char *der, unsigned char *der_end)
2445 unsigned char *body_end = der_end;
2446 for (NSData *datum in data) {
2447 der_end = ccder_encode_body([datum length], [datum bytes], der, der_end);
2448 if (der_end == NULL)
2451 return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, body_end, der, der_end);
2454 static size_t sizeof_piggy(size_t identities_size, size_t tlk_size)
2456 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE,
2457 ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, identities_size) +
2458 ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, tlk_size));
2461 static NSData *encode_piggy(size_t IdentitiesBudget,
2463 NSArray<NSData*>* identities,
2464 NSArray<NSDictionary*>* tlks)
2466 NSArray<NSData *> *encodedTLKs = build_tlks(tlks);
2467 NSArray<NSData *> *encodedIdentities = build_identities(identities);
2468 NSMutableArray<NSData *> *budgetArray = [NSMutableArray array];
2469 NSMutableArray<NSData *> *identitiesArray = [NSMutableArray array];
2470 size_t payloadSize = 0, identitiesSize = 0;
2471 NSMutableData *result = NULL;
2473 for (NSData *tlk in encodedTLKs) {
2474 if (TLKBudget - payloadSize < [tlk length])
2476 [budgetArray addObject:tlk];
2477 payloadSize += tlk.length;
2479 secnotice("piggy", "sending %d tlks", (int)budgetArray.count);
2481 for (NSData *ident in encodedIdentities) {
2482 if (IdentitiesBudget - identitiesSize < [ident length])
2484 [identitiesArray addObject:ident];
2485 identitiesSize += ident.length;
2487 secnotice("piggy", "sending %d identities", (int)identitiesArray.count);
2490 size_t piggySize = sizeof_piggy(identitiesSize, payloadSize);
2492 result = [NSMutableData dataWithLength:piggySize];
2494 unsigned char *der = [result mutableBytes];
2495 unsigned char *der_end = der + [result length];
2497 if (ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
2498 encode_data_array(identitiesArray, der,
2499 encode_data_array(budgetArray, der, der_end))) != [result mutableBytes])
2505 static const size_t SOSCCIdentitiesBudget = 120;
2506 static const size_t SOSCCTLKBudget = 500;
2509 SOSPiggyCreateInitialSyncData(NSArray<NSData*>* identities, NSArray<NSDictionary *>* tlks)
2511 return encode_piggy(SOSCCIdentitiesBudget, SOSCCTLKBudget, identities, tlks);
2514 CFMutableArrayRef SOSAccountCopyiCloudIdentities(SOSAccount* account)
2516 CFMutableArrayRef identities = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
2518 SOSCircleForEachActivePeer(account.trust.trustedCircle, ^(SOSPeerInfoRef peer) {
2519 if(SOSPeerInfoIsCloudIdentity(peer)) {
2520 CFArrayAppendValue(identities, peer);
2526 CFDataRef SOSAccountCopyInitialSyncData(SOSAccount* account, CFErrorRef *error) {
2527 CFMutableArrayRef identities = SOSAccountCopyiCloudIdentities(account);
2528 secnotice("piggy", "identities: %@", identities);
2530 NSMutableArray *encodedIdenities = [NSMutableArray array];
2531 CFIndex i, count = CFArrayGetCount(identities);
2532 for (i = 0; i < count; i++) {
2533 SOSPeerInfoRef fpi = (SOSPeerInfoRef)CFArrayGetValueAtIndex(identities, i);
2534 NSData *data = CFBridgingRelease(SOSPeerInfoCopyData(fpi, error));
2536 [encodedIdenities addObject:data];
2538 CFRelease(identities);
2540 NSMutableArray* tlks = SOSAccountGetAllTLKs();
2542 return CFBridgingRetain(SOSPiggyCreateInitialSyncData(encodedIdenities, tlks));
2545 CFDataRef SOSAccountCopyCircleJoiningBlob(SOSAccount* account, SOSPeerInfoRef applicant, CFErrorRef *error) {
2546 SOSGenCountRef gencount = NULL;
2547 CFDataRef signature = NULL;
2548 SecKeyRef ourKey = NULL;
2550 CFDataRef pbblob = NULL;
2552 secnotice("circleJoin", "Making circle joining blob as sponsor (SOSAccountCopyCircleJoiningBlob)");
2554 SecKeyRef userKey = SOSAccountGetTrustedPublicCredential(account, error);
2555 require_quiet(userKey, errOut);
2557 require_action_quiet(applicant, errOut, SOSCreateError(kSOSErrorProcessingFailure, CFSTR("No applicant provided"), (error != NULL) ? *error : NULL, error));
2558 require_quiet(SOSPeerInfoApplicationVerify(applicant, userKey, error), errOut);
2561 SOSFullPeerInfoRef fpi = account.fullPeerInfo;
2562 ourKey = SOSFullPeerInfoCopyDeviceKey(fpi, error);
2563 require_quiet(ourKey, errOut);
2566 SOSCircleRef currentCircle = [account.trust getCircle:error];
2567 require_quiet(currentCircle, errOut);
2569 SOSCircleRef prunedCircle = SOSCircleCopyCircle(NULL, currentCircle, error);
2570 require_quiet(prunedCircle, errOut);
2571 require_quiet(SOSCirclePreGenerationSign(prunedCircle, userKey, error), errOut);
2573 gencount = SOSGenerationIncrementAndCreate(SOSCircleGetGeneration(prunedCircle));
2575 signature = SOSCircleCopyNextGenSignatureWithPeerAdded(prunedCircle, applicant, ourKey, error);
2576 require_quiet(signature, errOut);
2578 pbblob = SOSPiggyBackBlobCopyEncodedData(gencount, ourKey, signature, error);
2581 CFReleaseNull(gencount);
2582 CFReleaseNull(signature);
2583 CFReleaseNull(ourKey);
2585 if(!pbblob && error != NULL) {
2586 secnotice("circleJoin", "Failed to make circle joining blob as sponsor %@", *error);
2592 bool SOSAccountJoinWithCircleJoiningBlob(SOSAccount* account, CFDataRef joiningBlob, PiggyBackProtocolVersion version, CFErrorRef *error) {
2593 bool retval = false;
2594 SecKeyRef userKey = NULL;
2595 SOSAccountTrustClassic *trust = account.trust;
2596 SOSGenCountRef gencount = NULL;
2597 CFDataRef signature = NULL;
2598 SecKeyRef pubKey = NULL;
2599 bool setInitialSyncTimeoutToV0 = false;
2601 secnotice("circleJoin", "Joining circles through piggy-back (SOSAccountCopyCircleJoiningBlob)");
2603 if (!isData(joiningBlob))
2606 userKey = SOSAccountGetPrivateCredential(account, error);
2610 if (!SOSPiggyBackBlobCreateFromData(&gencount, &pubKey, &signature, joiningBlob, version, &setInitialSyncTimeoutToV0, error))
2613 if(setInitialSyncTimeoutToV0){
2614 secnotice("piggy", "setting flag in account for piggybacking v0");
2615 SOSAccountSetValue(account, kSOSInitialSyncTimeoutV0, kCFBooleanTrue, NULL);
2618 secnotice("piggy", "setting flag in account for piggybacking v0");
2619 SOSAccountClearValue(account, kSOSInitialSyncTimeoutV0, NULL);
2621 SOSAccountSetValue(account, kSOSUnsyncedViewsKey, kCFBooleanTrue, NULL);
2623 retval = [trust modifyCircle:account.circle_transport err:error action:^bool(SOSCircleRef copyOfCurrent) {
2624 return SOSCircleAcceptPeerFromHSA2(copyOfCurrent, userKey,
2628 trust.fullPeerInfo, error);;
2632 CFReleaseNull(gencount);
2633 CFReleaseNull(pubKey);
2634 CFReleaseNull(signature);
2639 static char boolToChars(bool val, char truechar, char falsechar) {
2640 return val? truechar: falsechar;
2643 #define ACCOUNTLOGSTATE "accountLogState"
2644 void SOSAccountLogState(SOSAccount* account) {
2645 bool hasPubKey = account.accountKey != NULL;
2646 SOSAccountTrustClassic *trust = account.trust;
2647 bool pubTrusted = account.accountKeyIsTrusted;
2648 bool hasPriv = account.accountPrivateKey != NULL;
2649 SOSCCStatus stat = [account getCircleStatus:NULL];
2651 CFStringRef userPubKeyID = (account.accountKey) ? SOSCopyIDOfKeyWithLength(account.accountKey, 8, NULL):
2652 CFStringCreateCopy(kCFAllocatorDefault, CFSTR("*No Key*"));
2654 secnotice(ACCOUNTLOGSTATE, "Start");
2656 secnotice(ACCOUNTLOGSTATE, "ACCOUNT: [keyStatus: %c%c%c hpub %@] [SOSCCStatus: %@]",
2657 boolToChars(hasPubKey, 'U', 'u'), boolToChars(pubTrusted, 'T', 't'), boolToChars(hasPriv, 'I', 'i'),
2659 SOSAccountGetSOSCCStatusString(stat)
2661 CFReleaseNull(userPubKeyID);
2662 if(trust.trustedCircle) SOSCircleLogState(ACCOUNTLOGSTATE, trust.trustedCircle, account.accountKey, (__bridge CFStringRef)(account.peerID));
2663 else secnotice(ACCOUNTLOGSTATE, "ACCOUNT: No Circle");
2666 void SOSAccountLogViewState(SOSAccount* account) {
2667 bool isInCircle = [account.trust isInCircle:NULL];
2668 require_quiet(isInCircle, imOut);
2669 SOSPeerInfoRef mpi = account.peerInfo;
2670 bool isInitialComplete = SOSAccountHasCompletedInitialSync(account);
2671 bool isBackupComplete = SOSAccountHasCompletedRequiredBackupSync(account);
2673 CFSetRef views = mpi ? SOSPeerInfoCopyEnabledViews(mpi) : NULL;
2674 CFStringSetPerformWithDescription(views, ^(CFStringRef description) {
2675 secnotice(ACCOUNTLOGSTATE, "Sync: %c%c PeerViews: %@",
2676 boolToChars(isInitialComplete, 'I', 'i'),
2677 boolToChars(isBackupComplete, 'B', 'b'),
2680 CFReleaseNull(views);
2681 CFSetRef unsyncedViews = SOSAccountCopyOutstandingViews(account);
2682 CFStringSetPerformWithDescription(views, ^(CFStringRef description) {
2683 secnotice(ACCOUNTLOGSTATE, "outstanding views: %@", description);
2685 CFReleaseNull(unsyncedViews);
2688 secnotice(ACCOUNTLOGSTATE, "Finish");
2694 void SOSAccountSetTestSerialNumber(SOSAccount* account, CFStringRef serial) {
2695 if(!isString(serial)) return;
2696 CFMutableDictionaryRef newv2dict = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
2697 CFDictionarySetValue(newv2dict, sSerialNumberKey, serial);
2698 [account.trust updateV2Dictionary:account v2:newv2dict];
2701 void SOSAccountResetOTRNegotiationCoder(SOSAccountTransaction* txn, CFStringRef peerid)
2703 secnotice("otrtimer", "timer fired!");
2704 CFErrorRef error = NULL;
2705 SecADAddValueForScalarKey((__bridge CFStringRef) SecSOSAggdReattemptOTRNegotiation,1);
2706 __block SOSAccount* account = txn.account;
2708 SOSEngineRef engine = SOSDataSourceFactoryGetEngineForDataSourceName(account.factory, SOSCircleGetName(account.trust.trustedCircle), NULL);
2709 SOSEngineWithPeerID(engine, peerid, &error, ^(SOSPeerRef peer, SOSCoderRef coder, SOSDataSourceRef dataSource, SOSTransactionRef txn, bool *forceSaveState) {
2710 if(SOSCoderIsCoderInAwaitingState(coder)){
2711 secnotice("otrtimer", "coder is in awaiting state, restarting coder");
2712 CFErrorRef localError = NULL;
2713 SOSCoderReset(coder);
2714 if(SOSCoderStart(coder, &localError) == kSOSCoderFailure){
2715 secerror("Attempt to recover coder failed to restart: %@", localError);
2718 secnotice("otrtimer", "coder restarted!");
2719 SOSEngineSetCodersNeedSaving(engine, true);
2720 SOSPeerSetMustSendMessage(peer, true);
2721 SOSCCRequestSyncWithPeer(SOSPeerGetID(peer));
2723 SOSPeerOTRTimerIncreaseOTRNegotiationRetryCount(account, (__bridge NSString*)SOSPeerGetID(peer));
2724 SOSPeerRemoveOTRTimerEntry(peer);
2725 SOSPeerOTRTimerRemoveRTTTimeoutForPeer(account, (__bridge NSString*)SOSPeerGetID(peer));
2726 SOSPeerOTRTimerRemoveLastSentMessageTimestamp(account, (__bridge NSString*)SOSPeerGetID(peer));
2729 secnotice("otrtimer", "time fired but out of negotiation! Not restarting coder");
2734 secnotice("otrtimer","error grabbing engine for peer id: %@, error:%@", peerid, error);
2736 CFReleaseNull(error);
2739 void SOSAccountTimerFiredSendNextMessage(SOSAccountTransaction* txn, NSString* peerid, NSString* accessGroup)
2741 __block SOSAccount* account = txn.account;
2742 CFErrorRef error = NULL;
2744 SOSEngineRef engine = SOSDataSourceFactoryGetEngineForDataSourceName(txn.account.factory, SOSCircleGetName(account.trust.trustedCircle), NULL);
2745 SOSEngineWithPeerID(engine, (__bridge CFStringRef)peerid, &error, ^(SOSPeerRef peer, SOSCoderRef coder, SOSDataSourceRef dataSource, SOSTransactionRef txn, bool *forceSaveState) {
2747 NSString *peer_id = (__bridge NSString*)SOSPeerGetID(peer);
2748 PeerRateLimiter *limiter = (__bridge PeerRateLimiter*)SOSPeerGetRateLimiter(peer);
2749 CFErrorRef error = NULL;
2750 NSData* message = [limiter.accessGroupToNextMessageToSend objectForKey:accessGroup];
2753 secnotice("ratelimit","SOSPeerRateLimiter timer went off! sending:%@ \n to peer:%@", message, peer_id);
2754 bool sendResult = [account.ids_message_transport SOSTransportMessageSendMessage:account.ids_message_transport id:(__bridge CFStringRef)peer_id messageToSend:(__bridge CFDataRef)message err:&error];
2756 if(!sendResult || error){
2757 secnotice("ratelimit", "could not send message: %@", error);
2760 [limiter.accessGroupRateLimitState setObject:[[NSNumber alloc]initWithLong:RateLimitStateCanSend] forKey:accessGroup];
2761 [limiter.accessGroupToTimer removeObjectForKey:accessGroup];
2762 [limiter.accessGroupToNextMessageToSend removeObjectForKey:accessGroup];
2767 secnotice("otrtimer","error grabbing engine for peer id: %@, error:%@", peerid, error);
2769 CFReleaseNull(error);