]> git.saurik.com Git - apple/security.git/blob - keychain/ot/OctagonCheckTrustStateOperation.m
Security-59754.41.1.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.cuttlefishXPCWrapper fetchTrustStateWithContainer:self.deps.containerName
49 context:self.deps.contextID
50 reply:^(TrustedPeersHelperPeerState * _Nullable selfPeerState,
51 NSArray<TrustedPeersHelperPeer *> * _Nullable trustedPeers,
52 NSError * _Nullable error) {
53 STRONGIFY(self);
54 if(error || !selfPeerState || !trustedPeers) {
55 secerror("octagon: TPH was unable to determine current peer state: %@", error);
56 self.error = error;
57 self.nextState = OctagonStateError;
58 [self runBeforeGroupFinished:self.finishedOp];
59
60 } else {
61 [self afterTPHTrustState:selfPeerState trustedPeers:trustedPeers];
62 }
63 }];
64 }
65
66 - (void)afterTPHTrustState:(TrustedPeersHelperPeerState*)peerState
67 trustedPeers:(NSArray<TrustedPeersHelperPeer *> *)trustedPeers
68 {
69 NSError* localError = nil;
70
71 if (peerState.memberChanges) {
72 secnotice("octagon", "Member list changed");
73 [self.deps.octagonAdapter sendTrustedPeerSetChangedUpdate];
74 }
75
76 bool changedCurrentAccountMetadata = false;
77 OTAccountMetadataClassC* currentAccountMetadata = [self.deps.stateHolder loadOrCreateAccountMetadata:&localError];
78
79 if(localError) {
80 if([self.deps.lockStateTracker isLockedError:localError]) {
81 secerror("octagon-consistency: Unable to fetch current account state due to lock state: %@", localError);
82 self.nextState = OctagonStateWaitForClassCUnlock;
83 [self runBeforeGroupFinished:self.finishedOp];
84 return;
85 }
86
87 secerror("octagon-consistency: Unable to fetch current account state. Can't ensure consistency: %@", localError);
88 [self runBeforeGroupFinished:self.finishedOp];
89 return;
90 }
91
92 // What state does TPH think it's in?
93 // This code is slightly duplicated with rpcTrustStatus; we should probably fix that
94 OTAccountMetadataClassC_TrustState trustState = OTAccountMetadataClassC_TrustState_UNKNOWN;
95 if(peerState.peerStatus & TPPeerStatusExcluded) {
96 trustState = OTAccountMetadataClassC_TrustState_UNTRUSTED;
97
98 } else if(peerState.peerStatus & TPPeerStatusUnknown) {
99 trustState = OTAccountMetadataClassC_TrustState_UNTRUSTED;
100
101 } else if((peerState.peerStatus & TPPeerStatusSelfTrust) ||
102 (peerState.peerStatus & TPPeerStatusFullyReciprocated) ||
103 (peerState.peerStatus & TPPeerStatusPartiallyReciprocated)) {
104 trustState = OTAccountMetadataClassC_TrustState_TRUSTED;
105 }
106
107 secnotice("octagon-consistency", "TPH's trust state (%@) is considered %@",
108 TPPeerStatusToString(peerState.peerStatus),
109 OTAccountMetadataClassC_TrustStateAsString(trustState));
110
111 if(trustState == currentAccountMetadata.trustState) {
112 secnotice("octagon-consistency", "TPH peer status matches cache: (%@)", TPPeerStatusToString(peerState.peerStatus));
113 [[CKKSAnalytics logger] logSuccessForEventNamed:OctagonEventCheckTrustState];
114 } else {
115 secerror("octagon-consistency: Locally cached status (%@) does not match TPH's current peer status (%@)",
116 OTAccountMetadataClassC_TrustStateAsString(currentAccountMetadata.trustState),
117 OTAccountMetadataClassC_TrustStateAsString(trustState));
118
119 if (currentAccountMetadata.trustState == OTAccountMetadataClassC_TrustState_TRUSTED && trustState == OTAccountMetadataClassC_TrustState_UNTRUSTED) {
120 [[CKKSAnalytics logger] logHardFailureForEventNamed:OctagonEventCheckTrustState withAttributes:nil];
121 }
122
123 currentAccountMetadata.trustState = trustState;
124 changedCurrentAccountMetadata = true;
125 }
126
127 if([peerState.peerID isEqualToString:currentAccountMetadata.peerID] ||
128 (peerState.peerID == nil && currentAccountMetadata.peerID == nil)) {
129 secnotice("octagon-consistency", "TPH peer ID matches cache: (%@)", peerState.peerID);
130 } else {
131 secerror("octagon-consistency: Locally cached peer ID (%@) does not match TPH's current peer ID (%@)",
132 currentAccountMetadata.peerID,
133 peerState.peerID);
134
135 currentAccountMetadata.peerID = peerState.peerID;
136 changedCurrentAccountMetadata = true;
137 }
138
139 if(changedCurrentAccountMetadata) {
140
141 NSError* localError = nil;
142 BOOL persisted = [self.deps.stateHolder persistAccountChanges:^OTAccountMetadataClassC * _Nonnull(OTAccountMetadataClassC * _Nonnull metadata) {
143 metadata.trustState = currentAccountMetadata.trustState;
144 metadata.peerID = currentAccountMetadata.peerID;
145 return metadata;
146 } error:&localError];
147
148 if(!persisted || localError) {
149 if([self.deps.lockStateTracker isLockedError:localError]) {
150 secerror("octagon-consistency: Unable to save new account state due to lock state: %@", localError);
151 self.nextState = OctagonStateWaitForClassCUnlock;
152 [self runBeforeGroupFinished:self.finishedOp];
153 return;
154 }
155
156 secerror("octagon-consistency: Unable to save new account state. Erroring: %@", localError);
157 self.error = localError;
158 self.nextState = OctagonStateError;
159 [self runBeforeGroupFinished:self.finishedOp];
160 return;
161 }
162
163 secnotice("octagon-consistency", "Saved new account metadata");
164 }
165
166 // See if we possibly should do an update because peer data in Octagon
167 // is different from local state, ie we upgraded
168 if (peerState.osVersion) {
169 NSString *osVersion = self.deps.deviceInformationAdapter.osVersion;
170 if (osVersion && ![osVersion isEqualToString:peerState.osVersion]) {
171 OctagonPendingFlag *pendingFlag = [[OctagonPendingFlag alloc] initWithFlag:OctagonFlagCuttlefishNotification
172 conditions:OctagonPendingConditionsDeviceUnlocked];
173 [self.deps.flagHandler handlePendingFlag:pendingFlag];
174 }
175 }
176
177 // And determine where to go from here!
178
179 if(currentAccountMetadata.peerID && currentAccountMetadata.trustState == OTAccountMetadataClassC_TrustState_TRUSTED) {
180 secnotice("octagon", "Appear to be trusted for peer %@; ensuring correct state", currentAccountMetadata.peerID);
181 self.nextState = OctagonStateEnsureConsistency;
182
183 } else if(self.deps.sosAdapter.sosEnabled &&
184 currentAccountMetadata.trustState != OTAccountMetadataClassC_TrustState_TRUSTED &&
185 OctagonPerformSOSUpgrade()) {
186 secnotice("octagon", "Have iCloud account but not trusted in Octagon yet: %@; attempting SOS upgrade",
187 [currentAccountMetadata trustStateAsString:currentAccountMetadata.trustState]);
188
189 self.nextState = OctagonStateAttemptSOSUpgrade;
190
191 } else if(currentAccountMetadata.trustState != OTAccountMetadataClassC_TrustState_TRUSTED) {
192 secnotice("octagon", "Have iCloud account but not trusted in Octagon (%@)",
193 OTAccountMetadataClassC_TrustStateAsString(currentAccountMetadata.trustState));
194 #if TARGET_OS_WATCH
195 self.nextState = OctagonStateStartCompanionPairing;
196 #else
197 self.nextState = OctagonStateBecomeUntrusted;
198 #endif
199 } else {
200 secnotice("octagon", "Unknown trust state (%@). Assuming untrusted...",
201 OTAccountMetadataClassC_TrustStateAsString(currentAccountMetadata.trustState));
202 self.nextState = OctagonStateBecomeUntrusted;
203 }
204
205 [self runBeforeGroupFinished:self.finishedOp];
206 }
207
208 @end
209
210 #endif // OCTAGON