]> git.saurik.com Git - apple/security.git/blob - keychain/ot/OTCheckHealthOperation.m
Security-59306.61.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 _skipRateLimitingCheck = skipRateLimitedCheck;
65 }
66 return self;
67 }
68
69 - (BOOL) checkIfPasscodeIsSetForDevice
70 {
71 BOOL passcodeIsSet = NO;
72 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
73 int lockState = MKBGetDeviceLockState(NULL);
74 if (lockState != kMobileKeyBagDisabled && lockState >= 0) {
75 passcodeIsSet = YES;
76 }
77 #elif TARGET_OS_MAC && !TARGET_OS_SIMULATOR
78 NSDictionary *options = @{ (id)kKeyBagDeviceHandle : [[NSNumber alloc] initWithInt: getuid()] };
79 int lockState = MKBGetDeviceLockState((__bridge CFDictionaryRef)(options));
80 if (lockState != kMobileKeyBagDisabled && lockState >= 0) {
81 passcodeIsSet = YES;
82 }
83 #else
84 #endif
85 return passcodeIsSet;
86 }
87
88 - (void)groupStart
89 {
90 secnotice("octagon-health", "Beginning cuttlefish health checkup");
91
92 self.finishOp = [[NSOperation alloc] init];
93 [self dependOnBeforeGroupFinished:self.finishOp];
94
95 if(self.skipRateLimitingCheck == NO) {
96 secnotice("octagon-health", "running rate limiting checks!");
97 NSDate* lastUpdate = nil;
98 NSError* accountLoadError = nil;
99 self.error = nil;
100
101 lastUpdate = [self.deps.stateHolder lastHealthCheckupDate:&accountLoadError];
102 if([self.deps.viewManager.lockStateTracker isLockedError: accountLoadError]) {
103 secnotice("octagon-health", "device is locked, not performing cuttlefish check");
104 [self runBeforeGroupFinished:self.finishOp];
105 return;
106 }
107 secnotice("octagon-health", "last health check timestamp: %@", lastUpdate);
108
109 // Only query cuttlefish for trust status every 3 days (1 day for internal installs)
110 NSDateComponents* offset = [[NSDateComponents alloc] init];
111 if(SecIsInternalRelease()) {
112 [offset setHour:-23];
113 } else {
114 [offset setHour:-3*24];
115 }
116 NSDate *now = [NSDate date];
117 NSDate* deadline = [[NSCalendar currentCalendar] dateByAddingComponents:offset toDate:now options:0];
118
119 if(lastUpdate == nil || [lastUpdate compare: deadline] == NSOrderedAscending) {
120 secnotice("octagon-health", "Not rate-limiting: last updated %@ vs %@", lastUpdate, deadline);
121 } else {
122 secnotice("octagon-health", "Last update is within 3 days (%@); rate-limiting this operation", lastUpdate);
123 NSString *description = [NSString stringWithFormat:@"Rate-limited the OTCheckHealthOperation:%@", lastUpdate];
124 NSError *rateLimitedError = [NSError errorWithDomain:@"securityd"
125 code:errSecInternalError
126 userInfo:@{NSLocalizedDescriptionKey: description}];
127 secnotice("octagon-health", "rate limited! %@", rateLimitedError);
128 self.nextState = self.intendedState; //not setting the error on the results op as I don't want a CFU posted.
129 [self runBeforeGroupFinished:self.finishOp];
130 return;
131 }
132 NSError* persistedError = nil;
133 BOOL persisted = [self.deps.stateHolder persistLastHealthCheck:now error:&persistedError];
134
135 if([self.deps.viewManager.lockStateTracker isLockedError: persistedError]) {
136 secnotice("octagon-health", "device is locked, not performing cuttlefish check");
137 [self runBeforeGroupFinished:self.finishOp];
138 return;
139 }
140 if(persisted == NO || persistedError) {
141 secerror("octagon-health: failed to persist last health check value:%@", persistedError);
142 [self runBeforeGroupFinished:self.finishOp];
143 return;
144 }
145 } else {
146 secnotice("octagon-health", "NOT running rate limiting checks!");
147 }
148 WEAKIFY(self);
149
150 [self.deps.cuttlefishXPCWrapper requestHealthCheckWithContainer:self.deps.containerName
151 context:self.deps.contextID
152 requiresEscrowCheck: [self checkIfPasscodeIsSetForDevice]
153 reply:^(BOOL postRepairCFU, BOOL postEscrowCFU, BOOL resetOctagon, NSError *error) {
154 STRONGIFY(self);
155 if(error) {
156 secerror("octagon-health: error: %@", error);
157 self.error = error;
158
159 [self runBeforeGroupFinished:self.finishOp];
160 return;
161 } else {
162 secnotice("octagon-health", "cuttlefish came back with these suggestions\n: post repair? %d\n, post escrow? %d\n, reset octagon? %d", postRepairCFU, postEscrowCFU, resetOctagon);
163 [self handleRepairSuggestions:postRepairCFU
164 postEscrowCFU:postEscrowCFU
165 resetOctagon:resetOctagon];
166 }
167 }];
168 }
169
170 - (void)handleRepairSuggestions:(BOOL)postRepairCFU postEscrowCFU:(BOOL)postEscrowCFU resetOctagon:(BOOL)resetOctagon
171 {
172 self.postEscrowCFU = postEscrowCFU;
173 self.postRepairCFU = postRepairCFU;
174 self.resetOctagon = resetOctagon;
175
176 if (resetOctagon) {
177 secnotice("octagon-health", "Resetting Octagon as per Cuttlefish request");
178 self.nextState = OctagonStateHealthCheckReset;
179 } else {
180 self.nextState = self.intendedState;
181 }
182
183 [self runBeforeGroupFinished:self.finishOp];
184 }
185
186 @end
187
188 #endif // OCTAGON