3 #import <Foundation/Foundation.h>
4 #import <Foundation/NSXPCConnection_Private.h>
5 #import <Security/SecItemPriv.h>
6 #import <Security/Security.h>
9 #include "utilities/SecCFWrappers.h"
10 #include "utilities/SecInternalReleasePriv.h"
11 #import "utilities/debugging.h"
13 #import "keychain/ot/OT.h"
14 #import "keychain/ot/OTConstants.h"
15 #import "keychain/ot/OTControl.h"
16 #import "keychain/otctl/OTControlCLI.h"
18 // Mutual recursion to set up an object for jsonification
19 static NSDictionary* cleanDictionaryForJSON(NSDictionary* dict);
21 static id cleanObjectForJSON(id obj) {
25 if([obj isKindOfClass:[NSError class]]) {
26 NSError* obje = (NSError*) obj;
27 NSMutableDictionary* newErrorDict = [@{@"code": @(obje.code), @"domain": obje.domain} mutableCopy];
28 newErrorDict[@"userInfo"] = cleanDictionaryForJSON(obje.userInfo);
30 } else if([NSJSONSerialization isValidJSONObject:obj]) {
33 } else if ([obj isKindOfClass: [NSNumber class]]) {
36 } else if([obj isKindOfClass: [NSData class]]) {
37 NSData* dataObj = (NSData*)obj;
38 return [dataObj base64EncodedStringWithOptions:0];
40 } else if ([obj isKindOfClass: [NSDictionary class]]) {
41 return cleanDictionaryForJSON((NSDictionary*) obj);
43 } else if ([obj isKindOfClass: [NSArray class]]) {
44 NSArray* arrayObj = (NSArray*)obj;
45 NSMutableArray* cleanArray = [NSMutableArray arrayWithCapacity:arrayObj.count];
47 for(id x in arrayObj) {
48 [cleanArray addObject: cleanObjectForJSON(x)];
53 return [obj description];
57 static NSDictionary* cleanDictionaryForJSON(NSDictionary* dict) {
61 NSMutableDictionary* mutDict = [dict mutableCopy];
62 for(id key in mutDict.allKeys) {
63 id obj = mutDict[key];
64 mutDict[key] = cleanObjectForJSON(obj);
69 static void print_json(NSDictionary* dict)
73 NSData* json = [NSJSONSerialization dataWithJSONObject:cleanDictionaryForJSON(dict)
74 options:(NSJSONWritingPrettyPrinted | NSJSONWritingSortedKeys)
77 NSLog(@"error during JSONification: %@", err.localizedDescription);
79 printf("%s\n", [[[NSString alloc] initWithData:json encoding:NSUTF8StringEncoding] UTF8String]);
84 @implementation OTControlCLI
86 - (instancetype)initWithOTControl:(OTControl*)control {
87 if((self = [super init])) {
94 - (long)startOctagonStateMachine:(NSString*)container context:(NSString*)contextID {
96 __block long ret = -1;
98 [self.control startOctagonStateMachine:container
100 reply:^(NSError* _Nullable error) {
102 printf("Error starting state machine: %s\n", [[error description] UTF8String]);
105 printf("state machine started.\n");
111 printf("Unimplemented.\n");
116 - (long)signIn:(NSString*)altDSID container:(NSString* _Nullable)container context:(NSString*)contextID {
118 __block long ret = -1;
120 [self.control signIn:altDSID
123 reply:^(NSError* _Nullable error) {
125 printf("Error signing in: %s\n", [[error description] UTF8String]);
127 printf("Sign in complete.\n");
134 printf("Unimplemented.\n");
139 - (long)signOut:(NSString* _Nullable)container context:(NSString*)contextID {
141 __block long ret = -1;
142 [self.control signOut:container
144 reply:^(NSError* _Nullable error) {
146 printf("Error signing out: %s\n", [[error description] UTF8String]);
148 printf("Sign out complete.\n");
154 printf("Unimplemented.\n");
159 - (long)depart:(NSString* _Nullable)container context:(NSString*)contextID {
161 __block long ret = -1;
163 [self.control leaveClique:container
165 reply:^(NSError* _Nullable error) {
167 printf("Error departing clique: %s\n", [[error description] UTF8String]);
169 printf("Departing clique completed.\n");
175 printf("Unimplemented.\n");
180 - (long)resetOctagon:(NSString*)container context:(NSString*)contextID altDSID:(NSString*)altDSID {
182 __block long ret = -1;
184 [self.control resetAndEstablish:container
187 resetReason:CuttlefishResetReasonUserInitiatedReset
188 reply:^(NSError* _Nullable error) {
190 printf("Error resetting: %s\n", [[error description] UTF8String]);
192 printf("reset and establish:\n");
199 printf("Unimplemented.\n");
204 - (void)printPeer:(NSDictionary*)peerInformation prefix:(NSString* _Nullable)prefix {
205 NSString* peerID = peerInformation[@"peerID"];
206 NSString* model = peerInformation[@"permanentInfo"][@"model_id"];
207 NSNumber* epoch = peerInformation[@"permanentInfo"][@"epoch"];
208 NSString* deviceName = peerInformation[@"stableInfo"][@"device_name"];
209 NSString* serialNumber = peerInformation[@"stableInfo"][@"serial_number"];
210 NSString* os = peerInformation[@"stableInfo"][@"os_version"];
212 printf("%s%s hw:'%s' name:'%s' serial: '%s' os:'%s' epoch:%d\n",
213 (prefix ? [prefix UTF8String] : ""),
216 [deviceName UTF8String],
217 [serialNumber UTF8String],
222 - (void)printPeers:(NSArray<NSString*>*)peerIDs
223 egoPeerID:(NSString* _Nullable)egoPeerID
224 informationOnPeers:(NSDictionary<NSString*, NSDictionary*>*)informationOnPeers {
225 for(NSString* peerID in peerIDs) {
226 NSDictionary* peerInformation = informationOnPeers[peerID];
228 if(!peerInformation) {
229 printf(" Peer: %s; further information missing\n", [peerID UTF8String]);
233 if([peerID isEqualToString:egoPeerID]) {
234 [self printPeer:peerInformation prefix:@" Self: "];
236 [self printPeer:peerInformation prefix:@" Peer: "];
241 - (long)status:(NSString* _Nullable)container context:(NSString*)contextID json:(bool)json {
243 __block long ret = 0;
245 [self.control status:container
247 reply:^(NSDictionary* result, NSError* _Nullable error) {
250 print_json(@{@"error" : [error description]});
252 printf("Error fetching status: %s\n", [[error description] UTF8String]);
259 printf("Status for %s,%s:\n", [result[@"containerName"] UTF8String], [result[@"contextID"] UTF8String]);
262 printf("State: %s\n", [[result[@"state"] description] UTF8String]);
263 printf("Flags: %s; Flags Pending: %s\n\n",
264 ([result[@"stateFlags"] count] == 0u) ? "none" : [[result[@"stateFlags"] description] UTF8String],
265 ([result[@"statePendingFlags"] count] == 0u) ? "none" : [[result[@"statePendingFlags"] description] UTF8String]);
267 NSDictionary* contextDump = result[@"contextDump"];
269 // Make it easy to find peer information
270 NSMutableDictionary<NSString*, NSDictionary*>* peers = [NSMutableDictionary dictionary];
271 NSMutableArray<NSString*>* allPeerIDs = [NSMutableArray array];
272 for(NSDictionary* peerInformation in contextDump[@"peers"]) {
273 NSString* peerID = peerInformation[@"peerID"];
275 peers[peerID] = peerInformation;
276 [allPeerIDs addObject:peerID];
280 NSDictionary* egoInformation = contextDump[@"self"];
281 NSString* egoPeerID = egoInformation[@"peerID"];
282 NSDictionary* egoDynamicInfo = egoInformation[@"dynamicInfo"];
285 NSMutableArray *otherPeers = [allPeerIDs mutableCopy];
286 [self printPeer:egoInformation prefix:@" Self: "];
289 // The self peer is technically a peer, so, shove it on in there
290 peers[egoPeerID] = egoInformation;
292 NSArray<NSString*>* includedPeers = egoDynamicInfo[@"included"];
293 printf("Trusted peers (by me):\n");
294 if(includedPeers && includedPeers.count > 0) {
295 [self printPeers:includedPeers egoPeerID:egoPeerID informationOnPeers:peers];
296 [otherPeers removeObjectsInArray:includedPeers];
298 printf(" No trusted peers.\n");
302 NSArray<NSString*>* excludedPeers = egoDynamicInfo[@"excluded"];
303 printf("Excluded peers (by me):\n");
304 if(excludedPeers && excludedPeers.count > 0) {
305 [self printPeers:excludedPeers egoPeerID:egoPeerID informationOnPeers:peers];
306 [otherPeers removeObjectsInArray:excludedPeers];
308 printf(" No excluded peers.\n");
312 printf("Other peers (included/excluded by others):\n");
313 if(otherPeers.count > 0) {
314 [self printPeers:otherPeers egoPeerID:egoPeerID informationOnPeers:peers];
316 printf(" No other peers.\n");
321 printf("No current identity for this device.\n\n");
323 if(allPeerIDs.count > 0) {
324 printf("All peers currently in this account:\n");
325 [self printPeers:allPeerIDs egoPeerID:nil informationOnPeers:peers];
327 printf("No peers currently exist for this account.\n");
338 printf("Unimplemented.\n");
343 - (long)recoverUsingBottleID:(NSString*)bottleID
344 entropy:(NSData*)entropy
345 altDSID:(NSString*)altDSID
346 containerName:(NSString*)containerName
347 context:(NSString*)context
348 control:(OTControl*)control {
349 __block long ret = 0;
352 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
354 [control restore:containerName
359 reply:^(NSError* _Nullable error) {
362 printf("Error recovering: %s\n", [[error description] UTF8String]);
364 printf("Succeeded recovering bottled peer %s\n", [[bottleID description] UTF8String]);
366 dispatch_semaphore_signal(sema);
369 if(dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 60)) != 0) {
370 printf("timed out waiting for restore/recover\n");
381 - (long)fetchAllBottles:(NSString*)altDSID
382 containerName:(NSString*)containerName
383 context:(NSString*)context
384 control:(OTControl*)control {
385 __block long ret = 0;
388 __block NSError* localError = nil;
390 __block NSArray<NSString*>* localViableBottleIDs = nil;
391 __block NSArray<NSString*>* localPartiallyViableBottleIDs = nil;
393 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
395 [control fetchAllViableBottles:containerName
397 reply:^(NSArray<NSString*>* _Nullable sortedBottleIDs,
398 NSArray<NSString*>* _Nullable sortedPartialBottleIDs,
399 NSError* _Nullable controlError) {
401 secnotice("clique", "findOptimalBottleIDsWithContextData errored: %@\n", controlError);
403 secnotice("clique", "findOptimalBottleIDsWithContextData succeeded: %@, %@\n", sortedBottleIDs, sortedPartialBottleIDs);
405 localError = controlError;
406 localViableBottleIDs = sortedBottleIDs;
407 localPartiallyViableBottleIDs = sortedPartialBottleIDs;
408 dispatch_semaphore_signal(sema);
411 if(dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 60)) != 0) {
412 secnotice("clique", "findOptimalBottleIDsWithContextData failed to fetch bottles\n");
416 [localViableBottleIDs enumerateObjectsUsingBlock:^(NSString* obj, NSUInteger idx, BOOL* stop) {
417 printf("preferred bottleID: %s\n", [obj UTF8String]);
420 [localPartiallyViableBottleIDs enumerateObjectsUsingBlock:^(NSString* obj, NSUInteger idx, BOOL* stop) {
421 printf("partial recovery bottleID: %s\n", [obj UTF8String]);
431 - (long)healthCheck:(NSString* _Nullable)container context:(NSString*)contextID skipRateLimitingCheck:(BOOL)skipRateLimitingCheck
434 __block long ret = -1;
436 [self.control healthCheck:container
438 skipRateLimitingCheck:skipRateLimitingCheck
439 reply:^(NSError* _Nullable error) {
441 printf("Error checking health: %s\n", [[error description] UTF8String]);
443 printf("Checking Octagon Health completed.\n");
449 printf("Unimplemented.\n");
454 - (long)tapToRadar:(NSString *)action description:(NSString *)description radar:(NSString *)radar
457 __block long ret = 1;
459 [self.control tapToRadar:action
460 description:description
462 reply:^(NSError* _Nullable error) {
464 printf("Error trigger TTR: %s\n", [[error description] UTF8String]);
466 printf("Trigger TTR completed.\n");
472 printf("Unimplemented.\n");