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@
32 #include <Security/SecItem.h>
34 #include <CoreFoundation/CFNumber.h>
35 #include <CoreFoundation/CFString.h>
37 #include <SecureObjectSync/SOSCloudCircle.h>
38 #include <SecureObjectSync/SOSCloudCircleInternal.h>
39 #include <SecureObjectSync/SOSPeerInfo.h>
40 #include <SecureObjectSync/SOSKVSKeys.h>
41 #include <securityd/SOSCloudCircleServer.h>
43 #include <CKBridge/SOSCloudKeychainClient.h>
45 #include <utilities/SecCFWrappers.h>
46 #include <utilities/debugging.h>
48 #include <SecurityTool/readline.h>
51 #include "SOSCommands.h"
53 #define printmsg(format, ...) _printcfmsg(stdout, format, __VA_ARGS__)
54 #define printerr(format, ...) _printcfmsg(stderr, format, __VA_ARGS__)
56 static void _printcfmsg(FILE *ff
, CFStringRef format
, ...)
59 va_start(args
, format
);
60 CFStringRef message
= CFStringCreateWithFormatAndArguments(kCFAllocatorDefault
, NULL
, format
, args
);
62 CFStringPerformWithCString(message
, ^(const char *utf8String
) { fprintf(ff
, utf8String
, ""); });
66 static bool clearAllKVS(CFErrorRef
*error
)
68 __block
bool result
= false;
69 const uint64_t maxTimeToWaitInSeconds
= 30ull * NSEC_PER_SEC
;
70 dispatch_queue_t processQueue
= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0);
71 dispatch_semaphore_t waitSemaphore
= dispatch_semaphore_create(0);
72 dispatch_time_t finishTime
= dispatch_time(DISPATCH_TIME_NOW
, maxTimeToWaitInSeconds
);
74 SOSCloudKeychainClearAll(processQueue
, ^(CFDictionaryRef returnedValues
, CFErrorRef cerror
)
76 result
= (cerror
!= NULL
);
77 dispatch_semaphore_signal(waitSemaphore
);
80 dispatch_semaphore_wait(waitSemaphore
, finishTime
);
81 dispatch_release(waitSemaphore
);
86 static const char *getSOSCCStatusDescription(SOSCCStatus ccstatus
)
90 case kSOSCCInCircle
: return "In Circle";
91 case kSOSCCNotInCircle
: return "Not in Circle";
92 case kSOSCCRequestPending
: return "Request pending";
93 case kSOSCCCircleAbsent
: return "Circle absent";
94 case kSOSCCError
: return "Circle error";
97 return "<unknown ccstatus>";
102 static void dumpCircleInfo()
104 CFErrorRef error
= NULL
;
105 CFArrayRef peerPeerInfos
= NULL
;
106 CFArrayRef applicants
= NULL
;
107 CFArrayRef retirees
= NULL
;
108 CFArrayRef peerInfos
= NULL
;
109 CFArrayRef generations
= NULL
;
110 bool is_user_public_trusted
= false;
111 __block
int count
= 0;
113 SOSCCStatus ccstatus
= SOSCCThisDeviceIsInCircle(&error
);
114 printmsg(CFSTR("ccstatus: %s (%d), error: %@\n"), getSOSCCStatusDescription(ccstatus
), ccstatus
, error
);
116 if(ccstatus
== kSOSCCError
) {
117 printmsg(CFSTR("End of Dump - unable to proceed due to ccstatus -\n\t%s\n"), getSOSCCStatusDescription(ccstatus
));
121 is_user_public_trusted
= SOSCCValidateUserPublic(&error
);
122 if(is_user_public_trusted
)
123 printmsg(CFSTR("Account user public is trusted%@"),CFSTR("\n"));
125 printmsg(CFSTR("Account user public is not trusted%@"),CFSTR("\n"));
127 // Now look at current peers
128 peerPeerInfos
= SOSCCCopyValidPeerPeerInfo(&error
);
132 printmsg(CFSTR("Valid Peers: %ld, error: %@\n"), (long)CFArrayGetCount(peerPeerInfos
), error
);
133 CFArrayForEach(peerPeerInfos
, ^(const void *value
) {
134 SOSPeerInfoRef peer
= (SOSPeerInfoRef
)value
;
135 CFStringRef peerName
= SOSPeerInfoGetPeerName(peer
);
136 printmsg(CFSTR("Valid Peer: %@ (%@)\n"), peerName
, peer
);
140 printmsg(CFSTR("No peers, error: %@\n"), error
);
142 CFReleaseNull(peerPeerInfos
);
143 peerPeerInfos
= SOSCCCopyNotValidPeerPeerInfo(&error
);
147 printmsg(CFSTR("Non Valid Peers: %ld, error: %@\n"), (long)CFArrayGetCount(peerPeerInfos
), error
);
148 CFArrayForEach(peerPeerInfos
, ^(const void *value
) {
149 SOSPeerInfoRef peer
= (SOSPeerInfoRef
)value
;
150 CFStringRef peerName
= SOSPeerInfoGetPeerName(peer
);
151 printmsg(CFSTR("Not Valid Peer: %@ (%@)\n"), peerName
, peer
);
154 CFReleaseNull(peerPeerInfos
);
156 applicants
= SOSCCCopyApplicantPeerInfo(&error
);
159 printmsg(CFSTR("Applicants: %ld, error: %@\n"), (long)CFArrayGetCount(applicants
), error
);
160 CFArrayForEach(applicants
, ^(const void *value
) {
161 SOSPeerInfoRef peer
= (SOSPeerInfoRef
)value
;
162 CFStringRef peerName
= SOSPeerInfoGetPeerName(peer
);
163 printmsg(CFSTR("Applicant: %@ (%@)\n"), peerName
, peer
);
167 printmsg(CFSTR("No applicants, error: %@\n"), error
);
168 CFReleaseNull(applicants
);
171 peerInfos
= SOSCCCopyConcurringPeerPeerInfo(&error
);
174 printmsg(CFSTR("Concurring Peers: %ld, error: %@\n"), (long)CFArrayGetCount(peerInfos
), error
);
175 CFArrayForEach(peerInfos
, ^(const void *value
) {
176 SOSPeerInfoRef peer
= (SOSPeerInfoRef
)value
;
177 CFStringRef peerName
= SOSPeerInfoGetPeerName(peer
);
178 printmsg(CFSTR("Concurr: %@ (%@)\n"), peerName
, peer
);
182 printmsg(CFSTR("No concurring peers, error: %@\n"), error
);
184 CFReleaseNull(peerInfos
);
185 retirees
= SOSCCCopyRetirementPeerInfo(&error
);
188 printmsg(CFSTR("Retired Peers: %ld, error: %@\n"), (long)CFArrayGetCount(retirees
), error
);
189 CFArrayForEach(retirees
, ^(const void *value
) {
190 SOSPeerInfoRef peer
= (SOSPeerInfoRef
)value
;
191 CFStringRef peerName
= SOSPeerInfoGetPeerName(peer
);
192 printmsg(CFSTR("Retiree: %@ (%@)\n"), peerName
, peer
);
196 printmsg(CFSTR("No retired peers, error: %@\n"), error
);
197 CFReleaseNull(retirees
);
199 generations
= SOSCCCopyGenerationPeerInfo(&error
);
202 CFArrayForEach(generations
, ^(const void *value
) {
205 printmsg(CFSTR("Circle name: %@, "),value
);
208 printmsg(CFSTR("Generation Count: %@\n"), value
);
212 printmsg(CFSTR("No generation count: %@\n"), error
);
213 CFReleaseNull(generations
);
216 static bool requestToJoinCircle(CFErrorRef
*error
)
218 // Set the visual state of switch based on membership in circle
219 bool hadError
= false;
220 SOSCCStatus ccstatus
= SOSCCThisDeviceIsInCircle(error
);
224 case kSOSCCCircleAbsent
:
225 hadError
= !SOSCCResetToOffering(error
);
227 case kSOSCCNotInCircle
:
228 hadError
= !SOSCCRequestToJoinCircle(error
);
231 printerr(CFSTR("Request to join circle with bad status: %@ (%d)\n"), SOSCCGetStatusDescription(ccstatus
), ccstatus
);
237 static bool setPassword(char *labelAndPassword
, CFErrorRef
*err
)
240 char *token0
= strtok_r(labelAndPassword
, ":", &last
);
241 char *token1
= strtok_r(NULL
, "", &last
);
242 CFStringRef label
= token1
? CFStringCreateWithCString(NULL
, token0
, kCFStringEncodingUTF8
) : CFSTR("security command line tool");
243 char *password_token
= token1
? token1
: token0
;
244 password_token
= password_token
? password_token
: "";
245 CFDataRef password
= CFDataCreate(NULL
, (const UInt8
*) password_token
, strlen(password_token
));
246 bool returned
= !SOSCCSetUserCredentials(label
, password
, err
);
252 static bool tryPassword(char *labelAndPassword
, CFErrorRef
*err
)
255 char *token0
= strtok_r(labelAndPassword
, ":", &last
);
256 char *token1
= strtok_r(NULL
, "", &last
);
257 CFStringRef label
= token1
? CFStringCreateWithCString(NULL
, token0
, kCFStringEncodingUTF8
) : CFSTR("security command line tool");
258 char *password_token
= token1
? token1
: token0
;
259 password_token
= password_token
? password_token
: "";
260 CFDataRef password
= CFDataCreate(NULL
, (const UInt8
*) password_token
, strlen(password_token
));
261 bool returned
= !SOSCCTryUserCredentials(label
, password
, err
);
267 static CFTypeRef
getObjectsFromCloud(CFArrayRef keysToGet
, dispatch_queue_t processQueue
, dispatch_group_t dgroup
)
269 __block CFTypeRef object
= NULL
;
271 const uint64_t maxTimeToWaitInSeconds
= 30ull * NSEC_PER_SEC
;
272 dispatch_semaphore_t waitSemaphore
= dispatch_semaphore_create(0);
273 dispatch_time_t finishTime
= dispatch_time(DISPATCH_TIME_NOW
, maxTimeToWaitInSeconds
);
275 dispatch_group_enter(dgroup
);
277 CloudKeychainReplyBlock replyBlock
=
278 ^ (CFDictionaryRef returnedValues
, CFErrorRef error
)
280 secerror("SOSCloudKeychainGetObjectsFromCloud returned: %@", returnedValues
);
281 object
= returnedValues
;
286 secerror("SOSCloudKeychainGetObjectsFromCloud returned error: %@", error
);
287 // CFRelease(*error);
289 dispatch_group_leave(dgroup
);
290 secerror("SOSCloudKeychainGetObjectsFromCloud block exit: %@", object
);
291 dispatch_semaphore_signal(waitSemaphore
);
295 SOSCloudKeychainGetAllObjectsFromCloud(processQueue
, replyBlock
);
297 SOSCloudKeychainGetObjectsFromCloud(keysToGet
, processQueue
, replyBlock
);
299 dispatch_semaphore_wait(waitSemaphore
, finishTime
);
300 dispatch_release(waitSemaphore
);
301 if (object
&& (CFGetTypeID(object
) == CFNullGetTypeID())) // return a NULL instead of a CFNull
306 secerror("returned: %@", object
);
310 static void displayCircles(CFTypeRef objects
)
312 // SOSCCCopyApplicantPeerInfo doesn't display all info, e.g. in the case where we are not in circle
313 CFDictionaryForEach(objects
, ^(const void *key
, const void *value
) {
314 if (SOSKVSKeyGetKeyType(key
) == kCircleKey
)
316 CFErrorRef localError
= NULL
;
319 SOSCircleRef circle
= SOSCircleCreateFromData(NULL
, (CFDataRef
) value
, &localError
);
320 printmsg(CFSTR("circle: %@ %@"), key
, circle
);
321 CFReleaseSafe(circle
);
324 printmsg(CFSTR("non-circle: %@ %@"), key
, value
);
329 static bool dumpKVS(char *itemName
, CFErrorRef
*err
)
331 CFArrayRef keysToGet
= NULL
;
334 CFStringRef itemStr
= CFStringCreateWithCString(kCFAllocatorDefault
, itemName
, kCFStringEncodingUTF8
);
335 printf("Retrieving %s from KVS\n", itemName
);
336 keysToGet
= CFArrayCreateForCFTypes(kCFAllocatorDefault
, itemStr
, NULL
);
337 CFReleaseSafe(itemStr
);
339 dispatch_queue_t generalq
= dispatch_queue_create("general", DISPATCH_QUEUE_SERIAL
);
340 dispatch_group_t work_group
= dispatch_group_create();
341 CFTypeRef objects
= getObjectsFromCloud(keysToGet
, generalq
, work_group
);
342 CFReleaseSafe(keysToGet
);
343 printmsg(CFSTR(" : %@\n"), objects
);
345 displayCircles(objects
);
350 static bool syncAndWait(char *itemName
, CFErrorRef
*err
)
352 CFArrayRef keysToGet
= NULL
;
353 __block CFTypeRef objects
= NULL
;
356 fprintf(stderr
, "No item keys supplied\n");
360 CFStringRef itemStr
= CFStringCreateWithCString(kCFAllocatorDefault
, itemName
, kCFStringEncodingUTF8
);
361 printf("Retrieving %s from KVS\n", itemName
);
362 keysToGet
= CFArrayCreateForCFTypes(kCFAllocatorDefault
, itemStr
, NULL
);
363 CFReleaseSafe(itemStr
);
365 dispatch_queue_t generalq
= dispatch_queue_create("general", DISPATCH_QUEUE_SERIAL
);
367 const uint64_t maxTimeToWaitInSeconds
= 30ull * NSEC_PER_SEC
;
368 dispatch_semaphore_t waitSemaphore
= dispatch_semaphore_create(0);
369 dispatch_time_t finishTime
= dispatch_time(DISPATCH_TIME_NOW
, maxTimeToWaitInSeconds
);
371 CloudKeychainReplyBlock replyBlock
= ^ (CFDictionaryRef returnedValues
, CFErrorRef error
)
373 secerror("SOSCloudKeychainSynchronizeAndWait returned: %@", returnedValues
);
375 secerror("SOSCloudKeychainSynchronizeAndWait returned error: %@", error
);
376 objects
= returnedValues
;
379 secerror("SOSCloudKeychainGetObjectsFromCloud block exit: %@", objects
);
380 dispatch_semaphore_signal(waitSemaphore
);
383 SOSCloudKeychainSynchronizeAndWait(keysToGet
, generalq
, replyBlock
);
385 dispatch_semaphore_wait(waitSemaphore
, finishTime
);
386 dispatch_release(waitSemaphore
);
388 CFReleaseSafe(keysToGet
);
389 printmsg(CFSTR(" : %@\n"), objects
);
391 displayCircles(objects
);
396 // enable, disable, accept, reject, status, Reset, Clear
398 keychain_sync(int argc
, char * const *argv
)
401 " -e Enable Keychain Syncing (join/create circle)\n"
402 " -d Disable Keychain Syncing\n"
403 " -a Accept all applicants\n"
404 " -r Reject all applicants\n"
406 " -k Pend all registered kvs keys\n"
407 " -s Schedule sync with all peers\n"
408 " -E Ensure Fresh Parameters\n"
410 " -O ResetToOffering\n"
411 " -q Sign out of Circle\n"
412 " -C Clear all values from KVS\n"
413 " -P [label:]password Set password (optionally for a given label) for sync\n"
414 " -D [itemName] Dump contents of KVS\n"
415 " -P [label:]password Set password (optionally for a given label) for sync\n"
416 " -T [label:]password Try password (optionally for a given label) for sync\n"
417 " -U Purge private key material cache\n"
418 " -D [itemName] Dump contents of KVS\n"
419 " -W itemNames sync and dump\n"
420 " -X [limit] Best effort bail from circle in limit seconds\n"
421 " -p Retrieve IDS Device ID\n"
422 " -g Set IDS Device ID\n"
425 CFErrorRef error
= NULL
;
426 bool hadError
= false;
428 while ((ch
= getopt(argc
, argv
, "pedakrisEROChP:T:DW:UX:g:q:")) != -1)
434 printf("Signing out of circle\n");
435 bool immediately
= false;
436 CFStringRef leaveImmediately
= CFStringCreateWithCString(kCFAllocatorDefault
, (char *)optarg
, kCFStringEncodingUTF8
);
437 if(CFStringCompare(CFSTR("true"), leaveImmediately
, 0) == 0){
440 else if(CFStringCompare(CFSTR("false"), leaveImmediately
, 0) == 0){
444 printf("Please provide a \"true\" or \"false\" whether you'd like to leave the circle immediately\n");
446 hadError
= !SOSCCSignedOut(immediately
, &error
);
451 printf("Grabbing DS ID\n");
452 CFStringRef deviceID
= SOSCCRequestDeviceID(&error
);
457 if(!isNull(deviceID
)){
458 const char* id
= CFStringGetCStringPtr(deviceID
, kCFStringEncodingUTF8
);
460 printf("IDS Device ID: %s\n", id
);
462 printf("IDS Device ID is null!\n");
468 CFStringRef deviceID
= CFStringCreateWithCString(kCFAllocatorDefault
, (char *)optarg
, kCFStringEncodingUTF8
);
469 printf("Setting DS ID: %s\n", optarg
);
470 hadError
= SOSCCSetDeviceID(deviceID
, &error
);
471 CFReleaseNull(deviceID
);
475 printf("Keychain syncing is being turned ON\n");
476 hadError
= requestToJoinCircle(&error
);
479 printf("Keychain syncing is being turned OFF\n");
480 hadError
= !SOSCCRemoveThisDeviceFromCircle(&error
);
484 CFArrayRef applicants
= SOSCCCopyApplicantPeerInfo(NULL
);
487 hadError
= !SOSCCAcceptApplicants(applicants
, &error
);
488 CFRelease(applicants
);
491 fprintf(stderr
, "No applicants to accept\n");
496 CFArrayRef applicants
= SOSCCCopyApplicantPeerInfo(NULL
);
499 hadError
= !SOSCCRejectApplicants(applicants
, &error
);
500 CFRelease(applicants
);
503 fprintf(stderr
, "No applicants to reject\n");
510 notify_post("com.apple.security.cloudkeychain.forceupdate");
513 //SOSCloudKeychainRequestSyncWithAllPeers(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), NULL);
517 printf("Ensuring Fresh Parameters\n");
518 bool result
= SOSCCRequestEnsureFreshParameters(&error
);
524 printf("Refreshed Parameters Ensured!\n");
527 printf("Problem trying to ensure fresh parameters\n");
532 hadError
= !SOSCCResetToEmpty(&error
);
535 hadError
= !SOSCCResetToOffering(&error
);
538 hadError
= clearAllKVS(&error
);
541 hadError
= setPassword(optarg
, &error
);
544 hadError
= tryPassword(optarg
, &error
);
548 uint64_t limit
= strtoul(optarg
, NULL
, 10);
549 hadError
= !SOSCCBailFromCircle_BestEffort(limit
, &error
);
553 hadError
= !SOSCCPurgeUserCredentials(&error
);
556 hadError
= dumpKVS(optarg
, &error
);
559 hadError
= syncAndWait(optarg
, &error
);
563 return 2; /* Return 2 triggers usage message. */
574 printerr(CFSTR("Error: %@\n"), error
);
576 // sec_perror("SecItemAdd", result);