]> git.saurik.com Git - apple/security.git/blob - sec/SOSCircle/Tool/keychain_sync.c
Security-55471.14.18.tar.gz
[apple/security.git] / sec / SOSCircle / Tool / keychain_sync.c
1 /*
2 * Copyright (c) 2003-2007,2009-2010 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
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 applicantPeerInfos = NULL;
106 CFArrayRef peerInfos = NULL;
107
108 SOSCCStatus ccstatus = SOSCCThisDeviceIsInCircle(&error);
109 printerr(CFSTR("ccstatus: %s (%d), error: %@\n"), getSOSCCStatusDescription(ccstatus), ccstatus, error);
110
111 if(ccstatus == kSOSCCError) {
112 printerr(CFSTR("End of Dump - unable to proceed due to ccstatus -\n\t%s\n"), getSOSCCStatusDescription(ccstatus));
113 return;
114 }
115
116 // Now look at current applicants
117 applicantPeerInfos = SOSCCCopyApplicantPeerInfo(&error);
118 if (applicantPeerInfos)
119 {
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);
125 });
126 }
127 else
128 printerr(CFSTR("No applicants, error: %@\n"), error);
129
130
131 peerInfos = SOSCCCopyPeerPeerInfo(&error);
132 if (peerInfos)
133 {
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);
139 });
140 }
141 else
142 printerr(CFSTR("No peers, error: %@\n"), error);
143
144 peerInfos = SOSCCCopyConcurringPeerPeerInfo(&error);
145 if (peerInfos)
146 {
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);
152 });
153 }
154 else
155 printerr(CFSTR("No concurring peers, error: %@\n"), error);
156 }
157
158 static bool requestToJoinCircle(CFErrorRef *error)
159 {
160 // Set the visual state of switch based on membership in circle
161 bool hadError = false;
162 SOSCCStatus ccstatus = SOSCCThisDeviceIsInCircle(error);
163
164 switch (ccstatus)
165 {
166 case kSOSCCCircleAbsent:
167 hadError = !SOSCCResetToOffering(error);
168 break;
169 case kSOSCCNotInCircle:
170 hadError = !SOSCCRequestToJoinCircle(error);
171 break;
172 default:
173 printerr(CFSTR("Request to join circle with bad status: %@ (%d)\n"), SOSCCGetStatusDescription(ccstatus), ccstatus);
174 break;
175 }
176 return hadError;
177 }
178
179 static bool setPassword(char *labelAndPassword, CFErrorRef *err)
180 {
181 char *last = NULL;
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);
189 CFRelease(label);
190 CFRelease(password);
191 return returned;
192 }
193
194 static bool tryPassword(char *labelAndPassword, CFErrorRef *err)
195 {
196 char *last = NULL;
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);
204 CFRelease(label);
205 CFRelease(password);
206 return returned;
207 }
208
209 static CFTypeRef getObjectsFromCloud(CFArrayRef keysToGet, dispatch_queue_t processQueue, dispatch_group_t dgroup)
210 {
211 __block CFTypeRef object = NULL;
212
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);
216
217 dispatch_group_enter(dgroup);
218
219 CloudKeychainReplyBlock replyBlock =
220 ^ (CFDictionaryRef returnedValues, CFErrorRef error)
221 {
222 secerror("SOSCloudKeychainGetObjectsFromCloud returned: %@", returnedValues);
223 object = returnedValues;
224 if (object)
225 CFRetain(object);
226 if (error)
227 {
228 secerror("SOSCloudKeychainGetObjectsFromCloud returned error: %@", error);
229 // CFRelease(*error);
230 }
231 dispatch_group_leave(dgroup);
232 secerror("SOSCloudKeychainGetObjectsFromCloud block exit: %@", object);
233 dispatch_semaphore_signal(waitSemaphore);
234 };
235
236 if (!keysToGet)
237 SOSCloudKeychainGetAllObjectsFromCloud(processQueue, replyBlock);
238 else
239 SOSCloudKeychainGetObjectsFromCloud(keysToGet, processQueue, replyBlock);
240
241 dispatch_semaphore_wait(waitSemaphore, finishTime);
242 dispatch_release(waitSemaphore);
243 if (object && (CFGetTypeID(object) == CFNullGetTypeID())) // return a NULL instead of a CFNull
244 {
245 CFRelease(object);
246 object = NULL;
247 }
248 secerror("returned: %@", object);
249 return object;
250 }
251
252 static void displayCircles(CFTypeRef objects)
253 {
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)
257 {
258 CFErrorRef localError = NULL;
259 if (isData(value))
260 {
261 SOSCircleRef circle = SOSCircleCreateFromData(NULL, (CFDataRef) value, &localError);
262 printmsg(CFSTR("circle: %@ %@"), key, circle);
263 CFReleaseSafe(circle);
264 }
265 else
266 printmsg(CFSTR("non-circle: %@ %@"), key, value);
267 }
268 });
269 }
270
271 static bool dumpKVS(char *itemName, CFErrorRef *err)
272 {
273 CFArrayRef keysToGet = NULL;
274 if (itemName)
275 {
276 CFStringRef itemStr = CFStringCreateWithCString(kCFAllocatorDefault, itemName, kCFStringEncodingUTF8);
277 printf("Retrieving %s from KVS\n", itemName);
278 keysToGet = CFArrayCreateForCFTypes(kCFAllocatorDefault, itemStr, NULL);
279 CFReleaseSafe(itemStr);
280 }
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);
286 if (objects)
287 displayCircles(objects);
288 printf("\n");
289 return false;
290 }
291
292 static bool syncAndWait(char *itemName, CFErrorRef *err)
293 {
294 CFArrayRef keysToGet = NULL;
295 __block CFTypeRef objects = NULL;
296 if (!itemName)
297 {
298 fprintf(stderr, "No item keys supplied\n");
299 return false;
300 }
301
302 CFStringRef itemStr = CFStringCreateWithCString(kCFAllocatorDefault, itemName, kCFStringEncodingUTF8);
303 printf("Retrieving %s from KVS\n", itemName);
304 keysToGet = CFArrayCreateForCFTypes(kCFAllocatorDefault, itemStr, NULL);
305 CFReleaseSafe(itemStr);
306
307 dispatch_queue_t generalq = dispatch_queue_create("general", DISPATCH_QUEUE_SERIAL);
308
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);
312
313 CloudKeychainReplyBlock replyBlock = ^ (CFDictionaryRef returnedValues, CFErrorRef error)
314 {
315 secerror("SOSCloudKeychainSynchronizeAndWait returned: %@", returnedValues);
316 if (error)
317 secerror("SOSCloudKeychainSynchronizeAndWait returned error: %@", error);
318 objects = returnedValues;
319 if (objects)
320 CFRetain(objects);
321 secerror("SOSCloudKeychainGetObjectsFromCloud block exit: %@", objects);
322 dispatch_semaphore_signal(waitSemaphore);
323 };
324
325 SOSCloudKeychainSynchronizeAndWait(keysToGet, generalq, replyBlock);
326
327 dispatch_semaphore_wait(waitSemaphore, finishTime);
328 dispatch_release(waitSemaphore);
329
330 CFReleaseSafe(keysToGet);
331 printmsg(CFSTR(" : %@\n"), objects);
332 if (objects)
333 displayCircles(objects);
334 printf("\n");
335 return false;
336 }
337
338 // enable, disable, accept, reject, status, Reset, Clear
339 int
340 keychain_sync(int argc, char * const *argv)
341 {
342 /*
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"
347 " -i Info\n"
348 " -k Pend all registered kvs keys\n"
349 " -s Schedule sync with all peers\n"
350 " -R Reset\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"
361 */
362 int ch, result = 0;
363 CFErrorRef error = NULL;
364 bool hadError = false;
365
366 while ((ch = getopt(argc, argv, "edakrisROChP:T:DW:UX:")) != -1)
367 {
368 switch (ch)
369 {
370 case 'e':
371 printf("Keychain syncing is being turned ON\n");
372 hadError = requestToJoinCircle(&error);
373 break;
374 case 'd':
375 printf("Keychain syncing is being turned OFF\n");
376 hadError = !SOSCCRemoveThisDeviceFromCircle(&error);
377 break;
378 case 'a':
379 {
380 CFArrayRef applicants = SOSCCCopyApplicantPeerInfo(NULL);
381 if (applicants)
382 {
383 hadError = !SOSCCAcceptApplicants(applicants, &error);
384 CFRelease(applicants);
385 }
386 else
387 fprintf(stderr, "No applicants to accept\n");
388 }
389 break;
390 case 'r':
391 {
392 CFArrayRef applicants = SOSCCCopyApplicantPeerInfo(NULL);
393 if (applicants)
394 {
395 hadError = !SOSCCRejectApplicants(applicants, &error);
396 CFRelease(applicants);
397 }
398 else
399 fprintf(stderr, "No applicants to reject\n");
400 }
401 break;
402 case 'i':
403 dumpCircleInfo();
404 break;
405 case 'k':
406 notify_post("com.apple.security.cloudkeychain.forceupdate");
407 break;
408 case 's':
409 //SOSCloudKeychainRequestSyncWithAllPeers(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), NULL);
410 break;
411 case 'R':
412 hadError = !SOSCCResetToEmpty(&error);
413 break;
414 case 'O':
415 hadError = !SOSCCResetToOffering(&error);
416 break;
417 case 'C':
418 hadError = clearAllKVS(&error);
419 break;
420 case 'P':
421 hadError = setPassword(optarg, &error);
422 break;
423 case 'T':
424 hadError = tryPassword(optarg, &error);
425 break;
426 case 'X':
427 {
428 uint64_t limit = strtoul(optarg, NULL, 10);
429 hadError = !SOSCCBailFromCircle_BestEffort(limit, &error);
430 }
431 break;
432 case 'U':
433 hadError = !SOSCCPurgeUserCredentials(&error);
434 break;
435 case 'D':
436 hadError = dumpKVS(optarg, &error);
437 break;
438 case 'W':
439 hadError = syncAndWait(optarg, &error);
440 break;
441 case '?':
442 default:
443 return 2; /* Return 2 triggers usage message. */
444 }
445 }
446
447 argc -= optind;
448 argv += optind;
449
450 // if (argc == 0)
451 // return 2;
452
453 if (hadError)
454 printerr(CFSTR("Error: %@\n"), error);
455
456 // sec_perror("SecItemAdd", result);
457
458 return result;
459 }