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 "secToolFileIO.h"
62 #include <Security/SecPasswordGenerate.h>
64 #define MAXKVSKEYTYPE kUnknownKey
65 #define DATE_LENGTH 18
68 static bool clearAllKVS(CFErrorRef
*error
)
70 __block
bool result
= false;
71 const uint64_t maxTimeToWaitInSeconds
= 30ull * NSEC_PER_SEC
;
72 dispatch_queue_t processQueue
= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0);
73 dispatch_semaphore_t waitSemaphore
= dispatch_semaphore_create(0);
74 dispatch_time_t finishTime
= dispatch_time(DISPATCH_TIME_NOW
, maxTimeToWaitInSeconds
);
76 SOSCloudKeychainClearAll(processQueue
, ^(CFDictionaryRef returnedValues
, CFErrorRef cerror
)
78 result
= (cerror
!= NULL
);
79 dispatch_semaphore_signal(waitSemaphore
);
82 dispatch_semaphore_wait(waitSemaphore
, finishTime
);
83 dispatch_release(waitSemaphore
);
88 static const char *getSOSCCStatusDescription(SOSCCStatus ccstatus
)
92 case kSOSCCInCircle
: return "In Circle";
93 case kSOSCCNotInCircle
: return "Not in Circle";
94 case kSOSCCRequestPending
: return "Request pending";
95 case kSOSCCCircleAbsent
: return "Circle absent";
96 case kSOSCCError
: return "Circle error";
99 return "<unknown ccstatus>";
104 static void printPeerInfos(char *label
, CFArrayRef (^getArray
)(CFErrorRef
*error
)) {
105 CFErrorRef error
= NULL
;
106 CFArrayRef ppi
= getArray(&error
);
107 SOSPeerInfoRef me
= SOSCCCopyMyPeerInfo(NULL
);
108 CFStringRef mypeerID
= SOSPeerInfoGetPeerID(me
);
111 printmsg(CFSTR("%s count: %ld\n"), label
, (long)CFArrayGetCount(ppi
));
112 CFArrayForEach(ppi
, ^(const void *value
) {
114 SOSPeerInfoRef peer
= (SOSPeerInfoRef
)value
;
115 CFIndex version
= SOSPeerInfoGetVersion(peer
);
116 CFStringRef peerName
= SOSPeerInfoGetPeerName(peer
);
117 CFStringRef devtype
= SOSPeerInfoGetPeerDeviceType(peer
);
118 CFStringRef peerID
= SOSPeerInfoGetPeerID(peer
);
119 CFStringRef transportType
= CFSTR("KVS");
120 CFStringRef deviceID
= CFSTR("");
121 CFDictionaryRef gestalt
= SOSPeerInfoCopyPeerGestalt(peer
);
122 CFStringRef osVersion
= CFDictionaryGetValue(gestalt
, CFSTR("OSVersion"));
123 CFReleaseNull(gestalt
);
127 CFDictionaryRef v2Dictionary
= peer
->v2Dictionary
;
128 transportType
= CFDictionaryGetValue(v2Dictionary
, sTransportType
);
129 deviceID
= CFDictionaryGetValue(v2Dictionary
, sDeviceID
);
131 char *pname
= CFStringToCString(peerName
);
132 char *dname
= CFStringToCString(devtype
);
133 char *tname
= CFStringToCString(transportType
);
134 char *iname
= CFStringToCString(deviceID
);
135 char *osname
= CFStringToCString(osVersion
);
136 const char *me
= CFEqualSafe(mypeerID
, peerID
) ? "me>" : " ";
139 snprintf(buf
, 160, "%s %s: %-16s %-16s %-16s %-16s", me
, label
, pname
, dname
, tname
, iname
);
143 CFStringRef pid
= SOSPeerInfoGetPeerID(peer
);
144 CFIndex vers
= SOSPeerInfoGetVersion(peer
);
145 printmsg(CFSTR("%s %@ V%d OS:%s\n"), buf
, pid
, vers
, osname
);
149 printmsg(CFSTR("No %s, error: %@\n"), label
, error
);
152 CFReleaseNull(error
);
155 static void dumpCircleInfo()
157 CFErrorRef error
= NULL
;
158 CFArrayRef generations
= NULL
;
159 CFArrayRef confirmedDigests
= NULL
;
160 bool is_user_public_trusted
= false;
161 __block
int count
= 0;
163 SOSCCStatus ccstatus
= SOSCCThisDeviceIsInCircle(&error
);
164 if(ccstatus
== kSOSCCError
) {
165 printmsg(CFSTR("End of Dump - unable to proceed due to ccstatus (%s) error: %@\n"), getSOSCCStatusDescription(ccstatus
), error
);
168 printmsg(CFSTR("ccstatus: %s (%d)\n"), getSOSCCStatusDescription(ccstatus
), ccstatus
, error
);
170 is_user_public_trusted
= SOSCCValidateUserPublic(&error
);
171 if(is_user_public_trusted
)
172 printmsg(CFSTR("Account user public is trusted%@"),CFSTR("\n"));
174 printmsg(CFSTR("Account user public is not trusted error:(%@)\n"), error
);
175 CFReleaseNull(error
);
177 generations
= SOSCCCopyGenerationPeerInfo(&error
);
179 CFArrayForEach(generations
, ^(const void *value
) {
182 printmsg(CFSTR("Circle name: %@, "),value
);
185 CFStringRef genDesc
= SOSGenerationCountCopyDescription(value
);
186 printmsg(CFSTR("Generation Count: %@"), genDesc
);
187 CFReleaseNull(genDesc
);
189 printmsg(CFSTR("%s\n"), "");
192 printmsg(CFSTR("No generation count: %@\n"), error
);
194 CFReleaseNull(generations
);
195 CFReleaseNull(error
);
197 printPeerInfos(" Peers", ^(CFErrorRef
*error
) { return SOSCCCopyValidPeerPeerInfo(error
); });
198 printPeerInfos(" Invalid", ^(CFErrorRef
*error
) { return SOSCCCopyNotValidPeerPeerInfo(error
); });
199 printPeerInfos(" Retired", ^(CFErrorRef
*error
) { return SOSCCCopyRetirementPeerInfo(error
); });
200 printPeerInfos(" Concur", ^(CFErrorRef
*error
) { return SOSCCCopyConcurringPeerPeerInfo(error
); });
201 printPeerInfos("Applicants", ^(CFErrorRef
*error
) { return SOSCCCopyApplicantPeerInfo(error
); });
203 confirmedDigests
= SOSCCCopyEngineState(&error
);
207 CFArrayForEach(confirmedDigests
, ^(const void *value
) {
210 printmsg(CFSTR("%@"), value
);
213 CFStringRef hexDigest
= CFDataCopyHexString(value
);
214 printmsg(CFSTR(" %@\n"), hexDigest
);
215 CFReleaseSafe(hexDigest
);
220 printmsg(CFSTR("No engine peers: %@\n"), error
);
221 CFReleaseNull(confirmedDigests
);
224 static bool enableDefaultViews()
227 CFMutableSetRef viewsToEnable
= CFSetCreateMutable(NULL
, 0, NULL
);
228 CFMutableSetRef viewsToDisable
= CFSetCreateMutable(NULL
, 0, NULL
);
229 CFSetAddValue(viewsToEnable
, (void*)kSOSViewWiFi
);
230 CFSetAddValue(viewsToEnable
, (void*)kSOSViewAutofillPasswords
);
231 CFSetAddValue(viewsToEnable
, (void*)kSOSViewSafariCreditCards
);
232 CFSetAddValue(viewsToEnable
, (void*)kSOSViewOtherSyncable
);
234 result
= SOSCCViewSet(viewsToEnable
, viewsToDisable
);
235 CFRelease(viewsToEnable
);
236 CFRelease(viewsToDisable
);
240 static bool requestToJoinCircle(CFErrorRef
*error
)
242 // Set the visual state of switch based on membership in circle
243 bool hadError
= false;
244 SOSCCStatus ccstatus
= SOSCCThisDeviceIsInCircle(error
);
248 case kSOSCCCircleAbsent
:
249 hadError
= !SOSCCResetToOffering(error
);
250 hadError
&= enableDefaultViews();
252 case kSOSCCNotInCircle
:
253 hadError
= !SOSCCRequestToJoinCircle(error
);
254 hadError
&= enableDefaultViews();
257 printerr(CFSTR("Request to join circle with bad status: %@ (%d)\n"), SOSCCGetStatusDescription(ccstatus
), ccstatus
);
263 static bool setPassword(char *labelAndPassword
, CFErrorRef
*err
)
266 char *token0
= strtok_r(labelAndPassword
, ":", &last
);
267 char *token1
= strtok_r(NULL
, "", &last
);
268 CFStringRef label
= token1
? CFStringCreateWithCString(NULL
, token0
, kCFStringEncodingUTF8
) : CFSTR("security command line tool");
269 char *password_token
= token1
? token1
: token0
;
270 password_token
= password_token
? password_token
: "";
271 CFDataRef password
= CFDataCreate(NULL
, (const UInt8
*) password_token
, strlen(password_token
));
272 bool returned
= !SOSCCSetUserCredentials(label
, password
, err
);
278 static bool tryPassword(char *labelAndPassword
, CFErrorRef
*err
)
281 char *token0
= strtok_r(labelAndPassword
, ":", &last
);
282 char *token1
= strtok_r(NULL
, "", &last
);
283 CFStringRef label
= token1
? CFStringCreateWithCString(NULL
, token0
, kCFStringEncodingUTF8
) : CFSTR("security command line tool");
284 char *password_token
= token1
? token1
: token0
;
285 password_token
= password_token
? password_token
: "";
286 CFDataRef password
= CFDataCreate(NULL
, (const UInt8
*) password_token
, strlen(password_token
));
287 bool returned
= !SOSCCTryUserCredentials(label
, password
, err
);
293 static CFTypeRef
getObjectsFromCloud(CFArrayRef keysToGet
, dispatch_queue_t processQueue
, dispatch_group_t dgroup
)
295 __block CFTypeRef object
= NULL
;
297 const uint64_t maxTimeToWaitInSeconds
= 30ull * NSEC_PER_SEC
;
298 dispatch_semaphore_t waitSemaphore
= dispatch_semaphore_create(0);
299 dispatch_time_t finishTime
= dispatch_time(DISPATCH_TIME_NOW
, maxTimeToWaitInSeconds
);
301 dispatch_group_enter(dgroup
);
303 CloudKeychainReplyBlock replyBlock
=
304 ^ (CFDictionaryRef returnedValues
, CFErrorRef error
)
306 secinfo("sync", "SOSCloudKeychainGetObjectsFromCloud returned: %@", returnedValues
);
307 object
= returnedValues
;
312 secerror("SOSCloudKeychainGetObjectsFromCloud returned error: %@", error
);
313 // CFRelease(*error);
315 dispatch_group_leave(dgroup
);
316 secinfo("sync", "SOSCloudKeychainGetObjectsFromCloud block exit: %@", object
);
317 dispatch_semaphore_signal(waitSemaphore
);
321 SOSCloudKeychainGetAllObjectsFromCloud(processQueue
, replyBlock
);
323 SOSCloudKeychainGetObjectsFromCloud(keysToGet
, processQueue
, replyBlock
);
325 dispatch_semaphore_wait(waitSemaphore
, finishTime
);
326 dispatch_release(waitSemaphore
);
327 if (object
&& (CFGetTypeID(object
) == CFNullGetTypeID())) // return a NULL instead of a CFNull
332 secerror("returned: %@", object
);
336 static CFStringRef
printFullDataString(CFDataRef data
){
337 __block CFStringRef fullData
= NULL
;
339 BufferPerformWithHexString(CFDataGetBytePtr(data
), CFDataGetLength(data
), ^(CFStringRef dataHex
) {
340 fullData
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%@"), dataHex
);
346 static void displayLastKeyParameters(CFTypeRef key
, CFTypeRef value
)
348 CFDataRef valueAsData
= asData(value
, NULL
);
350 CFDataRef dateData
= CFDataCreateCopyFromRange(kCFAllocatorDefault
, valueAsData
, CFRangeMake(0, DATE_LENGTH
));
351 CFDataRef keyParameterData
= CFDataCreateCopyFromPositions(kCFAllocatorDefault
, valueAsData
, DATE_LENGTH
, CFDataGetLength(valueAsData
));
352 CFStringRef dateString
= CFStringCreateFromExternalRepresentation(kCFAllocatorDefault
, dateData
, kCFStringEncodingUTF8
);
353 CFStringRef keyParameterDescription
= UserParametersDescription(keyParameterData
);
354 if(keyParameterDescription
)
355 printmsg(CFSTR("%@: %@: %@\n"), key
, dateString
, keyParameterDescription
);
357 printmsg(CFSTR("%@: %@\n"), key
, printFullDataString(value
));
358 CFReleaseNull(dateString
);
359 CFReleaseNull(keyParameterData
);
360 CFReleaseNull(dateData
);
361 CFReleaseNull(keyParameterDescription
);
364 printmsg(CFSTR("%@: %@\n"), key
, value
);
368 static void displayKeyParameters(CFTypeRef key
, CFTypeRef value
)
371 CFStringRef keyParameterDescription
= UserParametersDescription((CFDataRef
)value
);
373 if(keyParameterDescription
)
374 printmsg(CFSTR("%@: %@\n"), key
, keyParameterDescription
);
376 printmsg(CFSTR("%@: %@\n"), key
, value
);
378 CFReleaseNull(keyParameterDescription
);
381 printmsg(CFSTR("%@: %@\n"), key
, value
);
385 static void displayLastCircle(CFTypeRef key
, CFTypeRef value
)
387 CFDataRef valueAsData
= asData(value
, NULL
);
389 CFErrorRef localError
= NULL
;
391 CFDataRef dateData
= CFDataCreateCopyFromRange(kCFAllocatorDefault
, valueAsData
, CFRangeMake(0, DATE_LENGTH
));
392 CFDataRef circleData
= CFDataCreateCopyFromPositions(kCFAllocatorDefault
, valueAsData
, DATE_LENGTH
, CFDataGetLength(valueAsData
));
393 CFStringRef dateString
= CFStringCreateFromExternalRepresentation(kCFAllocatorDefault
, dateData
, kCFStringEncodingUTF8
);
394 SOSCircleRef circle
= SOSCircleCreateFromData(NULL
, (CFDataRef
) circleData
, &localError
);
398 CFNumberRef idLength
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberCFIndexType
, &size
);
399 CFDictionaryRef format
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, CFSTR("SyncD"), CFSTR("SyncD"), CFSTR("idLength"), idLength
, NULL
);
400 printmsgWithFormatOptions(format
, CFSTR("%@: %@: %@\n"), key
, dateString
, circle
);
401 CFReleaseNull(idLength
);
402 CFReleaseNull(format
);
406 printmsg(CFSTR("%@: %@\n"), key
, printFullDataString(circleData
));
408 CFReleaseNull(dateString
);
409 CFReleaseNull(circleData
);
410 CFReleaseSafe(circle
);
411 CFReleaseNull(dateData
);
412 CFReleaseNull(localError
);
415 printmsg(CFSTR("%@: %@\n"), key
, value
);
419 static void displayCircle(CFTypeRef key
, CFTypeRef value
)
421 CFDataRef circleData
= (CFDataRef
)value
;
423 CFErrorRef localError
= NULL
;
424 if (isData(circleData
))
427 CFNumberRef idLength
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberCFIndexType
, &size
);
428 CFDictionaryRef format
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, CFSTR("SyncD"), CFSTR("SyncD"), CFSTR("idLength"), idLength
, NULL
);
429 SOSCircleRef circle
= SOSCircleCreateFromData(NULL
, circleData
, &localError
);
430 printmsgWithFormatOptions(format
, CFSTR("%@: %@\n"), key
, circle
);
431 CFReleaseSafe(circle
);
432 CFReleaseNull(idLength
);
433 CFReleaseNull(format
);
437 printmsg(CFSTR("%@: %@\n"), key
, value
);
440 static void displayMessage(CFTypeRef key
, CFTypeRef value
)
442 CFDataRef message
= (CFDataRef
)value
;
444 const char* messageType
= SecOTRPacketTypeString(message
);
445 printmsg(CFSTR("%@: %s: %ld\n"), key
, messageType
, CFDataGetLength(message
));
448 printmsg(CFSTR("%@: %@\n"), key
, value
);
451 static void printEverything(CFTypeRef objects
)
453 CFDictionaryForEach(objects
, ^(const void *key
, const void *value
) {
456 printmsg(CFSTR("%@: %@\n\n"), key
, printFullDataString(value
));
459 printmsg(CFSTR("%@: %@\n"), key
, value
);
464 static void decodeForKeyType(CFTypeRef key
, CFTypeRef value
, SOSKVSKeyType type
){
467 displayCircle(key
, value
);
471 displayMessage(key
, value
);
474 displayKeyParameters(key
, value
);
476 case kLastKeyParameterKey
:
477 displayLastKeyParameters(key
, value
);
480 displayLastCircle(key
, value
);
482 case kInitialSyncKey
:
483 case kAccountChangedKey
:
488 printmsg(CFSTR("%@: %@\n"), key
, value
);
493 static void decodeAllTheValues(CFTypeRef objects
){
494 SOSKVSKeyType keyType
= 0;
495 __block
bool didPrint
= false;
497 for (keyType
= 0; keyType
<= MAXKVSKEYTYPE
; keyType
++){
498 CFDictionaryForEach(objects
, ^(const void *key
, const void *value
) {
499 if(SOSKVSKeyGetKeyType(key
) == keyType
){
500 decodeForKeyType(key
, value
, keyType
);
505 printmsg(CFSTR("%@\n"), CFSTR(""));
509 static bool dumpKVS(char *itemName
, CFErrorRef
*err
)
511 CFArrayRef keysToGet
= NULL
;
514 CFStringRef itemStr
= CFStringCreateWithCString(kCFAllocatorDefault
, itemName
, kCFStringEncodingUTF8
);
515 fprintf(outFile
, "Retrieving %s from KVS\n", itemName
);
516 keysToGet
= CFArrayCreateForCFTypes(kCFAllocatorDefault
, itemStr
, NULL
);
517 CFReleaseSafe(itemStr
);
519 dispatch_queue_t generalq
= dispatch_queue_create("general", DISPATCH_QUEUE_SERIAL
);
520 dispatch_group_t work_group
= dispatch_group_create();
521 CFTypeRef objects
= getObjectsFromCloud(keysToGet
, generalq
, work_group
);
522 CFReleaseSafe(keysToGet
);
525 fprintf(outFile
, "All keys and values straight from KVS\n");
526 printEverything(objects
);
527 fprintf(outFile
, "\nAll values in decoded form...\n");
528 decodeAllTheValues(objects
);
530 fprintf(outFile
, "\n");
534 static bool syncAndWait(char *itemName
, CFErrorRef
*err
)
536 CFArrayRef keysToGet
= NULL
;
537 __block CFTypeRef objects
= NULL
;
540 fprintf(errFile
, "No item keys supplied\n");
544 CFStringRef itemStr
= CFStringCreateWithCString(kCFAllocatorDefault
, itemName
, kCFStringEncodingUTF8
);
545 fprintf(outFile
, "Retrieving %s from KVS\n", itemName
);
546 keysToGet
= CFArrayCreateForCFTypes(kCFAllocatorDefault
, itemStr
, NULL
);
547 CFReleaseSafe(itemStr
);
549 dispatch_queue_t generalq
= dispatch_queue_create("general", DISPATCH_QUEUE_SERIAL
);
551 const uint64_t maxTimeToWaitInSeconds
= 30ull * NSEC_PER_SEC
;
552 dispatch_semaphore_t waitSemaphore
= dispatch_semaphore_create(0);
553 dispatch_time_t finishTime
= dispatch_time(DISPATCH_TIME_NOW
, maxTimeToWaitInSeconds
);
555 CloudKeychainReplyBlock replyBlock
= ^ (CFDictionaryRef returnedValues
, CFErrorRef error
)
557 secinfo("sync", "SOSCloudKeychainSynchronizeAndWait returned: %@", returnedValues
);
559 secerror("SOSCloudKeychainSynchronizeAndWait returned error: %@", error
);
560 objects
= returnedValues
;
563 secinfo("sync", "SOSCloudKeychainGetObjectsFromCloud block exit: %@", objects
);
564 dispatch_semaphore_signal(waitSemaphore
);
567 SOSCloudKeychainSynchronizeAndWait(keysToGet
, generalq
, replyBlock
);
569 dispatch_semaphore_wait(waitSemaphore
, finishTime
);
570 dispatch_release(waitSemaphore
);
572 CFReleaseSafe(keysToGet
);
574 fprintf(outFile
, "\n");
580 const CFStringRef
*viewspec
;
583 "keychain", &kSOSViewKeychainV0
585 "masterkey", &kSOSViewPCSMasterKey
,
587 "iclouddrive", &kSOSViewPCSiCloudDrive
,
589 "photos", &kSOSViewPCSPhotos
,
591 "escrow", &kSOSViewPCSEscrow
,
593 "fde", &kSOSViewPCSFDE
,
595 "maildrop", &kSOSViewPCSMailDrop
,
597 "icloudbackup", &kSOSViewPCSiCloudBackup
,
599 "notes", &kSOSViewPCSNotes
,
601 "imessage", &kSOSViewPCSiMessage
,
603 "feldspar", &kSOSViewPCSFeldspar
,
605 "appletv", &kSOSViewAppleTV
,
607 "homekit", &kSOSViewHomeKit
,
609 "wifi", &kSOSViewWiFi
,
611 "passwords", &kSOSViewAutofillPasswords
,
613 "creditcards", &kSOSViewSafariCreditCards
,
615 "icloudidentity", &kSOSViewiCloudIdentity
,
617 "othersyncable", &kSOSViewOtherSyncable
,
621 static CFStringRef
convertStringToView(char *viewname
) {
624 for (n
= 0; n
< sizeof(string2View
)/sizeof(string2View
[0]); n
++) {
625 if (strcmp(string2View
[n
].name
, viewname
) == 0)
626 return *string2View
[n
].viewspec
;
629 // Leak this, since it's a getter.
630 return CFStringCreateWithCString(kCFAllocatorDefault
, viewname
, kCFStringEncodingUTF8
);
633 static CFStringRef
convertViewReturnCodeToString(SOSViewActionCode ac
) {
634 CFStringRef retval
= NULL
;
636 case kSOSCCGeneralViewError
:
637 retval
= CFSTR("General Error"); break;
638 case kSOSCCViewMember
:
639 retval
= CFSTR("Is Member of View"); break;
640 case kSOSCCViewNotMember
:
641 retval
= CFSTR("Is Not Member of View"); break;
642 case kSOSCCViewNotQualified
:
643 retval
= CFSTR("Is not qualified for View"); break;
644 case kSOSCCNoSuchView
:
645 retval
= CFSTR("No Such View"); break;
650 static bool viewcmd(char *itemName
, CFErrorRef
*err
) {
651 char *cmd
, *viewname
;
652 SOSViewActionCode ac
= kSOSCCViewQuery
;
653 CFStringRef viewspec
;
655 viewname
= strchr(itemName
, ':');
656 if(viewname
== NULL
) return false;
661 if(strcmp(cmd
, "enable") == 0) {
662 ac
= kSOSCCViewEnable
;
663 } else if(strcmp(cmd
, "disable") == 0) {
664 ac
= kSOSCCViewDisable
;
665 } else if(strcmp(cmd
, "query") == 0) {
666 ac
= kSOSCCViewQuery
;
671 if(strchr(viewname
, ',') == NULL
) { // original single value version
672 viewspec
= convertStringToView(viewname
);
673 if(!viewspec
) return false;
675 SOSViewResultCode rc
= SOSCCView(viewspec
, ac
, err
);
676 CFStringRef resultString
= convertViewReturnCodeToString(rc
);
678 printmsg(CFSTR("View Result: %@ : %@\n"), resultString
, viewspec
);
682 if(ac
== kSOSCCViewQuery
) return false;
684 // new multi-view version
685 char *viewlist
= strdup(viewname
);
687 char *tofree
= viewlist
;
688 CFMutableSetRef viewSet
= CFSetCreateMutable(NULL
, 0, &kCFCopyStringSetCallBacks
);
690 while ((token
= strsep(&viewlist
, ",")) != NULL
) {
691 CFStringRef resultString
= convertStringToView(token
);
692 CFSetAddValue(viewSet
, resultString
);
695 printmsg(CFSTR("viewSet provided is %@\n"), viewSet
);
700 if(ac
== kSOSCCViewEnable
) retcode
= SOSCCViewSet(viewSet
, NULL
);
701 else retcode
= SOSCCViewSet(NULL
, viewSet
);
703 fprintf(outFile
, "SOSCCViewSet returned %s\n", (retcode
)? "true": "false");
708 static bool listviewcmd(CFErrorRef
*err
) {
711 for (n
= 0; n
< sizeof(string2View
)/sizeof(string2View
[0]); n
++) {
712 CFStringRef viewspec
= *string2View
[n
].viewspec
;
714 SOSViewResultCode rc
= SOSCCView(viewspec
, kSOSCCViewQuery
, err
);
715 CFStringRef resultString
= convertViewReturnCodeToString(rc
);
717 printmsg(CFSTR("View Result: %@ : %@\n"), resultString
, viewspec
);
723 static CFStringRef
convertStringToProperty(char *propertyname
) {
724 CFStringRef propertyspec
= NULL
;
726 if(strcmp(propertyname
, "hasentropy") == 0) {
727 propertyspec
= kSOSSecPropertyHasEntropy
;
728 } else if(strcmp(propertyname
, "screenlock") == 0) {
729 propertyspec
= kSOSSecPropertyScreenLock
;
730 } else if(strcmp(propertyname
, "SEP") == 0) {
731 propertyspec
= kSOSSecPropertySEP
;
732 } else if(strcmp(propertyname
, "IOS") == 0) {
733 propertyspec
= kSOSSecPropertyIOS
;
739 static CFStringRef
convertPropertyReturnCodeToString(SOSSecurityPropertyResultCode ac
) {
740 CFStringRef retval
= NULL
;
742 case kSOSCCGeneralSecurityPropertyError
:
743 retval
= CFSTR("General Error"); break;
744 case kSOSCCSecurityPropertyValid
:
745 retval
= CFSTR("Is Member of Security Property"); break;
746 case kSOSCCSecurityPropertyNotValid
:
747 retval
= CFSTR("Is Not Member of Security Property"); break;
748 case kSOSCCSecurityPropertyNotQualified
:
749 retval
= CFSTR("Is not qualified for Security Property"); break;
750 case kSOSCCNoSuchSecurityProperty
:
751 retval
= CFSTR("No Such Security Property"); break;
757 static bool SecPropertycmd(char *itemName
, CFErrorRef
*err
) {
758 char *cmd
, *propertyname
;
759 SOSSecurityPropertyActionCode ac
= kSOSCCSecurityPropertyQuery
;
760 CFStringRef propertyspec
;
762 propertyname
= strchr(itemName
, ':');
763 if(propertyname
== NULL
) return false;
768 if(strcmp(cmd
, "enable") == 0) {
769 ac
= kSOSCCSecurityPropertyEnable
;
770 } else if(strcmp(cmd
, "disable") == 0) {
771 ac
= kSOSCCSecurityPropertyDisable
;
772 } else if(strcmp(cmd
, "query") == 0) {
773 ac
= kSOSCCSecurityPropertyQuery
;
778 propertyspec
= convertStringToProperty(propertyname
);
779 if(!propertyspec
) return false;
781 SOSSecurityPropertyResultCode rc
= SOSCCSecurityProperty(propertyspec
, ac
, err
);
782 CFStringRef resultString
= convertPropertyReturnCodeToString(rc
);
784 printmsg(CFSTR("Property Result: %@ : %@\n"), resultString
, propertyspec
);
789 static void dumpStringSet(CFStringRef label
, CFSetRef s
) {
790 if(!s
|| !label
) return;
792 printmsg(CFSTR("%@: { "), label
);
793 __block
bool first
= true;
794 CFSetForEach(s
, ^(const void *p
) {
795 CFStringRef fmt
= CFSTR(", %@");
799 CFStringRef string
= (CFStringRef
) p
;
800 printmsg(fmt
, string
);
803 printmsg(CFSTR(" }\n"), NULL
);
806 static bool dumpMyPeer(CFErrorRef
*error
) {
807 SOSPeerInfoRef myPeer
= SOSCCCopyMyPeerInfo(error
);
809 if (!myPeer
) return false;
811 CFStringRef peerID
= SOSPeerInfoGetPeerID(myPeer
);
812 CFStringRef peerName
= SOSPeerInfoGetPeerName(myPeer
);
813 CFIndex peerVersion
= SOSPeerInfoGetVersion(myPeer
);
814 bool retirement
= SOSPeerInfoIsRetirementTicket(myPeer
);
816 printmsg(CFSTR("Peer Name: %@ PeerID: %@ Version: %d\n"), peerName
, peerID
, peerVersion
);
818 CFDateRef retdate
= SOSPeerInfoGetRetirementDate(myPeer
);
819 printmsg(CFSTR("Retired: %@\n"), retdate
);
823 if(peerVersion
>= 2) {
824 CFMutableSetRef views
= SOSPeerInfoV2DictionaryCopySet(myPeer
, sViewsKey
);
825 CFStringRef serialNumber
= SOSPeerInfoV2DictionaryCopyString(myPeer
, sSerialNumberKey
);
826 CFBooleanRef preferIDS
= SOSPeerInfoV2DictionaryCopyBoolean(myPeer
, sPreferIDS
);
827 CFStringRef transportType
= SOSPeerInfoV2DictionaryCopyString(myPeer
, sTransportType
);
828 CFStringRef idsDeviceID
= SOSPeerInfoV2DictionaryCopyString(myPeer
, sDeviceID
);
829 CFMutableSetRef properties
= SOSPeerInfoV2DictionaryCopySet(myPeer
, sSecurityPropertiesKey
);
831 printmsg(CFSTR("Serial#: %@ PrefIDS#: %@ transportType#: %@ idsDeviceID#: %@\n"),
832 serialNumber
, preferIDS
, transportType
, idsDeviceID
);
833 dumpStringSet(CFSTR(" Views: "), views
);
834 dumpStringSet(CFSTR("SecurityProperties: "), properties
);
836 CFReleaseSafe(serialNumber
);
837 CFReleaseSafe(preferIDS
);
838 CFReleaseSafe(views
);
839 CFReleaseSafe(transportType
);
840 CFReleaseSafe(idsDeviceID
);
841 CFReleaseSafe(properties
);
845 return myPeer
!= NULL
;
848 static bool setBag(char *itemName
, CFErrorRef
*err
)
850 __block
bool success
= false;
851 __block CFErrorRef error
= NULL
;
853 CFStringRef random
= SecPasswordCreateWithRandomDigits(10, NULL
);
855 CFStringPerformWithUTF8CFData(random
, ^(CFDataRef stringAsData
) {
856 if (0 == strncasecmp(optarg
, "single", 6) || 0 == strncasecmp(optarg
, "all", 3)) {
857 bool includeV0
= (0 == strncasecmp(optarg
, "all", 3));
858 printmsg(CFSTR("Setting iCSC single using entropy from string: %@\n"), random
);
859 CFDataRef aks_bag
= SecAKSCopyBackupBagWithSecret(CFDataGetLength(stringAsData
), (uint8_t*)CFDataGetBytePtr(stringAsData
), &error
);
862 success
= SOSCCRegisterSingleRecoverySecret(aks_bag
, includeV0
, &error
);
864 printmsg(CFSTR("Failed registering single secret %@"), error
);
865 CFReleaseNull(aks_bag
);
868 printmsg(CFSTR("Failed to create aks_bag: %@"), error
);
870 CFReleaseNull(aks_bag
);
871 } else if (0 == strncasecmp(optarg
, "device", 6)) {
872 printmsg(CFSTR("Setting Device Secret using entropy from string: %@\n"), random
);
874 SOSPeerInfoRef me
= SOSCCCopyMyPeerWithNewDeviceRecoverySecret(stringAsData
, &error
);
876 success
= me
!= NULL
;
879 printmsg(CFSTR("Failed: %@\n"), err
);
882 printmsg(CFSTR("Unrecognized argument to -b %s\n"), optarg
);
890 static void prClientViewState(char *label
, bool result
) {
891 fprintf(outFile
, "Sync Status for %s: %s\n", label
, (result
) ? "enabled": "not enabled");
894 static bool clientViewStatus(CFErrorRef
*error
) {
895 prClientViewState("KeychainV0", SOSCCIsIcloudKeychainSyncing());
896 prClientViewState("Safari", SOSCCIsSafariSyncing());
897 prClientViewState("AppleTV", SOSCCIsAppleTVSyncing());
898 prClientViewState("HomeKit", SOSCCIsHomeKitSyncing());
899 prClientViewState("Wifi", SOSCCIsWiFiSyncing());
904 static bool dumpYetToSync(CFErrorRef
*error
) {
905 CFArrayRef yetToSyncViews
= SOSCCCopyYetToSyncViewsList(error
);
907 bool hadError
= yetToSyncViews
;
909 if (yetToSyncViews
) {
910 __block CFStringRef separator
= CFSTR("");
912 printmsg(CFSTR("Yet to sync views: ["), NULL
);
914 CFArrayForEach(yetToSyncViews
, ^(const void *value
) {
915 if (isString(value
)) {
916 printmsg(CFSTR("%@%@"), separator
, value
);
918 separator
= CFSTR(", ");
921 printmsg(CFSTR("]\n"), NULL
);
928 // enable, disable, accept, reject, status, Reset, Clear
930 keychain_sync(int argc
, char * const *argv
)
935 " -e enable (join/create circle)"
936 " -i info (current status)"
938 " -S schedule sync with all peers"
940 "Account/Circle Management"
941 " -a accept all applicants"
942 " -l [reason] sign out of circle + set custom departure reason"
943 " -q sign out of circle"
944 " -r reject all applicants"
945 " -E ensure fresh parameters"
946 " -b device|all|single Register a backup bag - THIS RESETS BACKUPS!\n"
947 " -A Apply to a ring\n"
948 " -B Withdrawl from a ring\n"
951 " -I Dump Ring Information\n"
953 " -N (re-)set to new account (USE WITH CARE: device will not leave circle before resetting account!)"
954 " -O reset to offering"
956 " -X [limit] best effort bail from circle in limit seconds"
957 " -o list view unaware peers in circle"
958 " -0 boot view unaware peers from circle"
959 " -1 grab account state from the keychain"
960 " -2 delete account state from the keychain"
961 " -3 grab engine state from the keychain"
962 " -4 delete engine state from the keychain"
965 " -g set IDS device id"
966 " -p retrieve IDS device id"
967 " -x ping all devices in an IDS account"
968 " -w check IDS availability"
969 " -z retrieve IDS id through IDSKeychainSyncingProxy"
972 " -P [label:]password set password (optionally for a given label) for sync"
973 " -T [label:]password try password (optionally for a given label) for sync"
976 " -k pend all registered kvs keys"
977 " -C clear all values from KVS"
978 " -D [itemName] dump contents of KVS"
979 " -W itemNames sync and dump"
982 " -v [enable|disable|query:viewname] enable, disable, or query my PeerInfo's view set"
983 " viewnames are: keychain|masterkey|iclouddrive|photos|cloudkit|escrow|fde|maildrop|icloudbackup|notes|imessage|appletv|homekit|"
984 " wifi|passwords|creditcards|icloudidentity|othersyncable"
985 " -L list all known view and their status"
986 " -S [enable|disable|propertyname] enable, disable, or query my PeerInfo's Security Property set"
987 " propertynames are: hasentropy|screenlock|SEP|IOS\n"
988 " -U purge private key material cache\n"
989 " -V Report View Sync Status on all known clients.\n"
990 " -H Set escrow record.\n"
991 " -J Get the escrow record.\n"
992 " -M Check peer availability.\n"
996 CFErrorRef error
= NULL
;
997 bool hadError
= false;
998 setOutputTo(NULL
, NULL
);
1000 while ((ch
= getopt(argc
, argv
, "ab:deg:hikl:mopq:rsSv:w:x:zA:B:MNJCDEF:HG:ILOP:RT:UW:X:VY01234")) != -1)
1004 fprintf(outFile
, "Signing out of circle\n");
1005 hadError
= !SOSCCSignedOut(true, &error
);
1008 int reason
= (int) strtoul(optarg
, NULL
, 10);
1010 reason
< kSOSDepartureReasonError
||
1011 reason
>= kSOSNumDepartureReasons
) {
1012 fprintf(errFile
, "Invalid custom departure reason %s\n", optarg
);
1014 fprintf(outFile
, "Setting custom departure reason %d\n", reason
);
1015 hadError
= !SOSCCSetLastDepartureReason(reason
, &error
);
1016 notify_post(kSOSCCCircleChangedNotification
);
1024 fprintf(outFile
, "Signing out of circle\n");
1025 bool signOutImmediately
= false;
1026 if (strcasecmp(optarg
, "true") == 0) {
1027 signOutImmediately
= true;
1028 } else if (strcasecmp(optarg
, "false") == 0) {
1029 signOutImmediately
= false;
1031 fprintf(outFile
, "Please provide a \"true\" or \"false\" whether you'd like to leave the circle immediately\n");
1033 hadError
= !SOSCCSignedOut(signOutImmediately
, &error
);
1034 notify_post(kSOSCCCircleChangedNotification
);
1040 fprintf(outFile
, "Grabbing DS ID\n");
1041 CFStringRef deviceID
= SOSCCCopyDeviceID(&error
);
1046 if (!isNull(deviceID
)) {
1047 const char *id
= CFStringGetCStringPtr(deviceID
, kCFStringEncodingUTF8
);
1049 fprintf(outFile
, "IDS Device ID: %s\n", id
);
1051 fprintf(outFile
, "IDS Device ID is null!\n");
1053 CFReleaseNull(deviceID
);
1059 fprintf(outFile
, "Setting DS ID: %s\n", optarg
);
1060 CFStringRef deviceID
= CFStringCreateWithCString(kCFAllocatorDefault
, optarg
, kCFStringEncodingUTF8
);
1061 hadError
= SOSCCSetDeviceID(deviceID
, &error
);
1062 CFReleaseNull(deviceID
);
1068 fprintf(outFile
, "Attempting to send this message over IDS: %s\n", optarg
);
1069 CFStringRef message
= CFStringCreateWithCString(kCFAllocatorDefault
, optarg
, kCFStringEncodingUTF8
);
1070 hadError
= SOSCCIDSServiceRegistrationTest(message
, &error
);
1072 printerr(CFSTR("IDS is not ready: %@\n"), error
);
1075 CFReleaseNull(message
);
1081 fprintf(outFile
, "Starting ping test using this message: %s\n", optarg
);
1082 CFStringRef message
= CFStringCreateWithCString(kCFAllocatorDefault
, optarg
, kCFStringEncodingUTF8
);
1083 hadError
= SOSCCIDSPingTest(message
, &error
);
1085 printerr(CFSTR("Ping test failed to start: %@\n"), error
);
1088 CFReleaseNull(message
);
1093 hadError
= SOSCCIDSDeviceIDIsAvailableTest(&error
);
1095 printerr(CFSTR("Failed to retrieve IDS device ID: %@\n"), error
);
1101 fprintf(outFile
, "Turning ON keychain syncing\n");
1102 hadError
= requestToJoinCircle(&error
);
1106 fprintf(outFile
, "Turning OFF keychain syncing\n");
1107 hadError
= !SOSCCRemoveThisDeviceFromCircle(&error
);
1112 CFArrayRef applicants
= SOSCCCopyApplicantPeerInfo(NULL
);
1114 hadError
= !SOSCCAcceptApplicants(applicants
, &error
);
1115 CFRelease(applicants
);
1117 fprintf(errFile
, "No applicants to accept\n");
1124 CFArrayRef applicants
= SOSCCCopyApplicantPeerInfo(NULL
);
1126 hadError
= !SOSCCRejectApplicants(applicants
, &error
);
1127 CFRelease(applicants
);
1129 fprintf(errFile
, "No applicants to reject\n");
1139 notify_post("com.apple.security.cloudkeychain.forceupdate");
1144 printPeerInfos("view-unaware", ^(CFErrorRef
*error
) { return SOSCCCopyViewUnawarePeerInfo(error
); });
1150 CFArrayRef unawares
= SOSCCCopyViewUnawarePeerInfo(&error
);
1152 hadError
= !SOSCCRemovePeersFromCircle(unawares
, &error
);
1156 CFReleaseNull(unawares
);
1161 CFDataRef accountState
= SOSCCCopyAccountState(&error
);
1163 printmsg(CFSTR(" %@\n"), CFDataCopyHexString(accountState
));
1167 CFReleaseNull(accountState
);
1172 bool status
= SOSCCDeleteAccountState(&error
);
1174 printmsg(CFSTR("Deleted account from the keychain %d\n"), status
);
1182 CFDataRef engineState
= SOSCCCopyEngineData(&error
);
1184 printmsg(CFSTR(" %@\n"), CFDataCopyHexString(engineState
));
1188 CFReleaseNull(engineState
);
1193 bool status
= SOSCCDeleteEngineState(&error
);
1195 printmsg(CFSTR("Deleted engine-state from the keychain %d\n"), status
);
1203 #if TARGET_OS_EMBEDDED
1204 SOSCloudKeychainRequestSyncWithAllPeers(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), NULL
);
1206 fprintf(outFile
, "not exported yet...\n");
1212 fprintf(outFile
, "Ensuring Fresh Parameters\n");
1213 bool result
= SOSCCRequestEnsureFreshParameters(&error
);
1219 fprintf(outFile
, "Refreshed Parameters Ensured!\n");
1221 fprintf(outFile
, "Problem trying to ensure fresh parameters\n");
1227 fprintf(outFile
, "Applying to Ring\n");
1228 CFStringRef ringName
= CFStringCreateWithCString(kCFAllocatorDefault
, (char *)optarg
, kCFStringEncodingUTF8
);
1229 hadError
= SOSCCApplyToARing(ringName
, &error
);
1234 fprintf(outFile
, "Withdrawing from Ring\n");
1235 CFStringRef ringName
= CFStringCreateWithCString(kCFAllocatorDefault
, (char *)optarg
, kCFStringEncodingUTF8
);
1236 hadError
= SOSCCWithdrawlFromARing(ringName
, &error
);
1241 fprintf(outFile
, "Status of this device in the Ring\n");
1242 CFStringRef ringName
= CFStringCreateWithCString(kCFAllocatorDefault
, (char *)optarg
, kCFStringEncodingUTF8
);
1243 hadError
= SOSCCRingStatus(ringName
, &error
);
1248 fprintf(outFile
, "Enabling Ring\n");
1249 CFStringRef ringName
= CFStringCreateWithCString(kCFAllocatorDefault
, (char *)optarg
, kCFStringEncodingUTF8
);
1250 hadError
= SOSCCEnableRing(ringName
, &error
);
1255 fprintf(outFile
, "Setting random escrow record\n");
1256 bool success
= SOSCCSetEscrowRecord(CFSTR("label"), 8, &error
);
1265 CFDictionaryRef attempts
= SOSCCCopyEscrowRecord(&error
);
1267 CFDictionaryForEach(attempts
, ^(const void *key
, const void *value
) {
1269 char *keyString
= CFStringToCString(key
);
1270 fprintf(outFile
, "%s:\n", keyString
);
1273 if(isDictionary(value
)){
1274 CFDictionaryForEach(value
, ^(const void *key
, const void *value
) {
1276 char *keyString
= CFStringToCString(key
);
1277 fprintf(outFile
, "%s: ", keyString
);
1280 if(isString(value
)){
1281 char *time
= CFStringToCString(value
);
1282 fprintf(outFile
, "timestamp: %s\n", time
);
1285 else if(isNumber(value
)){
1287 CFNumberGetValue(value
, kCFNumberLongLongType
, &tries
);
1288 fprintf(outFile
, "date: %llu\n", tries
);
1295 CFReleaseNull(attempts
);
1301 bool success
= SOSCCCheckPeerAvailability(&error
);
1310 fprintf(outFile
, "Printing all the rings\n");
1311 CFStringRef ringdescription
= SOSCCGetAllTheRings(&error
);
1312 if(!ringdescription
)
1315 fprintf(outFile
, "Rings: %s", CFStringToCString(ringdescription
));
1321 hadError
= !SOSCCAccountSetToNew(&error
);
1323 notify_post(kSOSCCCircleChangedNotification
);
1327 hadError
= !SOSCCResetToEmpty(&error
);
1331 hadError
= !SOSCCResetToOffering(&error
);
1335 hadError
= !dumpMyPeer(&error
);
1339 hadError
= clearAllKVS(&error
);
1343 hadError
= setPassword(optarg
, &error
);
1347 hadError
= tryPassword(optarg
, &error
);
1352 uint64_t limit
= strtoul(optarg
, NULL
, 10);
1353 hadError
= !SOSCCBailFromCircle_BestEffort(limit
, &error
);
1358 hadError
= !SOSCCPurgeUserCredentials(&error
);
1362 hadError
= !dumpKVS(optarg
, &error
);
1366 hadError
= syncAndWait(optarg
, &error
);
1370 hadError
= !viewcmd(optarg
, &error
);
1374 hadError
= clientViewStatus(&error
);
1377 hadError
= !listviewcmd(&error
);
1381 hadError
= !SecPropertycmd(optarg
, &error
);
1385 hadError
= setBag(optarg
, &error
);
1389 hadError
= dumpYetToSync(&error
);
1394 return 2; /* Return 2 triggers usage message. */
1398 printerr(CFSTR("Error: %@\n"), error
);