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