]> git.saurik.com Git - apple/security.git/blob - keychain/escrowrequest/operations/EscrowRequestInformCloudServicesOperation.m
Security-59306.11.20.tar.gz
[apple/security.git] / keychain / escrowrequest / operations / EscrowRequestInformCloudServicesOperation.m
1
2 #import <CloudServices/SecureBackup.h>
3
4 #import "utilities/debugging.h"
5 #import "keychain/ckks/CKKSLockStateTracker.h"
6
7 #import "keychain/escrowrequest/EscrowRequestController.h"
8 #import "keychain/escrowrequest/operations/EscrowRequestInformCloudServicesOperation.h"
9 #import "keychain/escrowrequest/generated_source/SecEscrowPendingRecord.h"
10 #import "keychain/escrowrequest/SecEscrowPendingRecord+KeychainSupport.h"
11
12 @interface EscrowRequestInformCloudServicesOperation()
13 @property CKKSLockStateTracker* lockStateTracker;
14 @end
15
16 @implementation EscrowRequestInformCloudServicesOperation
17 @synthesize nextState = _nextState;
18 @synthesize intendedState = _intendedState;
19
20 - (instancetype)initWithIntendedState:(OctagonState*)intendedState
21 errorState:(OctagonState*)errorState
22 lockStateTracker:(CKKSLockStateTracker*)lockStateTracker
23 {
24 if((self = [super init])) {
25 _intendedState = intendedState;
26 _nextState = errorState;
27 _lockStateTracker = lockStateTracker;
28 }
29 return self;
30 }
31
32 - (void)main
33 {
34 secnotice("escrowrequest", "Telling CloudServices about any pending requests");
35
36 NSError* error = nil;
37 NSArray<SecEscrowPendingRecord*>* records = [SecEscrowPendingRecord loadAllFromKeychain:&error];
38 if(error && !([error.domain isEqualToString:NSOSStatusErrorDomain] && error.code == errSecItemNotFound)) {
39 secnotice("escrowrequest", "failed to fetch records from keychain: %@", error);
40 if([self.lockStateTracker isLockedError:error]) {
41 secnotice("escrowrequest", "Trying again after unlock");
42 self.nextState = EscrowRequestStateWaitForUnlock;
43 } else {
44 self.nextState = EscrowRequestStateNothingToDo;
45 }
46 self.error = error;
47 return;
48 }
49 error = nil;
50
51 SecEscrowPendingRecord* record = nil;
52
53 for(SecEscrowPendingRecord* existingRecord in records) {
54 if(!existingRecord.hasCertCached) {
55 record = existingRecord;
56 break;
57 }
58 }
59
60 if(!record) {
61 secnotice("escrowrequest", "No pending escrow request needs a certificate");
62 self.nextState = EscrowRequestStateNothingToDo;
63 return;
64 }
65
66 // Next, see if CloudServices can cache a certificate
67 NSData* cachedCert = [EscrowRequestInformCloudServicesOperation triggerCloudServicesPasscodeRequest:record.uuid error:&error];
68 record.lastCloudServicesTriggerTime = (uint64_t) ([[NSDate date] timeIntervalSince1970] * 1000);
69
70 if(!cachedCert || error) {
71 secerror("escrowrequest: cloudservices reports an issue caching the certificate, so we'll have to try again later: %@", error);
72 self.error = error;
73 // TODO: wait for network?
74 self.nextState = EscrowRequestStateNothingToDo;
75
76 NSError* saveCacheTimeError = nil;
77 [record saveToKeychain:&saveCacheTimeError];
78 if(saveCacheTimeError) {
79 secerror("escrowrequest: unable to save the last attempt time: %@", saveCacheTimeError);
80 }
81
82 return;
83 }
84
85 record.certCached = true;
86 [record saveToKeychain:&error];
87
88 if(error) {
89 // Ignore this error, since we've successfully triggered the update. We'll probably re-cache the certificate later, but that's okay.
90 secerror("escrowrequest: unable to save escrow update request certificate status, so we'll have to try again later: %@", error);
91 self.error = error;
92
93 if([self.lockStateTracker isLockedError:error]) {
94 secnotice("escrowrequest", "Trying again after unlock");
95 self.nextState = EscrowRequestStateWaitForUnlock;
96 } else {
97 self.nextState = EscrowRequestStateNothingToDo;
98 }
99
100 return;
101 }
102
103 secnotice("escrowrequest", "CloudService successfully cached a certificate; request is ready for passcode");
104 self.nextState = EscrowRequestStateNothingToDo;
105 }
106
107 // Separated into a class method for mocking
108 // Returns any cert that CS has cached
109 + (NSData* _Nullable)triggerCloudServicesPasscodeRequest:(NSString*)uuid error:(NSError**)error
110 {
111 SecureBackup* sb = [[SecureBackup alloc] init];
112
113 NSError* localError = nil;
114 SecureBackupBeginPasscodeRequestResults* results = [sb beginHSA2PasscodeRequest:true
115 uuid:uuid
116 error:error];
117
118 if(!results || localError) {
119 secerror("escrowrequest: unable to begin passcode request: %@", localError);
120 if(error) {
121 *error = localError;
122 }
123 return nil;
124 }
125
126 if(!results.cert) {
127 secerror("escrowrequest: sbd failed to cache a certificate");
128 // TODO fill in error
129 return nil;
130 }
131
132 return results.cert;
133 }
134
135 @end