2 * Copyright (c) 2003-2007,2009-2010,2013-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
31 #include <sys/utsname.h>
35 #include <Security/SecItem.h>
37 #include <CoreFoundation/CFNumber.h>
38 #include <CoreFoundation/CFString.h>
40 #include <Security/SecureObjectSync/SOSCloudCircle.h>
41 #include <Security/SecureObjectSync/SOSCloudCircleInternal.h>
42 #include <Security/SecureObjectSync/SOSPeerInfo.h>
43 #include <Security/SecureObjectSync/SOSPeerInfoPriv.h>
44 #include <Security/SecureObjectSync/SOSPeerInfoV2.h>
45 #include <Security/SecureObjectSync/SOSUserKeygen.h>
46 #include <Security/SecureObjectSync/SOSKVSKeys.h>
47 #include <securityd/SOSCloudCircleServer.h>
48 #include <Security/SecureObjectSync/SOSBackupSliceKeyBag.h>
49 #include <Security/SecOTRSession.h>
50 #include <SOSCircle/CKBridge/SOSCloudKeychainClient.h>
52 #include <utilities/SecCFWrappers.h>
53 #include <utilities/debugging.h>
55 #include <SecurityTool/readline.h>
58 #include "keychain_sync.h"
59 #include "keychain_log.h"
60 #include "syncbackup.h"
62 #include "secToolFileIO.h"
63 #include "secViewDisplay.h"
65 #include <Security/SecPasswordGenerate.h>
67 #define MAXKVSKEYTYPE kUnknownKey
68 #define DATE_LENGTH 18
71 static bool clearAllKVS(CFErrorRef *error)
73 __block bool result = false;
74 const uint64_t maxTimeToWaitInSeconds = 30ull * NSEC_PER_SEC;
75 dispatch_queue_t processQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
76 dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
77 dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds);
79 SOSCloudKeychainClearAll(processQueue, ^(CFDictionaryRef returnedValues, CFErrorRef cerror)
81 result = (cerror != NULL);
82 dispatch_semaphore_signal(waitSemaphore);
85 dispatch_semaphore_wait(waitSemaphore, finishTime);
90 static const char *getSOSCCStatusDescription(SOSCCStatus ccstatus)
94 case kSOSCCInCircle: return "In Circle";
95 case kSOSCCNotInCircle: return "Not in Circle";
96 case kSOSCCRequestPending: return "Request pending";
97 case kSOSCCCircleAbsent: return "Circle absent";
98 case kSOSCCError: return "Circle error";
101 return "<unknown ccstatus>";
106 static void printPeerInfos(char *label, CFArrayRef (^getArray)(CFErrorRef *error)) {
107 CFErrorRef error = NULL;
108 CFArrayRef ppi = getArray(&error);
109 SOSPeerInfoRef me = SOSCCCopyMyPeerInfo(NULL);
110 CFStringRef mypeerID = SOSPeerInfoGetPeerID(me);
113 printmsg(CFSTR("%s count: %ld\n"), label, (long)CFArrayGetCount(ppi));
114 CFArrayForEach(ppi, ^(const void *value) {
116 SOSPeerInfoRef peer = (SOSPeerInfoRef)value;
117 CFIndex version = SOSPeerInfoGetVersion(peer);
118 CFStringRef peerName = SOSPeerInfoGetPeerName(peer);
119 CFStringRef devtype = SOSPeerInfoGetPeerDeviceType(peer);
120 CFStringRef peerID = SOSPeerInfoGetPeerID(peer);
121 CFStringRef transportType = CFSTR("KVS");
122 CFStringRef deviceID = CFSTR("");
123 CFDictionaryRef gestalt = SOSPeerInfoCopyPeerGestalt(peer);
124 CFStringRef osVersion = CFDictionaryGetValue(gestalt, CFSTR("OSVersion"));
125 CFReleaseNull(gestalt);
129 CFDictionaryRef v2Dictionary = peer->v2Dictionary;
130 transportType = CFDictionaryGetValue(v2Dictionary, sTransportType);
131 deviceID = CFDictionaryGetValue(v2Dictionary, sDeviceID);
133 char *pname = CFStringToCString(peerName);
134 char *dname = CFStringToCString(devtype);
135 char *tname = CFStringToCString(transportType);
136 char *iname = CFStringToCString(deviceID);
137 char *osname = CFStringToCString(osVersion);
138 const char *me = CFEqualSafe(mypeerID, peerID) ? "me>" : " ";
141 snprintf(buf, 160, "%s %s: %-16s %-16s %-16s %-16s", me, label, pname, dname, tname, iname);
145 CFStringRef pid = SOSPeerInfoGetPeerID(peer);
146 CFIndex vers = SOSPeerInfoGetVersion(peer);
147 printmsg(CFSTR("%s %@ V%d OS:%s\n"), buf, pid, vers, osname);
151 printmsg(CFSTR("No %s, error: %@\n"), label, error);
154 CFReleaseNull(error);
157 static void dumpCircleInfo()
159 CFErrorRef error = NULL;
160 CFArrayRef generations = NULL;
161 CFArrayRef confirmedDigests = NULL;
162 bool is_accountKeyIsTrusted = false;
163 __block int count = 0;
165 SOSCCStatus ccstatus = SOSCCThisDeviceIsInCircle(&error);
166 if(ccstatus == kSOSCCError) {
167 printmsg(CFSTR("End of Dump - unable to proceed due to ccstatus (%s) error: %@\n"), getSOSCCStatusDescription(ccstatus), error);
170 printmsg(CFSTR("ccstatus: %s (%d)\n"), getSOSCCStatusDescription(ccstatus), ccstatus, error);
172 is_accountKeyIsTrusted = SOSCCValidateUserPublic(&error);
173 if(is_accountKeyIsTrusted)
174 printmsg(CFSTR("Account user public is trusted%@"),CFSTR("\n"));
176 printmsg(CFSTR("Account user public is not trusted error:(%@)\n"), error);
177 CFReleaseNull(error);
179 generations = SOSCCCopyGenerationPeerInfo(&error);
181 CFArrayForEach(generations, ^(const void *value) {
184 printmsg(CFSTR("Circle name: %@, "),value);
187 CFStringRef genDesc = SOSGenerationCountCopyDescription(value);
188 printmsg(CFSTR("Generation Count: %@"), genDesc);
189 CFReleaseNull(genDesc);
191 printmsg(CFSTR("%s\n"), "");
194 printmsg(CFSTR("No generation count: %@\n"), error);
196 CFReleaseNull(generations);
197 CFReleaseNull(error);
199 printPeerInfos(" Peers", ^(CFErrorRef *error) { return SOSCCCopyValidPeerPeerInfo(error); });
200 printPeerInfos(" Invalid", ^(CFErrorRef *error) { return SOSCCCopyNotValidPeerPeerInfo(error); });
201 printPeerInfos(" Retired", ^(CFErrorRef *error) { return SOSCCCopyRetirementPeerInfo(error); });
202 printPeerInfos(" Concur", ^(CFErrorRef *error) { return SOSCCCopyConcurringPeerPeerInfo(error); });
203 printPeerInfos("Applicants", ^(CFErrorRef *error) { return SOSCCCopyApplicantPeerInfo(error); });
205 if (!SOSCCForEachEngineStateAsString(&error, ^(CFStringRef oneStateString) {
206 printmsg(CFSTR("%@\n"), oneStateString);
208 printmsg(CFSTR("No engine peers: %@\n"), error);
211 CFReleaseNull(error);
212 CFReleaseNull(confirmedDigests);
215 static bool enableDefaultViews()
218 CFMutableSetRef viewsToEnable = SOSViewCopyViewSet(kViewSetV0);
219 CFMutableSetRef viewsToDisable = CFSetCreateMutable(NULL, 0, NULL);
221 result = SOSCCViewSet(viewsToEnable, viewsToDisable);
222 CFRelease(viewsToEnable);
223 CFRelease(viewsToDisable);
227 static bool requestToJoinCircle(CFErrorRef *error)
229 // Set the visual state of switch based on membership in circle
230 bool hadError = false;
231 SOSCCStatus ccstatus = SOSCCThisDeviceIsInCircle(error);
235 case kSOSCCCircleAbsent:
236 hadError = !SOSCCResetToOffering(error);
237 hadError &= enableDefaultViews();
239 case kSOSCCNotInCircle:
240 hadError = !SOSCCRequestToJoinCircle(error);
241 hadError &= enableDefaultViews();
244 printerr(CFSTR("Request to join circle with bad status: %@ (%d)\n"), SOSCCGetStatusDescription(ccstatus), ccstatus);
250 static bool setPassword(char *labelAndPassword, CFErrorRef *err)
253 char *token0 = strtok_r(labelAndPassword, ":", &last);
254 char *token1 = strtok_r(NULL, "", &last);
255 CFStringRef label = token1 ? CFStringCreateWithCString(NULL, token0, kCFStringEncodingUTF8) : CFSTR("security command line tool");
256 char *password_token = token1 ? token1 : token0;
257 password_token = password_token ? password_token : "";
258 CFDataRef password = CFDataCreate(NULL, (const UInt8*) password_token, strlen(password_token));
259 bool returned = !SOSCCSetUserCredentials(label, password, err);
265 static bool tryPassword(char *labelAndPassword, CFErrorRef *err)
268 char *token0 = strtok_r(labelAndPassword, ":", &last);
269 char *token1 = strtok_r(NULL, "", &last);
270 CFStringRef label = token1 ? CFStringCreateWithCString(NULL, token0, kCFStringEncodingUTF8) : CFSTR("security command line tool");
271 char *password_token = token1 ? token1 : token0;
272 password_token = password_token ? password_token : "";
273 CFDataRef password = CFDataCreate(NULL, (const UInt8*) password_token, strlen(password_token));
274 bool returned = !SOSCCTryUserCredentials(label, password, err);
280 static CFTypeRef getObjectsFromCloud(CFArrayRef keysToGet, dispatch_queue_t processQueue, dispatch_group_t dgroup)
282 __block CFTypeRef object = NULL;
284 const uint64_t maxTimeToWaitInSeconds = 30ull * NSEC_PER_SEC;
285 dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
286 dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds);
288 dispatch_group_enter(dgroup);
290 CloudKeychainReplyBlock replyBlock =
291 ^ (CFDictionaryRef returnedValues, CFErrorRef error)
293 secinfo("sync", "SOSCloudKeychainGetObjectsFromCloud returned: %@", returnedValues);
294 object = returnedValues;
299 secerror("SOSCloudKeychainGetObjectsFromCloud returned error: %@", error);
301 dispatch_group_leave(dgroup);
302 secinfo("sync", "SOSCloudKeychainGetObjectsFromCloud block exit: %@", object);
303 dispatch_semaphore_signal(waitSemaphore);
307 SOSCloudKeychainGetAllObjectsFromCloud(processQueue, replyBlock);
309 SOSCloudKeychainGetObjectsFromCloud(keysToGet, processQueue, replyBlock);
311 dispatch_semaphore_wait(waitSemaphore, finishTime);
312 if (object && (CFGetTypeID(object) == CFNullGetTypeID())) // return a NULL instead of a CFNull
317 secerror("returned: %@", object);
321 static CFStringRef printFullDataString(CFDataRef data){
322 __block CFStringRef fullData = NULL;
324 BufferPerformWithHexString(CFDataGetBytePtr(data), CFDataGetLength(data), ^(CFStringRef dataHex) {
325 fullData = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@"), dataHex);
331 static void displayLastKeyParameters(CFTypeRef key, CFTypeRef value)
333 CFDataRef valueAsData = asData(value, NULL);
335 CFDataRef dateData = CFDataCreateCopyFromRange(kCFAllocatorDefault, valueAsData, CFRangeMake(0, DATE_LENGTH));
336 CFDataRef keyParameterData = CFDataCreateCopyFromPositions(kCFAllocatorDefault, valueAsData, DATE_LENGTH, CFDataGetLength(valueAsData));
337 CFStringRef dateString = CFStringCreateFromExternalRepresentation(kCFAllocatorDefault, dateData, kCFStringEncodingUTF8);
338 CFStringRef keyParameterDescription = UserParametersDescription(keyParameterData);
339 if(keyParameterDescription)
340 printmsg(CFSTR("%@: %@: %@\n"), key, dateString, keyParameterDescription);
342 printmsg(CFSTR("%@: %@\n"), key, printFullDataString(value));
343 CFReleaseNull(dateString);
344 CFReleaseNull(keyParameterData);
345 CFReleaseNull(dateData);
346 CFReleaseNull(keyParameterDescription);
349 printmsg(CFSTR("%@: %@\n"), key, value);
353 static void displayKeyParameters(CFTypeRef key, CFTypeRef value)
356 CFStringRef keyParameterDescription = UserParametersDescription((CFDataRef)value);
358 if(keyParameterDescription)
359 printmsg(CFSTR("%@: %@\n"), key, keyParameterDescription);
361 printmsg(CFSTR("%@: %@\n"), key, value);
363 CFReleaseNull(keyParameterDescription);
366 printmsg(CFSTR("%@: %@\n"), key, value);
370 static void displayLastCircle(CFTypeRef key, CFTypeRef value)
372 CFDataRef valueAsData = asData(value, NULL);
374 CFErrorRef localError = NULL;
376 CFDataRef dateData = CFDataCreateCopyFromRange(kCFAllocatorDefault, valueAsData, CFRangeMake(0, DATE_LENGTH));
377 CFDataRef circleData = CFDataCreateCopyFromPositions(kCFAllocatorDefault, valueAsData, DATE_LENGTH, CFDataGetLength(valueAsData));
378 CFStringRef dateString = CFStringCreateFromExternalRepresentation(kCFAllocatorDefault, dateData, kCFStringEncodingUTF8);
379 SOSCircleRef circle = SOSCircleCreateFromData(NULL, (CFDataRef) circleData, &localError);
383 CFNumberRef idLength = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &size);
384 CFDictionaryRef format = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, CFSTR("SyncD"), CFSTR("SyncD"), CFSTR("idLength"), idLength, NULL);
385 printmsgWithFormatOptions(format, CFSTR("%@: %@: %@\n"), key, dateString, circle);
386 CFReleaseNull(idLength);
387 CFReleaseNull(format);
391 printmsg(CFSTR("%@: %@\n"), key, printFullDataString(circleData));
393 CFReleaseNull(dateString);
394 CFReleaseNull(circleData);
395 CFReleaseSafe(circle);
396 CFReleaseNull(dateData);
397 CFReleaseNull(localError);
400 printmsg(CFSTR("%@: %@\n"), key, value);
404 static void displayCircle(CFTypeRef key, CFTypeRef value)
406 CFDataRef circleData = (CFDataRef)value;
408 CFErrorRef localError = NULL;
409 if (isData(circleData))
412 CFNumberRef idLength = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &size);
413 CFDictionaryRef format = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, CFSTR("SyncD"), CFSTR("SyncD"), CFSTR("idLength"), idLength, NULL);
414 SOSCircleRef circle = SOSCircleCreateFromData(NULL, circleData, &localError);
415 printmsgWithFormatOptions(format, CFSTR("%@: %@\n"), key, circle);
416 CFReleaseSafe(circle);
417 CFReleaseNull(idLength);
418 CFReleaseNull(format);
422 printmsg(CFSTR("%@: %@\n"), key, value);
425 static void displayMessage(CFTypeRef key, CFTypeRef value)
427 CFDataRef message = (CFDataRef)value;
429 const char* messageType = SecOTRPacketTypeString(message);
430 printmsg(CFSTR("%@: %s: %ld\n"), key, messageType, CFDataGetLength(message));
433 printmsg(CFSTR("%@: %@\n"), key, value);
436 static void printEverything(CFTypeRef objects)
438 CFDictionaryForEach(objects, ^(const void *key, const void *value) {
441 printmsg(CFSTR("%@: %@\n\n"), key, printFullDataString(value));
444 printmsg(CFSTR("%@: %@\n"), key, value);
449 static void decodeForKeyType(CFTypeRef key, CFTypeRef value, SOSKVSKeyType type){
452 displayCircle(key, value);
456 displayMessage(key, value);
459 displayKeyParameters(key, value);
461 case kLastKeyParameterKey:
462 displayLastKeyParameters(key, value);
465 displayLastCircle(key, value);
467 case kInitialSyncKey:
468 case kAccountChangedKey:
472 printmsg(CFSTR("%@: %@\n"), key, value);
477 static void decodeAllTheValues(CFTypeRef objects){
478 SOSKVSKeyType keyType = 0;
479 __block bool didPrint = false;
481 for (keyType = 0; keyType <= MAXKVSKEYTYPE; keyType++){
482 CFDictionaryForEach(objects, ^(const void *key, const void *value) {
483 if(SOSKVSKeyGetKeyType(key) == keyType){
484 decodeForKeyType(key, value, keyType);
489 printmsg(CFSTR("%@\n"), CFSTR(""));
493 static bool dumpKVS(char *itemName, CFErrorRef *err)
495 CFArrayRef keysToGet = NULL;
498 CFStringRef itemStr = CFStringCreateWithCString(kCFAllocatorDefault, itemName, kCFStringEncodingUTF8);
499 fprintf(outFile, "Retrieving %s from KVS\n", itemName);
500 keysToGet = CFArrayCreateForCFTypes(kCFAllocatorDefault, itemStr, NULL);
501 CFReleaseSafe(itemStr);
503 dispatch_queue_t generalq = dispatch_queue_create("general", DISPATCH_QUEUE_SERIAL);
504 dispatch_group_t work_group = dispatch_group_create();
505 CFTypeRef objects = getObjectsFromCloud(keysToGet, generalq, work_group);
506 CFReleaseSafe(keysToGet);
509 fprintf(outFile, "All keys and values straight from KVS\n");
510 printEverything(objects);
511 fprintf(outFile, "\nAll values in decoded form...\n");
512 decodeAllTheValues(objects);
514 fprintf(outFile, "\n");
518 static bool syncAndWait(CFErrorRef *err)
520 __block CFTypeRef objects = NULL;
522 dispatch_queue_t generalq = dispatch_queue_create("general", DISPATCH_QUEUE_SERIAL);
524 const uint64_t maxTimeToWaitInSeconds = 30ull * NSEC_PER_SEC;
525 dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
526 dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds);
528 CloudKeychainReplyBlock replyBlock = ^ (CFDictionaryRef returnedValues, CFErrorRef error)
530 secinfo("sync", "SOSCloudKeychainSynchronizeAndWait returned: %@", returnedValues);
532 secerror("SOSCloudKeychainSynchronizeAndWait returned error: %@", error);
533 objects = CFRetainSafe(returnedValues);
535 secinfo("sync", "SOSCloudKeychainGetObjectsFromCloud block exit: %@", objects);
536 dispatch_semaphore_signal(waitSemaphore);
539 SOSCloudKeychainSynchronizeAndWait(generalq, replyBlock);
541 dispatch_semaphore_wait(waitSemaphore, finishTime);
544 fprintf(outFile, "\n");
548 static CFStringRef convertStringToProperty(char *propertyname) {
549 CFStringRef propertyspec = NULL;
551 if(strcmp(propertyname, "hasentropy") == 0) {
552 propertyspec = kSOSSecPropertyHasEntropy;
553 } else if(strcmp(propertyname, "screenlock") == 0) {
554 propertyspec = kSOSSecPropertyScreenLock;
555 } else if(strcmp(propertyname, "SEP") == 0) {
556 propertyspec = kSOSSecPropertySEP;
557 } else if(strcmp(propertyname, "IOS") == 0) {
558 propertyspec = kSOSSecPropertyIOS;
564 static CFStringRef convertPropertyReturnCodeToString(SOSSecurityPropertyResultCode ac) {
565 CFStringRef retval = NULL;
567 case kSOSCCGeneralSecurityPropertyError:
568 retval = CFSTR("General Error"); break;
569 case kSOSCCSecurityPropertyValid:
570 retval = CFSTR("Is Member of Security Property"); break;
571 case kSOSCCSecurityPropertyNotValid:
572 retval = CFSTR("Is Not Member of Security Property"); break;
573 case kSOSCCSecurityPropertyNotQualified:
574 retval = CFSTR("Is not qualified for Security Property"); break;
575 case kSOSCCNoSuchSecurityProperty:
576 retval = CFSTR("No Such Security Property"); break;
582 static bool SecPropertycmd(char *itemName, CFErrorRef *err) {
583 char *cmd, *propertyname;
584 SOSSecurityPropertyActionCode ac = kSOSCCSecurityPropertyQuery;
585 CFStringRef propertyspec;
587 propertyname = strchr(itemName, ':');
588 if(propertyname == NULL) return false;
593 if(strcmp(cmd, "enable") == 0) {
594 ac = kSOSCCSecurityPropertyEnable;
595 } else if(strcmp(cmd, "disable") == 0) {
596 ac = kSOSCCSecurityPropertyDisable;
597 } else if(strcmp(cmd, "query") == 0) {
598 ac = kSOSCCSecurityPropertyQuery;
603 propertyspec = convertStringToProperty(propertyname);
604 if(!propertyspec) return false;
606 SOSSecurityPropertyResultCode rc = SOSCCSecurityProperty(propertyspec, ac, err);
607 CFStringRef resultString = convertPropertyReturnCodeToString(rc);
609 printmsg(CFSTR("Property Result: %@ : %@\n"), resultString, propertyspec);
614 static void dumpStringSet(CFStringRef label, CFSetRef s) {
615 if(!s || !label) return;
617 printmsg(CFSTR("%@: { "), label);
618 __block bool first = true;
619 CFSetForEach(s, ^(const void *p) {
620 CFStringRef fmt = CFSTR(", %@");
624 CFStringRef string = (CFStringRef) p;
625 printmsg(fmt, string);
628 printmsg(CFSTR(" }\n"), NULL);
631 static bool dumpMyPeer(CFErrorRef *error) {
632 SOSPeerInfoRef myPeer = SOSCCCopyMyPeerInfo(error);
634 if (!myPeer) return false;
636 CFStringRef peerID = SOSPeerInfoGetPeerID(myPeer);
637 CFStringRef peerName = SOSPeerInfoGetPeerName(myPeer);
638 CFIndex peerVersion = SOSPeerInfoGetVersion(myPeer);
639 bool retirement = SOSPeerInfoIsRetirementTicket(myPeer);
641 printmsg(CFSTR("Peer Name: %@ PeerID: %@ Version: %d\n"), peerName, peerID, peerVersion);
643 CFDateRef retdate = SOSPeerInfoGetRetirementDate(myPeer);
644 printmsg(CFSTR("Retired: %@\n"), retdate);
648 if(peerVersion >= 2) {
649 CFMutableSetRef views = SOSPeerInfoV2DictionaryCopySet(myPeer, sViewsKey);
650 CFStringRef serialNumber = SOSPeerInfoV2DictionaryCopyString(myPeer, sSerialNumberKey);
651 CFBooleanRef preferIDS = SOSPeerInfoV2DictionaryCopyBoolean(myPeer, sPreferIDS);
652 CFBooleanRef preferIDSFragmentation = SOSPeerInfoV2DictionaryCopyBoolean(myPeer, sPreferIDSFragmentation);
653 CFBooleanRef preferIDSACKModel = SOSPeerInfoV2DictionaryCopyBoolean(myPeer, sPreferIDSACKModel);
654 CFStringRef transportType = SOSPeerInfoV2DictionaryCopyString(myPeer, sTransportType);
655 CFStringRef idsDeviceID = SOSPeerInfoV2DictionaryCopyString(myPeer, sDeviceID);
656 CFMutableSetRef properties = SOSPeerInfoV2DictionaryCopySet(myPeer, sSecurityPropertiesKey);
658 printmsg(CFSTR("Serial#: %@ PrefIDS#: %@ PrefFragmentation#: %@ PrefACK#: %@ transportType#: %@ idsDeviceID#: %@\n"),
659 serialNumber, preferIDS, preferIDSFragmentation, preferIDSACKModel, transportType, idsDeviceID);
660 dumpStringSet(CFSTR(" Views: "), views);
661 dumpStringSet(CFSTR("SecurityProperties: "), properties);
663 CFReleaseSafe(serialNumber);
664 CFReleaseSafe(preferIDS);
665 CFReleaseSafe(preferIDSFragmentation);
666 CFReleaseSafe(views);
667 CFReleaseSafe(transportType);
668 CFReleaseSafe(idsDeviceID);
669 CFReleaseSafe(properties);
673 return myPeer != NULL;
676 static bool setBag(char *itemName, CFErrorRef *err)
678 __block bool success = false;
679 __block CFErrorRef error = NULL;
681 CFStringRef random = SecPasswordCreateWithRandomDigits(10, NULL);
683 CFStringPerformWithUTF8CFData(random, ^(CFDataRef stringAsData) {
684 if (0 == strncasecmp(optarg, "single", 6) || 0 == strncasecmp(optarg, "all", 3)) {
685 bool includeV0 = (0 == strncasecmp(optarg, "all", 3));
686 printmsg(CFSTR("Setting iCSC single using entropy from string: %@\n"), random);
687 CFDataRef aks_bag = SecAKSCopyBackupBagWithSecret(CFDataGetLength(stringAsData), (uint8_t*)CFDataGetBytePtr(stringAsData), &error);
690 success = SOSCCRegisterSingleRecoverySecret(aks_bag, includeV0, &error);
692 printmsg(CFSTR("Failed registering single secret %@"), error);
693 CFReleaseNull(aks_bag);
696 printmsg(CFSTR("Failed to create aks_bag: %@"), error);
698 CFReleaseNull(aks_bag);
699 } else if (0 == strncasecmp(optarg, "device", 6)) {
700 printmsg(CFSTR("Setting Device Secret using entropy from string: %@\n"), random);
702 SOSPeerInfoRef me = SOSCCCopyMyPeerWithNewDeviceRecoverySecret(stringAsData, &error);
704 success = me != NULL;
707 printmsg(CFSTR("Failed: %@\n"), err);
710 printmsg(CFSTR("Unrecognized argument to -b %s\n"), optarg);
718 static void prClientViewState(char *label, bool result) {
719 fprintf(outFile, "Sync Status for %s: %s\n", label, (result) ? "enabled": "not enabled");
722 static bool clientViewStatus(CFErrorRef *error) {
723 prClientViewState("KeychainV0", SOSCCIsIcloudKeychainSyncing());
724 prClientViewState("Safari", SOSCCIsSafariSyncing());
725 prClientViewState("AppleTV", SOSCCIsAppleTVSyncing());
726 prClientViewState("HomeKit", SOSCCIsHomeKitSyncing());
727 prClientViewState("Wifi", SOSCCIsWiFiSyncing());
728 prClientViewState("AlwaysOnNoInitialSync", SOSCCIsContinuityUnlockSyncing());
733 static bool dumpYetToSync(CFErrorRef *error) {
734 CFArrayRef yetToSyncViews = SOSCCCopyYetToSyncViewsList(error);
736 bool hadError = yetToSyncViews;
738 if (yetToSyncViews) {
739 __block CFStringRef separator = CFSTR("");
741 printmsg(CFSTR("Yet to sync views: ["), NULL);
743 CFArrayForEach(yetToSyncViews, ^(const void *value) {
744 if (isString(value)) {
745 printmsg(CFSTR("%@%@"), separator, value);
747 separator = CFSTR(", ");
750 printmsg(CFSTR("]\n"), NULL);
757 // enable, disable, accept, reject, status, Reset, Clear
759 keychain_sync(int argc, char * const *argv)
764 " -e enable (join/create circle)"
765 " -i info (current status)"
768 "Account/Circle Management"
769 " -a accept all applicants"
770 " -l [reason] sign out of circle + set custom departure reason"
771 " -q sign out of circle"
772 " -r reject all applicants"
773 " -E ensure fresh parameters"
774 " -b device|all|single Register a backup bag - THIS RESETS BACKUPS!\n"
775 " -A Apply to a ring\n"
776 " -B Withdrawl from a ring\n"
779 " -I Dump Ring Information\n"
781 " -N (re-)set to new account (USE WITH CARE: device will not leave circle before resetting account!)"
782 " -O reset to offering"
784 " -X [limit] best effort bail from circle in limit seconds"
785 " -o list view unaware peers in circle"
786 " -0 boot view unaware peers from circle"
787 " -1 grab account state from the keychain"
788 " -2 delete account state from the keychain"
789 " -3 grab engine state from the keychain"
790 " -4 delete engine state from the keychain"
791 " -5 cleanup old KVS keys in KVS"
792 " -6 [test]populate KVS with garbage KVS keys
795 " -g set IDS device id"
796 " -p retrieve IDS device id"
797 " -x ping all devices in an IDS account"
798 " -w check IDS availability"
799 " -z retrieve IDS id through KeychainSyncingOverIDSProxy"
802 " -P [label:]password set password (optionally for a given label) for sync"
803 " -T [label:]password try password (optionally for a given label) for sync"
806 " -k pend all registered kvs keys"
807 " -C clear all values from KVS"
808 " -D [itemName] dump contents of KVS"
812 " -v [enable|disable|query:viewname] enable, disable, or query my PeerInfo's view set"
813 " viewnames are: keychain|masterkey|iclouddrive|photos|cloudkit|escrow|fde|maildrop|icloudbackup|notes|imessage|appletv|homekit|"
814 " wifi|passwords|creditcards|icloudidentity|othersyncable"
815 " -L list all known view and their status"
816 " -S [enable|disable|propertyname] enable, disable, or query my PeerInfo's Security Property set"
817 " propertynames are: hasentropy|screenlock|SEP|IOS\n"
818 " -U purge private key material cache\n"
819 " -V Report View Sync Status on all known clients.\n"
820 " -H Set escrow record.\n"
821 " -J Get the escrow record.\n"
822 " -M Check peer availability.\n"
825 CFErrorRef error = NULL;
826 bool hadError = false;
827 SOSLogSetOutputTo(NULL, NULL);
829 while ((ch = getopt(argc, argv, "ab:deg:hikl:mopq:rSv:w:x:zA:B:MNJCDEF:HG:ILOP:RT:UWX:VY0123456")) != -1)
833 fprintf(outFile, "Signing out of circle\n");
834 hadError = !SOSCCSignedOut(true, &error);
837 int reason = (int) strtoul(optarg, NULL, 10);
839 reason < kSOSDepartureReasonError ||
840 reason >= kSOSNumDepartureReasons) {
841 fprintf(errFile, "Invalid custom departure reason %s\n", optarg);
843 fprintf(outFile, "Setting custom departure reason %d\n", reason);
844 hadError = !SOSCCSetLastDepartureReason(reason, &error);
845 notify_post(kSOSCCCircleChangedNotification);
853 fprintf(outFile, "Signing out of circle\n");
854 bool signOutImmediately = false;
855 if (strcasecmp(optarg, "true") == 0) {
856 signOutImmediately = true;
857 } else if (strcasecmp(optarg, "false") == 0) {
858 signOutImmediately = false;
860 fprintf(outFile, "Please provide a \"true\" or \"false\" whether you'd like to leave the circle immediately\n");
862 hadError = !SOSCCSignedOut(signOutImmediately, &error);
863 notify_post(kSOSCCCircleChangedNotification);
869 fprintf(outFile, "Grabbing DS ID\n");
870 CFStringRef deviceID = SOSCCCopyDeviceID(&error);
875 if (!isNull(deviceID)) {
876 const char *id = CFStringGetCStringPtr(deviceID, kCFStringEncodingUTF8);
878 fprintf(outFile, "IDS Device ID: %s\n", id);
880 fprintf(outFile, "IDS Device ID is null!\n");
882 CFReleaseNull(deviceID);
888 fprintf(outFile, "Setting DS ID: %s\n", optarg);
889 CFStringRef deviceID = CFStringCreateWithCString(kCFAllocatorDefault, optarg, kCFStringEncodingUTF8);
890 hadError = SOSCCSetDeviceID(deviceID, &error);
891 CFReleaseNull(deviceID);
897 fprintf(outFile, "Attempting to send this message over IDS: %s\n", optarg);
898 CFStringRef message = CFStringCreateWithCString(kCFAllocatorDefault, optarg, kCFStringEncodingUTF8);
899 hadError = SOSCCIDSServiceRegistrationTest(message, &error);
901 printerr(CFSTR("IDS is not ready: %@\n"), error);
904 CFReleaseNull(message);
910 fprintf(outFile, "Starting ping test using this message: %s\n", optarg);
911 CFStringRef message = CFStringCreateWithCString(kCFAllocatorDefault, optarg, kCFStringEncodingUTF8);
912 hadError = SOSCCIDSPingTest(message, &error);
914 printerr(CFSTR("Ping test failed to start: %@\n"), error);
917 CFReleaseNull(message);
922 hadError = SOSCCIDSDeviceIDIsAvailableTest(&error);
924 printerr(CFSTR("Failed to retrieve IDS device ID: %@\n"), error);
930 fprintf(outFile, "Turning ON keychain syncing\n");
931 hadError = requestToJoinCircle(&error);
935 fprintf(outFile, "Turning OFF keychain syncing\n");
936 hadError = !SOSCCRemoveThisDeviceFromCircle(&error);
941 CFArrayRef applicants = SOSCCCopyApplicantPeerInfo(NULL);
943 hadError = !SOSCCAcceptApplicants(applicants, &error);
944 CFRelease(applicants);
946 fprintf(errFile, "No applicants to accept\n");
953 CFArrayRef applicants = SOSCCCopyApplicantPeerInfo(NULL);
955 hadError = !SOSCCRejectApplicants(applicants, &error);
956 CFRelease(applicants);
958 fprintf(errFile, "No applicants to reject\n");
968 notify_post("com.apple.security.cloudkeychain.forceupdate");
973 printPeerInfos("view-unaware", ^(CFErrorRef *error) { return SOSCCCopyViewUnawarePeerInfo(error); });
979 CFArrayRef unawares = SOSCCCopyViewUnawarePeerInfo(&error);
981 hadError = !SOSCCRemovePeersFromCircle(unawares, &error);
985 CFReleaseNull(unawares);
990 CFDataRef accountState = SOSCCCopyAccountState(&error);
992 printmsg(CFSTR(" %@\n"), CFDataCopyHexString(accountState));
996 CFReleaseNull(accountState);
1001 bool status = SOSCCDeleteAccountState(&error);
1003 printmsg(CFSTR("Deleted account from the keychain %d\n"), status);
1011 CFDataRef engineState = SOSCCCopyEngineData(&error);
1013 printmsg(CFSTR(" %@\n"), CFDataCopyHexString(engineState));
1017 CFReleaseNull(engineState);
1022 bool status = SOSCCDeleteEngineState(&error);
1024 printmsg(CFSTR("Deleted engine-state from the keychain %d\n"), status);
1032 bool result = SOSCCCleanupKVSKeys(&error);
1035 printmsg(CFSTR("Got all the keys from KVS %d\n"), result);
1043 bool result = SOSCCTestPopulateKVSWithBadKeys(&error);
1046 printmsg(CFSTR("Populated KVS with garbage %d\n"), result);
1054 fprintf(outFile, "Ensuring Fresh Parameters\n");
1055 bool result = SOSCCRequestEnsureFreshParameters(&error);
1061 fprintf(outFile, "Refreshed Parameters Ensured!\n");
1063 fprintf(outFile, "Problem trying to ensure fresh parameters\n");
1069 fprintf(outFile, "Applying to Ring\n");
1070 CFStringRef ringName = CFStringCreateWithCString(kCFAllocatorDefault, (char *)optarg, kCFStringEncodingUTF8);
1071 hadError = SOSCCApplyToARing(ringName, &error);
1072 CFReleaseNull(ringName);
1077 fprintf(outFile, "Withdrawing from Ring\n");
1078 CFStringRef ringName = CFStringCreateWithCString(kCFAllocatorDefault, (char *)optarg, kCFStringEncodingUTF8);
1079 hadError = SOSCCWithdrawlFromARing(ringName, &error);
1080 CFReleaseNull(ringName);
1085 fprintf(outFile, "Status of this device in the Ring\n");
1086 CFStringRef ringName = CFStringCreateWithCString(kCFAllocatorDefault, (char *)optarg, kCFStringEncodingUTF8);
1087 hadError = SOSCCRingStatus(ringName, &error);
1088 CFReleaseNull(ringName);
1093 fprintf(outFile, "Enabling Ring\n");
1094 CFStringRef ringName = CFStringCreateWithCString(kCFAllocatorDefault, (char *)optarg, kCFStringEncodingUTF8);
1095 hadError = SOSCCEnableRing(ringName, &error);
1096 CFReleaseNull(ringName);
1101 fprintf(outFile, "Setting random escrow record\n");
1102 bool success = SOSCCSetEscrowRecord(CFSTR("label"), 8, &error);
1111 CFDictionaryRef attempts = SOSCCCopyEscrowRecord(&error);
1113 CFDictionaryForEach(attempts, ^(const void *key, const void *value) {
1115 char *keyString = CFStringToCString(key);
1116 fprintf(outFile, "%s:\n", keyString);
1119 if(isDictionary(value)){
1120 CFDictionaryForEach(value, ^(const void *key, const void *value) {
1122 char *keyString = CFStringToCString(key);
1123 fprintf(outFile, "%s: ", keyString);
1126 if(isString(value)){
1127 char *time = CFStringToCString(value);
1128 fprintf(outFile, "timestamp: %s\n", time);
1131 else if(isNumber(value)){
1133 CFNumberGetValue(value, kCFNumberLongLongType, &tries);
1134 fprintf(outFile, "date: %llu\n", tries);
1141 CFReleaseNull(attempts);
1147 bool success = SOSCCCheckPeerAvailability(&error);
1156 fprintf(outFile, "Printing all the rings\n");
1157 CFStringRef ringdescription = SOSCCGetAllTheRings(&error);
1158 if(!ringdescription)
1161 fprintf(outFile, "Rings: %s", CFStringToCString(ringdescription));
1167 hadError = !SOSCCAccountSetToNew(&error);
1169 notify_post(kSOSCCCircleChangedNotification);
1173 hadError = !SOSCCResetToEmpty(&error);
1177 hadError = !SOSCCResetToOffering(&error);
1181 hadError = !dumpMyPeer(&error);
1185 hadError = clearAllKVS(&error);
1189 hadError = setPassword(optarg, &error);
1193 hadError = tryPassword(optarg, &error);
1198 uint64_t limit = strtoul(optarg, NULL, 10);
1199 hadError = !SOSCCBailFromCircle_BestEffort(limit, &error);
1204 hadError = !SOSCCPurgeUserCredentials(&error);
1208 hadError = !dumpKVS(optarg, &error);
1212 hadError = syncAndWait(&error);
1216 hadError = !viewcmd(optarg, &error);
1220 hadError = clientViewStatus(&error);
1223 hadError = !listviewcmd(&error);
1227 hadError = !SecPropertycmd(optarg, &error);
1231 hadError = setBag(optarg, &error);
1235 hadError = dumpYetToSync(&error);
1239 return 2; /* Return 2 triggers usage message. */
1243 printerr(CFSTR("Error: %@\n"), error);