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 56static 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 66static 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 86static 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 102static 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 158static 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 179static 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 194static 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 209static 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 252static 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 271static 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 292static 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 339int 340keychain_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} 460