5 // Created by Richard Murphy on 1/27/16.
10 #include "SOSCloudCircleInternal.h"
15 #include <sys/utsname.h>
21 #include <Security/SecItem.h>
23 #include <CoreFoundation/CFNumber.h>
24 #include <CoreFoundation/CFString.h>
26 #include <Security/SecureObjectSync/SOSCloudCircle.h>
27 #include <Security/SecureObjectSync/SOSCloudCircleInternal.h>
28 #include <Security/SecureObjectSync/SOSPeerInfo.h>
29 #include <Security/SecureObjectSync/SOSPeerInfoPriv.h>
30 #include <Security/SecureObjectSync/SOSPeerInfoV2.h>
31 #include <Security/SecureObjectSync/SOSUserKeygen.h>
32 #include <Security/SecureObjectSync/SOSKVSKeys.h>
33 #include <securityd/SOSCloudCircleServer.h>
34 #include <Security/SecOTRSession.h>
35 #include <SOSCircle/CKBridge/SOSCloudKeychainClient.h>
37 #include <utilities/SecCFWrappers.h>
38 #include <utilities/debugging.h>
40 #include <SecurityTool/readline.h>
42 #include "keychain_log.h"
43 #include "secToolFileIO.h"
44 #include "secViewDisplay.h"
47 #include <Security/SecPasswordGenerate.h>
49 /* Copied from CFPriv.h */
50 // #include <CoreFoundation/CFPriv.h>
52 CF_EXPORT CFDictionaryRef
_CFCopySystemVersionDictionary(void);
53 CF_EXPORT
const CFStringRef _kCFSystemVersionProductNameKey
;
54 CF_EXPORT
const CFStringRef _kCFSystemVersionProductVersionKey
;
55 CF_EXPORT
const CFStringRef _kCFSystemVersionBuildVersionKey
;
59 static char *CFDictionaryCopyCStringWithDefault(CFDictionaryRef dict
, const void *key
, char *defaultString
) {
61 require_quiet(dict
, use_default
);
62 CFStringRef val
= CFDictionaryGetValue(dict
, key
);
63 retval
= CFStringToCString(val
);
65 if(!retval
) retval
= strdup(defaultString
);
71 #define MAXKVSKEYTYPE kUnknownKey
72 #define DATE_LENGTH 18
78 // Created by Richard Murphy on 1/22/16.
84 #include <utilities/SecCFWrappers.h>
86 #define printmsg(format, ...) _printcfmsg(outFile, NULL, format, __VA_ARGS__)
87 #define printmsgWithFormatOptions(formatOptions, format, ...) _printcfmsg(outFile, formatOptions, format, __VA_ARGS__)
88 #define printerr(format, ...) _printcfmsg(errFile, NULL, format, __VA_ARGS__)
94 void _printcfmsg(FILE *ff
, CFDictionaryRef formatOptions
, CFStringRef format
, ...)
97 va_start(args
, format
);
98 CFStringRef message
= CFStringCreateWithFormatAndArguments(kCFAllocatorDefault
, formatOptions
, format
, args
);
100 CFStringPerformWithCString(message
, ^(const char *utf8String
) { fprintf(ff
, utf8String
, ""); });
105 int setOutputTo(char *dir
, char *filename
) {
108 if(dir
&& filename
) {
109 pathlen
= strlen(dir
) + strlen(filename
) + 2;
111 snprintf(path
, pathlen
, "%s/%s", dir
, filename
);
112 outFile
= fopen(path
, "a");
113 } else if(dir
|| filename
) {
123 void closeOutput(void) {
124 if(outFile
!= stdout
) {
130 int copyFileToOutputDir(char *dir
, char *toCopy
) {
131 char *bname
= basename(toCopy
);
134 copyfile_state_t cpfilestate
= copyfile_state_alloc();
136 status
= snprintf(destpath
, 256, "%s/%s", dir
, bname
);
137 if(status
< 0 || status
> 256) return -1;
139 int retval
= copyfile(toCopy
, destpath
, cpfilestate
, COPYFILE_ALL
);
141 copyfile_state_free(cpfilestate
);
147 static const char *getSOSCCStatusDescription(SOSCCStatus ccstatus
)
151 case kSOSCCInCircle
: return "In Circle";
152 case kSOSCCNotInCircle
: return "Not in Circle";
153 case kSOSCCRequestPending
: return "Request pending";
154 case kSOSCCCircleAbsent
: return "Circle absent";
155 case kSOSCCError
: return "Circle error";
158 return "<unknown ccstatus>";
163 static void printPeerInfos(char *label
, CFArrayRef (^getArray
)(CFErrorRef
*error
)) {
164 CFErrorRef error
= NULL
;
165 CFArrayRef ppi
= getArray(&error
);
166 SOSPeerInfoRef me
= SOSCCCopyMyPeerInfo(NULL
);
167 CFStringRef mypeerID
= SOSPeerInfoGetPeerID(me
);
170 printmsg(CFSTR("%s count: %ld\n"), label
, (long)CFArrayGetCount(ppi
));
171 CFArrayForEach(ppi
, ^(const void *value
) {
173 SOSPeerInfoRef peer
= (SOSPeerInfoRef
)value
;
174 CFIndex version
= SOSPeerInfoGetVersion(peer
);
175 CFStringRef peerName
= SOSPeerInfoGetPeerName(peer
);
176 CFStringRef devtype
= SOSPeerInfoGetPeerDeviceType(peer
);
177 CFStringRef peerID
= SOSPeerInfoGetPeerID(peer
);
178 CFStringRef transportType
= CFSTR("KVS");
179 CFStringRef deviceID
= CFSTR("");
180 CFDictionaryRef gestalt
= SOSPeerInfoCopyPeerGestalt(peer
);
181 CFStringRef osVersion
= CFDictionaryGetValue(gestalt
, CFSTR("OSVersion"));
182 CFReleaseNull(gestalt
);
186 CFDictionaryRef v2Dictionary
= peer
->v2Dictionary
;
187 transportType
= CFDictionaryGetValue(v2Dictionary
, sTransportType
);
188 deviceID
= CFDictionaryGetValue(v2Dictionary
, sDeviceID
);
190 char *pname
= CFStringToCString(peerName
);
191 char *dname
= CFStringToCString(devtype
);
192 char *tname
= CFStringToCString(transportType
);
193 char *iname
= CFStringToCString(deviceID
);
194 char *osname
= CFStringToCString(osVersion
);
195 const char *me
= CFEqualSafe(mypeerID
, peerID
) ? "me>" : " ";
198 snprintf(buf
, 160, "%s %s: %-16s %-16s %-16s %-16s", me
, label
, pname
, dname
, tname
, iname
);
202 CFStringRef pid
= SOSPeerInfoGetPeerID(peer
);
203 CFIndex vers
= SOSPeerInfoGetVersion(peer
);
204 printmsg(CFSTR("%s %@ V%d OS:%s\n"), buf
, pid
, vers
, osname
);
208 printmsg(CFSTR("No %s, error: %@\n"), label
, error
);
211 CFReleaseNull(error
);
214 static void dumpCircleInfo()
216 CFErrorRef error
= NULL
;
217 CFArrayRef generations
= NULL
;
218 CFArrayRef confirmedDigests
= NULL
;
219 bool is_user_public_trusted
= false;
220 __block
int count
= 0;
222 SOSCCStatus ccstatus
= SOSCCThisDeviceIsInCircle(&error
);
223 if(ccstatus
== kSOSCCError
) {
224 printmsg(CFSTR("End of Dump - unable to proceed due to ccstatus (%s) error: %@\n"), getSOSCCStatusDescription(ccstatus
), error
);
227 printmsg(CFSTR("ccstatus: %s (%d)\n"), getSOSCCStatusDescription(ccstatus
), ccstatus
, error
);
229 is_user_public_trusted
= SOSCCValidateUserPublic(&error
);
230 if(is_user_public_trusted
)
231 printmsg(CFSTR("Account user public is trusted%@"),CFSTR("\n"));
233 printmsg(CFSTR("Account user public is not trusted error:(%@)\n"), error
);
234 CFReleaseNull(error
);
236 generations
= SOSCCCopyGenerationPeerInfo(&error
);
238 CFArrayForEach(generations
, ^(const void *value
) {
241 printmsg(CFSTR("Circle name: %@, "),value
);
244 CFStringRef genDesc
= SOSGenerationCountCopyDescription(value
);
245 printmsg(CFSTR("Generation Count: %@"), genDesc
);
246 CFReleaseNull(genDesc
);
248 printmsg(CFSTR("%s\n"), "");
251 printmsg(CFSTR("No generation count: %@\n"), error
);
253 CFReleaseNull(generations
);
254 CFReleaseNull(error
);
256 printPeerInfos(" Peers", ^(CFErrorRef
*error
) { return SOSCCCopyValidPeerPeerInfo(error
); });
257 printPeerInfos(" Invalid", ^(CFErrorRef
*error
) { return SOSCCCopyNotValidPeerPeerInfo(error
); });
258 printPeerInfos(" Retired", ^(CFErrorRef
*error
) { return SOSCCCopyRetirementPeerInfo(error
); });
259 printPeerInfos(" Concur", ^(CFErrorRef
*error
) { return SOSCCCopyConcurringPeerPeerInfo(error
); });
260 printPeerInfos("Applicants", ^(CFErrorRef
*error
) { return SOSCCCopyApplicantPeerInfo(error
); });
262 confirmedDigests
= SOSCCCopyEngineState(&error
);
266 CFArrayForEach(confirmedDigests
, ^(const void *value
) {
269 printmsg(CFSTR("%@"), value
);
272 CFStringRef hexDigest
= CFDataCopyHexString(value
);
273 printmsg(CFSTR(" %@\n"), hexDigest
);
274 CFReleaseSafe(hexDigest
);
279 printmsg(CFSTR("No engine peers: %@\n"), error
);
280 CFReleaseNull(confirmedDigests
);
283 static CFTypeRef
getObjectsFromCloud(CFArrayRef keysToGet
, dispatch_queue_t processQueue
, dispatch_group_t dgroup
)
285 __block CFTypeRef object
= NULL
;
287 const uint64_t maxTimeToWaitInSeconds
= 30ull * NSEC_PER_SEC
;
288 dispatch_semaphore_t waitSemaphore
= dispatch_semaphore_create(0);
289 dispatch_time_t finishTime
= dispatch_time(DISPATCH_TIME_NOW
, maxTimeToWaitInSeconds
);
291 dispatch_group_enter(dgroup
);
293 CloudKeychainReplyBlock replyBlock
=
294 ^ (CFDictionaryRef returnedValues
, CFErrorRef error
)
296 secinfo("sync", "SOSCloudKeychainGetObjectsFromCloud returned: %@", returnedValues
);
297 object
= returnedValues
;
302 secerror("SOSCloudKeychainGetObjectsFromCloud returned error: %@", error
);
303 // CFRelease(*error);
305 dispatch_group_leave(dgroup
);
306 secinfo("sync", "SOSCloudKeychainGetObjectsFromCloud block exit: %@", object
);
307 dispatch_semaphore_signal(waitSemaphore
);
311 SOSCloudKeychainGetAllObjectsFromCloud(processQueue
, replyBlock
);
313 SOSCloudKeychainGetObjectsFromCloud(keysToGet
, processQueue
, replyBlock
);
315 dispatch_semaphore_wait(waitSemaphore
, finishTime
);
316 dispatch_release(waitSemaphore
);
317 if (object
&& (CFGetTypeID(object
) == CFNullGetTypeID())) // return a NULL instead of a CFNull
322 secerror("returned: %@", object
);
326 static CFStringRef
printFullDataString(CFDataRef data
){
327 __block CFStringRef fullData
= NULL
;
329 BufferPerformWithHexString(CFDataGetBytePtr(data
), CFDataGetLength(data
), ^(CFStringRef dataHex
) {
330 fullData
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%@"), dataHex
);
336 static void displayLastKeyParameters(CFTypeRef key
, CFTypeRef value
)
338 CFDataRef valueAsData
= asData(value
, NULL
);
340 CFDataRef dateData
= CFDataCreateCopyFromRange(kCFAllocatorDefault
, valueAsData
, CFRangeMake(0, DATE_LENGTH
));
341 CFDataRef keyParameterData
= CFDataCreateCopyFromPositions(kCFAllocatorDefault
, valueAsData
, DATE_LENGTH
, CFDataGetLength(valueAsData
));
342 CFStringRef dateString
= CFStringCreateFromExternalRepresentation(kCFAllocatorDefault
, dateData
, kCFStringEncodingUTF8
);
343 CFStringRef keyParameterDescription
= UserParametersDescription(keyParameterData
);
344 if(keyParameterDescription
)
345 printmsg(CFSTR("%@: %@: %@\n"), key
, dateString
, keyParameterDescription
);
347 printmsg(CFSTR("%@: %@\n"), key
, printFullDataString(value
));
348 CFReleaseNull(dateString
);
349 CFReleaseNull(keyParameterData
);
350 CFReleaseNull(dateData
);
351 CFReleaseNull(keyParameterDescription
);
354 printmsg(CFSTR("%@: %@\n"), key
, value
);
358 static void displayKeyParameters(CFTypeRef key
, CFTypeRef value
)
361 CFStringRef keyParameterDescription
= UserParametersDescription((CFDataRef
)value
);
363 if(keyParameterDescription
)
364 printmsg(CFSTR("%@: %@\n"), key
, keyParameterDescription
);
366 printmsg(CFSTR("%@: %@\n"), key
, value
);
368 CFReleaseNull(keyParameterDescription
);
371 printmsg(CFSTR("%@: %@\n"), key
, value
);
375 static void displayLastCircle(CFTypeRef key
, CFTypeRef value
)
377 CFDataRef valueAsData
= asData(value
, NULL
);
379 CFErrorRef localError
= NULL
;
381 CFDataRef dateData
= CFDataCreateCopyFromRange(kCFAllocatorDefault
, valueAsData
, CFRangeMake(0, DATE_LENGTH
));
382 CFDataRef circleData
= CFDataCreateCopyFromPositions(kCFAllocatorDefault
, valueAsData
, DATE_LENGTH
, CFDataGetLength(valueAsData
));
383 CFStringRef dateString
= CFStringCreateFromExternalRepresentation(kCFAllocatorDefault
, dateData
, kCFStringEncodingUTF8
);
384 SOSCircleRef circle
= SOSCircleCreateFromData(NULL
, (CFDataRef
) circleData
, &localError
);
388 CFNumberRef idLength
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberCFIndexType
, &size
);
389 CFDictionaryRef format
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, CFSTR("SyncD"), CFSTR("SyncD"), CFSTR("idLength"), idLength
, NULL
);
390 printmsgWithFormatOptions(format
, CFSTR("%@: %@: %@\n"), key
, dateString
, circle
);
391 CFReleaseNull(idLength
);
392 CFReleaseNull(format
);
396 printmsg(CFSTR("%@: %@\n"), key
, printFullDataString(circleData
));
398 CFReleaseNull(dateString
);
399 CFReleaseNull(circleData
);
400 CFReleaseSafe(circle
);
401 CFReleaseNull(dateData
);
402 CFReleaseNull(localError
);
405 printmsg(CFSTR("%@: %@\n"), key
, value
);
409 static void displayCircle(CFTypeRef key
, CFTypeRef value
)
411 CFDataRef circleData
= (CFDataRef
)value
;
413 CFErrorRef localError
= NULL
;
414 if (isData(circleData
))
417 CFNumberRef idLength
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberCFIndexType
, &size
);
418 CFDictionaryRef format
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, CFSTR("SyncD"), CFSTR("SyncD"), CFSTR("idLength"), idLength
, NULL
);
419 SOSCircleRef circle
= SOSCircleCreateFromData(NULL
, circleData
, &localError
);
420 printmsgWithFormatOptions(format
, CFSTR("%@: %@\n"), key
, circle
);
421 CFReleaseSafe(circle
);
422 CFReleaseNull(idLength
);
423 CFReleaseNull(format
);
427 printmsg(CFSTR("%@: %@\n"), key
, value
);
430 static void displayMessage(CFTypeRef key
, CFTypeRef value
)
432 CFDataRef message
= (CFDataRef
)value
;
434 const char* messageType
= SecOTRPacketTypeString(message
);
435 printmsg(CFSTR("%@: %s: %ld\n"), key
, messageType
, CFDataGetLength(message
));
438 printmsg(CFSTR("%@: %@\n"), key
, value
);
441 static void printEverything(CFTypeRef objects
)
443 CFDictionaryForEach(objects
, ^(const void *key
, const void *value
) {
446 printmsg(CFSTR("%@: %@\n\n"), key
, printFullDataString(value
));
449 printmsg(CFSTR("%@: %@\n"), key
, value
);
454 static void decodeForKeyType(CFTypeRef key
, CFTypeRef value
, SOSKVSKeyType type
){
457 displayCircle(key
, value
);
461 displayMessage(key
, value
);
464 displayKeyParameters(key
, value
);
466 case kLastKeyParameterKey
:
467 displayLastKeyParameters(key
, value
);
470 displayLastCircle(key
, value
);
472 case kInitialSyncKey
:
473 case kAccountChangedKey
:
478 printmsg(CFSTR("%@: %@\n"), key
, value
);
483 static void decodeAllTheValues(CFTypeRef objects
){
484 SOSKVSKeyType keyType
= 0;
485 __block
bool didPrint
= false;
487 for (keyType
= 0; keyType
<= MAXKVSKEYTYPE
; keyType
++){
488 CFDictionaryForEach(objects
, ^(const void *key
, const void *value
) {
489 if(SOSKVSKeyGetKeyType(key
) == keyType
){
490 decodeForKeyType(key
, value
, keyType
);
495 printmsg(CFSTR("%@\n"), CFSTR(""));
499 static bool dumpKVS(char *itemName
, CFErrorRef
*err
)
501 CFArrayRef keysToGet
= NULL
;
504 CFStringRef itemStr
= CFStringCreateWithCString(kCFAllocatorDefault
, itemName
, kCFStringEncodingUTF8
);
505 fprintf(outFile
, "Retrieving %s from KVS\n", itemName
);
506 keysToGet
= CFArrayCreateForCFTypes(kCFAllocatorDefault
, itemStr
, NULL
);
507 CFReleaseSafe(itemStr
);
509 dispatch_queue_t generalq
= dispatch_queue_create("general", DISPATCH_QUEUE_SERIAL
);
510 dispatch_group_t work_group
= dispatch_group_create();
511 CFTypeRef objects
= getObjectsFromCloud(keysToGet
, generalq
, work_group
);
512 CFReleaseSafe(keysToGet
);
515 fprintf(outFile
, "All keys and values straight from KVS\n");
516 printEverything(objects
);
517 fprintf(outFile
, "\nAll values in decoded form...\n");
518 decodeAllTheValues(objects
);
520 fprintf(outFile
, "\n");
525 static char *createDateStrNow() {
532 tmstruct
= localtime(&clock
);
535 sprintf(retval
, "%04d%02d%02d%02d%02d%02d", tmstruct
->tm_year
+1900, tmstruct
->tm_mon
+1, tmstruct
->tm_mday
, tmstruct
->tm_hour
, tmstruct
->tm_min
, tmstruct
->tm_sec
);
539 #if !TARGET_OS_EMBEDDED
540 static char *assemblePath(char *dir
, char *fname
) {
541 size_t length
= strlen(dir
) + strlen(fname
) + 2;
542 char *outputDir
= malloc(length
);
543 int status
= snprintf(outputDir
, length
, "%s/%s", dir
, fname
);
544 if(status
< 0) return NULL
;
548 static char *homedirPath() {
550 struct passwd
* pwd
= getpwuid(getuid());
551 if (pwd
) homeDir
= pwd
->pw_dir
;
556 static char *sysdiagnose_dir(const char *passedIn
, const char *hostname
, const char *productVersion
, const char *now
) {
557 if(passedIn
) return (char *) passedIn
;
559 // OUTPUTBASE=ckcdiagnose_snapshot_${HOSTNAME}_${PRODUCT_VERSION}_${NOW}
560 char *outputParent
= NULL
;
561 size_t length
= strlen("ckcdiagnose_snapshot___") + strlen(hostname
) + strlen(productVersion
) + strlen(now
) + 1;
562 char *outputBase
= malloc(length
);
563 int status
= snprintf(outputBase
, length
, "ckcdiagnose_snapshot_%s_%s_%s", hostname
, productVersion
, now
);
564 if(status
< 0) outputBase
= "";
566 #if TARGET_OS_EMBEDDED
567 outputParent
= "/Library/Logs/CrashReporter";
569 outputParent
= "/var/tmp";
571 length
= strlen(outputParent
) + strlen(outputBase
) + 2;
572 char *outputDir
= malloc(length
);
573 status
= snprintf(outputDir
, length
, "%s/%s", outputParent
, outputBase
);
574 if(status
< 0) return NULL
;
579 static char *sysdiagnose_dump(const char *dirname
) {
580 char *outputDir
= NULL
;
582 char *productName
= NULL
;
583 char *productVersion
= NULL
;
584 char *buildVersion
= NULL
;
585 char *keysToRegister
= NULL
;
586 char *cloudkeychainproxy3
= NULL
;
587 char *now
= createDateStrNow();
589 CFDictionaryRef sysfdef
= _CFCopySystemVersionDictionary();
590 productName
= CFDictionaryCopyCStringWithDefault(sysfdef
, _kCFSystemVersionProductNameKey
, "unknownProduct");
591 productVersion
= CFDictionaryCopyCStringWithDefault(sysfdef
, _kCFSystemVersionProductVersionKey
, "unknownProductVersion");
592 buildVersion
= CFDictionaryCopyCStringWithDefault(sysfdef
, _kCFSystemVersionBuildVersionKey
, "unknownVersion");
594 if(gethostname(hostname
, 80)) {
595 strcpy(hostname
, "unknownhost");
598 #if TARGET_OS_EMBEDDED
599 keysToRegister
= "/private/var/preferences/com.apple.security.cloudkeychainproxy3.keysToRegister.plist";
600 cloudkeychainproxy3
= "/var/mobile/Library/SyncedPreferences/com.apple.security.cloudkeychainproxy3.plist";
602 char *homeDir
= homedirPath();
603 keysToRegister
= assemblePath(homeDir
, "Library/Preferences/com.apple.security.cloudkeychainproxy3.keysToRegister.plist");
604 cloudkeychainproxy3
= assemblePath(homeDir
, "Library/SyncedPreferences/com.apple.security.cloudkeychainproxy3.plist");
607 outputDir
= sysdiagnose_dir(dirname
, hostname
, productVersion
, now
);
608 if(!outputDir
) goto errOut
;
610 mkdir(outputDir
, 0700);
612 setOutputTo(outputDir
, "sw_vers.log");
613 // report uname stuff + hostname
614 fprintf(outFile
, "HostName: %s\n", hostname
);
615 fprintf(outFile
, "ProductName: %s\n", productName
);
616 fprintf(outFile
, "ProductVersion: %s\n", productVersion
);
617 fprintf(outFile
, "BuildVersion: %s\n", buildVersion
);
620 setOutputTo(outputDir
, "syncD.log");
622 dumpKVS(optarg
, NULL
);
625 setOutputTo(outputDir
, "synci.log");
630 setOutputTo(outputDir
, "syncL.log");
635 copyFileToOutputDir(outputDir
, keysToRegister
);
636 copyFileToOutputDir(outputDir
, cloudkeychainproxy3
);
640 CFReleaseNull(sysfdef
);
641 #if ! TARGET_OS_EMBEDDED
642 free(keysToRegister
);
643 free(cloudkeychainproxy3
);
645 if(productName
) free(productName
);
646 if(productVersion
) free(productVersion
);
647 if(buildVersion
) free(buildVersion
);
652 char *SOSCCSysdiagnose(const char *directoryname
) {
653 sysdiagnose_dump(directoryname
);