]> git.saurik.com Git - apple/security.git/blob - keychain/ot/OTCheckHealthOperation.m
Security-59306.101.1.tar.gz
[apple/security.git] / keychain / ot / OTCheckHealthOperation.m
1 /*
2 * Copyright (c) 2019 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #if OCTAGON
25
26 #import "keychain/ot/OTCheckHealthOperation.h"
27 #import "keychain/ot/OTOperationDependencies.h"
28 #import "keychain/ot/OTStates.h"
29 #import "keychain/ot/ObjCImprovements.h"
30 #import "keychain/TrustedPeersHelper/TrustedPeersHelperProtocol.h"
31 #import <Security/SecInternalReleasePriv.h>
32
33 #if !TARGET_OS_SIMULATOR
34 #import <MobileKeyBag/MobileKeyBag.h>
35 #endif
36
37 #if TARGET_OS_MAC && !TARGET_OS_SIMULATOR
38 #include <unistd.h>
39 #endif
40
41 @interface OTCheckHealthOperation ()
42 @property OTOperationDependencies* deps;
43
44 @property NSOperation* finishOp;
45 @property BOOL requiresEscrowCheck;
46 @end
47
48 @implementation OTCheckHealthOperation
49 @synthesize intendedState = _intendedState;
50
51 - (instancetype)initWithDependencies:(OTOperationDependencies*)dependencies
52 intendedState:(OctagonState*)intendedState
53 errorState:(OctagonState*)errorState
54 deviceInfo:(nonnull OTDeviceInformation *)deviceInfo
55 skipRateLimitedCheck:(BOOL)skipRateLimitedCheck
56 {
57 if((self = [super init])) {
58 _deps = dependencies;
59 _intendedState = intendedState;
60 _nextState = errorState;
61 _postRepairCFU = NO;
62 _postEscrowCFU = NO;
63 _resetOctagon = NO;
64 _leaveTrust = NO;
65 _skipRateLimitingCheck = skipRateLimitedCheck;
66 }
67 return self;
68 }
69
70 - (BOOL) checkIfPasscodeIsSetForDevice
71 {
72 BOOL passcodeIsSet = NO;
73 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
74 int lockState = MKBGetDeviceLockState(NULL);
75 if (lockState != kMobileKeyBagDisabled && lockState >= 0) {
76 passcodeIsSet = YES;
77 }
78 #elif TARGET_OS_MAC && !TARGET_OS_SIMULATOR
79 NSDictionary *options = @{ (id)kKeyBagDeviceHandle : [[NSNumber alloc] initWithInt: getuid()] };
80 int lockState = MKBGetDeviceLockState((__bridge CFDictionaryRef)(options));
81 if (lockState != kMobileKeyBagDisabled && lockState >= 0) {
82 passcodeIsSet = YES;
83 }
84 #else
85 #endif
86 return passcodeIsSet;
87 }
88
89 - (void)groupStart
90 {
91 secnotice("octagon-health", "Beginning cuttlefish health checkup");
92
93 self.finishOp = [[NSOperation alloc] init];
94 [self dependOnBeforeGroupFinished:self.finishOp];
95
96 if(self.skipRateLimitingCheck == NO) {
97 secnotice("octagon-health", "running rate limiting checks!");
98 NSDate* lastUpdate = nil;
99 NSError* accountLoadError = nil;
100 self.error = nil;
101
102 lastUpdate = [self.deps.stateHolder lastHealthCheckupDate:&accountLoadError];
103
104 CKKSViewManager* viewManager = self.deps.viewManager;
105 if([viewManager.lockStateTracker isLockedError: accountLoadError]) {
106 secnotice("octagon-health", "device is locked, not performing cuttlefish check");
107 [self runBeforeGroupFinished:self.finishOp];
108 return;
109 }
110 secnotice("octagon-health", "last health check timestamp: %@", lastUpdate);
111
112 // Only query cuttlefish for trust status every 3 days (1 day for internal installs)
113 NSDateComponents* offset = [[NSDateComponents alloc] init];
114 if(SecIsInternalRelease()) {
115 [offset setHour:-23];
116 } else {
117 [offset setHour:-3*24];
118 }
119 NSDate *now = [NSDate date];
120 NSDate* deadline = [[NSCalendar currentCalendar] dateByAddingComponents:offset toDate:now options:0];
121
122 if(lastUpdate == nil || [lastUpdate compare: deadline] == NSOrderedAscending) {
123 secnotice("octagon-health", "Not rate-limiting: last updated %@ vs %@", lastUpdate, deadline);
124 } else {
125 secnotice("octagon-health", "Last update is within 3 days (%@); rate-limiting this operation", lastUpdate);
126 NSString *description = [NSString stringWithFormat:@"Rate-limited the OTCheckHealthOperation:%@", lastUpdate];
127 NSError *rateLimitedError = [NSError errorWithDomain:@"securityd"
128 code:errSecInternalError
129 userInfo:@{NSLocalizedDescriptionKey: description}];
130 secnotice("octagon-health", "rate limited! %@", rateLimitedError);
131 self.nextState = self.intendedState; //not setting the error on the results op as I don't want a CFU posted.
132 [self runBeforeGroupFinished:self.finishOp];
133 return;
134 }
135 NSError* persistedError = nil;
136 BOOL persisted = [self.deps.stateHolder persistLastHealthCheck:now error:&persistedError];
137
138 if([viewManager.lockStateTracker isLockedError: persistedError]) {
139 secnotice("octagon-health", "device is locked, not performing cuttlefish check");
140 [self runBeforeGroupFinished:self.finishOp];
141 return;
142 }
143 if(persisted == NO || persistedError) {
144 secerror("octagon-health: failed to persist last health check value:%@", persistedError);
145 [self runBeforeGroupFinished:self.finishOp];
146 return;
147 }
148 } else {
149 secnotice("octagon-health", "NOT running rate limiting checks!");
150 }
151 WEAKIFY(self);
152
153 [self.deps.cuttlefishXPCWrapper requestHealthCheckWithContainer:self.deps.containerName
154 context:self.deps.contextID
155 requiresEscrowCheck: [self checkIfPasscodeIsSetForDevice]
156 reply:^(BOOL postRepairCFU, BOOL postEscrowCFU, BOOL resetOctagon, BOOL leaveTrust, NSError *error) {
157 STRONGIFY(self);
158 if(error) {
159 secerror("octagon-health: error: %@", error);
160 self.error = error;
161
162 [self runBeforeGroupFinished:self.finishOp];
163 return;
164 } else {
165 secnotice("octagon-health", "cuttlefish came back with these suggestions\n: post repair? %d\n, post escrow? %d\n, reset octagon? %d\n leave trust? %d\n", postRepairCFU, postEscrowCFU, resetOctagon, leaveTrust);
166 [self handleRepairSuggestions:postRepairCFU
167 postEscrowCFU:postEscrowCFU
168 resetOctagon:resetOctagon
169 leaveTrust:leaveTrust];
170 }
171 }];
172 }
173
174 - (void)handleRepairSuggestions:(BOOL)postRepairCFU postEscrowCFU:(BOOL)postEscrowCFU resetOctagon:(BOOL)resetOctagon leaveTrust:(BOOL)leaveTrust
175 {
176 self.postEscrowCFU = postEscrowCFU;
177 self.postRepairCFU = postRepairCFU;
178 self.resetOctagon = resetOctagon;
179 self.leaveTrust = leaveTrust;
180
181 if (resetOctagon) {
182 secnotice("octagon-health", "Resetting Octagon as per Cuttlefish request");
183 self.nextState = OctagonStateHealthCheckReset;
184 } else if(leaveTrust) {
185 secnotice("octagon-health", "Leaving clique as per Cuttlefish request");
186 self.nextState = OctagonStateHealthCheckLeaveClique;
187 } else {
188 self.nextState = self.intendedState;
189 }
190
191 [self runBeforeGroupFinished:self.finishOp];
192 }
193
194 @end
195
196 #endif // OCTAGON