]> git.saurik.com Git - apple/security.git/blob - keychain/ot/OctagonCheckTrustStateOperation.m
Security-59306.11.20.tar.gz
[apple/security.git] / keychain / ot / OctagonCheckTrustStateOperation.m
1
2 #if OCTAGON
3
4 #import "utilities/debugging.h"
5
6 #import "keychain/ot/OTConstants.h"
7 #import "keychain/ot/OctagonCheckTrustStateOperation.h"
8 #import "keychain/ot/OctagonCKKSPeerAdapter.h"
9 #import "keychain/ot/OTCuttlefishAccountStateHolder.h"
10 #import "keychain/ot/OTFetchCKKSKeysOperation.h"
11 #import "keychain/ot/OTStates.h"
12 #import "keychain/ckks/CKKSCurrentKeyPointer.h"
13 #import "keychain/ckks/CKKSKeychainView.h"
14
15 #import "keychain/ot/categories/OTAccountMetadataClassC+KeychainSupport.h"
16 #import "keychain/TrustedPeersHelper/TrustedPeersHelperProtocol.h"
17 #import "keychain/ot/ObjCImprovements.h"
18
19 @interface OctagonCheckTrustStateOperation ()
20 @property OTOperationDependencies* deps;
21
22 @property NSOperation* finishedOp;
23 @end
24
25 @implementation OctagonCheckTrustStateOperation
26 @synthesize intendedState = _intendedState;
27
28 - (instancetype)initWithDependencies:(OTOperationDependencies*)dependencies
29 intendedState:(OctagonState*)intendedState
30 errorState:(OctagonState*)errorState
31 {
32 if((self = [super init])) {
33 _deps = dependencies;
34
35 _intendedState = intendedState;
36 _nextState = errorState;
37 }
38 return self;
39 }
40
41 - (void)groupStart
42 {
43 self.finishedOp = [[NSOperation alloc] init];
44 [self dependOnBeforeGroupFinished:self.finishedOp];
45
46 // Make sure we're in agreement with TPH about _which_ peer ID we should have
47 WEAKIFY(self);
48 [[self.deps.cuttlefishXPC remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
49 STRONGIFY(self);
50 secerror("octagon: Can't talk with TrustedPeersHelper, can't ensure check trust state: %@", error);
51 self.error = error;
52 [self runBeforeGroupFinished:self.finishedOp];
53
54 }] fetchTrustStateWithContainer:self.deps.containerName
55 context:self.deps.contextID
56 reply:^(TrustedPeersHelperPeerState * _Nullable selfPeerState,
57 NSArray<TrustedPeersHelperPeer *> * _Nullable trustedPeers,
58 NSError * _Nullable error) {
59 STRONGIFY(self);
60 if(error || !selfPeerState || !trustedPeers) {
61 secerror("octagon: TPH was unable to determine current peer state: %@", error);
62 self.error = error;
63 self.nextState = OctagonStateError;
64 [self runBeforeGroupFinished:self.finishedOp];
65
66 } else {
67 [self afterTPHTrustState:selfPeerState trustedPeers:trustedPeers];
68 }
69 }];
70 }
71
72 - (void)afterTPHTrustState:(TrustedPeersHelperPeerState*)peerState
73 trustedPeers:(NSArray<TrustedPeersHelperPeer *> *)trustedPeers
74 {
75 NSError* localError = nil;
76
77 if (peerState.memberChanges) {
78 secnotice("octagon", "Member list changed");
79 [self.deps.octagonAdapter sendTrustedPeerSetChangedUpdate];
80 }
81
82 bool changedCurrentAccountMetadata = false;
83 OTAccountMetadataClassC* currentAccountMetadata = [self.deps.stateHolder loadOrCreateAccountMetadata:&localError];
84
85 if(localError) {
86 if([self.deps.lockStateTracker isLockedError:localError]) {
87 secerror("octagon-consistency: Unable to fetch current account state due to lock state: %@", localError);
88 self.nextState = OctagonStateWaitForUnlock;
89 [self runBeforeGroupFinished:self.finishedOp];
90 return;
91 }
92
93 secerror("octagon-consistency: Unable to fetch current account state. Can't ensure consistency: %@", localError);
94 [self runBeforeGroupFinished:self.finishedOp];
95 return;
96 }
97
98 // What state does TPH think it's in?
99 // This code is slightly duplicated with rpcTrustStatus; we should probably fix that
100 OTAccountMetadataClassC_TrustState trustState = OTAccountMetadataClassC_TrustState_UNKNOWN;
101 if(peerState.peerStatus & TPPeerStatusExcluded) {
102 trustState = OTAccountMetadataClassC_TrustState_UNTRUSTED;
103
104 } else if(peerState.peerStatus & TPPeerStatusUnknown) {
105 trustState = OTAccountMetadataClassC_TrustState_UNTRUSTED;
106
107 } else if((peerState.peerStatus & TPPeerStatusSelfTrust) ||
108 (peerState.peerStatus & TPPeerStatusFullyReciprocated) ||
109 (peerState.peerStatus & TPPeerStatusPartiallyReciprocated)) {
110 trustState = OTAccountMetadataClassC_TrustState_TRUSTED;
111 }
112
113 secnotice("octagon-consistency", "TPH's trust state (%@) is considered %@",
114 TPPeerStatusToString(peerState.peerStatus),
115 OTAccountMetadataClassC_TrustStateAsString(trustState));
116
117 if(trustState == currentAccountMetadata.trustState) {
118 secnotice("octagon-consistency", "TPH peer status matches cache: (%@)", TPPeerStatusToString(peerState.peerStatus));
119 [[CKKSAnalytics logger] logSuccessForEventNamed:OctagonEventCheckTrustState];
120 } else {
121 secerror("octagon-consistency: Locally cached status (%@) does not match TPH's current peer status (%@)",
122 OTAccountMetadataClassC_TrustStateAsString(currentAccountMetadata.trustState),
123 OTAccountMetadataClassC_TrustStateAsString(trustState));
124
125 if (currentAccountMetadata.trustState == OTAccountMetadataClassC_TrustState_TRUSTED && trustState == OTAccountMetadataClassC_TrustState_UNTRUSTED) {
126 [[CKKSAnalytics logger] logHardFailureForEventNamed:OctagonEventCheckTrustState withAttributes:nil];
127 }
128
129 currentAccountMetadata.trustState = trustState;
130 changedCurrentAccountMetadata = true;
131 }
132
133 if([peerState.peerID isEqualToString:currentAccountMetadata.peerID] ||
134 (peerState.peerID == nil && currentAccountMetadata.peerID == nil)) {
135 secnotice("octagon-consistency", "TPH peer ID matches cache: (%@)", peerState.peerID);
136 } else {
137 secerror("octagon-consistency: Locally cached peer ID (%@) does not match TPH's current peer ID (%@)",
138 currentAccountMetadata.peerID,
139 peerState.peerID);
140
141 currentAccountMetadata.peerID = peerState.peerID;
142 changedCurrentAccountMetadata = true;
143 }
144
145 if(changedCurrentAccountMetadata) {
146
147 NSError* localError = nil;
148 BOOL persisted = [self.deps.stateHolder persistAccountChanges:^OTAccountMetadataClassC * _Nonnull(OTAccountMetadataClassC * _Nonnull metadata) {
149 metadata.trustState = currentAccountMetadata.trustState;
150 metadata.peerID = currentAccountMetadata.peerID;
151 return metadata;
152 } error:&localError];
153
154 if(!persisted || localError) {
155 if([self.deps.lockStateTracker isLockedError:localError]) {
156 secerror("octagon-consistency: Unable to save new account state due to lock state: %@", localError);
157 self.nextState = OctagonStateWaitForUnlock;
158 [self runBeforeGroupFinished:self.finishedOp];
159 return;
160 }
161
162 secerror("octagon-consistency: Unable to save new account state. Erroring: %@", localError);
163 self.error = localError;
164 self.nextState = OctagonStateError;
165 [self runBeforeGroupFinished:self.finishedOp];
166 return;
167 }
168
169 secnotice("octagon-consistency", "Saved new account metadata");
170 }
171
172 // And determine where to go from here!
173
174 if(currentAccountMetadata.peerID && currentAccountMetadata.trustState == OTAccountMetadataClassC_TrustState_TRUSTED) {
175 secnotice("octagon", "Appear to be trusted for peer %@; ensuring correct state", currentAccountMetadata.peerID);
176 self.nextState = OctagonStateEnsureConsistency;
177
178 } else if(self.deps.sosAdapter.sosEnabled &&
179 currentAccountMetadata.trustState != OTAccountMetadataClassC_TrustState_TRUSTED &&
180 OctagonPerformSOSUpgrade()) {
181 secnotice("octagon", "Have iCloud account but not trusted in Octagon yet; inspecting SOS status: %@",
182 [currentAccountMetadata trustStateAsString:currentAccountMetadata.trustState]);
183
184 NSError* circleError = nil;
185 SOSCCStatus sosStatus = [self.deps.sosAdapter circleStatus:&circleError];
186
187 if(sosStatus == kSOSCCInCircle) {
188 secnotice("octagon", "SOS status is 'trusted'; requesting SOS upgrade");
189 [self.deps.flagHandler handleFlag:OctagonFlagAttemptSOSUpgrade];
190 } else {
191 secnotice("octagon", "SOS status is %d (error: %@)", (int)sosStatus, circleError);
192 }
193 self.nextState = OctagonStateBecomeUntrusted;
194
195 } else if(currentAccountMetadata.trustState != OTAccountMetadataClassC_TrustState_TRUSTED) {
196 secnotice("octagon", "Have iCloud account but not trusted in Octagon (%@)",
197 OTAccountMetadataClassC_TrustStateAsString(currentAccountMetadata.trustState));
198 #if TARGET_OS_WATCH
199 self.nextState = OctagonStateStartCompanionPairing;
200 #else
201 self.nextState = OctagonStateBecomeUntrusted;
202 #endif
203 } else {
204 secnotice("octagon", "Unknown trust state (%@). Assuming untrusted...",
205 OTAccountMetadataClassC_TrustStateAsString(currentAccountMetadata.trustState));
206 self.nextState = OctagonStateBecomeUntrusted;
207 }
208
209 [self runBeforeGroupFinished:self.finishedOp];
210 }
211
212 @end
213
214 #endif // OCTAGON