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 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 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 216static 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 237static 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 252static 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 267static 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 310static 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 329static 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 350static 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 397int 398keychain_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} 580