]> git.saurik.com Git - apple/security.git/blob - keychain/otctl/otctl.m
Security-58286.70.7.tar.gz
[apple/security.git] / keychain / otctl / otctl.m
1 //
2 // Security
3 //
4
5 #import <Foundation/Foundation.h>
6 #import <Foundation/NSXPCConnection_Private.h>
7 #import <Security/Security.h>
8 #import <Security/SecItemPriv.h>
9 #import <xpc/xpc.h>
10 #import <err.h>
11 #import <utilities/debugging.h>
12 #import "keychain/ot/OTControl.h"
13 #import "keychain/ot/OTConstants.h"
14 #include "lib/SecArgParse.h"
15 #include <utilities/SecCFWrappers.h>
16 #include <utilities/SecInternalReleasePriv.h>
17
18 @interface OTControlCLI : NSObject
19 @property OTControl* control;
20 @end
21
22 @implementation OTControlCLI
23
24
25 - (instancetype) initWithOTControl:(OTControl*)control {
26 if ((self = [super init])) {
27 _control = control;
28 }
29
30 return self;
31 }
32
33 - (long)preflightBottledPeer:(NSString*)contextID dsid:(NSString*)dsid
34 {
35 __block long ret = 0;
36
37 #if OCTAGON
38 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
39
40 [self.control preflightBottledPeer:contextID dsid:dsid reply:^(NSData * _Nullable entropy, NSString * _Nullable bottleID, NSData * _Nullable signingPublicKey, NSError * _Nullable error) {
41 if(error){
42 printf("Error pushing: %s\n", [[error description] UTF8String]);
43 ret = (error.code == 0 ? -1 : error.code);
44 }else if(entropy && bottleID && signingPublicKey){
45 printf("\nSuccessfully preflighted bottle ID: %s\n", [bottleID UTF8String]);
46 printf("\nEntropy used: %s\n", [[entropy base64EncodedStringWithOptions:0] UTF8String]);
47 printf("\nSigning Public Key: %s\n", [[signingPublicKey base64EncodedStringWithOptions:0] UTF8String]);
48 ret = 0;
49 }else{
50 printf("Failed to preflight bottle and no error was returned..");
51 ret = -1;
52 }
53
54 dispatch_semaphore_signal(sema);
55 }];
56
57 if(dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 65)) != 0) {
58 printf("\n\nError: timed out waiting for response\n");
59 return -1;
60 }
61 return ret;
62 #else
63 return -1;
64 #endif
65 }
66
67 - (long)launchBottledPeer:(NSString*)contextID bottleID:(NSString*)bottleID
68 {
69 __block long ret = 0;
70
71 #if OCTAGON
72 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
73
74 [self.control launchBottledPeer:contextID bottleID:bottleID reply:^(NSError * _Nullable error) {
75 if(error)
76 {
77 printf("Error pushing: %s\n", [[error description] UTF8String]);
78 ret = (error.code == 0 ? -1 : error.code);
79 } else {
80 printf("\nSuccessfully launched bottleID: %s\n", [bottleID UTF8String]);
81 ret = 0;
82 }
83
84 dispatch_semaphore_signal(sema);
85 }];
86
87 if(dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 65)) != 0) {
88 printf("\n\nError: timed out waiting for response\n");
89 return -1;
90 }
91 return ret;
92 #else
93 return -1;
94 #endif
95 }
96
97 - (long)scrubBottledPeer:(NSString*)contextID bottleID:(NSString*)bottleID
98 {
99 __block long ret = 0;
100
101 #if OCTAGON
102 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
103
104 [self.control scrubBottledPeer:contextID bottleID:bottleID reply:^(NSError * _Nullable error) {
105 if(error)
106 {
107 printf("Error pushing: %s\n", [[error description] UTF8String]);
108 ret = (error.code == 0 ? -1 : error.code);
109 } else {
110 printf("\nSuccessfully scrubbed bottle ID: %s\n", [bottleID UTF8String]);
111 ret = 0;
112 }
113
114 dispatch_semaphore_signal(sema);
115 }];
116
117 if(dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 65)) != 0) {
118 printf("\n\nError: timed out waiting for response\n");
119 return -1;
120 }
121 return ret;
122 #else
123 ret = -1;
124 return ret;
125 #endif
126 }
127
128
129 - (long)enroll:(NSString*)contextID dsid:(NSString*)dsid
130 {
131 __block long ret = 0;
132
133 #if OCTAGON
134 dispatch_semaphore_t semaForPreFlight = dispatch_semaphore_create(0);
135 dispatch_semaphore_t semaForLaunch = dispatch_semaphore_create(0);
136 __block NSString* bottleRecordID = nil;
137 __block NSError* localError = nil;
138
139 [self.control preflightBottledPeer:contextID dsid:dsid reply:^(NSData * _Nullable entropy, NSString * _Nullable bottleID, NSData * _Nullable signingPublicKey, NSError * _Nullable error) {
140 if(error)
141 {
142 localError = error;
143 printf("Error pushing: %s\n", [[error description] UTF8String]);
144 ret = (error.code == 0 ? -1 : error.code);
145 } else {
146 bottleRecordID = bottleID;
147 printf("\nSuccessfully preflighted bottle ID: %s\n", [bottleID UTF8String]);
148 printf("\nEntropy used: %s\n", [[entropy base64EncodedStringWithOptions:0] UTF8String]);
149 printf("\nSigning Public Key: %s\n", [[signingPublicKey base64EncodedStringWithOptions:0] UTF8String]);
150 ret = 0;
151 }
152
153 dispatch_semaphore_signal(semaForPreFlight);
154 }];
155
156 if(dispatch_semaphore_wait(semaForPreFlight, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 65)) != 0) {
157 printf("\n\nError: timed out waiting for response\n");
158 return -1;
159 }
160
161 if(localError == nil){
162 [self.control launchBottledPeer:contextID bottleID:bottleRecordID reply:^(NSError * _Nullable error) {
163 if(error)
164 {
165 printf("Error pushing: %s\n", [[error description] UTF8String]);
166 ret = (error.code == 0 ? -1 : error.code);
167 } else {
168 printf("\nSuccessfully launched bottleID: %s\n", [bottleRecordID UTF8String]);
169 ret = 0;
170 }
171
172 dispatch_semaphore_signal(semaForLaunch);
173 }];
174
175 if(dispatch_semaphore_wait(semaForLaunch, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 65)) != 0) {
176 printf("\n\nError: timed out waiting for response\n");
177 return -1;
178 }
179 }
180 printf("Complete.\n");
181 return ret;
182 #else
183 return -1;
184 #endif
185 }
186
187
188 - (long)restore:(NSString*)contextID dsid:(NSString*)dsid secret:(NSData*)secret escrowRecordID:(NSString*)escrowRecordID
189 {
190 __block long ret = 0;
191
192 #if OCTAGON
193 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
194
195 [self.control restore:contextID dsid:dsid secret:secret escrowRecordID:escrowRecordID reply:^(NSData* signingKeyData, NSData* encryptionKeyData, NSError *error) {
196 if(error)
197 {
198 printf("Error pushing: %s\n", [[error description] UTF8String]);
199 ret = (error.code == 0 ? -1 : error.code);
200 } else {
201
202 printf("Complete.\n");
203 ret = 0;
204 }
205
206 NSString* signingKeyString = [signingKeyData base64EncodedStringWithOptions:0];
207 NSString* encryptionKeyString = [encryptionKeyData base64EncodedStringWithOptions:0];
208
209 printf("Signing Key:\n %s\n", [signingKeyString UTF8String]);
210 printf("Encryption Key:\n %s\n", [encryptionKeyString UTF8String]);
211 dispatch_semaphore_signal(sema);
212 }];
213
214 if(dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 65)) != 0) {
215 printf("\n\nError: timed out waiting for response\n");
216 return -1;
217 }
218 return ret;
219 #else
220 ret = -1;
221 return ret;
222 #endif
223 }
224
225 - (long) reset
226 {
227 __block long ret = 0;
228
229 #if OCTAGON
230 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
231 [self.control reset:^(BOOL reset, NSError* error){
232 if(error)
233 {
234 printf("Error pushing: %s\n", [[error description] UTF8String]);
235 ret = (error.code == 0 ? -1 : error.code);
236 } else {
237 printf("success\n");
238 }
239
240 dispatch_semaphore_signal(sema);
241 }];
242
243 if(dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 65)) != 0) {
244 printf("\n\nError: timed out waiting for response\n");
245 return -1;
246 }
247
248 printf("Complete.\n");
249 return ret;
250 #else
251 ret = -1;
252 return ret;
253 #endif
254 }
255
256 - (long) listOfRecords
257 {
258 __block long ret = 0;
259
260 #if OCTAGON
261 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
262 [self.control listOfRecords:^(NSArray* list, NSError* error){
263 if(error)
264 {
265 printf("Error pushing: %s\n", [[error description] UTF8String]);
266 ret = (error.code == 0 ? -1 : error.code);
267 } else {
268 [list enumerateObjectsUsingBlock:^(NSString* _Nonnull escrowRecordID, NSUInteger idx, BOOL * _Nonnull stop) {
269 printf("escrowRecordID: %s\n", [escrowRecordID UTF8String]);
270 }];
271 ret = 0;
272 }
273
274 dispatch_semaphore_signal(sema);
275 }];
276
277 if(dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 65)) != 0) {
278 printf("\n\nError: timed out waiting for response\n");
279 return -1;
280 }
281
282 printf("Complete.\n");
283 return ret;
284 #else
285 ret = -1;
286 return ret;
287 #endif
288 }
289
290 - (long)octagonKeys
291 {
292 __block long ret = 0;
293
294 #if OCTAGON
295 dispatch_semaphore_t semaForGettingEncryptionKey = dispatch_semaphore_create(0);
296 dispatch_semaphore_t semaForGettingSigningKey = dispatch_semaphore_create(0);
297 [self.control encryptionKey:^(NSData *encryptionKey, NSError * error) {
298 if(error)
299 {
300 printf("Error pushing: %s\n", [[error description] UTF8String]);
301 ret = (error.code == 0 ? -1 : error.code);
302 } else {
303 NSString* encryptionKeyString = [encryptionKey base64EncodedStringWithOptions:0];
304 printf("Encryption Key:\n %s\n", [encryptionKeyString UTF8String]);
305 ret = 0;
306 }
307
308 dispatch_semaphore_signal(semaForGettingEncryptionKey);
309 }];
310
311 [self.control signingKey:^(NSData *signingKey, NSError * error) {
312 if(error)
313 {
314 printf("Error pushing: %s\n", [[error description] UTF8String]);
315 ret = (error.code == 0 ? -1 : error.code);
316 } else {
317 NSString* signingKeyString = [signingKey base64EncodedStringWithOptions:0];
318 printf("Signing Key:\n %s\n", [signingKeyString UTF8String]);
319 ret = 0;
320 }
321
322 dispatch_semaphore_signal(semaForGettingSigningKey);
323 }];
324
325
326 if(dispatch_semaphore_wait(semaForGettingEncryptionKey, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 65)) != 0) {
327 printf("\n\nError: timed out waiting for response\n");
328 return -1;
329 }
330 if(dispatch_semaphore_wait(semaForGettingSigningKey, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 65)) != 0) {
331 printf("\n\nError: timed out waiting for response\n");
332 return -1;
333 }
334 printf("Complete.\n");
335 return ret;
336 #else
337 ret = -1;
338 return ret;
339 #endif
340 }
341
342 @end
343
344 static bool SecOTIsEnabled(void) {
345
346 bool userDefaultsShouldBottledPeer = true;
347 CFBooleanRef enabled = (CFBooleanRef)CFPreferencesCopyValue(CFSTR("EnableOTRestore"),
348 CFSTR("com.apple.security"),
349 kCFPreferencesAnyUser, kCFPreferencesAnyHost);
350 if(enabled && CFGetTypeID(enabled) == CFBooleanGetTypeID()){
351 if(enabled == kCFBooleanFalse){
352 secnotice("octagon", "Octagon Restore Disabled");
353 userDefaultsShouldBottledPeer = false;
354 }
355 if(enabled == kCFBooleanTrue){
356 secnotice("octagon", "Octagon Restore Enabled");
357 userDefaultsShouldBottledPeer = true;
358 }
359 }
360
361 CFReleaseNull(enabled);
362 return userDefaultsShouldBottledPeer;
363 }
364
365 static int enroll = false;
366 static int restore = false;
367 static int octagonkeys = false;
368 static int reset = false;
369
370 static int prepbp = false;
371 static int launch = false;
372 static int scrub = false;
373
374 static int listOfRecords = false;
375 static char* bottleIDArg = NULL;
376 static char* contextNameArg = NULL;
377 static char* secretArg = NULL;
378
379 int main(int argc, char **argv)
380 {
381 if(!SecIsInternalRelease())
382 {
383 secnotice("octagon", "Tool not available on non internal builds");
384 return -1;
385 }
386
387 if(!SecOTIsEnabled())
388 {
389 printf("To use this tool, enable defaults write for EnableOTRestore\n defaults write (~)/Library/Preferences/com.apple.security EnableOTRestore -bool YES\n");
390 return -1;
391 }
392 static struct argument options[] = {
393 { .shortname='s', .longname="secret", .argument=&secretArg, .description="escrow secret"},
394 { .shortname='e', .longname="bottleID", .argument=&bottleIDArg, .description="bottle record id"},
395 { .shortname='c', .longname="context", .argument=&contextNameArg, .description="context name"},
396
397 { .command="restore", .flag=&restore, .flagval=true, .description="Restore fake bottled peer"},
398 { .command="enroll", .flag=&enroll, .flagval=true, .description="Enroll fake bottled peer"},
399 { .command="keys", .flag=&octagonkeys, .shortname='k', .flagval=true, .description="Octagon Signing + Encryption Keys"},
400 { .command="reset", .flag=&reset, .flagval=true, .description="Reset Octagon Trust Zone"},
401 { .command="list", .flag=&listOfRecords, .flagval=true, .description="List of current Bottled Peer Records IDs"},
402
403 { .command="prepbp", .flag=&prepbp, .shortname='p', .flagval=true, .description="Preflights a bottled peer"},
404 { .command="launch", .flag=&launch, .flagval=true, .description="Launches a bottled peer"},
405 { .command="scrub", .flag=&scrub, .flagval=true, .description="Scrub bottled peer"},
406
407 {}
408 };
409
410 static struct arguments args = {
411 .programname="otctl",
412 .description="Control and report on Octagon Trust",
413 .arguments = options,
414 };
415
416 if(!options_parse(argc, argv, &args)) {
417 printf("\n");
418 print_usage(&args);
419 return -1;
420 }
421
422 @autoreleasepool {
423 NSError* error = nil;
424
425 OTControl* rpc = [OTControl controlObject:&error];
426 if(error || !rpc) {
427 errx(1, "no OTControl failed: %s", [[error description] UTF8String]);
428 }
429
430 OTControlCLI* ctl = [[OTControlCLI alloc] initWithOTControl:rpc];
431
432 if(enroll) {
433 long ret = 0;
434 NSString* context = contextNameArg ? [NSString stringWithCString: contextNameArg encoding: NSUTF8StringEncoding] : OTDefaultContext;
435 ret = [ctl enroll:context dsid:@"12345678"];
436 return (int)ret;
437 }
438 if(prepbp){
439 long ret = 0;
440 NSString* context = contextNameArg ? [NSString stringWithCString: contextNameArg encoding: NSUTF8StringEncoding] : OTDefaultContext;
441
442 //requires secret, context is optional
443 ret = [ctl preflightBottledPeer:context dsid:@"12345678"];
444 return (int)ret;
445 }
446 if(launch){
447 long ret = 0;
448
449 NSString* bottleID = bottleIDArg ? [NSString stringWithCString: bottleIDArg encoding: NSUTF8StringEncoding] : nil;
450 NSString* context = contextNameArg ? [NSString stringWithCString: contextNameArg encoding: NSUTF8StringEncoding] : OTDefaultContext;
451 //requires bottleID, context is optional
452 if(bottleID && [bottleID length] > 0 && ![bottleID isEqualToString:@"(null)"]){
453 ret = [ctl launchBottledPeer:context bottleID:bottleID];
454 }
455 else{
456 print_usage(&args);
457 return -1;
458 }
459
460 return (int)ret;
461 }
462 if(scrub){
463 long ret = 0;
464
465 NSString* bottleID = bottleIDArg ? [NSString stringWithCString: bottleIDArg encoding: NSUTF8StringEncoding] : nil;
466 NSString* context = contextNameArg ? [NSString stringWithCString: contextNameArg encoding: NSUTF8StringEncoding] : OTDefaultContext;
467
468 //requires bottle ID, context is optional
469 if(bottleID && [bottleID length] > 0 && ![bottleID isEqualToString:@"(null)"]){
470 ret = [ctl scrubBottledPeer:context bottleID:bottleID];
471 }
472 else{
473 print_usage(&args);
474 return -1;
475 }
476 return (int)ret;
477 }
478
479 if(restore) {
480 long ret = 0;
481 NSData* secretData = nil;
482 NSString* secretString = secretArg ? [NSString stringWithCString: secretArg encoding: NSUTF8StringEncoding] : nil;
483 NSString* bottleID = bottleIDArg ? [NSString stringWithCString: bottleIDArg encoding: NSUTF8StringEncoding] : nil;
484 NSString* context = contextNameArg ? [NSString stringWithCString: contextNameArg encoding: NSUTF8StringEncoding] : OTDefaultContext;
485
486 //requires secret and bottle ID, context is optional
487 if(secretString && [secretString length] > 0){
488 secretData = [[NSData alloc] initWithBase64EncodedString:secretString options:0];;
489 }
490 else{
491 print_usage(&args);
492 return -1;
493 }
494
495
496 if(bottleID && [bottleID length] > 0 && ![bottleID isEqualToString:@"(null)"]){
497 ret = [ctl restore:context dsid:@"12345678" secret:secretData escrowRecordID:bottleID];
498 }
499 else{
500 print_usage(&args);
501 return -1;
502 }
503 return (int)ret;
504 }
505 if(octagonkeys){
506 long ret = 0;
507 ret = [ctl octagonKeys];
508 return (int)ret;
509 }
510 if(listOfRecords){
511 long ret = 0;
512 ret = [ctl listOfRecords];
513 return (int)ret;
514 }
515 if(reset){
516 long ret = 0;
517 ret = [ctl reset];
518 return (int)ret;
519 }
520 else {
521 print_usage(&args);
522 return -1;
523 }
524
525
526 }
527 return 0;
528 }
529