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 reply:^(NSError* _Nullable error) {
189 printf("Error resetting: %s\n", [[error description] UTF8String]);
191 printf("reset and establish:\n");
198 printf("Unimplemented.\n");
203 - (void)printPeer:(NSDictionary*)peerInformation prefix:(NSString* _Nullable)prefix {
204 NSString* peerID = peerInformation[@"peerID"];
205 NSString* model = peerInformation[@"permanentInfo"][@"model_id"];
206 NSNumber* epoch = peerInformation[@"permanentInfo"][@"epoch"];
207 NSString* deviceName = peerInformation[@"stableInfo"][@"device_name"];
208 NSString* serialNumber = peerInformation[@"stableInfo"][@"serial_number"];
209 NSString* os = peerInformation[@"stableInfo"][@"os_version"];
211 printf("%s%s hw:'%s' name:'%s' serial: '%s' os:'%s' epoch:%d\n",
212 (prefix ? [prefix UTF8String] : ""),
215 [deviceName UTF8String],
216 [serialNumber UTF8String],
221 - (void)printPeers:(NSArray<NSString*>*)peerIDs
222 egoPeerID:(NSString* _Nullable)egoPeerID
223 informationOnPeers:(NSDictionary<NSString*, NSDictionary*>*)informationOnPeers {
224 for(NSString* peerID in peerIDs) {
225 NSDictionary* peerInformation = informationOnPeers[peerID];
227 if(!peerInformation) {
228 printf(" Peer: %s; further information missing\n", [peerID UTF8String]);
232 if([peerID isEqualToString:egoPeerID]) {
233 [self printPeer:peerInformation prefix:@" Self: "];
235 [self printPeer:peerInformation prefix:@" Peer: "];
240 - (long)status:(NSString* _Nullable)container context:(NSString*)contextID json:(bool)json {
242 __block long ret = 0;
244 [self.control status:container
246 reply:^(NSDictionary* result, NSError* _Nullable error) {
249 print_json(@{@"error" : [error description]});
251 printf("Error fetching status: %s\n", [[error description] UTF8String]);
258 printf("Status for %s,%s:\n", [result[@"containerName"] UTF8String], [result[@"contextID"] UTF8String]);
261 printf("State: %s\n", [[result[@"state"] description] UTF8String]);
262 printf("Flags: %s; Flags Pending: %s\n\n",
263 ([result[@"stateFlags"] count] == 0u) ? "none" : [[result[@"stateFlags"] description] UTF8String],
264 ([result[@"statePendingFlags"] count] == 0u) ? "none" : [[result[@"statePendingFlags"] description] UTF8String]);
266 NSDictionary* contextDump = result[@"contextDump"];
268 // Make it easy to find peer information
269 NSMutableDictionary<NSString*, NSDictionary*>* peers = [NSMutableDictionary dictionary];
270 NSMutableArray<NSString*>* allPeerIDs = [NSMutableArray array];
271 for(NSDictionary* peerInformation in contextDump[@"peers"]) {
272 NSString* peerID = peerInformation[@"peerID"];
274 peers[peerID] = peerInformation;
275 [allPeerIDs addObject:peerID];
279 NSDictionary* egoInformation = contextDump[@"self"];
280 NSString* egoPeerID = egoInformation[@"peerID"];
281 NSDictionary* egoDynamicInfo = egoInformation[@"dynamicInfo"];
284 NSMutableArray *otherPeers = [allPeerIDs mutableCopy];
285 [self printPeer:egoInformation prefix:@" Self: "];
288 // The self peer is technically a peer, so, shove it on in there
289 peers[egoPeerID] = egoInformation;
291 NSArray<NSString*>* includedPeers = egoDynamicInfo[@"included"];
292 printf("Trusted peers (by me):\n");
293 if(includedPeers && includedPeers.count > 0) {
294 [self printPeers:includedPeers egoPeerID:egoPeerID informationOnPeers:peers];
295 [otherPeers removeObjectsInArray:includedPeers];
297 printf(" No trusted peers.\n");
301 NSArray<NSString*>* excludedPeers = egoDynamicInfo[@"excluded"];
302 printf("Excluded peers (by me):\n");
303 if(excludedPeers && excludedPeers.count > 0) {
304 [self printPeers:excludedPeers egoPeerID:egoPeerID informationOnPeers:peers];
305 [otherPeers removeObjectsInArray:excludedPeers];
307 printf(" No excluded peers.\n");
311 printf("Other peers (included/excluded by others):\n");
312 if(otherPeers.count > 0) {
313 [self printPeers:otherPeers egoPeerID:egoPeerID informationOnPeers:peers];
315 printf(" No other peers.\n");
320 printf("No current identity for this device.\n\n");
322 if(allPeerIDs.count > 0) {
323 printf("All peers currently in this account:\n");
324 [self printPeers:allPeerIDs egoPeerID:nil informationOnPeers:peers];
326 printf("No peers currently exist for this account.\n");
337 printf("Unimplemented.\n");
342 - (long)recoverUsingBottleID:(NSString*)bottleID
343 entropy:(NSData*)entropy
344 altDSID:(NSString*)altDSID
345 containerName:(NSString*)containerName
346 context:(NSString*)context
347 control:(OTControl*)control {
348 __block long ret = 0;
351 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
353 [control restore:containerName
358 reply:^(NSError* _Nullable error) {
361 printf("Error recovering: %s\n", [[error description] UTF8String]);
363 printf("Succeeded recovering bottled peer %s\n", [[bottleID description] UTF8String]);
365 dispatch_semaphore_signal(sema);
368 if(dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 60)) != 0) {
369 printf("timed out waiting for restore/recover\n");
380 - (long)fetchAllBottles:(NSString*)altDSID
381 containerName:(NSString*)containerName
382 context:(NSString*)context
383 control:(OTControl*)control {
384 __block long ret = 0;
387 __block NSError* localError = nil;
389 __block NSArray<NSString*>* localViableBottleIDs = nil;
390 __block NSArray<NSString*>* localPartiallyViableBottleIDs = nil;
392 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
394 [control fetchAllViableBottles:containerName
396 reply:^(NSArray<NSString*>* _Nullable sortedBottleIDs,
397 NSArray<NSString*>* _Nullable sortedPartialBottleIDs,
398 NSError* _Nullable controlError) {
400 secnotice("clique", "findOptimalBottleIDsWithContextData errored: %@\n", controlError);
402 secnotice("clique", "findOptimalBottleIDsWithContextData succeeded: %@, %@\n", sortedBottleIDs, sortedPartialBottleIDs);
404 localError = controlError;
405 localViableBottleIDs = sortedBottleIDs;
406 localPartiallyViableBottleIDs = sortedPartialBottleIDs;
407 dispatch_semaphore_signal(sema);
410 if(dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 60)) != 0) {
411 secnotice("clique", "findOptimalBottleIDsWithContextData failed to fetch bottles\n");
415 [localViableBottleIDs enumerateObjectsUsingBlock:^(NSString* obj, NSUInteger idx, BOOL* stop) {
416 printf("preferred bottleID: %s\n", [obj UTF8String]);
419 [localPartiallyViableBottleIDs enumerateObjectsUsingBlock:^(NSString* obj, NSUInteger idx, BOOL* stop) {
420 printf("partial recovery bottleID: %s\n", [obj UTF8String]);
430 - (long)healthCheck:(NSString* _Nullable)container context:(NSString*)contextID skipRateLimitingCheck:(BOOL)skipRateLimitingCheck
433 __block long ret = -1;
435 [self.control healthCheck:container
437 skipRateLimitingCheck:skipRateLimitingCheck
438 reply:^(NSError* _Nullable error) {
440 printf("Error checking health: %s\n", [[error description] UTF8String]);
442 printf("Checking Octagon Health completed.\n");
448 printf("Unimplemented.\n");
453 - (long)tapToRadar:(NSString *)action description:(NSString *)description radar:(NSString *)radar
456 __block long ret = 1;
458 [self.control tapToRadar:action
459 description:description
461 reply:^(NSError* _Nullable error) {
463 printf("Error trigger TTR: %s\n", [[error description] UTF8String]);
465 printf("Trigger TTR completed.\n");
471 printf("Unimplemented.\n");