]> git.saurik.com Git - apple/security.git/blob - keychain/otctl/otctl.m
Security-59754.41.1.tar.gz
[apple/security.git] / keychain / otctl / otctl.m
1 //
2 // Security
3 //
4
5 #import <TargetConditionals.h>
6 #import <Foundation/Foundation.h>
7 #import <Security/SecInternalReleasePriv.h>
8 #import <Security/Security.h>
9 #import <err.h>
10 #import <OctagonTrust/OctagonTrust.h>
11
12 #import "keychain/otctl/OTControlCLI.h"
13 #import "keychain/otctl/EscrowRequestCLI.h"
14 #import "keychain/escrowrequest/Framework/SecEscrowRequest.h"
15 #include "lib/SecArgParse.h"
16 #include "utilities/debugging.h"
17
18 #if TARGET_OS_WATCH
19 #import "keychain/otpaird/OTPairingClient.h"
20 #endif /* TARGET_OS_WATCH */
21
22 static int start = false;
23 static int signIn = false;
24 static int signOut = false;
25 static int resetoctagon = false;
26 static int resetProtectedData = false;
27 static int userControllableViewsSyncStatus = false;
28
29 static int fetchAllBottles = false;
30 static int recover = false;
31 static int depart = false;
32
33 static int status = false;
34
35 static int er_trigger = false;
36 static int er_status = false;
37 static int er_reset = false;
38 static int er_store = false;
39 static int ckks_policy_flag = false;
40
41 static int ttr_flag = false;
42
43 static int fetch_escrow_records = false;
44 static int fetch_all_escrow_records = false;
45
46 static int recoverRecord = false;
47 static int recoverSilentRecord = false;
48
49 static int health = false;
50
51 #if TARGET_OS_WATCH
52 static int pairme = false;
53 #endif /* TARGET_OS_WATCH */
54
55 static char* bottleIDArg = NULL;
56 static char* contextNameArg = NULL;
57 static char* secretArg = NULL;
58 static char* skipRateLimitingCheckArg = NULL;
59 static char* recordID = NULL;
60
61 static int argEnable = false;
62 static int argPause = false;
63
64 static int json = false;
65
66 static char* altDSIDArg = NULL;
67 static char* containerStr = NULL;
68 static char* radarNumber = NULL;
69 static char* appleIDArg = NULL;
70 static char* dsidArg = NULL;
71
72 static void internalOnly(void)
73 {
74 if(!SecIsInternalRelease()) {
75 secnotice("octagon", "Tool not available on non internal builds");
76 errx(1, "Tool not available on non internal builds");
77 }
78 }
79
80 int main(int argc, char** argv)
81 {
82 static struct argument options[] = {
83 {.shortname = 's', .longname = "secret", .argument = &secretArg, .description = "escrow secret"},
84 {.shortname = 'e', .longname = "bottleID", .argument = &bottleIDArg, .description = "bottle record id"},
85 {.shortname = 'r', .longname = "skipRateLimiting", .argument = &skipRateLimitingCheckArg, .description = " enter values YES or NO, option defaults to NO, This gives you the opportunity to skip the rate limiting check when performing the cuttlefish health check"},
86 {.shortname = 'j', .longname = "json", .flag = &json, .flagval = true, .description = "Output in JSON"},
87 {.shortname = 'i', .longname = "recordID", .argument = &recordID, .flagval = true, .description = "recordID"},
88
89 {.shortname = 'E', .longname = "enable", .flag = &argEnable, .flagval = true, .description = "Enable something (pair with a modification command)"},
90 {.shortname = 'P', .longname = "pause", .flag = &argPause, .flagval = true, .description = "Pause something (pair with a modification command)"},
91
92 {.longname = "altDSID", .argument = &altDSIDArg, .description = "altDSID (for sign-in/out)"},
93 {.longname = "entropy", .argument = &secretArg, .description = "escrowed entropy in JSON"},
94
95 {.longname = "appleID", .argument = &appleIDArg, .description = "AppleID"},
96 {.longname = "dsid", .argument = &dsidArg, .description = "DSID"},
97
98 {.longname = "container", .argument = &containerStr, .description = "CloudKit container name"},
99 {.longname = "radar", .argument = &radarNumber, .description = "Radar number"},
100
101 {.command = "start", .flag = &start, .flagval = true, .description = "Start Octagon state machine"},
102 {.command = "sign-in", .flag = &signIn, .flagval = true, .description = "Inform Cuttlefish container of sign in"},
103 {.command = "sign-out", .flag = &signOut, .flagval = true, .description = "Inform Cuttlefish container of sign out"},
104 {.command = "status", .flag = &status, .flagval = true, .description = "Report Octagon status"},
105
106 {.command = "resetoctagon", .flag = &resetoctagon, .flagval = true, .description = "Reset and establish new Octagon trust"},
107 {.command = "resetProtectedData", .flag = &resetProtectedData, .flagval = true, .description = "Reset ProtectedData"},
108
109 {.command = "user-controllable-views", .flag = &userControllableViewsSyncStatus, .flagval = true, .description = "Modify or view user-controllable views status (If one of --enable or --pause is passed, will modify status)"},
110
111 {.command = "allBottles", .flag = &fetchAllBottles, .flagval = true, .description = "Fetch all viable bottles"},
112 {.command = "recover", .flag = &recover, .flagval = true, .description = "Recover using this bottle"},
113 {.command = "depart", .flag = &depart, .flagval = true, .description = "Depart from Octagon Trust"},
114
115 {.command = "er-trigger", .flag = &er_trigger, .flagval = true, .description = "Trigger an Escrow Request request"},
116 {.command = "er-status", .flag = &er_status, .flagval = true, .description = "Report status on any pending Escrow Request requests"},
117 {.command = "er-reset", .flag = &er_reset, .flagval = true, .description = "Delete all Escrow Request requests"},
118 {.command = "er-store", .flag = &er_store, .flagval = true, .description = "Store any pending Escrow Request prerecords"},
119
120 {.command = "health", .flag = &health, .flagval = true, .description = "Check Octagon Health status"},
121 {.command = "ckks-policy", .flag = &ckks_policy_flag, .flagval = true, .description = "Trigger a refetch of the CKKS policy"},
122
123 {.command = "taptoradar", .flag = &ttr_flag, .flagval = true, .description = "Trigger a TapToRadar"},
124
125 {.command = "fetchEscrowRecords", .flag = &fetch_escrow_records, .flagval = true, .description = "Fetch Escrow Records"},
126 {.command = "fetchAllEscrowRecords", .flag = &fetch_all_escrow_records, .flagval = true, .description = "Fetch All Escrow Records"},
127
128 {.command = "recover-record", .flag = &recoverRecord, .flagval = true, .description = "Recover record"},
129 {.command = "recover-record-silent", .flag = &recoverSilentRecord, .flagval = true, .description = "Silent record recovery"},
130
131
132
133 #if TARGET_OS_WATCH
134 {.command = "pairme", .flag = &pairme, .flagval = true, .description = "Perform pairing (watchOS only)"},
135 #endif /* TARGET_OS_WATCH */
136 {}};
137
138 static struct arguments args = {
139 .programname = "otctl",
140 .description = "Control and report on Octagon Trust",
141 .arguments = options,
142 };
143
144 if(!options_parse(argc, argv, &args)) {
145 printf("\n");
146 print_usage(&args);
147 return -1;
148 }
149
150 @autoreleasepool {
151 NSError* error = nil;
152
153 // Use a synchronous control object
154 OTControl* rpc = [OTControl controlObject:true error:&error];
155 if(error || !rpc) {
156 errx(1, "no OTControl failed: %s", [[error description] UTF8String]);
157 }
158
159 NSString* context = contextNameArg ? [NSString stringWithCString:contextNameArg encoding:NSUTF8StringEncoding] : OTDefaultContext;
160 NSString* container = containerStr ? [NSString stringWithCString:containerStr encoding:NSUTF8StringEncoding] : nil;
161 NSString* altDSID = altDSIDArg ? [NSString stringWithCString:altDSIDArg encoding:NSUTF8StringEncoding] : nil;
162 NSString* dsid = dsidArg ? [NSString stringWithCString:dsidArg encoding:NSUTF8StringEncoding] : nil;
163 NSString* appleID = appleIDArg ? [NSString stringWithCString:appleIDArg encoding:NSUTF8StringEncoding] : nil;
164
165 NSString* skipRateLimitingCheck = skipRateLimitingCheckArg ? [NSString stringWithCString:skipRateLimitingCheckArg encoding:NSUTF8StringEncoding] : @"NO";
166
167 OTControlCLI* ctl = [[OTControlCLI alloc] initWithOTControl:rpc];
168
169 NSError* escrowRequestError = nil;
170 EscrowRequestCLI* escrowctl = [[EscrowRequestCLI alloc] initWithEscrowRequest:[SecEscrowRequest request:&escrowRequestError]];
171 if(escrowRequestError) {
172 errx(1, "SecEscrowRequest failed: %s", [[escrowRequestError description] UTF8String]);
173 }
174 if(resetoctagon) {
175 long ret = [ctl resetOctagon:container context:context altDSID:altDSID];
176 return (int)ret;
177 }
178 if(resetProtectedData) {
179 internalOnly();
180 long ret = [ctl resetProtectedData:container context:context altDSID:altDSID appleID:appleID dsid:dsid];
181 return (int)ret;
182 }
183 if(userControllableViewsSyncStatus) {
184 internalOnly();
185
186 if(argEnable && argPause) {
187 print_usage(&args);
188 return -1;
189 }
190
191 if(argEnable == false && argPause == false) {
192 return (int)[ctl fetchUserControllableViewsSyncStatus:container contextID:context];
193 }
194
195 // At this point, we're sure that either argEnabled or argPause is set; so the value of argEnabled captures the user's intention
196 return (int)[ctl setUserControllableViewsSyncStatus:container contextID:context enabled:argEnable];
197 }
198
199 if(fetchAllBottles) {
200 return (int)[ctl fetchAllBottles:altDSID containerName:container context:context control:rpc];
201 }
202 if(recover) {
203 NSString* entropyJSON = secretArg ? [NSString stringWithCString:secretArg encoding:NSUTF8StringEncoding] : nil;
204 NSString* bottleID = bottleIDArg ? [NSString stringWithCString:bottleIDArg encoding:NSUTF8StringEncoding] : nil;
205
206 if(!entropyJSON || !bottleID) {
207 print_usage(&args);
208 return -1;
209 }
210
211 NSData* entropy = [[NSData alloc] initWithBase64EncodedString:entropyJSON options:0];
212 if(!entropy) {
213 print_usage(&args);
214 return -1;
215 }
216
217 return (int)[ctl recoverUsingBottleID:bottleID
218 entropy:entropy
219 altDSID:altDSID
220 containerName:container
221 context:context
222 control:rpc];
223 }
224 if(depart) {
225 return (int)[ctl depart:container context:context];
226 }
227 if(start) {
228 internalOnly();
229 return (int)[ctl startOctagonStateMachine:container context:context];
230 }
231 if(signIn) {
232 internalOnly();
233 return (int)[ctl signIn:altDSID container:container context:context];
234 }
235 if(signOut) {
236 internalOnly();
237 return (int)[ctl signOut:container context:context];
238 }
239
240 if(status) {
241 return (int)[ctl status:container context:context json:json];
242 }
243 if(fetch_escrow_records) {
244 return (int)[ctl fetchEscrowRecords:container context:context];
245 }
246 if(fetch_all_escrow_records) {
247 return (int)[ctl fetchAllEscrowRecords:container context:context];
248 }
249 if(recoverRecord) {
250 NSString* recordIDString = recordID ? [NSString stringWithCString:recordID encoding:NSUTF8StringEncoding] : nil;
251 NSString* secret = secretArg ? [NSString stringWithCString:secretArg encoding:NSUTF8StringEncoding] : nil;
252
253 if(!recordIDString || !secret || !appleID) {
254 print_usage(&args);
255 return -1;
256 }
257
258 return (int)[ctl performEscrowRecovery:container context:context recordID:recordIDString appleID:appleID secret:secret];
259 }
260 if(recoverSilentRecord){
261 NSString* secret = secretArg ? [NSString stringWithCString:secretArg encoding:NSUTF8StringEncoding] : nil;
262
263 if(!secret || !appleID) {
264 print_usage(&args);
265 return -1;
266 }
267
268 return (int)[ctl performSilentEscrowRecovery:container context:context appleID:appleID secret:secret];
269 }
270 if(health) {
271 BOOL skip = NO;
272 if([skipRateLimitingCheck isEqualToString:@"YES"]) {
273 skip = YES;
274 } else {
275 skip = NO;
276 }
277 return (int)[ctl healthCheck:container context:context skipRateLimitingCheck:skip];
278 }
279 if(ckks_policy_flag) {
280 return (int)[ctl refetchCKKSPolicy:container context:context];
281 }
282 if (ttr_flag) {
283 if (radarNumber == NULL) {
284 radarNumber = "1";
285 }
286 return (int)[ctl tapToRadar:@"action" description:@"description" radar:[NSString stringWithUTF8String:radarNumber]];
287 }
288
289 if(er_trigger) {
290 internalOnly();
291 return (int)[escrowctl trigger];
292 }
293 if(er_status) {
294 return (int)[escrowctl status];
295 }
296 if(er_reset) {
297 return (int)[escrowctl reset];
298 }
299 if(er_store) {
300 return (int)[escrowctl storePrerecordsInEscrow];
301 }
302
303
304 #if TARGET_OS_WATCH
305 if (pairme) {
306 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
307 OTPairingInitiateWithCompletion(NULL, ^(bool success, NSError *pairingError) {
308 if (success) {
309 printf("successfully paired with companion\n");
310 } else {
311 printf("failed to pair with companion: %s\n", pairingError.description.UTF8String);
312 }
313 dispatch_semaphore_signal(sema);
314 });
315 dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
316 return 0;
317 }
318 #endif /* TARGET_OS_WATCH */
319
320 print_usage(&args);
321 return -1;
322 }
323 return 0;
324 }