/ keychain / SecureObjectSync / SOSAuthKitHelpers.m
SOSAuthKitHelpers.m
  1  //
  2  //  SOSAuthKitHelpers.m
  3  //  Security
  4  //
  5  //
  6  
  7  #import <Foundation/Foundation.h>
  8  #import "SOSAuthKitHelpers.h"
  9  #import <utilities/debugging.h>
 10  #import "keychain/SecureObjectSync/SOSAccount.h"
 11  #import "keychain/SecureObjectSync/SOSAccountPriv.h"
 12  #import "keychain/SecureObjectSync/SOSFullPeerInfo.h"
 13  #import "keychain/SecureObjectSync/SOSPeerInfoV2.h"
 14  #import "keychain/SecureObjectSync/SOSPeerInfoPriv.h"
 15  
 16  #if !TARGET_OS_BRIDGE && !TARGET_OS_SIMULATOR && __OBJC2__
 17  #import <AppleAccount/AppleAccount_Private.h>
 18  #import <AuthKit/AuthKit.h>
 19  #import <AuthKit/AuthKit_Private.h>
 20  #import <SoftLinking/SoftLinking.h>
 21  
 22  #define SUPPORT_MID 1
 23  
 24  SOFT_LINK_FRAMEWORK(PrivateFrameworks, AuthKit);
 25  SOFT_LINK_FRAMEWORK(Frameworks, Accounts);
 26  
 27  #pragma clang diagnostic push
 28  #pragma clang diagnostic ignored "-Wstrict-prototypes"
 29  SOFT_LINK_CLASS(AuthKit, AKAccountManager);
 30  SOFT_LINK_CLASS(AuthKit, AKAnisetteProvisioningController);
 31  SOFT_LINK_CLASS(AuthKit, AKAppleIDAuthenticationController);
 32  SOFT_LINK_CLASS(AuthKit, AKDeviceListRequestContext);
 33  SOFT_LINK_CLASS(Accounts, ACAccountStore);
 34  SOFT_LINK_CONSTANT(AuthKit, AKServiceNameiCloud, const NSString *);
 35  #pragma clang diagnostic pop
 36  
 37  static void *accountsFramework = NULL;
 38  static void *appleAccountFramework = NULL;
 39  
 40  static void
 41  initAccountsFramework(void) {
 42      static dispatch_once_t onceToken;
 43      dispatch_once(&onceToken, ^{
 44          accountsFramework = dlopen("/System/Library/Frameworks/Accounts.framework/Accounts", RTLD_LAZY);
 45          appleAccountFramework = dlopen("/System/Library/PrivateFrameworks/AppleAccount.framework/AppleAccount", RTLD_LAZY);
 46      });
 47  }
 48  
 49  @implementation SOSAuthKitHelpers
 50  
 51  @class SOSAuthKitHelpers;
 52  
 53  + (NSString *) machineID {
 54      NSError *error = nil;
 55      NSString *retval = nil;
 56      secnotice("sosauthkit", "Entering machineID");
 57  
 58      AKAnisetteProvisioningController *anisetteController = [getAKAnisetteProvisioningControllerClass() new];
 59      if(anisetteController) {
 60          AKAnisetteData *anisetteData = [anisetteController anisetteDataWithError:&error];
 61          if (anisetteData) {
 62              retval = [anisetteData.machineID copy];
 63              if(retval) {
 64                  secnotice("sosauthkit", "machineID is %@", retval);
 65              } else {
 66                  secnotice("sosauthkit", "Failed to get machineID");
 67              }
 68          } else {
 69              secnotice("sosauthkit", "can't get mID: %@", error);
 70          }
 71      } else {
 72          secnotice("sosauthkit", "can't get controller");
 73      }
 74      return retval;
 75  }
 76  
 77  static ACAccount *GetPrimaryAccount(void) {
 78      ACAccount *primaryAccount;
 79      
 80      initAccountsFramework();
 81  
 82      ACAccountStore *store = [getACAccountStoreClass() new];
 83  
 84      if(!store) {
 85          secnotice("sosauthkit", "can't get store");
 86          return nil;
 87      }
 88      
 89      primaryAccount = [store aa_primaryAppleAccount];
 90  
 91      return primaryAccount;
 92  }
 93  
 94  
 95  + (void)activeMIDs:(void(^_Nonnull)(NSSet <SOSTrustedDeviceAttributes *> *activeMIDs, NSError *error))complete {
 96      ACAccount *primaryAccount;
 97      AKDeviceListRequestContext *context;
 98      
 99      primaryAccount = GetPrimaryAccount();
100  
101      if(!primaryAccount) {
102          secnotice("sosauthkit", "can't get account");
103          complete(NULL, [NSError errorWithDomain:(__bridge NSString *)kSecErrorDomain code:errSecParam userInfo:@{NSLocalizedDescriptionKey : @"no primary account"}]);
104          return;
105      }
106  
107      context = [getAKDeviceListRequestContextClass() new];
108      if (context == NULL) {
109          complete(NULL, [NSError errorWithDomain:(__bridge NSString *)kSecErrorDomain code:errSecParam userInfo:@{NSLocalizedDescriptionKey : @"can't get AKDeviceListRequestContextClass"}]);
110          return;
111      }
112      context.altDSID = primaryAccount.aa_altDSID;
113      context.services = @[ getAKServiceNameiCloud() ];
114      
115      // -[AKAppleIDAuthenticationController fetchDeviceListWithContext:error:] is not exposed, use a semaphore
116      AKAppleIDAuthenticationController *authController = [getAKAppleIDAuthenticationControllerClass() new];
117      if(!authController) {
118          complete(NULL, [NSError errorWithDomain:(__bridge NSString *)kSecErrorDomain code:errSecParam userInfo:@{NSLocalizedDescriptionKey : @"can't get authController"}]);
119          return;
120      }
121  
122      [authController fetchDeviceListWithContext:context completion:^(NSArray<AKRemoteDevice *> *deviceList, NSError *error) {
123          NSMutableSet *mids = [NSMutableSet new];
124          for(AKRemoteDevice *akdev in deviceList) {
125              SOSTrustedDeviceAttributes *newdev = [SOSTrustedDeviceAttributes new];
126              newdev.machineID = akdev.machineId;
127              newdev.serialNumber = akdev.serialNumber;
128              [mids addObject:newdev];
129          }
130          if([mids count] == 0) {
131              secnotice("sosauthkit", "found no devices in account");
132              mids = nil;
133          }
134          complete(mids, error);
135      }];
136  }
137  
138  + (bool) peerinfoHasMID: (SOSAccount *) account {
139      SOSPeerInfoRef pi = SOSFullPeerInfoGetPeerInfo(account.fullPeerInfo);
140      if(!pi) return true;  // if there's no PI then just say "we don't need one"
141      return SOSPeerInfoV2DictionaryHasString(pi, sMachineIDKey);
142  }
143  
144  
145  
146  + (bool) updateMIDInPeerInfo: (SOSAccount *) account {
147      NSString *mid = [SOSAuthKitHelpers machineID];
148      if(!mid) return true;
149      CFErrorRef error = NULL;
150      SOSAccountSetValue(account, sMachineIDKey, (__bridge CFStringRef)mid, &error);
151      bool peerUpdated = SOSAccountUpdatePeerInfoAndPush(account, CFSTR("Add Machine ID"), &error, ^bool(SOSPeerInfoRef pi, CFErrorRef *error) {
152          if(SOSPeerInfoV2DictionaryHasString(pi, sMachineIDKey)) {
153              return false;
154          }
155          secnotice("sosauthkit", "Setting PeerInfo MID to %@", mid);
156          SOSPeerInfoV2DictionarySetValue(pi, sMachineIDKey, (__bridge CFStringRef)mid);
157          return true;
158      });
159      if(!peerUpdated) {
160          secnotice("sosauthkit", "Failed to record MID in PeerInfo: %@", error);
161      }
162      CFReleaseNull(error);
163      return peerUpdated;
164  }
165  
166  
167  + (bool) accountIsHSA2 {
168      bool hsa2 = false;
169      
170      ACAccount *primaryAccount = GetPrimaryAccount();
171      AKAccountManager *manager = [getAKAccountManagerClass() new];
172  
173      if(manager && primaryAccount) {
174          ACAccount *account = [manager authKitAccountWithAltDSID:[manager altDSIDForAccount:primaryAccount]];
175          AKAppleIDSecurityLevel securityLevel = [manager securityLevelForAccount:account];
176          if(securityLevel == AKAppleIDSecurityLevelHSA2) {
177              hsa2 = true;
178          } else {
179              secnotice("sosauthkit", "Security level is %lu", (unsigned long)securityLevel);
180          }
181          secnotice("sosauthkit", "Account %s HSA2", (hsa2) ? "is": "isn't" );
182      } else {
183          secnotice("sosauthkit", "Failed to get manager");
184      }
185      return hsa2;
186  }
187  
188  
189  -(id) initWithActiveMIDS: (NSSet <SOSTrustedDeviceAttributes *> *) theMidList
190  {
191      if ((self = [super init])) {
192          NSMutableSet *MmachineIDs = [[NSMutableSet alloc] init];
193          NSMutableSet *MserialNumbers = [[NSMutableSet alloc] init];
194          _machineIDs = [[NSSet alloc] init];
195          _serialNumbers = [[NSSet alloc] init];
196  
197          if(!theMidList) return nil;
198          _midList = theMidList;
199  
200          for(SOSTrustedDeviceAttributes *dev in _midList) {
201              if(dev.machineID) {
202                  [MmachineIDs addObject:dev.machineID];
203              }
204              if(dev.serialNumber) {
205                  [MserialNumbers addObject:dev.serialNumber];
206              }
207  
208          }
209          _machineIDs = MmachineIDs;
210          _serialNumbers = MserialNumbers;
211      }
212      return self;
213  }
214  
215  // if the ID passed in is null, the peer doesn't have one, we'll say true - we can't tell from the list
216  - (bool) midIsValidInList: (NSString *) machineId {
217      return (machineId) ? [_machineIDs containsObject:machineId]: true;
218  }
219  
220  - (bool) serialIsValidInList: (NSString *) serialNumber {
221      return (serialNumber) ? [_serialNumbers containsObject:serialNumber]: true;
222  }
223  
224  - (bool) isUseful {
225      return [ _machineIDs count ] > 0;
226  }
227  
228  #else
229  
230  
231  @implementation SOSAuthKitHelpers
232  
233  @class SOSAuthKitHelpers;
234  
235  + (NSString *) machineID {
236      return nil;
237  }
238  
239  + (void)activeMIDs:(void(^_Nonnull)(NSSet<SOSTrustedDeviceAttributes*> *activeMIDs, NSError *error))complete {
240      complete(nil, nil);
241  }
242  
243  + (bool) updateMIDInPeerInfo: (SOSAccount *) account {
244      return true;
245  }
246  
247  + (bool) peerinfoHasMID: (SOSAccount *) account {
248      return true;
249  }
250  
251  + (bool) accountIsHSA2 {
252      return false;
253  }
254  
255  - (id _Nullable) initWithActiveMIDS: (NSSet *_Nullable) theMidList {
256      return nil;
257  }
258  
259  - (bool) midIsValidInList: (NSString *_Nullable) machineId {
260      return true;
261  }
262  
263  - (bool) serialIsValidInList: (NSString *_Nullable) serialNumber {
264      return true;
265  }
266  
267  - (bool) isUseful {
268      return false;
269  }
270  
271  #endif
272  
273  @end
274