]> git.saurik.com Git - apple/security.git/blob - keychain/ot/OTAuthKitAdapter.m
Security-59306.101.1.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 = NO;
57
58 AKAccountManager *manager = [AKAccountManager sharedInstance];
59 ACAccount *authKitAccount = [manager authKitAccountWithAltDSID:altDSID];
60 AKAppleIDSecurityLevel securityLevel = [manager securityLevelForAccount:authKitAccount];
61 if(securityLevel == AKAppleIDSecurityLevelHSA2) {
62 hsa2 = YES;
63 }
64 secnotice("security-authkit", "Security level for altDSID %@ is %lu", altDSID, (unsigned long)securityLevel);
65 return hsa2;
66 }
67
68 - (BOOL)accountIsDemoAccount:(NSError**)error
69 {
70 NSError* localError = nil;
71 NSString* altDSID = [self primaryiCloudAccountAltDSID:&localError];
72
73 if(altDSID == nil) {
74 secerror("octagon-authkit:could not retrieve altDSID");
75 }
76 if (localError) {
77 secerror("octagon-authkit: hit an error retrieving altDSID: %@", localError);
78 if(error){
79 *error = localError;
80 }
81 return NO;
82 }
83
84 AKAccountManager *manager = [AKAccountManager sharedInstance];
85 ACAccount *authKitAccount = [manager authKitAccountWithAltDSID:altDSID];
86 BOOL isDemo = [manager demoAccountForAccount:authKitAccount];
87
88 secnotice("security-authkit", "Account with altDSID %@ is a demo account: %@", altDSID, isDemo ? @"true" : @"false");
89
90 return isDemo;
91 }
92
93 - (NSString* _Nullable)machineID:(NSError**)error
94 {
95 AKAnisetteProvisioningController* anisetteController = [[AKAnisetteProvisioningController alloc] init];
96 NSError* localError = nil;
97 AKAnisetteData* anisetteData = [anisetteController anisetteDataWithError:&localError];
98 if(!anisetteData) {
99 secnotice("authkit", "Unable to fetch data: %@", localError);
100 if(error) {
101 *error = localError;
102 }
103 return nil;
104 }
105
106 NSString* machineID = anisetteData.machineID;
107 if(!machineID) {
108 secnotice("authkit", "Anisette data does not have machineID");
109 if(error) {
110 [SecABC triggerAutoBugCaptureWithType:@"AuthKit" subType:@"missingMID"];
111 *error = [NSError errorWithDomain:OctagonErrorDomain
112 code:OTAuthKitMachineIDMissing
113 description:@"Anisette data does not have machineID"];
114 }
115 return nil;
116 }
117
118 secnotice("authkit", "fetched current machine ID as: %@", machineID);
119
120 return machineID;
121 }
122
123 - (void)fetchCurrentDeviceList:(void (^)(NSSet<NSString*>* _Nullable machineIDs, NSError* _Nullable error))complete
124 {
125 AKDeviceListRequestContext* context = [[AKDeviceListRequestContext alloc] init];
126 if (context == nil) {
127 NSError *error = [NSError errorWithDomain:OctagonErrorDomain
128 code:OTAuthKitAKDeviceListRequestContextClass
129 description:@"can't get AKDeviceListRequestContextClass"];
130 [[CKKSAnalytics logger] logUnrecoverableError:error forEvent:OctagonEventAuthKitDeviceList withAttributes:nil];
131 complete(nil, error);
132 return;
133 }
134 NSError *authKitError = nil;
135 context.altDSID = [self primaryiCloudAccountAltDSID:&authKitError];
136 if (context.altDSID == NULL) {
137 secnotice("authkit", "Failed to get primary account AltDSID: %@", authKitError);
138 NSError *error = [NSError errorWithDomain:OctagonErrorDomain
139 code:OTAuthKitPrimaryAccountHaveNoDSID
140 description:@"Can't get primary AltDSID"
141 underlying:authKitError];
142 [[CKKSAnalytics logger] logUnrecoverableError:error forEvent:OctagonEventAuthKitDeviceList withAttributes:nil];
143 [SecABC triggerAutoBugCaptureWithType:@"AuthKit" subType:@"missingAltDSID"];
144 complete(nil, error);
145 return;
146 }
147
148 AKAppleIDAuthenticationController *authController = [[AKAppleIDAuthenticationController alloc] init];
149 if(authController == nil) {
150 NSError *error = [NSError errorWithDomain:OctagonErrorDomain
151 code:OTAuthKitNoAuthenticationController
152 description:@"can't get authController"];
153 [[CKKSAnalytics logger] logUnrecoverableError:error forEvent:OctagonEventAuthKitDeviceList withAttributes:nil];
154 complete(nil, error);
155 return;
156 }
157
158 [authController fetchDeviceListWithContext:context completion:^(NSArray<AKRemoteDevice *> *deviceList, NSError *error) {
159 if (deviceList) {
160 NSMutableSet *mids = [[NSMutableSet alloc] init];
161
162 for (AKRemoteDevice *device in deviceList) {
163 [mids addObject:device.machineId];
164 }
165
166 secnotice("authkit", "Current machine ID list: %@", mids);
167 complete(mids, error);
168 [[CKKSAnalytics logger] logSuccessForEventNamed:OctagonEventAuthKitDeviceList];
169
170 } else {
171 [[CKKSAnalytics logger] logUnrecoverableError:error forEvent:OctagonEventAuthKitDeviceList withAttributes:nil];
172 secnotice("authkit", "received no device list: %@", error);
173 complete(nil, error);
174 }
175 }];
176 }
177
178 - (void)registerNotification:(id<OTAuthKitAdapterNotifier>)newNotifier
179 {
180 if (self.notifiers == nil) {
181 self.notifiers = [[CKKSListenerCollection<OTAuthKitAdapterNotifier> alloc] initWithName:@"otauthkitadapter-notifiers"];
182 [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(notifyAKDeviceList:) name:AKDeviceListChangedNotification object:nil];
183 }
184 [self.notifiers registerListener:newNotifier];
185 }
186
187 - (void)notifyAKDeviceList:(NSNotification* _Nullable)notification
188 {
189 AKDeviceListDeltaMessagePayload *payload = nil;
190 NSDictionary *userInfo = nil;
191 if (notification != nil) {
192 userInfo = [notification userInfo];
193 if (userInfo != nil) {
194 payload = [[AKDeviceListDeltaMessagePayload alloc] initWithResponseBody:userInfo];
195 }
196 }
197
198 secnotice("authkit", "received notifyAKDeviceList: %@, read payload: %@",
199 notification.userInfo,
200 // Logging the payload logs an address, so clean it up here.
201 payload ? @"YES" : @"NO");
202
203 [self.notifiers iterateListeners:^(id<OTAuthKitAdapterNotifier> listener) {
204 NSString* altDSID = payload.altDSID;
205 NSArray<NSString*>* machineIDs = payload.machineIDs;
206
207 if (altDSID == nil || machineIDs == nil || machineIDs.count == 0) {
208 secnotice("authkit", "partial push or no machine IDs in list; treating as incomplete");
209 [listener incompleteNotificationOfMachineIDListChange];
210 return;
211 }
212 switch (payload.operation) {
213 case AKDeviceListDeltaOperationAdd:
214 [listener machinesAdded:machineIDs altDSID:altDSID];
215 return;
216 break;
217 case AKDeviceListDeltaOperationRemove:
218 [listener machinesRemoved:machineIDs altDSID:altDSID];
219 return;
220 break;
221 case AKDeviceListDeltaOperationUnknown:
222 default:
223 break;
224 }
225 [listener incompleteNotificationOfMachineIDListChange];
226 }];
227 }
228
229 @end
230
231 #endif // OCTAGON