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