accountCirclesViewsPrint.m
1 // 2 // accountCirclesViewsPrint.c 3 // Security 4 // 5 // Created by Richard Murphy on 12/8/16. 6 // 7 // 8 9 #include "accountCirclesViewsPrint.h" 10 11 // 12 // SOSSysdiagnose.c 13 // sec 14 // 15 // Created by Richard Murphy on 1/27/16. 16 // 17 // 18 19 20 #include <stdio.h> 21 #include <stdlib.h> 22 #include <string.h> 23 #include <unistd.h> 24 #include <sys/utsname.h> 25 #include <sys/stat.h> 26 #include <time.h> 27 #include <notify.h> 28 #include <pwd.h> 29 30 #include <Security/SecItem.h> 31 32 #include <CoreFoundation/CoreFoundation.h> 33 #include <CoreFoundation/CFPriv.h> 34 35 #include <Security/SecureObjectSync/SOSCloudCircle.h> 36 #include <Security/SecureObjectSync/SOSCloudCircleInternal.h> 37 #include <Security/SecureObjectSync/SOSPeerInfo.h> 38 #include "keychain/SecureObjectSync/SOSPeerInfoPriv.h" 39 #include "keychain/SecureObjectSync/SOSPeerInfoV2.h" 40 #include "keychain/SecureObjectSync/SOSUserKeygen.h" 41 #include "keychain/SecureObjectSync/SOSKVSKeys.h" 42 #include "keychain/securityd/SOSCloudCircleServer.h" 43 #include <Security/SecOTRSession.h> 44 #include "keychain/SecureObjectSync/CKBridge/SOSCloudKeychainClient.h" 45 46 #include <utilities/SecCFWrappers.h> 47 #include <utilities/debugging.h> 48 49 #include "SecurityTool/sharedTool/readline.h" 50 51 #include "keychain_log.h" 52 #include "secToolFileIO.h" 53 #include "secViewDisplay.h" 54 55 56 #include <Security/SecPasswordGenerate.h> 57 58 #define MAXKVSKEYTYPE kUnknownKey 59 #define DATE_LENGTH 18 60 61 #include <utilities/SecCFWrappers.h> 62 63 64 static const char *getSOSCCStatusDescription(SOSCCStatus ccstatus) 65 { 66 switch (ccstatus) 67 { 68 case kSOSCCInCircle: return "In Circle"; 69 case kSOSCCNotInCircle: return "Not in Circle"; 70 case kSOSCCRequestPending: return "Request pending"; 71 case kSOSCCCircleAbsent: return "Circle absent"; 72 case kSOSCCError: return "Circle error"; 73 74 default: 75 return "<unknown ccstatus>"; 76 break; 77 } 78 } 79 80 static const char * 81 getSOSCCLastDepartureReasonDescription(enum DepartureReason reason) 82 { 83 switch (reason) { 84 #define CASE_REASON(x) case kSOS##x: return #x 85 CASE_REASON(DepartureReasonError); 86 CASE_REASON(NeverLeftCircle); 87 CASE_REASON(WithdrewMembership); 88 CASE_REASON(MembershipRevoked); 89 CASE_REASON(LeftUntrustedCircle); 90 CASE_REASON(NeverAppliedToCircle); 91 CASE_REASON(DiscoveredRetirement); // we should all be so lucky 92 CASE_REASON(LostPrivateKey); 93 CASE_REASON(PasswordChanged); 94 #undef CASE_REASON 95 default: 96 return "Unknown"; 97 } 98 } 99 100 static void printPeerInfos(char *label, CFStringRef mypeerID, CFArrayRef (^copyPeers)(CFErrorRef *error)) { 101 CFErrorRef error = NULL; 102 CFArrayRef ppi = copyPeers(&error); 103 104 if(ppi) { 105 printmsg(CFSTR("%s count: %ld\n"), label, (long)CFArrayGetCount(ppi)); 106 CFArrayForEach(ppi, ^(const void *value) { 107 char buf[160]; 108 SOSPeerInfoRef peer = (SOSPeerInfoRef)value; 109 if(!peer) { return; } 110 CFStringRef peerName = SOSPeerInfoGetPeerName(peer); 111 CFStringRef devtype = SOSPeerInfoGetPeerDeviceType(peer); 112 CFStringRef peerID = SOSPeerInfoGetPeerID(peer); 113 CFStringRef transportType = CFSTR("KVS"); 114 CFStringRef deviceID = CFSTR(""); 115 CFStringRef machineID = CFSTR(""); 116 CFDictionaryRef gestalt = SOSPeerInfoCopyPeerGestalt(peer); 117 CFStringRef osVersion = NULL; 118 if(gestalt) { 119 osVersion = CFDictionaryGetValue(gestalt, CFSTR("OSVersion")); 120 } else { 121 osVersion = CFSTR("Unknown"); 122 } 123 124 if(SOSPeerInfoVersionHasV2Data(peer)){ 125 CFDictionaryRef v2Dictionary = peer->v2Dictionary; 126 if(v2Dictionary) { 127 transportType = CFDictionaryGetValue(v2Dictionary, CFSTR("TransportType")); 128 deviceID = CFDictionaryGetValue(v2Dictionary, CFSTR("DeviceID")); 129 machineID = CFDictionaryGetValue(v2Dictionary, CFSTR("MachineIDKey")); 130 } 131 } 132 char *pname = CFStringToCString(peerName); 133 char *dname = CFStringToCString(devtype); 134 char *tname = CFStringToCString(transportType); 135 char *iname = CFStringToCString(deviceID); 136 char *mname = CFStringToCString(machineID); 137 const char *me = CFEqualSafe(mypeerID, peerID) ? "me>" : " "; 138 139 140 snprintf(buf, 160, "%s %s: %-16s dev:%-16s trn:%-16s devid:%-36s mid: %-36s", me, label, pname, dname, tname, iname, mname); 141 142 free(pname); 143 free(dname); 144 free(tname); 145 free(iname); 146 free(mname); 147 148 // %s in (Core)Foundation format strings treats the string as MacRoman, need to do this to guarantee UTF8 handling 149 CFStringRef bufstr = CFStringCreateWithCString(NULL, buf, kCFStringEncodingUTF8); 150 CFStringRef pid = SOSPeerInfoGetPeerID(peer); 151 CFIndex vers = SOSPeerInfoGetVersion(peer); 152 bool isCKKSForAll = SOSPeerInfoSupportsCKKSForAll(peer); 153 printmsg(CFSTR("%@ pid:%@ V%d %@ OS:%@\n"), bufstr, pid, vers, isCKKSForAll ? CFSTR("c4a") : CFSTR("SOS"), osVersion ?: CFSTR("")); 154 CFRelease(bufstr); 155 156 CFReleaseNull(gestalt); 157 }); 158 } else { 159 printmsg(CFSTR("No %s, error: %@\n"), label, error); 160 } 161 CFReleaseNull(ppi); 162 CFReleaseNull(error); 163 } 164 165 void SOSCCDumpCircleInformation() 166 { 167 CFErrorRef error = NULL; 168 CFArrayRef generations = NULL; 169 bool is_accountKeyIsTrusted = false; 170 __block int count = 0; 171 172 173 SOSCCStatus ccstatus = SOSCCThisDeviceIsInCircle(&error); 174 printmsg(CFSTR("ccstatus: %s (%d)\n"), getSOSCCStatusDescription(ccstatus), ccstatus); 175 if (error != NULL) { 176 printmsg(CFSTR("Error checking circle status: %@\n"), error); 177 } 178 CFReleaseNull(error); 179 180 enum DepartureReason departureReason = SOSCCGetLastDepartureReason(&error); 181 printmsg(CFSTR("LastDepartureReason: %s (%d)\n"), getSOSCCLastDepartureReasonDescription(departureReason), departureReason); 182 if (error != NULL) { 183 printmsg(CFSTR("Error checking last departure reason error: %@\n"), error); 184 } 185 CFReleaseNull(error); 186 187 is_accountKeyIsTrusted = SOSCCValidateUserPublic(&error); 188 if(is_accountKeyIsTrusted) 189 printmsg(CFSTR("Account user public is trusted\n")); 190 else 191 printmsg(CFSTR("Account user public is not trusted error:(%@)\n"), error); 192 CFReleaseNull(error); 193 194 generations = SOSCCCopyGenerationPeerInfo(&error); 195 if(generations) { 196 CFArrayForEach(generations, ^(const void *value) { 197 count++; 198 if(count%2 == 0) 199 printmsg(CFSTR("Circle name: %@, "),value); 200 201 if(count%2 != 0) { 202 CFStringRef genDesc = SOSGenerationCountCopyDescription(value); 203 printmsg(CFSTR("Generation Count: %@"), genDesc); 204 CFReleaseNull(genDesc); 205 } 206 printmsg(CFSTR("%s\n"), ""); 207 }); 208 } else { 209 printmsg(CFSTR("No generation count: %@\n"), error); 210 } 211 CFReleaseNull(generations); 212 CFReleaseNull(error); 213 214 SOSPeerInfoRef me = SOSCCCopyMyPeerInfo(NULL); 215 CFStringRef mypeerID = SOSPeerInfoGetPeerID(me); 216 217 printPeerInfos(" Peers", mypeerID, ^(CFErrorRef *error) { return SOSCCCopyValidPeerPeerInfo(error); }); 218 printPeerInfos(" Invalid", mypeerID, ^(CFErrorRef *error) { return SOSCCCopyNotValidPeerPeerInfo(error); }); 219 printPeerInfos(" Retired", mypeerID, ^(CFErrorRef *error) { return SOSCCCopyRetirementPeerInfo(error); }); 220 printPeerInfos(" Concur", mypeerID, ^(CFErrorRef *error) { return SOSCCCopyConcurringPeerPeerInfo(error); }); 221 printPeerInfos("Applicants", mypeerID, ^(CFErrorRef *error) { return SOSCCCopyApplicantPeerInfo(error); }); 222 223 CFReleaseNull(me); 224 CFReleaseNull(error); 225 } 226 227 void 228 SOSCCDumpEngineInformation(void) 229 { 230 CFErrorRef error = NULL; 231 232 printmsg(CFSTR("Engine state:\n")); 233 if (!SOSCCForEachEngineStateAsString(&error, ^(CFStringRef oneStateString) { 234 printmsg(CFSTR("%@\n"), oneStateString); 235 })) { 236 printmsg(CFSTR("No engine state, got error: %@\n"), error); 237 } 238 } 239 240 // security sync -o 241 void 242 SOSCCDumpViewUnwarePeers(void) 243 { 244 SOSPeerInfoRef me = SOSCCCopyMyPeerInfo(NULL); 245 CFStringRef mypeerID = SOSPeerInfoGetPeerID(me); 246 247 printPeerInfos("view-unaware", mypeerID, ^(CFErrorRef *error) { return SOSCCCopyViewUnawarePeerInfo(error); }); 248 249 CFReleaseNull(me); 250 } 251 252 /* KVS Dumping Support for iCloud Keychain */ 253 254 static CFTypeRef getObjectsFromCloud(CFArrayRef keysToGet, dispatch_queue_t processQueue, dispatch_group_t dgroup) 255 { 256 __block CFTypeRef object = NULL; 257 258 const uint64_t maxTimeToWaitInSeconds = 30ull * NSEC_PER_SEC; 259 dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0); 260 dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds); 261 262 dispatch_group_enter(dgroup); 263 264 CloudKeychainReplyBlock replyBlock = 265 ^ (CFDictionaryRef returnedValues, CFErrorRef error) 266 { 267 secinfo("sync", "SOSCloudKeychainGetObjectsFromCloud returned: %@", returnedValues); 268 object = returnedValues; 269 if (object) 270 CFRetain(object); 271 if (error) 272 { 273 secerror("SOSCloudKeychainGetObjectsFromCloud returned error: %@", error); 274 } 275 dispatch_group_leave(dgroup); 276 secinfo("sync", "SOSCloudKeychainGetObjectsFromCloud block exit: %@", object); 277 dispatch_semaphore_signal(waitSemaphore); 278 }; 279 280 if (!keysToGet) 281 SOSCloudKeychainGetAllObjectsFromCloud(processQueue, replyBlock); 282 else 283 SOSCloudKeychainGetObjectsFromCloud(keysToGet, processQueue, replyBlock); 284 285 dispatch_semaphore_wait(waitSemaphore, finishTime); 286 if (object && (CFGetTypeID(object) == CFNullGetTypeID())) // return a NULL instead of a CFNull 287 { 288 CFRelease(object); 289 object = NULL; 290 } 291 secerror("returned: %@", object); 292 return object; 293 } 294 295 static CFStringRef printFullDataString(CFDataRef data){ 296 __block CFStringRef fullData = NULL; 297 298 BufferPerformWithHexString(CFDataGetBytePtr(data), CFDataGetLength(data), ^(CFStringRef dataHex) { 299 fullData = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@"), dataHex); 300 }); 301 302 return fullData; 303 } 304 305 static void displayLastKeyParameters(CFTypeRef key, CFTypeRef value) 306 { 307 CFDataRef valueAsData = asData(value, NULL); 308 if(valueAsData){ 309 CFDataRef dateData = CFDataCreateCopyFromRange(kCFAllocatorDefault, valueAsData, CFRangeMake(0, DATE_LENGTH)); 310 CFDataRef keyParameterData = CFDataCreateCopyFromPositions(kCFAllocatorDefault, valueAsData, DATE_LENGTH, CFDataGetLength(valueAsData)); 311 CFStringRef dateString = CFStringCreateFromExternalRepresentation(kCFAllocatorDefault, dateData, kCFStringEncodingUTF8); 312 CFStringRef keyParameterDescription = UserParametersDescription(keyParameterData); 313 if(keyParameterDescription) 314 printmsg(CFSTR("%@: %@: %@\n"), key, dateString, keyParameterDescription); 315 else 316 printmsg(CFSTR("%@: %@\n"), key, printFullDataString(value)); 317 CFReleaseNull(dateString); 318 CFReleaseNull(keyParameterData); 319 CFReleaseNull(dateData); 320 CFReleaseNull(keyParameterDescription); 321 } 322 else{ 323 printmsg(CFSTR("%@: %@\n"), key, value); 324 } 325 } 326 327 static void displayKeyParameters(CFTypeRef key, CFTypeRef value) 328 { 329 if(isData(value)){ 330 CFStringRef keyParameterDescription = UserParametersDescription((CFDataRef)value); 331 332 if(keyParameterDescription) 333 printmsg(CFSTR("%@: %@\n"), key, keyParameterDescription); 334 else 335 printmsg(CFSTR("%@: %@\n"), key, value); 336 337 CFReleaseNull(keyParameterDescription); 338 } 339 else{ 340 printmsg(CFSTR("%@: %@\n"), key, value); 341 } 342 } 343 344 static void displayLastCircle(CFTypeRef key, CFTypeRef value) 345 { 346 CFDataRef valueAsData = asData(value, NULL); 347 if(valueAsData){ 348 CFErrorRef localError = NULL; 349 350 CFDataRef dateData = CFDataCreateCopyFromRange(kCFAllocatorDefault, valueAsData, CFRangeMake(0, DATE_LENGTH)); 351 CFDataRef circleData = CFDataCreateCopyFromPositions(kCFAllocatorDefault, valueAsData, DATE_LENGTH, CFDataGetLength(valueAsData)); 352 CFStringRef dateString = CFStringCreateFromExternalRepresentation(kCFAllocatorDefault, dateData, kCFStringEncodingUTF8); 353 SOSCircleRef circle = SOSCircleCreateFromData(NULL, (CFDataRef) circleData, &localError); 354 355 if(circle){ 356 CFIndex size = 5; 357 CFNumberRef idLength = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &size); 358 CFDictionaryRef format = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, CFSTR("SyncD"), CFSTR("SyncD"), CFSTR("idLength"), idLength, NULL); 359 printmsgWithFormatOptions(format, CFSTR("%@: %@: %@\n"), key, dateString, circle); 360 CFReleaseNull(idLength); 361 CFReleaseNull(format); 362 363 } 364 else 365 printmsg(CFSTR("%@: %@\n"), key, printFullDataString(circleData)); 366 367 CFReleaseNull(dateString); 368 CFReleaseNull(circleData); 369 CFReleaseSafe(circle); 370 CFReleaseNull(dateData); 371 CFReleaseNull(localError); 372 } 373 else{ 374 printmsg(CFSTR("%@: %@\n"), key, value); 375 } 376 } 377 378 static void displayCircle(CFTypeRef key, CFTypeRef value) 379 { 380 CFDataRef circleData = (CFDataRef)value; 381 382 CFErrorRef localError = NULL; 383 if (isData(circleData)) 384 { 385 CFIndex size = 5; 386 CFNumberRef idLength = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &size); 387 CFDictionaryRef format = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, CFSTR("SyncD"), CFSTR("SyncD"), CFSTR("idLength"), idLength, NULL); 388 SOSCircleRef circle = SOSCircleCreateFromData(NULL, circleData, &localError); 389 printmsgWithFormatOptions(format, CFSTR("%@: %@\n"), key, circle); 390 CFReleaseSafe(circle); 391 CFReleaseNull(idLength); 392 CFReleaseNull(format); 393 394 } 395 else 396 printmsg(CFSTR("%@: %@\n"), key, value); 397 } 398 399 static void displayMessage(CFTypeRef key, CFTypeRef value) 400 { 401 CFDataRef message = (CFDataRef)value; 402 if(isData(message)){ 403 const char* messageType = SecOTRPacketTypeString(message); 404 printmsg(CFSTR("%@: %s: %ld\n"), key, messageType, CFDataGetLength(message)); 405 } 406 else 407 printmsg(CFSTR("%@: %@\n"), key, value); 408 } 409 410 static void printEverything(CFTypeRef objects) 411 { 412 CFDictionaryForEach(objects, ^(const void *key, const void *value) { 413 if (isData(value)) 414 { 415 printmsg(CFSTR("%@: %@\n\n"), key, printFullDataString(value)); 416 } 417 else 418 printmsg(CFSTR("%@: %@\n"), key, value); 419 }); 420 421 } 422 423 static void decodeForKeyType(CFTypeRef key, CFTypeRef value, SOSKVSKeyType type){ 424 switch (type) { 425 case kCircleKey: 426 displayCircle(key, value); 427 break; 428 case kRetirementKey: 429 case kMessageKey: 430 displayMessage(key, value); 431 break; 432 case kParametersKey: 433 displayKeyParameters(key, value); 434 break; 435 case kLastKeyParameterKey: 436 displayLastKeyParameters(key, value); 437 break; 438 case kLastCircleKey: 439 displayLastCircle(key, value); 440 break; 441 case kInitialSyncKey: 442 case kAccountChangedKey: 443 case kDebugInfoKey: 444 case kRingKey: 445 default: 446 printmsg(CFSTR("%@: %@\n"), key, value); 447 break; 448 } 449 } 450 451 static void decodeAllTheValues(CFTypeRef objects){ 452 SOSKVSKeyType keyType = 0; 453 __block bool didPrint = false; 454 455 for (keyType = 0; keyType <= MAXKVSKEYTYPE; keyType++){ 456 CFDictionaryForEach(objects, ^(const void *key, const void *value) { 457 if(SOSKVSKeyGetKeyType(key) == keyType){ 458 decodeForKeyType(key, value, keyType); 459 didPrint = true; 460 } 461 }); 462 if(didPrint) 463 printmsg(CFSTR("%@\n"), CFSTR("")); 464 didPrint = false; 465 } 466 } 467 468 bool SOSCCDumpCircleKVSInformation(char *itemName) { 469 CFArrayRef keysToGet = NULL; 470 if (itemName) 471 { 472 CFStringRef itemStr = CFStringCreateWithCString(kCFAllocatorDefault, itemName, kCFStringEncodingUTF8); 473 fprintf(outFile, "Retrieving %s from KVS\n", itemName); 474 keysToGet = CFArrayCreateForCFTypes(kCFAllocatorDefault, itemStr, NULL); 475 CFReleaseSafe(itemStr); 476 } 477 dispatch_queue_t generalq = dispatch_queue_create("general", DISPATCH_QUEUE_SERIAL); 478 dispatch_group_t work_group = dispatch_group_create(); 479 CFTypeRef objects = getObjectsFromCloud(keysToGet, generalq, work_group); 480 CFReleaseSafe(keysToGet); 481 if (objects) 482 { 483 fprintf(outFile, "All keys and values straight from KVS\n"); 484 printEverything(objects); 485 fprintf(outFile, "\nAll values in decoded form...\n"); 486 decodeAllTheValues(objects); 487 } 488 fprintf(outFile, "\n"); 489 return true; 490 }