2 * Copyright (c) 2003-2007,2009-2010 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>
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 applicantPeerInfos
= NULL
;
106 CFArrayRef peerInfos
= NULL
;
108 SOSCCStatus ccstatus
= SOSCCThisDeviceIsInCircle(&error
);
109 printerr(CFSTR("ccstatus: %s (%d), error: %@\n"), getSOSCCStatusDescription(ccstatus
), ccstatus
, error
);
111 if(ccstatus
== kSOSCCError
) {
112 printerr(CFSTR("End of Dump - unable to proceed due to ccstatus -\n\t%s\n"), getSOSCCStatusDescription(ccstatus
));
116 // Now look at current applicants
117 applicantPeerInfos
= SOSCCCopyApplicantPeerInfo(&error
);
118 if (applicantPeerInfos
)
120 printerr(CFSTR("Applicants: %ld, error: %@\n"), (long)CFArrayGetCount(applicantPeerInfos
), error
);
121 CFArrayForEach(applicantPeerInfos
, ^(const void *value
) {
122 SOSPeerInfoRef peer
= (SOSPeerInfoRef
)value
;
123 CFStringRef peerName
= SOSPeerInfoGetPeerName(peer
);
124 printerr(CFSTR("Applicant: %@ (%@)\n"), peerName
, peer
);
128 printerr(CFSTR("No applicants, error: %@\n"), error
);
131 peerInfos
= SOSCCCopyPeerPeerInfo(&error
);
134 printerr(CFSTR("Peers: %ld, error: %@\n"), (long)CFArrayGetCount(peerInfos
), error
);
135 CFArrayForEach(peerInfos
, ^(const void *value
) {
136 SOSPeerInfoRef peer
= (SOSPeerInfoRef
)value
;
137 CFStringRef peerName
= SOSPeerInfoGetPeerName(peer
);
138 printerr(CFSTR("Peer: %@ (%@)\n"), peerName
, peer
);
142 printerr(CFSTR("No peers, error: %@\n"), error
);
144 peerInfos
= SOSCCCopyConcurringPeerPeerInfo(&error
);
147 printerr(CFSTR("Concurring Peers: %ld, error: %@\n"), (long)CFArrayGetCount(peerInfos
), error
);
148 CFArrayForEach(peerInfos
, ^(const void *value
) {
149 SOSPeerInfoRef peer
= (SOSPeerInfoRef
)value
;
150 CFStringRef peerName
= SOSPeerInfoGetPeerName(peer
);
151 printerr(CFSTR("Concurr: %@ (%@)\n"), peerName
, peer
);
155 printerr(CFSTR("No concurring peers, error: %@\n"), error
);
158 static bool requestToJoinCircle(CFErrorRef
*error
)
160 // Set the visual state of switch based on membership in circle
161 bool hadError
= false;
162 SOSCCStatus ccstatus
= SOSCCThisDeviceIsInCircle(error
);
166 case kSOSCCCircleAbsent
:
167 hadError
= !SOSCCResetToOffering(error
);
169 case kSOSCCNotInCircle
:
170 hadError
= !SOSCCRequestToJoinCircle(error
);
173 printerr(CFSTR("Request to join circle with bad status: %@ (%d)\n"), SOSCCGetStatusDescription(ccstatus
), ccstatus
);
179 static bool setPassword(char *labelAndPassword
, CFErrorRef
*err
)
182 char *token0
= strtok_r(labelAndPassword
, ":", &last
);
183 char *token1
= strtok_r(NULL
, "", &last
);
184 CFStringRef label
= token1
? CFStringCreateWithCString(NULL
, token0
, kCFStringEncodingUTF8
) : CFSTR("security command line tool");
185 char *password_token
= token1
? token1
: token0
;
186 password_token
= password_token
? password_token
: "";
187 CFDataRef password
= CFDataCreate(NULL
, (const UInt8
*) password_token
, strlen(password_token
));
188 bool returned
= !SOSCCSetUserCredentials(label
, password
, err
);
194 static bool tryPassword(char *labelAndPassword
, CFErrorRef
*err
)
197 char *token0
= strtok_r(labelAndPassword
, ":", &last
);
198 char *token1
= strtok_r(NULL
, "", &last
);
199 CFStringRef label
= token1
? CFStringCreateWithCString(NULL
, token0
, kCFStringEncodingUTF8
) : CFSTR("security command line tool");
200 char *password_token
= token1
? token1
: token0
;
201 password_token
= password_token
? password_token
: "";
202 CFDataRef password
= CFDataCreate(NULL
, (const UInt8
*) password_token
, strlen(password_token
));
203 bool returned
= !SOSCCTryUserCredentials(label
, password
, err
);
209 static CFTypeRef
getObjectsFromCloud(CFArrayRef keysToGet
, dispatch_queue_t processQueue
, dispatch_group_t dgroup
)
211 __block CFTypeRef object
= NULL
;
213 const uint64_t maxTimeToWaitInSeconds
= 30ull * NSEC_PER_SEC
;
214 dispatch_semaphore_t waitSemaphore
= dispatch_semaphore_create(0);
215 dispatch_time_t finishTime
= dispatch_time(DISPATCH_TIME_NOW
, maxTimeToWaitInSeconds
);
217 dispatch_group_enter(dgroup
);
219 CloudKeychainReplyBlock replyBlock
=
220 ^ (CFDictionaryRef returnedValues
, CFErrorRef error
)
222 secerror("SOSCloudKeychainGetObjectsFromCloud returned: %@", returnedValues
);
223 object
= returnedValues
;
228 secerror("SOSCloudKeychainGetObjectsFromCloud returned error: %@", error
);
229 // CFRelease(*error);
231 dispatch_group_leave(dgroup
);
232 secerror("SOSCloudKeychainGetObjectsFromCloud block exit: %@", object
);
233 dispatch_semaphore_signal(waitSemaphore
);
237 SOSCloudKeychainGetAllObjectsFromCloud(processQueue
, replyBlock
);
239 SOSCloudKeychainGetObjectsFromCloud(keysToGet
, processQueue
, replyBlock
);
241 dispatch_semaphore_wait(waitSemaphore
, finishTime
);
242 dispatch_release(waitSemaphore
);
243 if (object
&& (CFGetTypeID(object
) == CFNullGetTypeID())) // return a NULL instead of a CFNull
248 secerror("returned: %@", object
);
252 static void displayCircles(CFTypeRef objects
)
254 // SOSCCCopyApplicantPeerInfo doesn't display all info, e.g. in the case where we are not in circle
255 CFDictionaryForEach(objects
, ^(const void *key
, const void *value
) {
256 if (SOSKVSKeyGetKeyType(key
) == kCircleKey
)
258 CFErrorRef localError
= NULL
;
261 SOSCircleRef circle
= SOSCircleCreateFromData(NULL
, (CFDataRef
) value
, &localError
);
262 printmsg(CFSTR("circle: %@ %@"), key
, circle
);
263 CFReleaseSafe(circle
);
266 printmsg(CFSTR("non-circle: %@ %@"), key
, value
);
271 static bool dumpKVS(char *itemName
, CFErrorRef
*err
)
273 CFArrayRef keysToGet
= NULL
;
276 CFStringRef itemStr
= CFStringCreateWithCString(kCFAllocatorDefault
, itemName
, kCFStringEncodingUTF8
);
277 printf("Retrieving %s from KVS\n", itemName
);
278 keysToGet
= CFArrayCreateForCFTypes(kCFAllocatorDefault
, itemStr
, NULL
);
279 CFReleaseSafe(itemStr
);
281 dispatch_queue_t generalq
= dispatch_queue_create("general", DISPATCH_QUEUE_SERIAL
);
282 dispatch_group_t work_group
= dispatch_group_create();
283 CFTypeRef objects
= getObjectsFromCloud(keysToGet
, generalq
, work_group
);
284 CFReleaseSafe(keysToGet
);
285 printmsg(CFSTR(" : %@\n"), objects
);
287 displayCircles(objects
);
292 static bool syncAndWait(char *itemName
, CFErrorRef
*err
)
294 CFArrayRef keysToGet
= NULL
;
295 __block CFTypeRef objects
= NULL
;
298 fprintf(stderr
, "No item keys supplied\n");
302 CFStringRef itemStr
= CFStringCreateWithCString(kCFAllocatorDefault
, itemName
, kCFStringEncodingUTF8
);
303 printf("Retrieving %s from KVS\n", itemName
);
304 keysToGet
= CFArrayCreateForCFTypes(kCFAllocatorDefault
, itemStr
, NULL
);
305 CFReleaseSafe(itemStr
);
307 dispatch_queue_t generalq
= dispatch_queue_create("general", DISPATCH_QUEUE_SERIAL
);
309 const uint64_t maxTimeToWaitInSeconds
= 30ull * NSEC_PER_SEC
;
310 dispatch_semaphore_t waitSemaphore
= dispatch_semaphore_create(0);
311 dispatch_time_t finishTime
= dispatch_time(DISPATCH_TIME_NOW
, maxTimeToWaitInSeconds
);
313 CloudKeychainReplyBlock replyBlock
= ^ (CFDictionaryRef returnedValues
, CFErrorRef error
)
315 secerror("SOSCloudKeychainSynchronizeAndWait returned: %@", returnedValues
);
317 secerror("SOSCloudKeychainSynchronizeAndWait returned error: %@", error
);
318 objects
= returnedValues
;
321 secerror("SOSCloudKeychainGetObjectsFromCloud block exit: %@", objects
);
322 dispatch_semaphore_signal(waitSemaphore
);
325 SOSCloudKeychainSynchronizeAndWait(keysToGet
, generalq
, replyBlock
);
327 dispatch_semaphore_wait(waitSemaphore
, finishTime
);
328 dispatch_release(waitSemaphore
);
330 CFReleaseSafe(keysToGet
);
331 printmsg(CFSTR(" : %@\n"), objects
);
333 displayCircles(objects
);
338 // enable, disable, accept, reject, status, Reset, Clear
340 keychain_sync(int argc
, char * const *argv
)
343 " -e Enable Keychain Syncing (join/create circle)\n"
344 " -d Disable Keychain Syncing\n"
345 " -a Accept all applicants\n"
346 " -r Reject all applicants\n"
348 " -k Pend all registered kvs keys\n"
349 " -s Schedule sync with all peers\n"
351 " -O ResetToOffering\n"
352 " -C Clear all values from KVS\n"
353 " -P [label:]password Set password (optionally for a given label) for sync\n"
354 " -D [itemName] Dump contents of KVS\n"
355 " -P [label:]password Set password (optionally for a given label) for sync\n"
356 " -T [label:]password Try password (optionally for a given label) for sync\n"
357 " -U Purge private key material cache\n"
358 " -D [itemName] Dump contents of KVS\n"
359 " -W itemNames sync and dump\n"
360 " -X [limit] Best effort bail from circle in limit seconds\n"
363 CFErrorRef error
= NULL
;
364 bool hadError
= false;
366 while ((ch
= getopt(argc
, argv
, "edakrisROChP:T:DW:UX:")) != -1)
371 printf("Keychain syncing is being turned ON\n");
372 hadError
= requestToJoinCircle(&error
);
375 printf("Keychain syncing is being turned OFF\n");
376 hadError
= !SOSCCRemoveThisDeviceFromCircle(&error
);
380 CFArrayRef applicants
= SOSCCCopyApplicantPeerInfo(NULL
);
383 hadError
= !SOSCCAcceptApplicants(applicants
, &error
);
384 CFRelease(applicants
);
387 fprintf(stderr
, "No applicants to accept\n");
392 CFArrayRef applicants
= SOSCCCopyApplicantPeerInfo(NULL
);
395 hadError
= !SOSCCRejectApplicants(applicants
, &error
);
396 CFRelease(applicants
);
399 fprintf(stderr
, "No applicants to reject\n");
406 notify_post("com.apple.security.cloudkeychain.forceupdate");
409 //SOSCloudKeychainRequestSyncWithAllPeers(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), NULL);
412 hadError
= !SOSCCResetToEmpty(&error
);
415 hadError
= !SOSCCResetToOffering(&error
);
418 hadError
= clearAllKVS(&error
);
421 hadError
= setPassword(optarg
, &error
);
424 hadError
= tryPassword(optarg
, &error
);
428 uint64_t limit
= strtoul(optarg
, NULL
, 10);
429 hadError
= !SOSCCBailFromCircle_BestEffort(limit
, &error
);
433 hadError
= !SOSCCPurgeUserCredentials(&error
);
436 hadError
= dumpKVS(optarg
, &error
);
439 hadError
= syncAndWait(optarg
, &error
);
443 return 2; /* Return 2 triggers usage message. */
454 printerr(CFSTR("Error: %@\n"), error
);
456 // sec_perror("SecItemAdd", result);