]> git.saurik.com Git - apple/security.git/blob - keychain/ot/OTCheckHealthOperation.m
Security-59306.11.20.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/ObjCImprovements.h"
29 #import "keychain/TrustedPeersHelper/TrustedPeersHelperProtocol.h"
30 #import <Security/SecInternalReleasePriv.h>
31
32 #if !TARGET_OS_SIMULATOR
33 #import <MobileKeyBag/MobileKeyBag.h>
34 #endif
35
36 #if TARGET_OS_MAC && !TARGET_OS_SIMULATOR
37 #include <unistd.h>
38 #endif
39
40 @interface OTCheckHealthOperation ()
41 @property OTOperationDependencies* deps;
42
43 @property NSOperation* finishOp;
44 @property BOOL requiresEscrowCheck;
45 @end
46
47 @implementation OTCheckHealthOperation
48 @synthesize intendedState = _intendedState;
49
50 - (instancetype)initWithDependencies:(OTOperationDependencies*)dependencies
51 intendedState:(OctagonState*)intendedState
52 errorState:(OctagonState*)errorState
53 deviceInfo:(nonnull OTDeviceInformation *)deviceInfo
54 skipRateLimitedCheck:(BOOL)skipRateLimitedCheck
55 {
56 if((self = [super init])) {
57 _deps = dependencies;
58 _intendedState = intendedState;
59 _nextState = errorState;
60 _postRepairCFU = NO;
61 _postEscrowCFU = NO;
62 _resetOctagon = NO;
63 _skipRateLimitingCheck = skipRateLimitedCheck;
64 }
65 return self;
66 }
67
68 - (BOOL) checkIfPasscodeIsSetForDevice
69 {
70 BOOL passcodeIsSet = NO;
71 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
72 int lockState = MKBGetDeviceLockState(NULL);
73 if (lockState != kMobileKeyBagDisabled && lockState >= 0) {
74 passcodeIsSet = YES;
75 }
76 #elif TARGET_OS_MAC && !TARGET_OS_SIMULATOR
77 NSDictionary *options = @{ (id)kKeyBagDeviceHandle : [[NSNumber alloc] initWithInt: getuid()] };
78 int lockState = MKBGetDeviceLockState((__bridge CFDictionaryRef)(options));
79 if (lockState != kMobileKeyBagDisabled && lockState >= 0) {
80 passcodeIsSet = YES;
81 }
82 #else
83 #endif
84 return passcodeIsSet;
85 }
86
87 - (void)groupStart
88 {
89 secnotice("octagon-health", "Beginning cuttlefish health checkup");
90
91 self.finishOp = [[NSOperation alloc] init];
92 [self dependOnBeforeGroupFinished:self.finishOp];
93
94 if(self.skipRateLimitingCheck == NO) {
95 secnotice("octagon-health", "running rate limiting checks!");
96 NSDate* lastUpdate = nil;
97 NSError* accountLoadError = nil;
98 self.error = nil;
99
100 lastUpdate = [self.deps.stateHolder lastHealthCheckupDate:&accountLoadError];
101 if([self.deps.viewManager.lockStateTracker isLockedError: accountLoadError]) {
102 secnotice("octagon-health", "device is locked, not performing cuttlefish check");
103 [self runBeforeGroupFinished:self.finishOp];
104 return;
105 }
106 secnotice("octagon-health", "last health check timestamp: %@", lastUpdate);
107
108 // Only query cuttlefish for trust status every 3 days (1 day for internal installs)
109 NSDateComponents* offset = [[NSDateComponents alloc] init];
110 if(SecIsInternalRelease()) {
111 [offset setHour:-23];
112 } else {
113 [offset setHour:-3*24];
114 }
115 NSDate *now = [NSDate date];
116 NSDate* deadline = [[NSCalendar currentCalendar] dateByAddingComponents:offset toDate:now options:0];
117
118 if(lastUpdate == nil || [lastUpdate compare: deadline] == NSOrderedAscending) {
119 secnotice("octagon-health", "Not rate-limiting: last updated %@ vs %@", lastUpdate, deadline);
120 } else {
121 secnotice("octagon-health", "Last update is within 3 days (%@); rate-limiting this operation", lastUpdate);
122 NSString *description = [NSString stringWithFormat:@"Rate-limited the OTCheckHealthOperation:%@", lastUpdate];
123 NSError *rateLimitedError = [NSError errorWithDomain:@"securityd"
124 code:errSecInternalError
125 userInfo:@{NSLocalizedDescriptionKey: description}];
126 secnotice("octagon-health", "rate limited! %@", rateLimitedError);
127 self.nextState = self.intendedState; //not setting the error on the results op as I don't want a CFU posted.
128 [self runBeforeGroupFinished:self.finishOp];
129 return;
130 }
131 NSError* persistedError = nil;
132 BOOL persisted = [self.deps.stateHolder persistLastHealthCheck:now error:&persistedError];
133
134 if([self.deps.viewManager.lockStateTracker isLockedError: persistedError]) {
135 secnotice("octagon-health", "device is locked, not performing cuttlefish check");
136 [self runBeforeGroupFinished:self.finishOp];
137 return;
138 }
139 if(persisted == NO || persistedError) {
140 secerror("octagon-health: failed to persist last health check value:%@", persistedError);
141 [self runBeforeGroupFinished:self.finishOp];
142 return;
143 }
144 } else {
145 secnotice("octagon-health", "NOT running rate limiting checks!");
146 }
147 WEAKIFY(self);
148
149 [[self.deps.cuttlefishXPC remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
150 STRONGIFY(self);
151 secerror("octagon-health: Can't talk with TrustedPeersHelper: %@", error);
152 self.error = error;
153 [self runBeforeGroupFinished:self.finishOp];
154 return;
155
156 }] requestHealthCheckWithContainer:self.deps.containerName
157 context:self.deps.contextID
158 requiresEscrowCheck: [self checkIfPasscodeIsSetForDevice]
159 reply:^(BOOL postRepairCFU, BOOL postEscrowCFU, BOOL resetOctagon, NSError *error) {
160 STRONGIFY(self);
161 if(error) {
162 secerror("octagon-health: error: %@", error);
163 self.error = error;
164 } else {
165 secnotice("octagon-health", "cuttlefish came back with these suggestions\n: post repair? %d\n, post escrow? %d\n, reset octagon? %d", postRepairCFU, postEscrowCFU, resetOctagon);
166 self.postEscrowCFU = postEscrowCFU;
167 self.postRepairCFU = postRepairCFU;
168 self.resetOctagon = resetOctagon;
169
170 self.nextState = self.intendedState;
171 }
172 [self runBeforeGroupFinished:self.finishOp];
173 }];
174 }
175
176 @end
177
178 #endif // OCTAGON