/ KeychainSyncAccountNotification / KeychainSyncAccountNotification.m
KeychainSyncAccountNotification.m
  1  //
  2  //  KeychainSyncAccountNotification.m
  3  //  Security
  4  //
  5  
  6  #import "KeychainSyncAccountNotification.h"
  7  #import <Accounts/Accounts.h>
  8  #import <Accounts/Accounts_Private.h>
  9  #import <AppleAccount/ACAccount+AppleAccount.h>
 10  #import <AccountsDaemon/ACDAccountStore.h>
 11  #import <Security/SecureObjectSync/SOSCloudCircle.h>
 12  #import <AuthKit/AuthKit.h>
 13  #import <AuthKit/AuthKit_Private.h>
 14  #if OCTAGON
 15  #import <keychain/ot/OTControl.h>
 16  #include <utilities/SecCFRelease.h>
 17  #endif
 18  #import "utilities/debugging.h"
 19  #import "OT.h"
 20  
 21  @implementation KeychainSyncAccountNotification
 22  
 23  - (bool)accountIsPrimary:(ACAccount *)account
 24  {
 25      return [account aa_isAccountClass:AAAccountClassPrimary];
 26  }
 27  
 28  // this is where we initialize SOS and OT for account sign-in
 29  // in the future we may bring this logic over to KeychainDataclassOwner and delete KeychainSyncAccountNotification, but accounts people say that's a change that today would require coordination across multiple teams
 30  // was asked to file this radar for accounts: <rdar://problem/40176124> Invoke DataclassOwner when enabling or signing into an account
 31  - (void)account:(ACAccount *)account didChangeWithType:(ACAccountChangeType)changeType inStore:(ACDAccountStore *)store oldAccount:(ACAccount *)oldAccount {
 32  
 33      if((changeType == kACAccountChangeTypeAdded || changeType == kACAccountChangeTypeModified || changeType == kACAccountChangeTypeWarmingUp) &&
 34         [account.accountType.identifier isEqualToString: ACAccountTypeIdentifierAppleAccount] &&
 35         [self accountIsPrimary:account]) {
 36  
 37          SOSCCLoggedIntoAccount(NULL);
 38  
 39  #if OCTAGON
 40          if(OctagonIsEnabled()){
 41              NSString* altDSID =  [account aa_altDSID];
 42              secnotice("octagon-account", "Received an primary Apple account modification (altDSID %@)", altDSID);
 43  
 44              __block NSError* error = nil;
 45  
 46              // Use asynchronous XPC here for speed and just hope it works
 47              OTControl* otcontrol = [OTControl controlObject:false error:&error];
 48              
 49              if (nil == otcontrol) {
 50                  secerror("octagon-account: Failed to get OTControl: %@", error.localizedDescription);
 51              } else {
 52                  [otcontrol signIn:altDSID container:nil context:OTDefaultContext reply:^(NSError * _Nullable signedInError) {
 53                      // take a retain on otcontrol so it won't invalidate the connection
 54                      (void)otcontrol;
 55  
 56                      if(signedInError) {
 57                          secerror("octagon-account: error signing in: %s", [[signedInError description] UTF8String]);
 58                      } else {
 59                          secnotice("octagon-account", "account now signed in for octagon operation");
 60                      }
 61                  }];
 62              }
 63          }else{
 64              secerror("Octagon not enabled; not signing in");
 65          }
 66  #endif
 67      }
 68  
 69      // If there is any change to any AuthKit account's security level, notify octagon
 70  
 71  #if OCTAGON
 72      if([account.accountType.identifier isEqualToString: ACAccountTypeIdentifierIDMS]) {
 73          NSString* altDSID = [account aa_altDSID];;
 74          secnotice("octagon-authkit", "Received an IDMS account modification (altDSID: %@)", altDSID);
 75  
 76          AKAccountManager *manager = [AKAccountManager sharedInstance];
 77  
 78          AKAppleIDSecurityLevel oldSecurityLevel = [manager securityLevelForAccount:oldAccount];
 79          AKAppleIDSecurityLevel newSecurityLevel = [manager securityLevelForAccount:account];
 80  
 81          if(oldSecurityLevel != newSecurityLevel) {
 82              secnotice("octagon-authkit", "IDMS security level has now moved to %ld for altDSID %@", (unsigned long)newSecurityLevel, altDSID);
 83  
 84              __block NSError* error = nil;
 85              // Use an asynchronous otcontrol for Speed But Not Necessarily Correctness
 86              OTControl* otcontrol = [OTControl controlObject:false error:&error];
 87              if(!otcontrol || error) {
 88                  secerror("octagon-authkit: Failed to get OTControl: %@", error);
 89              } else {
 90                   [otcontrol notifyIDMSTrustLevelChangeForContainer:nil context:OTDefaultContext reply:^(NSError * _Nullable idmsError) {
 91                       // take a retain on otcontrol so it won't invalidate the connection
 92                       (void)otcontrol;
 93  
 94                       if(idmsError) {
 95                           secerror("octagon-authkit: error with idms trust level change in: %s", [[idmsError description] UTF8String]);
 96                       } else {
 97                           secnotice("octagon-authkit", "informed octagon of IDMS trust level change");
 98                       }
 99                  }];
100              }
101  
102          } else {
103              secnotice("octagon-authkit", "No change to IDMS security level (%lu) for altDSID %@", (unsigned long)newSecurityLevel, altDSID);
104          }
105      }
106  #endif
107  
108      if ((changeType == kACAccountChangeTypeDeleted) && [oldAccount.accountType.identifier isEqualToString:ACAccountTypeIdentifierAppleAccount]) {
109          NSString* altDSID =  [oldAccount aa_altDSID];
110          secnotice("octagon-account", "Received an Apple account deletion (altDSID %@)", altDSID);
111  
112          NSString *accountIdentifier = oldAccount.identifier;
113          NSString *username = oldAccount.username;
114  
115          if(accountIdentifier != NULL && username !=NULL) {
116              if ([self accountIsPrimary:oldAccount]) {
117                  CFErrorRef removalError = NULL;
118  
119                  secinfo("accounts", "Performing SOS circle credential removal for account %@: %@", accountIdentifier, username);
120  
121                  if (!SOSCCLoggedOutOfAccount(&removalError)) {
122                      secerror("Account %@ could not leave the SOS circle: %@", accountIdentifier, removalError);
123                  }
124  
125  #if OCTAGON
126                  if(OctagonIsEnabled()){
127                      __block NSError* error = nil;
128  
129                      // Use an asynchronous control for Speed
130                      OTControl* otcontrol = [OTControl controlObject:false error:&error];
131  
132                      if (nil == otcontrol) {
133                          secerror("octagon-account: Failed to get OTControl: %@", error.localizedDescription);
134                      } else {
135                          [otcontrol signOut:nil context:OTDefaultContext reply:^(NSError * _Nullable signedInError) {
136                              // take a retain on otcontrol so it won't invalidate the connection
137                              (void)otcontrol;
138  
139                              if(signedInError) {
140                                  secerror("octagon-account: error signing out: %s", [[signedInError description] UTF8String]);
141                              } else {
142                                  secnotice("octagon-account", "signed out of octagon trust");
143                              }
144                          }];
145                      }
146                  } else {
147                      secerror("Octagon not enabled; not signing out");
148                  }
149  #endif
150              }
151          }
152      }
153  }
154  
155  @end