]> git.saurik.com Git - apple/security.git/blob - keychain/KeychainSettings/KeychainSettings.m
Security-59306.41.2.tar.gz
[apple/security.git] / keychain / KeychainSettings / KeychainSettings.m
1 //
2 // KeychainSettings.m
3 // Security
4 //
5
6 #import "KeychainSettings.h"
7 #import <Security/Security.h>
8 #import <Security/OTControl.h>
9 #import <Security/CKKSControl.h>
10 #import <os/log.h>
11 #import <AppleAccount/AppleAccount.h>
12 #import <AppleAccount/AppleAccount_Private.h>
13 #import <AuthKit/AuthKit.h>
14 #import <AuthKit/AuthKit_Private.h>
15 #import <AuthKit/AKDeviceListDeltaMessagePayload.h>
16 #import <Foundation/NSDistributedNotificationCenter.h>
17
18 #import <AppleAccount/ACAccount+AppleAccount.h>
19 #import <Accounts/ACAccountStore.h>
20 #import <UIKit/UIKit.h>
21 #import <utilities/debugging.h>
22
23 #import "keychain/ot/categories/OTAccountMetadataClassC+KeychainSupport.h"
24
25
26 @interface KeychainSettings ()
27 @property (strong) OTControl* control;
28 @property (strong) NSDictionary* status;
29 @property (strong) NSString *statusError;
30
31 + (OTControl *)sharedOTControl;
32 + (CKKSControl *)sharedCKKSControl;
33 @end
34
35 @implementation KeychainSettings
36
37 - (id)init {
38 if ((self = [super init]) != nil) {
39 [self updateCircleStatus];
40 }
41 return self;
42 }
43
44 + (OTControl *)sharedOTControl
45 {
46 static OTControl *control;
47 static dispatch_once_t onceToken;
48 dispatch_once(&onceToken, ^{
49 NSError *error = nil;
50
51 control = [OTControl controlObject:true error:&error];
52 if(error || !control) {
53 os_log(OS_LOG_DEFAULT, "no OTControl, failed: %@", error);
54 }
55 });
56 return control;
57 }
58
59 + (CKKSControl *)sharedCKKSControl
60 {
61 static CKKSControl *control;
62 static dispatch_once_t onceToken;
63 dispatch_once(&onceToken, ^{
64 NSError *error = nil;
65
66 // Use a synchronous control object
67 control = [CKKSControl CKKSControlObject:true error:&error];
68 if(error || !control) {
69 os_log(OS_LOG_DEFAULT, "no CKKSControl, failed: %@", error);
70 }
71 });
72 return control;
73
74 }
75
76
77 - (void)updateCircleStatus
78 {
79 [[KeychainSettings sharedOTControl] status:NULL
80 context:OTDefaultContext
81 reply:^(NSDictionary* result, NSError* _Nullable error) {
82 @synchronized (self) {
83 self.statusError = nil;
84 if (error) {
85 self.status = nil;
86 self.statusError = [error description];
87 } else {
88 self.status = result;
89 self.statusError = nil;
90 }
91 }
92 }];
93 }
94
95 - (NSArray *)specifiers {
96 if (!_specifiers) {
97 _specifiers = [self loadSpecifiersFromPlistName:@"KeychainSettings" target:self];
98 }
99 return _specifiers;
100 }
101
102 - (NSString *)octagonStatusString:(NSString *)key
103 {
104 __block id status = nil;
105 @synchronized (self) {
106 if (self.status) {
107 id value = self.status[key];
108 if ([value isKindOfClass:[NSString class]]) {
109 status = value;
110 } else if ([value isKindOfClass:[NSNumber class]]) {
111 NSNumber *number = value;
112 status = [number stringValue];
113 } else {
114 status = [key description];
115 }
116 }
117 if (status == nil && self.statusError) {
118 status = self.statusError;
119 }
120 }
121 if (status == nil) {
122 status = @"<unset>";
123 }
124 return status;
125
126 }
127
128 - (NSNumber *)octagonStatusNumber:(NSString *)key
129 {
130 __block NSNumber *status = nil;
131 @synchronized (self) {
132 NSNumber *value = self.status[key];
133 if ([value isKindOfClass:[NSNumber class]]) {
134 status = value;
135 }
136
137 }
138 return status;
139
140 }
141
142 - (NSString *)octagonStateMachine:(PSSpecifier *)specifier
143 {
144 return [self octagonStatusString:@"state"];
145 }
146
147 - (NSString *)prettyifyProtobufString:(NSString *)string
148 {
149 return [string.capitalizedString stringByReplacingOccurrencesOfString:@"_" withString:@" "];
150 }
151
152 - (NSString *)octagonTrustState:(PSSpecifier *)specifier
153 {
154 return [self prettyifyProtobufString:OTAccountMetadataClassC_TrustStateAsString([[self octagonStatusNumber:@"memoizedTrustState"] intValue])];
155 }
156
157 - (NSString *)octagonAccountState:(PSSpecifier *)specifier
158 {
159 return [self prettyifyProtobufString:OTAccountMetadataClassC_AccountStateAsString([[self octagonStatusNumber:@"memoizedAccountState"] intValue])];
160 }
161
162 - (NSString *)ckksAggregateStatus:(PSSpecifier *)specifier
163 {
164 __block NSString *status = NULL;
165 __block bool foundNonReady = false;
166 __block bool foundOne = false;
167
168 void (^replyBlock)(NSArray<NSDictionary *> * _Nullable result, NSError * _Nullable error) = ^(NSArray<NSDictionary *>* result, NSError* _Nullable error){
169 if (error) {
170 status = [NSString stringWithFormat:@"error: %@", error];
171 }
172 for(NSDictionary* view in result) {
173 NSString* viewName = view[@"view"];
174 if (viewName == NULL || [viewName isEqualToString:@"global"]) {
175 return;
176 }
177 foundOne = true;
178 NSString *viewStatus = view[@"keystate"];
179
180 if (![viewStatus isKindOfClass:[NSString class]] ||
181 !([viewStatus isEqualToString:@"ready"] || [viewStatus isEqualToString:@"readypendingunlock"])) {
182 foundNonReady = true;
183 }
184 }
185 };
186
187 [[KeychainSettings sharedCKKSControl] rpcFastStatus:NULL reply: replyBlock];
188
189 if (status) {
190 /* something already provided status */
191 } else if (foundNonReady) {
192 status = @"not ready";
193 } else if (foundOne) {
194 status = @"ready";
195 } else {
196 status = @"no status";
197 }
198
199 return status;
200 }
201
202 - (NSString* _Nullable)primaryiCloudAccountAltDSID
203 {
204 ACAccountStore *store = [[ACAccountStore alloc] init];
205 ACAccount* primaryAccount = [store aa_primaryAppleAccount];
206 if(!primaryAccount) {
207 return nil;
208 }
209
210 return [primaryAccount aa_altDSID];
211 }
212
213 - (void) resetOctagon:(PSSpecifier *)specifier
214 {
215 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
216
217 [[KeychainSettings sharedOTControl] resetAndEstablish:nil
218 context:OTDefaultContext
219 altDSID:[self primaryiCloudAccountAltDSID]
220 resetReason:CuttlefishResetReasonUserInitiatedReset
221 reply:^(NSError * _Nullable error) {
222 if(error) {
223
224 }
225 dispatch_semaphore_signal(sema);
226
227 }];
228 if(dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 60)) != 0) {
229 secerror("timed out attempting to reset octagon");
230 }
231 }
232
233 @end
234
235
236 @implementation KeychainSettingsOctagonPeers
237
238 - (void)addPeerIDs:(NSArray<NSString*>*)peerIDs
239 peers:(NSMutableDictionary<NSString*, NSDictionary*>*)peers
240 toSpecifiers:(NSMutableArray*)specifiers
241 {
242 NSMutableArray<NSDictionary*>* selectedPeers = [NSMutableArray array];
243
244 for (NSString *peerID in peerIDs) {
245 NSDictionary *peer = peers[peerID];
246 if (peer) {
247 [selectedPeers addObject:peer];
248 }
249 }
250
251 [selectedPeers sortUsingComparator:^NSComparisonResult(NSDictionary *_Nonnull obj1, NSDictionary *_Nonnull obj2) {
252 return [obj1[@"stableInfo"][@"device_name"] compare: obj2[@"stableInfo"][@"device_name"]];
253 }];
254
255 for (NSDictionary *peer in selectedPeers) {
256 PSSpecifier* spec = [PSSpecifier preferenceSpecifierNamed:peer[@"stableInfo"][@"device_name"] target:self set:nil get:nil detail:nil cell:PSTitleValueCell edit:nil];
257
258 [specifiers addObject:spec];
259 }
260
261 }
262
263 - (PSSpecifier *)groupSpecifier:(NSString *)name
264 {
265 return [PSSpecifier preferenceSpecifierNamed:name target:self set:nil get:nil detail:nil cell:PSGroupCell edit:nil];
266 }
267
268 - (NSArray *)specifiers {
269
270 if (!_specifiers) {
271 NSMutableArray* specifiers = [NSMutableArray array];
272
273 [specifiers addObjectsFromArray: [self loadSpecifiersFromPlistName:@"KeychainSettingsOctagonPeers" target:self]];
274
275 void (^replyBlock)(NSDictionary* result, NSError* _Nullable error) = ^(NSDictionary* result, NSError* _Nullable error) {
276 NSDictionary* contextDump = result[@"contextDump"];
277
278 // Make it easy to find peer information
279 NSMutableDictionary<NSString*, NSDictionary*>* peers = [NSMutableDictionary dictionary];
280 NSMutableArray<NSString*>* allPeerIDs = [NSMutableArray array];
281 for(NSDictionary* peerInformation in contextDump[@"peers"]) {
282 NSString* peerID = peerInformation[@"peerID"];
283 if(peerID) {
284 peers[peerID] = peerInformation;
285 [allPeerIDs addObject:peerID];
286 }
287 }
288
289 NSDictionary* egoInformation = contextDump[@"self"];
290 NSString* egoPeerID = egoInformation[@"peerID"];
291 NSDictionary* egoDynamicInfo = egoInformation[@"dynamicInfo"];
292
293 if(egoPeerID && egoInformation && egoDynamicInfo) {
294
295 peers[egoPeerID] = egoInformation;
296
297 [specifiers addObject:[self groupSpecifier:@"Me"]];
298 [self addPeerIDs:@[egoPeerID] peers:peers toSpecifiers:specifiers];
299
300 NSArray<NSString*>* included = egoDynamicInfo[@"included"];
301 [specifiers addObject:[self groupSpecifier:@"Included"]];
302 [self addPeerIDs:included peers:peers toSpecifiers:specifiers];
303 [peers removeObjectsForKeys:included];
304
305 NSArray<NSString*>* excluded = egoDynamicInfo[@"excluded"];
306 [specifiers addObject:[self groupSpecifier:@"Excluded"]];
307 [self addPeerIDs:excluded peers:peers toSpecifiers:specifiers];
308 [peers removeObjectsForKeys:excluded];
309
310 } else {
311 [specifiers addObject:[self groupSpecifier:@"Me (untrusted)"]];
312 }
313
314 if (peers.count) {
315 [specifiers addObject:[self groupSpecifier:@"Other peers"]];
316 [self addPeerIDs:peers.allKeys peers:peers toSpecifiers:specifiers];
317 }
318
319 };
320
321 [[KeychainSettings sharedOTControl] status:NULL context:OTDefaultContext reply:replyBlock];
322
323 _specifiers = specifiers;
324 }
325
326 return _specifiers;
327 }
328
329 @end
330
331 @implementation KeychainSettingsCKKSViews
332
333 - (NSArray *)specifiers {
334
335 if (!_specifiers) {
336 NSMutableArray* specifiers = [NSMutableArray array];
337
338 [specifiers addObjectsFromArray: [self loadSpecifiersFromPlistName:@"KeychainSettingsCKKSViews" target:self]];
339
340 void (^replyBlock)(NSArray<NSDictionary *> * _Nullable result, NSError * _Nullable error) = ^(NSArray<NSDictionary *>* result, NSError* _Nullable error){
341
342 NSMutableArray<NSDictionary*>* views = [NSMutableArray array];
343 for(NSDictionary* view in result) {
344 NSString* viewName = view[@"view"];
345 if (viewName == NULL || [viewName isEqualToString:@"global"]) {
346 return;
347 }
348
349 [views addObject:view];
350 }
351
352 [views sortUsingComparator:^NSComparisonResult(NSDictionary *_Nonnull obj1, NSDictionary *_Nonnull obj2) {
353 return [obj1[@"view"] compare: obj2[@"view"]];
354 }];
355
356 for (NSDictionary *view in views) {
357 NSString *description = [NSString stringWithFormat:@"%@ - %@", view[@"view"], view[@"keystate"]];
358 PSSpecifier* spec = [PSSpecifier preferenceSpecifierNamed:description target:self set:nil get:nil detail:nil cell:PSTitleValueCell edit:nil];
359
360 [specifiers addObject:spec];
361 }
362 };
363 [[KeychainSettings sharedCKKSControl] rpcFastStatus:NULL reply: replyBlock];
364
365 _specifiers = specifiers;
366 }
367
368 return _specifiers;
369 }
370
371
372
373 @end