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
);
86 dispatch_release(waitSemaphore
);
91 static const char *getSOSCCStatusDescription(SOSCCStatus ccstatus
)
95 case kSOSCCInCircle
: return "In Circle";
96 case kSOSCCNotInCircle
: return "Not in Circle";
97 case kSOSCCRequestPending
: return "Request pending";
98 case kSOSCCCircleAbsent
: return "Circle absent";
99 case kSOSCCError
: return "Circle error";
102 return "<unknown ccstatus>";
107 static void printPeerInfos(char *label
, CFArrayRef (^getArray
)(CFErrorRef
*error
)) {
108 CFErrorRef error
= NULL
;
109 CFArrayRef ppi
= getArray(&error
);
110 SOSPeerInfoRef me
= SOSCCCopyMyPeerInfo(NULL
);
111 CFStringRef mypeerID
= SOSPeerInfoGetPeerID(me
);
114 printmsg(CFSTR("%s count: %ld\n"), label
, (long)CFArrayGetCount(ppi
));
115 CFArrayForEach(ppi
, ^(const void *value
) {
117 SOSPeerInfoRef peer
= (SOSPeerInfoRef
)value
;
118 CFIndex version
= SOSPeerInfoGetVersion(peer
);
119 CFStringRef peerName
= SOSPeerInfoGetPeerName(peer
);
120 CFStringRef devtype
= SOSPeerInfoGetPeerDeviceType(peer
);
121 CFStringRef peerID
= SOSPeerInfoGetPeerID(peer
);
122 CFStringRef transportType
= CFSTR("KVS");
123 CFStringRef deviceID
= CFSTR("");
124 CFDictionaryRef gestalt
= SOSPeerInfoCopyPeerGestalt(peer
);
125 CFStringRef osVersion
= CFDictionaryGetValue(gestalt
, CFSTR("OSVersion"));
126 CFReleaseNull(gestalt
);
130 CFDictionaryRef v2Dictionary
= peer
->v2Dictionary
;
131 transportType
= CFDictionaryGetValue(v2Dictionary
, sTransportType
);
132 deviceID
= CFDictionaryGetValue(v2Dictionary
, sDeviceID
);
134 char *pname
= CFStringToCString(peerName
);
135 char *dname
= CFStringToCString(devtype
);
136 char *tname
= CFStringToCString(transportType
);
137 char *iname
= CFStringToCString(deviceID
);
138 char *osname
= CFStringToCString(osVersion
);
139 const char *me
= CFEqualSafe(mypeerID
, peerID
) ? "me>" : " ";
142 snprintf(buf
, 160, "%s %s: %-16s %-16s %-16s %-16s", me
, label
, pname
, dname
, tname
, iname
);
146 CFStringRef pid
= SOSPeerInfoGetPeerID(peer
);
147 CFIndex vers
= SOSPeerInfoGetVersion(peer
);
148 printmsg(CFSTR("%s %@ V%d OS:%s\n"), buf
, pid
, vers
, osname
);
152 printmsg(CFSTR("No %s, error: %@\n"), label
, error
);
155 CFReleaseNull(error
);
158 static void dumpCircleInfo()
160 CFErrorRef error
= NULL
;
161 CFArrayRef generations
= NULL
;
162 CFArrayRef confirmedDigests
= NULL
;
163 bool is_user_public_trusted
= false;
164 __block
int count
= 0;
166 SOSCCStatus ccstatus
= SOSCCThisDeviceIsInCircle(&error
);
167 if(ccstatus
== kSOSCCError
) {
168 printmsg(CFSTR("End of Dump - unable to proceed due to ccstatus (%s) error: %@\n"), getSOSCCStatusDescription(ccstatus
), error
);
171 printmsg(CFSTR("ccstatus: %s (%d)\n"), getSOSCCStatusDescription(ccstatus
), ccstatus
, error
);
173 is_user_public_trusted
= SOSCCValidateUserPublic(&error
);
174 if(is_user_public_trusted
)
175 printmsg(CFSTR("Account user public is trusted%@"),CFSTR("\n"));
177 printmsg(CFSTR("Account user public is not trusted error:(%@)\n"), error
);
178 CFReleaseNull(error
);
180 generations
= SOSCCCopyGenerationPeerInfo(&error
);
182 CFArrayForEach(generations
, ^(const void *value
) {
185 printmsg(CFSTR("Circle name: %@, "),value
);
188 CFStringRef genDesc
= SOSGenerationCountCopyDescription(value
);
189 printmsg(CFSTR("Generation Count: %@"), genDesc
);
190 CFReleaseNull(genDesc
);
192 printmsg(CFSTR("%s\n"), "");
195 printmsg(CFSTR("No generation count: %@\n"), error
);
197 CFReleaseNull(generations
);
198 CFReleaseNull(error
);
200 printPeerInfos(" Peers", ^(CFErrorRef
*error
) { return SOSCCCopyValidPeerPeerInfo(error
); });
201 printPeerInfos(" Invalid", ^(CFErrorRef
*error
) { return SOSCCCopyNotValidPeerPeerInfo(error
); });
202 printPeerInfos(" Retired", ^(CFErrorRef
*error
) { return SOSCCCopyRetirementPeerInfo(error
); });
203 printPeerInfos(" Concur", ^(CFErrorRef
*error
) { return SOSCCCopyConcurringPeerPeerInfo(error
); });
204 printPeerInfos("Applicants", ^(CFErrorRef
*error
) { return SOSCCCopyApplicantPeerInfo(error
); });
206 confirmedDigests
= SOSCCCopyEngineState(&error
);
210 CFArrayForEach(confirmedDigests
, ^(const void *value
) {
213 printmsg(CFSTR("%@"), value
);
216 CFStringRef hexDigest
= CFDataCopyHexString(value
);
217 printmsg(CFSTR(" %@\n"), hexDigest
);
218 CFReleaseSafe(hexDigest
);
223 printmsg(CFSTR("No engine peers: %@\n"), error
);
224 CFReleaseNull(confirmedDigests
);
227 static bool enableDefaultViews()
230 CFMutableSetRef viewsToEnable
= SOSViewCopyViewSet(kViewSetV0
);
231 CFMutableSetRef viewsToDisable
= CFSetCreateMutable(NULL
, 0, NULL
);
233 result
= SOSCCViewSet(viewsToEnable
, viewsToDisable
);
234 CFRelease(viewsToEnable
);
235 CFRelease(viewsToDisable
);
239 static bool requestToJoinCircle(CFErrorRef
*error
)
241 // Set the visual state of switch based on membership in circle
242 bool hadError
= false;
243 SOSCCStatus ccstatus
= SOSCCThisDeviceIsInCircle(error
);
247 case kSOSCCCircleAbsent
:
248 hadError
= !SOSCCResetToOffering(error
);
249 hadError
&= enableDefaultViews();
251 case kSOSCCNotInCircle
:
252 hadError
= !SOSCCRequestToJoinCircle(error
);
253 hadError
&= enableDefaultViews();
256 printerr(CFSTR("Request to join circle with bad status: %@ (%d)\n"), SOSCCGetStatusDescription(ccstatus
), ccstatus
);
262 static bool setPassword(char *labelAndPassword
, CFErrorRef
*err
)
265 char *token0
= strtok_r(labelAndPassword
, ":", &last
);
266 char *token1
= strtok_r(NULL
, "", &last
);
267 CFStringRef label
= token1
? CFStringCreateWithCString(NULL
, token0
, kCFStringEncodingUTF8
) : CFSTR("security command line tool");
268 char *password_token
= token1
? token1
: token0
;
269 password_token
= password_token
? password_token
: "";
270 CFDataRef password
= CFDataCreate(NULL
, (const UInt8
*) password_token
, strlen(password_token
));
271 bool returned
= !SOSCCSetUserCredentials(label
, password
, err
);
277 static bool tryPassword(char *labelAndPassword
, CFErrorRef
*err
)
280 char *token0
= strtok_r(labelAndPassword
, ":", &last
);
281 char *token1
= strtok_r(NULL
, "", &last
);
282 CFStringRef label
= token1
? CFStringCreateWithCString(NULL
, token0
, kCFStringEncodingUTF8
) : CFSTR("security command line tool");
283 char *password_token
= token1
? token1
: token0
;
284 password_token
= password_token
? password_token
: "";
285 CFDataRef password
= CFDataCreate(NULL
, (const UInt8
*) password_token
, strlen(password_token
));
286 bool returned
= !SOSCCTryUserCredentials(label
, password
, err
);
292 static CFTypeRef
getObjectsFromCloud(CFArrayRef keysToGet
, dispatch_queue_t processQueue
, dispatch_group_t dgroup
)
294 __block CFTypeRef object
= NULL
;
296 const uint64_t maxTimeToWaitInSeconds
= 30ull * NSEC_PER_SEC
;
297 dispatch_semaphore_t waitSemaphore
= dispatch_semaphore_create(0);
298 dispatch_time_t finishTime
= dispatch_time(DISPATCH_TIME_NOW
, maxTimeToWaitInSeconds
);
300 dispatch_group_enter(dgroup
);
302 CloudKeychainReplyBlock replyBlock
=
303 ^ (CFDictionaryRef returnedValues
, CFErrorRef error
)
305 secinfo("sync", "SOSCloudKeychainGetObjectsFromCloud returned: %@", returnedValues
);
306 object
= returnedValues
;
311 secerror("SOSCloudKeychainGetObjectsFromCloud returned error: %@", error
);
312 // CFRelease(*error);
314 dispatch_group_leave(dgroup
);
315 secinfo("sync", "SOSCloudKeychainGetObjectsFromCloud block exit: %@", object
);
316 dispatch_semaphore_signal(waitSemaphore
);
320 SOSCloudKeychainGetAllObjectsFromCloud(processQueue
, replyBlock
);
322 SOSCloudKeychainGetObjectsFromCloud(keysToGet
, processQueue
, replyBlock
);
324 dispatch_semaphore_wait(waitSemaphore
, finishTime
);
325 dispatch_release(waitSemaphore
);
326 if (object
&& (CFGetTypeID(object
) == CFNullGetTypeID())) // return a NULL instead of a CFNull
331 secerror("returned: %@", object
);
335 static CFStringRef
printFullDataString(CFDataRef data
){
336 __block CFStringRef fullData
= NULL
;
338 BufferPerformWithHexString(CFDataGetBytePtr(data
), CFDataGetLength(data
), ^(CFStringRef dataHex
) {
339 fullData
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%@"), dataHex
);
345 static void displayLastKeyParameters(CFTypeRef key
, CFTypeRef value
)
347 CFDataRef valueAsData
= asData(value
, NULL
);
349 CFDataRef dateData
= CFDataCreateCopyFromRange(kCFAllocatorDefault
, valueAsData
, CFRangeMake(0, DATE_LENGTH
));
350 CFDataRef keyParameterData
= CFDataCreateCopyFromPositions(kCFAllocatorDefault
, valueAsData
, DATE_LENGTH
, CFDataGetLength(valueAsData
));
351 CFStringRef dateString
= CFStringCreateFromExternalRepresentation(kCFAllocatorDefault
, dateData
, kCFStringEncodingUTF8
);
352 CFStringRef keyParameterDescription
= UserParametersDescription(keyParameterData
);
353 if(keyParameterDescription
)
354 printmsg(CFSTR("%@: %@: %@\n"), key
, dateString
, keyParameterDescription
);
356 printmsg(CFSTR("%@: %@\n"), key
, printFullDataString(value
));
357 CFReleaseNull(dateString
);
358 CFReleaseNull(keyParameterData
);
359 CFReleaseNull(dateData
);
360 CFReleaseNull(keyParameterDescription
);
363 printmsg(CFSTR("%@: %@\n"), key
, value
);
367 static void displayKeyParameters(CFTypeRef key
, CFTypeRef value
)
370 CFStringRef keyParameterDescription
= UserParametersDescription((CFDataRef
)value
);
372 if(keyParameterDescription
)
373 printmsg(CFSTR("%@: %@\n"), key
, keyParameterDescription
);
375 printmsg(CFSTR("%@: %@\n"), key
, value
);
377 CFReleaseNull(keyParameterDescription
);
380 printmsg(CFSTR("%@: %@\n"), key
, value
);
384 static void displayLastCircle(CFTypeRef key
, CFTypeRef value
)
386 CFDataRef valueAsData
= asData(value
, NULL
);
388 CFErrorRef localError
= NULL
;
390 CFDataRef dateData
= CFDataCreateCopyFromRange(kCFAllocatorDefault
, valueAsData
, CFRangeMake(0, DATE_LENGTH
));
391 CFDataRef circleData
= CFDataCreateCopyFromPositions(kCFAllocatorDefault
, valueAsData
, DATE_LENGTH
, CFDataGetLength(valueAsData
));
392 CFStringRef dateString
= CFStringCreateFromExternalRepresentation(kCFAllocatorDefault
, dateData
, kCFStringEncodingUTF8
);
393 SOSCircleRef circle
= SOSCircleCreateFromData(NULL
, (CFDataRef
) circleData
, &localError
);
397 CFNumberRef idLength
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberCFIndexType
, &size
);
398 CFDictionaryRef format
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, CFSTR("SyncD"), CFSTR("SyncD"), CFSTR("idLength"), idLength
, NULL
);
399 printmsgWithFormatOptions(format
, CFSTR("%@: %@: %@\n"), key
, dateString
, circle
);
400 CFReleaseNull(idLength
);
401 CFReleaseNull(format
);
405 printmsg(CFSTR("%@: %@\n"), key
, printFullDataString(circleData
));
407 CFReleaseNull(dateString
);
408 CFReleaseNull(circleData
);
409 CFReleaseSafe(circle
);
410 CFReleaseNull(dateData
);
411 CFReleaseNull(localError
);
414 printmsg(CFSTR("%@: %@\n"), key
, value
);
418 static void displayCircle(CFTypeRef key
, CFTypeRef value
)
420 CFDataRef circleData
= (CFDataRef
)value
;
422 CFErrorRef localError
= NULL
;
423 if (isData(circleData
))
426 CFNumberRef idLength
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberCFIndexType
, &size
);
427 CFDictionaryRef format
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, CFSTR("SyncD"), CFSTR("SyncD"), CFSTR("idLength"), idLength
, NULL
);
428 SOSCircleRef circle
= SOSCircleCreateFromData(NULL
, circleData
, &localError
);
429 printmsgWithFormatOptions(format
, CFSTR("%@: %@\n"), key
, circle
);
430 CFReleaseSafe(circle
);
431 CFReleaseNull(idLength
);
432 CFReleaseNull(format
);
436 printmsg(CFSTR("%@: %@\n"), key
, value
);
439 static void displayMessage(CFTypeRef key
, CFTypeRef value
)
441 CFDataRef message
= (CFDataRef
)value
;
443 const char* messageType
= SecOTRPacketTypeString(message
);
444 printmsg(CFSTR("%@: %s: %ld\n"), key
, messageType
, CFDataGetLength(message
));
447 printmsg(CFSTR("%@: %@\n"), key
, value
);
450 static void printEverything(CFTypeRef objects
)
452 CFDictionaryForEach(objects
, ^(const void *key
, const void *value
) {
455 printmsg(CFSTR("%@: %@\n\n"), key
, printFullDataString(value
));
458 printmsg(CFSTR("%@: %@\n"), key
, value
);
463 static void decodeForKeyType(CFTypeRef key
, CFTypeRef value
, SOSKVSKeyType type
){
466 displayCircle(key
, value
);
470 displayMessage(key
, value
);
473 displayKeyParameters(key
, value
);
475 case kLastKeyParameterKey
:
476 displayLastKeyParameters(key
, value
);
479 displayLastCircle(key
, value
);
481 case kInitialSyncKey
:
482 case kAccountChangedKey
:
487 printmsg(CFSTR("%@: %@\n"), key
, value
);
492 static void decodeAllTheValues(CFTypeRef objects
){
493 SOSKVSKeyType keyType
= 0;
494 __block
bool didPrint
= false;
496 for (keyType
= 0; keyType
<= MAXKVSKEYTYPE
; keyType
++){
497 CFDictionaryForEach(objects
, ^(const void *key
, const void *value
) {
498 if(SOSKVSKeyGetKeyType(key
) == keyType
){
499 decodeForKeyType(key
, value
, keyType
);
504 printmsg(CFSTR("%@\n"), CFSTR(""));
508 static bool dumpKVS(char *itemName
, CFErrorRef
*err
)
510 CFArrayRef keysToGet
= NULL
;
513 CFStringRef itemStr
= CFStringCreateWithCString(kCFAllocatorDefault
, itemName
, kCFStringEncodingUTF8
);
514 fprintf(outFile
, "Retrieving %s from KVS\n", itemName
);
515 keysToGet
= CFArrayCreateForCFTypes(kCFAllocatorDefault
, itemStr
, NULL
);
516 CFReleaseSafe(itemStr
);
518 dispatch_queue_t generalq
= dispatch_queue_create("general", DISPATCH_QUEUE_SERIAL
);
519 dispatch_group_t work_group
= dispatch_group_create();
520 CFTypeRef objects
= getObjectsFromCloud(keysToGet
, generalq
, work_group
);
521 CFReleaseSafe(keysToGet
);
524 fprintf(outFile
, "All keys and values straight from KVS\n");
525 printEverything(objects
);
526 fprintf(outFile
, "\nAll values in decoded form...\n");
527 decodeAllTheValues(objects
);
529 fprintf(outFile
, "\n");
533 static bool syncAndWait(CFErrorRef
*err
)
535 __block CFTypeRef objects
= NULL
;
537 dispatch_queue_t generalq
= dispatch_queue_create("general", DISPATCH_QUEUE_SERIAL
);
539 const uint64_t maxTimeToWaitInSeconds
= 30ull * NSEC_PER_SEC
;
540 dispatch_semaphore_t waitSemaphore
= dispatch_semaphore_create(0);
541 dispatch_time_t finishTime
= dispatch_time(DISPATCH_TIME_NOW
, maxTimeToWaitInSeconds
);
543 CloudKeychainReplyBlock replyBlock
= ^ (CFDictionaryRef returnedValues
, CFErrorRef error
)
545 secinfo("sync", "SOSCloudKeychainSynchronizeAndWait returned: %@", returnedValues
);
547 secerror("SOSCloudKeychainSynchronizeAndWait returned error: %@", error
);
548 objects
= CFRetainSafe(returnedValues
);
550 secinfo("sync", "SOSCloudKeychainGetObjectsFromCloud block exit: %@", objects
);
551 dispatch_semaphore_signal(waitSemaphore
);
554 SOSCloudKeychainSynchronizeAndWait(generalq
, replyBlock
);
556 dispatch_semaphore_wait(waitSemaphore
, finishTime
);
557 dispatch_release(waitSemaphore
);
560 fprintf(outFile
, "\n");
564 static CFStringRef
convertStringToProperty(char *propertyname
) {
565 CFStringRef propertyspec
= NULL
;
567 if(strcmp(propertyname
, "hasentropy") == 0) {
568 propertyspec
= kSOSSecPropertyHasEntropy
;
569 } else if(strcmp(propertyname
, "screenlock") == 0) {
570 propertyspec
= kSOSSecPropertyScreenLock
;
571 } else if(strcmp(propertyname
, "SEP") == 0) {
572 propertyspec
= kSOSSecPropertySEP
;
573 } else if(strcmp(propertyname
, "IOS") == 0) {
574 propertyspec
= kSOSSecPropertyIOS
;
580 static CFStringRef
convertPropertyReturnCodeToString(SOSSecurityPropertyResultCode ac
) {
581 CFStringRef retval
= NULL
;
583 case kSOSCCGeneralSecurityPropertyError
:
584 retval
= CFSTR("General Error"); break;
585 case kSOSCCSecurityPropertyValid
:
586 retval
= CFSTR("Is Member of Security Property"); break;
587 case kSOSCCSecurityPropertyNotValid
:
588 retval
= CFSTR("Is Not Member of Security Property"); break;
589 case kSOSCCSecurityPropertyNotQualified
:
590 retval
= CFSTR("Is not qualified for Security Property"); break;
591 case kSOSCCNoSuchSecurityProperty
:
592 retval
= CFSTR("No Such Security Property"); break;
598 static bool SecPropertycmd(char *itemName
, CFErrorRef
*err
) {
599 char *cmd
, *propertyname
;
600 SOSSecurityPropertyActionCode ac
= kSOSCCSecurityPropertyQuery
;
601 CFStringRef propertyspec
;
603 propertyname
= strchr(itemName
, ':');
604 if(propertyname
== NULL
) return false;
609 if(strcmp(cmd
, "enable") == 0) {
610 ac
= kSOSCCSecurityPropertyEnable
;
611 } else if(strcmp(cmd
, "disable") == 0) {
612 ac
= kSOSCCSecurityPropertyDisable
;
613 } else if(strcmp(cmd
, "query") == 0) {
614 ac
= kSOSCCSecurityPropertyQuery
;
619 propertyspec
= convertStringToProperty(propertyname
);
620 if(!propertyspec
) return false;
622 SOSSecurityPropertyResultCode rc
= SOSCCSecurityProperty(propertyspec
, ac
, err
);
623 CFStringRef resultString
= convertPropertyReturnCodeToString(rc
);
625 printmsg(CFSTR("Property Result: %@ : %@\n"), resultString
, propertyspec
);
630 static void dumpStringSet(CFStringRef label
, CFSetRef s
) {
631 if(!s
|| !label
) return;
633 printmsg(CFSTR("%@: { "), label
);
634 __block
bool first
= true;
635 CFSetForEach(s
, ^(const void *p
) {
636 CFStringRef fmt
= CFSTR(", %@");
640 CFStringRef string
= (CFStringRef
) p
;
641 printmsg(fmt
, string
);
644 printmsg(CFSTR(" }\n"), NULL
);
647 static bool dumpMyPeer(CFErrorRef
*error
) {
648 SOSPeerInfoRef myPeer
= SOSCCCopyMyPeerInfo(error
);
650 if (!myPeer
) return false;
652 CFStringRef peerID
= SOSPeerInfoGetPeerID(myPeer
);
653 CFStringRef peerName
= SOSPeerInfoGetPeerName(myPeer
);
654 CFIndex peerVersion
= SOSPeerInfoGetVersion(myPeer
);
655 bool retirement
= SOSPeerInfoIsRetirementTicket(myPeer
);
657 printmsg(CFSTR("Peer Name: %@ PeerID: %@ Version: %d\n"), peerName
, peerID
, peerVersion
);
659 CFDateRef retdate
= SOSPeerInfoGetRetirementDate(myPeer
);
660 printmsg(CFSTR("Retired: %@\n"), retdate
);
664 if(peerVersion
>= 2) {
665 CFMutableSetRef views
= SOSPeerInfoV2DictionaryCopySet(myPeer
, sViewsKey
);
666 CFStringRef serialNumber
= SOSPeerInfoV2DictionaryCopyString(myPeer
, sSerialNumberKey
);
667 CFBooleanRef preferIDS
= SOSPeerInfoV2DictionaryCopyBoolean(myPeer
, sPreferIDS
);
668 CFBooleanRef preferIDSFragmentation
= SOSPeerInfoV2DictionaryCopyBoolean(myPeer
, sPreferIDSFragmentation
);
669 CFStringRef transportType
= SOSPeerInfoV2DictionaryCopyString(myPeer
, sTransportType
);
670 CFStringRef idsDeviceID
= SOSPeerInfoV2DictionaryCopyString(myPeer
, sDeviceID
);
671 CFMutableSetRef properties
= SOSPeerInfoV2DictionaryCopySet(myPeer
, sSecurityPropertiesKey
);
673 printmsg(CFSTR("Serial#: %@ PrefIDS#: %@ PrefFragmentation#: %@ transportType#: %@ idsDeviceID#: %@\n"),
674 serialNumber
, preferIDS
, preferIDSFragmentation
, transportType
, idsDeviceID
);
675 dumpStringSet(CFSTR(" Views: "), views
);
676 dumpStringSet(CFSTR("SecurityProperties: "), properties
);
678 CFReleaseSafe(serialNumber
);
679 CFReleaseSafe(preferIDS
);
680 CFReleaseSafe(preferIDSFragmentation
);
681 CFReleaseSafe(views
);
682 CFReleaseSafe(transportType
);
683 CFReleaseSafe(idsDeviceID
);
684 CFReleaseSafe(properties
);
688 return myPeer
!= NULL
;
691 static bool setBag(char *itemName
, CFErrorRef
*err
)
693 __block
bool success
= false;
694 __block CFErrorRef error
= NULL
;
696 CFStringRef random
= SecPasswordCreateWithRandomDigits(10, NULL
);
698 CFStringPerformWithUTF8CFData(random
, ^(CFDataRef stringAsData
) {
699 if (0 == strncasecmp(optarg
, "single", 6) || 0 == strncasecmp(optarg
, "all", 3)) {
700 bool includeV0
= (0 == strncasecmp(optarg
, "all", 3));
701 printmsg(CFSTR("Setting iCSC single using entropy from string: %@\n"), random
);
702 CFDataRef aks_bag
= SecAKSCopyBackupBagWithSecret(CFDataGetLength(stringAsData
), (uint8_t*)CFDataGetBytePtr(stringAsData
), &error
);
705 success
= SOSCCRegisterSingleRecoverySecret(aks_bag
, includeV0
, &error
);
707 printmsg(CFSTR("Failed registering single secret %@"), error
);
708 CFReleaseNull(aks_bag
);
711 printmsg(CFSTR("Failed to create aks_bag: %@"), error
);
713 CFReleaseNull(aks_bag
);
714 } else if (0 == strncasecmp(optarg
, "device", 6)) {
715 printmsg(CFSTR("Setting Device Secret using entropy from string: %@\n"), random
);
717 SOSPeerInfoRef me
= SOSCCCopyMyPeerWithNewDeviceRecoverySecret(stringAsData
, &error
);
719 success
= me
!= NULL
;
722 printmsg(CFSTR("Failed: %@\n"), err
);
725 printmsg(CFSTR("Unrecognized argument to -b %s\n"), optarg
);
733 static void prClientViewState(char *label
, bool result
) {
734 fprintf(outFile
, "Sync Status for %s: %s\n", label
, (result
) ? "enabled": "not enabled");
737 static bool clientViewStatus(CFErrorRef
*error
) {
738 prClientViewState("KeychainV0", SOSCCIsIcloudKeychainSyncing());
739 prClientViewState("Safari", SOSCCIsSafariSyncing());
740 prClientViewState("AppleTV", SOSCCIsAppleTVSyncing());
741 prClientViewState("HomeKit", SOSCCIsHomeKitSyncing());
742 prClientViewState("Wifi", SOSCCIsWiFiSyncing());
743 prClientViewState("AlwaysOnNoInitialSync", SOSCCIsContinuityUnlockSyncing());
748 static bool dumpYetToSync(CFErrorRef
*error
) {
749 CFArrayRef yetToSyncViews
= SOSCCCopyYetToSyncViewsList(error
);
751 bool hadError
= yetToSyncViews
;
753 if (yetToSyncViews
) {
754 __block CFStringRef separator
= CFSTR("");
756 printmsg(CFSTR("Yet to sync views: ["), NULL
);
758 CFArrayForEach(yetToSyncViews
, ^(const void *value
) {
759 if (isString(value
)) {
760 printmsg(CFSTR("%@%@"), separator
, value
);
762 separator
= CFSTR(", ");
765 printmsg(CFSTR("]\n"), NULL
);
772 // enable, disable, accept, reject, status, Reset, Clear
774 keychain_sync(int argc
, char * const *argv
)
779 " -e enable (join/create circle)"
780 " -i info (current status)"
782 " -S schedule sync with all peers"
784 "Account/Circle Management"
785 " -a accept all applicants"
786 " -l [reason] sign out of circle + set custom departure reason"
787 " -q sign out of circle"
788 " -r reject all applicants"
789 " -E ensure fresh parameters"
790 " -b device|all|single Register a backup bag - THIS RESETS BACKUPS!\n"
791 " -A Apply to a ring\n"
792 " -B Withdrawl from a ring\n"
795 " -I Dump Ring Information\n"
797 " -N (re-)set to new account (USE WITH CARE: device will not leave circle before resetting account!)"
798 " -O reset to offering"
800 " -X [limit] best effort bail from circle in limit seconds"
801 " -o list view unaware peers in circle"
802 " -0 boot view unaware peers from circle"
803 " -1 grab account state from the keychain"
804 " -2 delete account state from the keychain"
805 " -3 grab engine state from the keychain"
806 " -4 delete engine state from the keychain"
809 " -g set IDS device id"
810 " -p retrieve IDS device id"
811 " -x ping all devices in an IDS account"
812 " -w check IDS availability"
813 " -z retrieve IDS id through IDSKeychainSyncingProxy"
816 " -P [label:]password set password (optionally for a given label) for sync"
817 " -T [label:]password try password (optionally for a given label) for sync"
820 " -k pend all registered kvs keys"
821 " -C clear all values from KVS"
822 " -D [itemName] dump contents of KVS"
826 " -v [enable|disable|query:viewname] enable, disable, or query my PeerInfo's view set"
827 " viewnames are: keychain|masterkey|iclouddrive|photos|cloudkit|escrow|fde|maildrop|icloudbackup|notes|imessage|appletv|homekit|"
828 " wifi|passwords|creditcards|icloudidentity|othersyncable"
829 " -L list all known view and their status"
830 " -S [enable|disable|propertyname] enable, disable, or query my PeerInfo's Security Property set"
831 " propertynames are: hasentropy|screenlock|SEP|IOS\n"
832 " -U purge private key material cache\n"
833 " -V Report View Sync Status on all known clients.\n"
834 " -H Set escrow record.\n"
835 " -J Get the escrow record.\n"
836 " -M Check peer availability.\n"
840 CFErrorRef error
= NULL
;
841 bool hadError
= false;
842 setOutputTo(NULL
, NULL
);
844 while ((ch
= getopt(argc
, argv
, "ab:deg:hikl:mopq:rsSv:w:x:zA:B:MNJCDEF:HG:ILOP:RT:UWX:VY01234")) != -1)
848 fprintf(outFile
, "Signing out of circle\n");
849 hadError
= !SOSCCSignedOut(true, &error
);
852 int reason
= (int) strtoul(optarg
, NULL
, 10);
854 reason
< kSOSDepartureReasonError
||
855 reason
>= kSOSNumDepartureReasons
) {
856 fprintf(errFile
, "Invalid custom departure reason %s\n", optarg
);
858 fprintf(outFile
, "Setting custom departure reason %d\n", reason
);
859 hadError
= !SOSCCSetLastDepartureReason(reason
, &error
);
860 notify_post(kSOSCCCircleChangedNotification
);
868 fprintf(outFile
, "Signing out of circle\n");
869 bool signOutImmediately
= false;
870 if (strcasecmp(optarg
, "true") == 0) {
871 signOutImmediately
= true;
872 } else if (strcasecmp(optarg
, "false") == 0) {
873 signOutImmediately
= false;
875 fprintf(outFile
, "Please provide a \"true\" or \"false\" whether you'd like to leave the circle immediately\n");
877 hadError
= !SOSCCSignedOut(signOutImmediately
, &error
);
878 notify_post(kSOSCCCircleChangedNotification
);
884 fprintf(outFile
, "Grabbing DS ID\n");
885 CFStringRef deviceID
= SOSCCCopyDeviceID(&error
);
890 if (!isNull(deviceID
)) {
891 const char *id
= CFStringGetCStringPtr(deviceID
, kCFStringEncodingUTF8
);
893 fprintf(outFile
, "IDS Device ID: %s\n", id
);
895 fprintf(outFile
, "IDS Device ID is null!\n");
897 CFReleaseNull(deviceID
);
903 fprintf(outFile
, "Setting DS ID: %s\n", optarg
);
904 CFStringRef deviceID
= CFStringCreateWithCString(kCFAllocatorDefault
, optarg
, kCFStringEncodingUTF8
);
905 hadError
= SOSCCSetDeviceID(deviceID
, &error
);
906 CFReleaseNull(deviceID
);
912 fprintf(outFile
, "Attempting to send this message over IDS: %s\n", optarg
);
913 CFStringRef message
= CFStringCreateWithCString(kCFAllocatorDefault
, optarg
, kCFStringEncodingUTF8
);
914 hadError
= SOSCCIDSServiceRegistrationTest(message
, &error
);
916 printerr(CFSTR("IDS is not ready: %@\n"), error
);
919 CFReleaseNull(message
);
925 fprintf(outFile
, "Starting ping test using this message: %s\n", optarg
);
926 CFStringRef message
= CFStringCreateWithCString(kCFAllocatorDefault
, optarg
, kCFStringEncodingUTF8
);
927 hadError
= SOSCCIDSPingTest(message
, &error
);
929 printerr(CFSTR("Ping test failed to start: %@\n"), error
);
932 CFReleaseNull(message
);
937 hadError
= SOSCCIDSDeviceIDIsAvailableTest(&error
);
939 printerr(CFSTR("Failed to retrieve IDS device ID: %@\n"), error
);
945 fprintf(outFile
, "Turning ON keychain syncing\n");
946 hadError
= requestToJoinCircle(&error
);
950 fprintf(outFile
, "Turning OFF keychain syncing\n");
951 hadError
= !SOSCCRemoveThisDeviceFromCircle(&error
);
956 CFArrayRef applicants
= SOSCCCopyApplicantPeerInfo(NULL
);
958 hadError
= !SOSCCAcceptApplicants(applicants
, &error
);
959 CFRelease(applicants
);
961 fprintf(errFile
, "No applicants to accept\n");
968 CFArrayRef applicants
= SOSCCCopyApplicantPeerInfo(NULL
);
970 hadError
= !SOSCCRejectApplicants(applicants
, &error
);
971 CFRelease(applicants
);
973 fprintf(errFile
, "No applicants to reject\n");
983 notify_post("com.apple.security.cloudkeychain.forceupdate");
988 printPeerInfos("view-unaware", ^(CFErrorRef
*error
) { return SOSCCCopyViewUnawarePeerInfo(error
); });
994 CFArrayRef unawares
= SOSCCCopyViewUnawarePeerInfo(&error
);
996 hadError
= !SOSCCRemovePeersFromCircle(unawares
, &error
);
1000 CFReleaseNull(unawares
);
1005 CFDataRef accountState
= SOSCCCopyAccountState(&error
);
1007 printmsg(CFSTR(" %@\n"), CFDataCopyHexString(accountState
));
1011 CFReleaseNull(accountState
);
1016 bool status
= SOSCCDeleteAccountState(&error
);
1018 printmsg(CFSTR("Deleted account from the keychain %d\n"), status
);
1026 CFDataRef engineState
= SOSCCCopyEngineData(&error
);
1028 printmsg(CFSTR(" %@\n"), CFDataCopyHexString(engineState
));
1032 CFReleaseNull(engineState
);
1037 bool status
= SOSCCDeleteEngineState(&error
);
1039 printmsg(CFSTR("Deleted engine-state from the keychain %d\n"), status
);
1047 #if TARGET_OS_EMBEDDED
1048 SOSCloudKeychainRequestSyncWithAllPeers(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), NULL
);
1050 fprintf(outFile
, "not exported yet...\n");
1056 fprintf(outFile
, "Ensuring Fresh Parameters\n");
1057 bool result
= SOSCCRequestEnsureFreshParameters(&error
);
1063 fprintf(outFile
, "Refreshed Parameters Ensured!\n");
1065 fprintf(outFile
, "Problem trying to ensure fresh parameters\n");
1071 fprintf(outFile
, "Applying to Ring\n");
1072 CFStringRef ringName
= CFStringCreateWithCString(kCFAllocatorDefault
, (char *)optarg
, kCFStringEncodingUTF8
);
1073 hadError
= SOSCCApplyToARing(ringName
, &error
);
1078 fprintf(outFile
, "Withdrawing from Ring\n");
1079 CFStringRef ringName
= CFStringCreateWithCString(kCFAllocatorDefault
, (char *)optarg
, kCFStringEncodingUTF8
);
1080 hadError
= SOSCCWithdrawlFromARing(ringName
, &error
);
1085 fprintf(outFile
, "Status of this device in the Ring\n");
1086 CFStringRef ringName
= CFStringCreateWithCString(kCFAllocatorDefault
, (char *)optarg
, kCFStringEncodingUTF8
);
1087 hadError
= SOSCCRingStatus(ringName
, &error
);
1092 fprintf(outFile
, "Enabling Ring\n");
1093 CFStringRef ringName
= CFStringCreateWithCString(kCFAllocatorDefault
, (char *)optarg
, kCFStringEncodingUTF8
);
1094 hadError
= SOSCCEnableRing(ringName
, &error
);
1099 fprintf(outFile
, "Setting random escrow record\n");
1100 bool success
= SOSCCSetEscrowRecord(CFSTR("label"), 8, &error
);
1109 CFDictionaryRef attempts
= SOSCCCopyEscrowRecord(&error
);
1111 CFDictionaryForEach(attempts
, ^(const void *key
, const void *value
) {
1113 char *keyString
= CFStringToCString(key
);
1114 fprintf(outFile
, "%s:\n", keyString
);
1117 if(isDictionary(value
)){
1118 CFDictionaryForEach(value
, ^(const void *key
, const void *value
) {
1120 char *keyString
= CFStringToCString(key
);
1121 fprintf(outFile
, "%s: ", keyString
);
1124 if(isString(value
)){
1125 char *time
= CFStringToCString(value
);
1126 fprintf(outFile
, "timestamp: %s\n", time
);
1129 else if(isNumber(value
)){
1131 CFNumberGetValue(value
, kCFNumberLongLongType
, &tries
);
1132 fprintf(outFile
, "date: %llu\n", tries
);
1139 CFReleaseNull(attempts
);
1145 bool success
= SOSCCCheckPeerAvailability(&error
);
1154 fprintf(outFile
, "Printing all the rings\n");
1155 CFStringRef ringdescription
= SOSCCGetAllTheRings(&error
);
1156 if(!ringdescription
)
1159 fprintf(outFile
, "Rings: %s", CFStringToCString(ringdescription
));
1165 hadError
= !SOSCCAccountSetToNew(&error
);
1167 notify_post(kSOSCCCircleChangedNotification
);
1171 hadError
= !SOSCCResetToEmpty(&error
);
1175 hadError
= !SOSCCResetToOffering(&error
);
1179 hadError
= !dumpMyPeer(&error
);
1183 hadError
= clearAllKVS(&error
);
1187 hadError
= setPassword(optarg
, &error
);
1191 hadError
= tryPassword(optarg
, &error
);
1196 uint64_t limit
= strtoul(optarg
, NULL
, 10);
1197 hadError
= !SOSCCBailFromCircle_BestEffort(limit
, &error
);
1202 hadError
= !SOSCCPurgeUserCredentials(&error
);
1206 hadError
= !dumpKVS(optarg
, &error
);
1210 hadError
= syncAndWait(&error
);
1214 hadError
= !viewcmd(optarg
, &error
);
1218 hadError
= clientViewStatus(&error
);
1221 hadError
= !listviewcmd(&error
);
1225 hadError
= !SecPropertycmd(optarg
, &error
);
1229 hadError
= setBag(optarg
, &error
);
1233 hadError
= dumpYetToSync(&error
);
1238 return 2; /* Return 2 triggers usage message. */
1242 printerr(CFSTR("Error: %@\n"), error
);