]> git.saurik.com Git - apple/security.git/blob - keychain/ot/OTAuthKitAdapter.m
Security-59306.80.4.tar.gz
[apple/security.git] / keychain / ot / OTAuthKitAdapter.m
1 #if OCTAGON
2
3 #import "OTAuthKitAdapter.h"
4 #import "OTConstants.h"
5
6 #import "utilities/SecCFError.h"
7 #import "keychain/categories/NSError+UsefulConstructors.h"
8
9 #import <AppleAccount/AppleAccount.h>
10 #import <AppleAccount/AppleAccount_Private.h>
11 #import <AuthKit/AuthKit.h>
12 #import <AuthKit/AuthKit_Private.h>
13 #import <AuthKit/AKDeviceListDeltaMessagePayload.h>
14 #import <Foundation/NSDistributedNotificationCenter.h>
15 #import "keychain/ckks/CKKSListenerCollection.h"
16 #import "keychain/ckks/CKKSAnalytics.h"
17
18 #include "utilities/SecABC.h"
19
20 #import <AppleAccount/ACAccount+AppleAccount.h>
21
22 @interface OTAuthKitActualAdapter ()
23 @property CKKSListenerCollection<OTAuthKitAdapterNotifier>* notifiers;
24 @end
25
26 @implementation OTAuthKitActualAdapter
27
28 - (NSString* _Nullable)primaryiCloudAccountAltDSID:(NSError **)error
29 {
30 ACAccountStore *store = [[ACAccountStore alloc] init];
31 ACAccount* primaryAccount = [store aa_primaryAppleAccount];
32 if(!primaryAccount) {
33 secnotice("authkit", "No primary account");
34 if (error) {
35 *error = [NSError errorWithDomain:OctagonErrorDomain
36 code:OTAuthKitNoPrimaryAccount
37 description:@"No primary account"];
38 }
39 return nil;
40 }
41
42 NSString *altDSID = [primaryAccount aa_altDSID];
43 if (altDSID == NULL) {
44 secnotice("authkit", "No altDSID on primary account");
45 if (error) {
46 *error = [NSError errorWithDomain:OctagonErrorDomain
47 code:OTAuthKitPrimaryAccountHaveNoDSID
48 description:@"No altdsid on primary account"];
49 }
50 }
51 return altDSID;
52 }
53
54 - (BOOL)accountIsHSA2ByAltDSID:(NSString*)altDSID
55 {
56 bool hsa2 = false;
57
58 AKAccountManager *manager = [AKAccountManager sharedInstance];
59 ACAccount *authKitAccount = [manager authKitAccountWithAltDSID:altDSID];
60 AKAppleIDSecurityLevel securityLevel = [manager securityLevelForAccount:authKitAccount];
61 if(securityLevel == AKAppleIDSecurityLevelHSA2) {
62 hsa2 = true;
63 }
64 secnotice("security-authkit", "Security level for altDSID %@ is %lu", altDSID, (unsigned long)securityLevel);
65 return hsa2;
66 }
67
68 - (NSString* _Nullable)machineID:(NSError**)error
69 {
70 AKAnisetteProvisioningController* anisetteController = [[AKAnisetteProvisioningController alloc] init];
71 NSError* localError = nil;
72 AKAnisetteData* anisetteData = [anisetteController anisetteDataWithError:&localError];
73 if(!anisetteData) {
74 secnotice("authkit", "Unable to fetch data: %@", localError);
75 if(error) {
76 *error = localError;
77 }
78 return nil;
79 }
80
81 NSString* machineID = anisetteData.machineID;
82 if(!machineID) {
83 secnotice("authkit", "Anisette data does not have machineID");
84 if(error) {
85 [SecABC triggerAutoBugCaptureWithType:@"AuthKit" subType:@"missingMID"];
86 *error = [NSError errorWithDomain:OctagonErrorDomain
87 code:OTAuthKitMachineIDMissing
88 description:@"Anisette data does not have machineID"];
89 }
90 return nil;
91 }
92
93 secnotice("authkit", "fetched current machine ID as: %@", machineID);
94
95 return machineID;
96 }
97
98 - (void)fetchCurrentDeviceList:(void (^)(NSSet<NSString*>* _Nullable machineIDs, NSError* _Nullable error))complete
99 {
100 AKDeviceListRequestContext* context = [[AKDeviceListRequestContext alloc] init];
101 if (context == nil) {
102 NSError *error = [NSError errorWithDomain:OctagonErrorDomain
103 code:OTAuthKitAKDeviceListRequestContextClass
104 description:@"can't get AKDeviceListRequestContextClass"];
105 [[CKKSAnalytics logger] logUnrecoverableError:error forEvent:OctagonEventAuthKitDeviceList withAttributes:nil];
106 complete(nil, error);
107 return;
108 }
109 NSError *authKitError = nil;
110 context.altDSID = [self primaryiCloudAccountAltDSID:&authKitError];
111 if (context.altDSID == NULL) {
112 secnotice("authkit", "Failed to get primary account AltDSID: %@", authKitError);
113 NSError *error = [NSError errorWithDomain:OctagonErrorDomain
114 code:OTAuthKitPrimaryAccountHaveNoDSID
115 description:@"Can't get primary AltDSID"
116 underlying:authKitError];
117 [[CKKSAnalytics logger] logUnrecoverableError:error forEvent:OctagonEventAuthKitDeviceList withAttributes:nil];
118 [SecABC triggerAutoBugCaptureWithType:@"AuthKit" subType:@"missingAltDSID"];
119 complete(nil, error);
120 return;
121 }
122
123 AKAppleIDAuthenticationController *authController = [[AKAppleIDAuthenticationController alloc] init];
124 if(authController == nil) {
125 NSError *error = [NSError errorWithDomain:OctagonErrorDomain
126 code:OTAuthKitNoAuthenticationController
127 description:@"can't get authController"];
128 [[CKKSAnalytics logger] logUnrecoverableError:error forEvent:OctagonEventAuthKitDeviceList withAttributes:nil];
129 complete(nil, error);
130 return;
131 }
132
133 [authController fetchDeviceListWithContext:context completion:^(NSArray<AKRemoteDevice *> *deviceList, NSError *error) {
134 if (deviceList) {
135 NSMutableSet *mids = [[NSMutableSet alloc] init];
136
137 for (AKRemoteDevice *device in deviceList) {
138 [mids addObject:device.machineId];
139 }
140
141 secnotice("authkit", "Current machine ID list: %@", mids);
142 complete(mids, error);
143 [[CKKSAnalytics logger] logSuccessForEventNamed:OctagonEventAuthKitDeviceList];
144
145 } else {
146 [[CKKSAnalytics logger] logUnrecoverableError:error forEvent:OctagonEventAuthKitDeviceList withAttributes:nil];
147 secnotice("authkit", "received no device list: %@", error);
148 complete(nil, error);
149 }
150 }];
151 }
152
153 - (void)registerNotification:(id<OTAuthKitAdapterNotifier>)newNotifier
154 {
155 if (self.notifiers == nil) {
156 self.notifiers = [[CKKSListenerCollection<OTAuthKitAdapterNotifier> alloc] initWithName:@"otauthkitadapter-notifiers"];
157 [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(notifyAKDeviceList:) name:AKDeviceListChangedNotification object:nil];
158 }
159 [self.notifiers registerListener:newNotifier];
160 }
161
162 - (void)notifyAKDeviceList:(NSNotification* _Nullable)notification
163 {
164 AKDeviceListDeltaMessagePayload *payload = nil;
165 NSDictionary *userInfo = nil;
166 if (notification != nil) {
167 userInfo = [notification userInfo];
168 if (userInfo != nil) {
169 payload = [[AKDeviceListDeltaMessagePayload alloc] initWithResponseBody:userInfo];
170 }
171 }
172
173 secnotice("authkit", "received notifyAKDeviceList: %@, read payload: %@",
174 notification.userInfo,
175 // Logging the payload logs an address, so clean it up here.
176 payload ? @"YES" : @"NO");
177
178 [self.notifiers iterateListeners:^(id<OTAuthKitAdapterNotifier> listener) {
179 NSString* altDSID = payload.altDSID;
180 NSArray<NSString*>* machineIDs = payload.machineIDs;
181
182 if (altDSID == nil || machineIDs == nil || machineIDs.count == 0) {
183 secnotice("authkit", "partial push or no machine IDs in list; treating as incomplete");
184 [listener incompleteNotificationOfMachineIDListChange];
185 return;
186 }
187 switch (payload.operation) {
188 case AKDeviceListDeltaOperationAdd:
189 [listener machinesAdded:machineIDs altDSID:altDSID];
190 return;
191 break;
192 case AKDeviceListDeltaOperationRemove:
193 [listener machinesRemoved:machineIDs altDSID:altDSID];
194 return;
195 break;
196 case AKDeviceListDeltaOperationUnknown:
197 default:
198 break;
199 }
200 [listener incompleteNotificationOfMachineIDListChange];
201 }];
202 }
203
204 @end
205
206 #endif // OCTAGON