]> git.saurik.com Git - apple/security.git/blob - Security/sec/SOSCircle/Tool/keychain_sync.c
Security-57031.1.35.tar.gz
[apple/security.git] / Security / sec / SOSCircle / Tool / keychain_sync.c
1 /*
2 * Copyright (c) 2003-2007,2009-2010,2013-2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 *
23 * keychain_add.c
24 */
25
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31
32 #include <Security/SecItem.h>
33
34 #include <CoreFoundation/CFNumber.h>
35 #include <CoreFoundation/CFString.h>
36
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>
42
43 #include <CKBridge/SOSCloudKeychainClient.h>
44
45 #include <utilities/SecCFWrappers.h>
46 #include <utilities/debugging.h>
47
48 #include <SecurityTool/readline.h>
49 #include <notify.h>
50
51 #include "SOSCommands.h"
52
53 #define printmsg(format, ...) _printcfmsg(stdout, format, __VA_ARGS__)
54 #define printerr(format, ...) _printcfmsg(stderr, format, __VA_ARGS__)
55
56 static void _printcfmsg(FILE *ff, CFStringRef format, ...)
57 {
58 va_list args;
59 va_start(args, format);
60 CFStringRef message = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, NULL, format, args);
61 va_end(args);
62 CFStringPerformWithCString(message, ^(const char *utf8String) { fprintf(ff, utf8String, ""); });
63 CFRelease(message);
64 }
65
66 static bool clearAllKVS(CFErrorRef *error)
67 {
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);
73
74 SOSCloudKeychainClearAll(processQueue, ^(CFDictionaryRef returnedValues, CFErrorRef cerror)
75 {
76 result = (cerror != NULL);
77 dispatch_semaphore_signal(waitSemaphore);
78 });
79
80 dispatch_semaphore_wait(waitSemaphore, finishTime);
81 dispatch_release(waitSemaphore);
82
83 return result;
84 }
85
86 static const char *getSOSCCStatusDescription(SOSCCStatus ccstatus)
87 {
88 switch (ccstatus)
89 {
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";
95
96 default:
97 return "<unknown ccstatus>";
98 break;
99 }
100 }
101
102 static void dumpCircleInfo()
103 {
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;
112
113 SOSCCStatus ccstatus = SOSCCThisDeviceIsInCircle(&error);
114 printmsg(CFSTR("ccstatus: %s (%d), error: %@\n"), getSOSCCStatusDescription(ccstatus), ccstatus, error);
115
116 if(ccstatus == kSOSCCError) {
117 printmsg(CFSTR("End of Dump - unable to proceed due to ccstatus -\n\t%s\n"), getSOSCCStatusDescription(ccstatus));
118 return;
119 }
120
121 is_user_public_trusted = SOSCCValidateUserPublic(&error);
122 if(is_user_public_trusted)
123 printmsg(CFSTR("Account user public is trusted%@"),CFSTR("\n"));
124 else
125 printmsg(CFSTR("Account user public is not trusted%@"),CFSTR("\n"));
126
127 // Now look at current peers
128 peerPeerInfos = SOSCCCopyValidPeerPeerInfo(&error);
129
130 if (peerPeerInfos)
131 {
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);
137 });
138 }
139 else
140 printmsg(CFSTR("No peers, error: %@\n"), error);
141
142 CFReleaseNull(peerPeerInfos);
143 peerPeerInfos = SOSCCCopyNotValidPeerPeerInfo(&error);
144
145 if (peerPeerInfos)
146 {
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);
152 });
153 }
154 CFReleaseNull(peerPeerInfos);
155
156 applicants = SOSCCCopyApplicantPeerInfo(&error);
157 if (applicants)
158 {
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);
164 });
165 }
166 else
167 printmsg(CFSTR("No applicants, error: %@\n"), error);
168 CFReleaseNull(applicants);
169
170
171 peerInfos = SOSCCCopyConcurringPeerPeerInfo(&error);
172 if (peerInfos)
173 {
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);
179 });
180 }
181 else
182 printmsg(CFSTR("No concurring peers, error: %@\n"), error);
183
184 CFReleaseNull(peerInfos);
185 retirees = SOSCCCopyRetirementPeerInfo(&error);
186 if (retirees)
187 {
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);
193 });
194 }
195 else
196 printmsg(CFSTR("No retired peers, error: %@\n"), error);
197 CFReleaseNull(retirees);
198
199 generations = SOSCCCopyGenerationPeerInfo(&error);
200 if(generations)
201 {
202 CFArrayForEach(generations, ^(const void *value) {
203 count++;
204 if(count%2 != 0)
205 printmsg(CFSTR("Circle name: %@, "),value);
206
207 if(count%2 == 0)
208 printmsg(CFSTR("Generation Count: %@\n"), value);
209 });
210 }
211 else
212 printmsg(CFSTR("No generation count: %@\n"), error);
213 CFReleaseNull(generations);
214 }
215
216 static bool requestToJoinCircle(CFErrorRef *error)
217 {
218 // Set the visual state of switch based on membership in circle
219 bool hadError = false;
220 SOSCCStatus ccstatus = SOSCCThisDeviceIsInCircle(error);
221
222 switch (ccstatus)
223 {
224 case kSOSCCCircleAbsent:
225 hadError = !SOSCCResetToOffering(error);
226 break;
227 case kSOSCCNotInCircle:
228 hadError = !SOSCCRequestToJoinCircle(error);
229 break;
230 default:
231 printerr(CFSTR("Request to join circle with bad status: %@ (%d)\n"), SOSCCGetStatusDescription(ccstatus), ccstatus);
232 break;
233 }
234 return hadError;
235 }
236
237 static bool setPassword(char *labelAndPassword, CFErrorRef *err)
238 {
239 char *last = NULL;
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);
247 CFRelease(label);
248 CFRelease(password);
249 return returned;
250 }
251
252 static bool tryPassword(char *labelAndPassword, CFErrorRef *err)
253 {
254 char *last = NULL;
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);
262 CFRelease(label);
263 CFRelease(password);
264 return returned;
265 }
266
267 static CFTypeRef getObjectsFromCloud(CFArrayRef keysToGet, dispatch_queue_t processQueue, dispatch_group_t dgroup)
268 {
269 __block CFTypeRef object = NULL;
270
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);
274
275 dispatch_group_enter(dgroup);
276
277 CloudKeychainReplyBlock replyBlock =
278 ^ (CFDictionaryRef returnedValues, CFErrorRef error)
279 {
280 secerror("SOSCloudKeychainGetObjectsFromCloud returned: %@", returnedValues);
281 object = returnedValues;
282 if (object)
283 CFRetain(object);
284 if (error)
285 {
286 secerror("SOSCloudKeychainGetObjectsFromCloud returned error: %@", error);
287 // CFRelease(*error);
288 }
289 dispatch_group_leave(dgroup);
290 secerror("SOSCloudKeychainGetObjectsFromCloud block exit: %@", object);
291 dispatch_semaphore_signal(waitSemaphore);
292 };
293
294 if (!keysToGet)
295 SOSCloudKeychainGetAllObjectsFromCloud(processQueue, replyBlock);
296 else
297 SOSCloudKeychainGetObjectsFromCloud(keysToGet, processQueue, replyBlock);
298
299 dispatch_semaphore_wait(waitSemaphore, finishTime);
300 dispatch_release(waitSemaphore);
301 if (object && (CFGetTypeID(object) == CFNullGetTypeID())) // return a NULL instead of a CFNull
302 {
303 CFRelease(object);
304 object = NULL;
305 }
306 secerror("returned: %@", object);
307 return object;
308 }
309
310 static void displayCircles(CFTypeRef objects)
311 {
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)
315 {
316 CFErrorRef localError = NULL;
317 if (isData(value))
318 {
319 SOSCircleRef circle = SOSCircleCreateFromData(NULL, (CFDataRef) value, &localError);
320 printmsg(CFSTR("circle: %@ %@"), key, circle);
321 CFReleaseSafe(circle);
322 }
323 else
324 printmsg(CFSTR("non-circle: %@ %@"), key, value);
325 }
326 });
327 }
328
329 static bool dumpKVS(char *itemName, CFErrorRef *err)
330 {
331 CFArrayRef keysToGet = NULL;
332 if (itemName)
333 {
334 CFStringRef itemStr = CFStringCreateWithCString(kCFAllocatorDefault, itemName, kCFStringEncodingUTF8);
335 printf("Retrieving %s from KVS\n", itemName);
336 keysToGet = CFArrayCreateForCFTypes(kCFAllocatorDefault, itemStr, NULL);
337 CFReleaseSafe(itemStr);
338 }
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);
344 if (objects)
345 displayCircles(objects);
346 printf("\n");
347 return false;
348 }
349
350 static bool syncAndWait(char *itemName, CFErrorRef *err)
351 {
352 CFArrayRef keysToGet = NULL;
353 __block CFTypeRef objects = NULL;
354 if (!itemName)
355 {
356 fprintf(stderr, "No item keys supplied\n");
357 return false;
358 }
359
360 CFStringRef itemStr = CFStringCreateWithCString(kCFAllocatorDefault, itemName, kCFStringEncodingUTF8);
361 printf("Retrieving %s from KVS\n", itemName);
362 keysToGet = CFArrayCreateForCFTypes(kCFAllocatorDefault, itemStr, NULL);
363 CFReleaseSafe(itemStr);
364
365 dispatch_queue_t generalq = dispatch_queue_create("general", DISPATCH_QUEUE_SERIAL);
366
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);
370
371 CloudKeychainReplyBlock replyBlock = ^ (CFDictionaryRef returnedValues, CFErrorRef error)
372 {
373 secerror("SOSCloudKeychainSynchronizeAndWait returned: %@", returnedValues);
374 if (error)
375 secerror("SOSCloudKeychainSynchronizeAndWait returned error: %@", error);
376 objects = returnedValues;
377 if (objects)
378 CFRetain(objects);
379 secerror("SOSCloudKeychainGetObjectsFromCloud block exit: %@", objects);
380 dispatch_semaphore_signal(waitSemaphore);
381 };
382
383 SOSCloudKeychainSynchronizeAndWait(keysToGet, generalq, replyBlock);
384
385 dispatch_semaphore_wait(waitSemaphore, finishTime);
386 dispatch_release(waitSemaphore);
387
388 CFReleaseSafe(keysToGet);
389 printmsg(CFSTR(" : %@\n"), objects);
390 if (objects)
391 displayCircles(objects);
392 printf("\n");
393 return false;
394 }
395
396 // enable, disable, accept, reject, status, Reset, Clear
397 int
398 keychain_sync(int argc, char * const *argv)
399 {
400 /*
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"
405 " -i Info\n"
406 " -k Pend all registered kvs keys\n"
407 " -s Schedule sync with all peers\n"
408 " -E Ensure Fresh Parameters\n"
409 " -R Reset\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"
423 */
424 int ch, result = 0;
425 CFErrorRef error = NULL;
426 bool hadError = false;
427
428 while ((ch = getopt(argc, argv, "pedakrisEROChP:T:DW:UX:g:q:")) != -1)
429 {
430 switch (ch)
431 {
432 case 'q':
433 {
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){
438 immediately = true;
439 }
440 else if(CFStringCompare(CFSTR("false"), leaveImmediately, 0) == 0){
441 immediately = false;
442 }
443 else{
444 printf("Please provide a \"true\" or \"false\" whether you'd like to leave the circle immediately\n");
445 }
446 hadError = !SOSCCSignedOut(immediately, &error);
447 }
448 break;
449 case 'p':
450 {
451 printf("Grabbing DS ID\n");
452 CFStringRef deviceID = SOSCCRequestDeviceID(&error);
453 if(error){
454 hadError = true;
455 break;
456 }
457 if(!isNull(deviceID)){
458 const char* id = CFStringGetCStringPtr(deviceID, kCFStringEncodingUTF8);
459 if(id)
460 printf("IDS Device ID: %s\n", id);
461 else
462 printf("IDS Device ID is null!\n");
463 }
464 }
465 break;
466 case 'g':
467 {
468 CFStringRef deviceID = CFStringCreateWithCString(kCFAllocatorDefault, (char *)optarg, kCFStringEncodingUTF8);
469 printf("Setting DS ID: %s\n", optarg);
470 hadError = SOSCCSetDeviceID(deviceID, &error);
471 CFReleaseNull(deviceID);
472 }
473 break;
474 case 'e':
475 printf("Keychain syncing is being turned ON\n");
476 hadError = requestToJoinCircle(&error);
477 break;
478 case 'd':
479 printf("Keychain syncing is being turned OFF\n");
480 hadError = !SOSCCRemoveThisDeviceFromCircle(&error);
481 break;
482 case 'a':
483 {
484 CFArrayRef applicants = SOSCCCopyApplicantPeerInfo(NULL);
485 if (applicants)
486 {
487 hadError = !SOSCCAcceptApplicants(applicants, &error);
488 CFRelease(applicants);
489 }
490 else
491 fprintf(stderr, "No applicants to accept\n");
492 }
493 break;
494 case 'r':
495 {
496 CFArrayRef applicants = SOSCCCopyApplicantPeerInfo(NULL);
497 if (applicants)
498 {
499 hadError = !SOSCCRejectApplicants(applicants, &error);
500 CFRelease(applicants);
501 }
502 else
503 fprintf(stderr, "No applicants to reject\n");
504 }
505 break;
506 case 'i':
507 dumpCircleInfo();
508 break;
509 case 'k':
510 notify_post("com.apple.security.cloudkeychain.forceupdate");
511 break;
512 case 's':
513 //SOSCloudKeychainRequestSyncWithAllPeers(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), NULL);
514 break;
515 case 'E':
516 {
517 printf("Ensuring Fresh Parameters\n");
518 bool result = SOSCCRequestEnsureFreshParameters(&error);
519 if(error){
520 hadError = true;
521 break;
522 }
523 if(result){
524 printf("Refreshed Parameters Ensured!\n");
525 }
526 else{
527 printf("Problem trying to ensure fresh parameters\n");
528 }
529 }
530 break;
531 case 'R':
532 hadError = !SOSCCResetToEmpty(&error);
533 break;
534 case 'O':
535 hadError = !SOSCCResetToOffering(&error);
536 break;
537 case 'C':
538 hadError = clearAllKVS(&error);
539 break;
540 case 'P':
541 hadError = setPassword(optarg, &error);
542 break;
543 case 'T':
544 hadError = tryPassword(optarg, &error);
545 break;
546 case 'X':
547 {
548 uint64_t limit = strtoul(optarg, NULL, 10);
549 hadError = !SOSCCBailFromCircle_BestEffort(limit, &error);
550 }
551 break;
552 case 'U':
553 hadError = !SOSCCPurgeUserCredentials(&error);
554 break;
555 case 'D':
556 hadError = dumpKVS(optarg, &error);
557 break;
558 case 'W':
559 hadError = syncAndWait(optarg, &error);
560 break;
561 case '?':
562 default:
563 return 2; /* Return 2 triggers usage message. */
564 }
565 }
566
567 //argc -= optind;
568 //argv += optind;
569
570 // if (argc == 0)
571 // return 2;
572
573 if (hadError)
574 printerr(CFSTR("Error: %@\n"), error);
575
576 // sec_perror("SecItemAdd", result);
577
578 return result;
579 }