1/* 2 * Copyright (c) 2012-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 24 25#include <AssertMacros.h> 26#include <CoreFoundation/CFURL.h> 27 28#include <securityd/SOSCloudCircleServer.h> 29#include <SecureObjectSync/SOSCloudCircle.h> 30#include <SecureObjectSync/SOSCloudCircleInternal.h> 31#include <SecureObjectSync/SOSCircle.h> 32#include <SecureObjectSync/SOSAccount.h> 33#include <SecureObjectSync/SOSAccountPriv.h> 34#include <SecureObjectSync/SOSFullPeerInfo.h> 35#include <SecureObjectSync/SOSPeerInfoInternal.h> 36#include <SecureObjectSync/SOSInternal.h> 37#include <SecureObjectSync/SOSUserKeygen.h> 38#include <SecureObjectSync/SOSMessage.h> 39#include <SecureObjectSync/SOSTransport.h> 40#include <SecureObjectSync/SOSKVSKeys.h> 41 42#include <utilities/SecCFWrappers.h> 43#include <utilities/SecCFRelease.h> 44#include <utilities/debugging.h> 45#include <CKBridge/SOSCloudKeychainClient.h> 46 47#include <corecrypto/ccrng.h> 48#include <corecrypto/ccrng_pbkdf2_prng.h> 49#include <corecrypto/ccec.h> 50#include <corecrypto/ccdigest.h> 51#include <corecrypto/ccsha2.h> 52#include <CommonCrypto/CommonRandomSPI.h> 53#include <Security/SecKeyPriv.h> 54#include <Security/SecFramework.h> 55 56#include <utilities/SecFileLocations.h> 57#include <utilities/SecAKSWrappers.h> 58#include <securityd/SecItemServer.h> 59#include <Security/SecItemPriv.h> 60 61#include <TargetConditionals.h> 62 63#include <utilities/iCloudKeychainTrace.h> 64 65#if TARGET_OS_EMBEDDED || TARGET_IPHONE_SIMULATOR 66#include <MobileGestalt.h> 67#else 68#include <AppleSystemInfo/AppleSystemInfo.h> 69 70// We need authorization, but that doesn't exist 71// on sec built for desktop (iOS in a process) 72// Define AuthorizationRef here to make SystemConfiguration work 73// as if it's on iOS. 74typedef const struct AuthorizationOpaqueRef * AuthorizationRef; 75#endif 76 77#define SOSCKCSCOPE "sync" 78 79#define USE_SYSTEMCONFIGURATION_PRIVATE_HEADERS 80#import <SystemConfiguration/SystemConfiguration.h> 81 82#include <notify.h> 83 84static SOSCCAccountDataSourceFactoryBlock accountDataSourceOverride = NULL; 85 86bool SOSKeychainAccountSetFactoryForAccount(SOSCCAccountDataSourceFactoryBlock block) 87{ 88 accountDataSourceOverride = Block_copy(block); 89 90 return true; 91} 92 93// 94// Forward declared 95// 96 97static void do_with_account(void (^action)(SOSAccountRef account)); 98static void do_with_account_async(void (^action)(SOSAccountRef account)); 99 100// 101// Constants 102// 103CFStringRef kSOSInternalAccessGroup = CFSTR("com.apple.security.sos"); 104 105CFStringRef kSOSAccountLabel = CFSTR("iCloud Keychain Account Meta-data"); 106 107static CFStringRef accountFileName = CFSTR("PersistedAccount.plist"); 108 109static CFDictionaryRef SOSItemCopyQueryForSyncItems(CFStringRef service, bool returnData) 110{ 111 return CFDictionaryCreateForCFTypes(kCFAllocatorDefault, 112 kSecClass, kSecClassGenericPassword, 113 kSecAttrService, service, 114 kSecAttrAccessGroup, kSOSInternalAccessGroup, 115 kSecReturnData, returnData ? kCFBooleanTrue : kCFBooleanFalse, 116 NULL); 117} 118 119CFDataRef SOSItemCopy(CFStringRef service, CFErrorRef* error) 120{ 121 CFDictionaryRef query = SOSItemCopyQueryForSyncItems(service, true); 122 123 CFDataRef result = NULL; 124 125 OSStatus copyResult = SecItemCopyMatching(query, (CFTypeRef*) &result); 126 127 CFReleaseNull(query); 128 129 if (copyResult != noErr) { 130 SecError(copyResult, error, CFSTR("Error %@ reading for service '%@'"), result, service); 131 CFReleaseNull(result); 132 return NULL; 133 } 134 135 if (!isData(result)) { 136 SOSCreateErrorWithFormat(kSOSErrorProcessingFailure, NULL, error, NULL, CFSTR("SecItemCopyMatching returned non-data in '%@'"), service); 137 CFReleaseNull(result); 138 return NULL; 139 } 140 141 return result; 142} 143 144static CFDataRef SOSKeychainCopySavedAccountData() 145{ 146 CFErrorRef error = NULL; 147 CFDataRef accountData = SOSItemCopy(kSOSAccountLabel, &error); 148 if (!accountData) 149 secnotice("account", "Failed to load account: %@", error); 150 CFReleaseNull(error); 151 152 return accountData; 153} 154 155bool SOSItemUpdateOrAdd(CFStringRef service, CFStringRef accessibility, CFDataRef data, CFErrorRef *error) 156{ 157 CFDictionaryRef query = SOSItemCopyQueryForSyncItems(service, false); 158 159 CFDictionaryRef update = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, 160 kSecValueData, data, 161 kSecAttrAccessible, accessibility, 162 NULL); 163 OSStatus saveStatus = SecItemUpdate(query, update); 164 165 if (errSecItemNotFound == saveStatus) { 166 CFMutableDictionaryRef add = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, query); 167 CFDictionaryForEach(update, ^(const void *key, const void *value) { 168 CFDictionaryAddValue(add, key, value); 169 }); 170 saveStatus = SecItemAdd(add, NULL); 171 CFReleaseNull(add); 172 } 173 174 CFReleaseNull(query); 175 CFReleaseNull(update); 176 177 return SecError(saveStatus, error, CFSTR("Error saving %@ to service '%@'"), data, service); 178} 179 180static CFStringRef accountStatusFileName = CFSTR("accountStatus.plist"); 181#include <utilities/der_plist.h> 182#include <utilities/der_plist_internal.h> 183#include <corecrypto/ccder.h> 184#if 0 185static const uint8_t* ccder_decode_bool(bool* boolean, const uint8_t* der, const uint8_t *der_end) 186{ 187 if (NULL == der) 188 return NULL; 189 190 size_t payload_size = 0; 191 const uint8_t *payload = ccder_decode_tl(CCDER_BOOLEAN, &payload_size, der, der_end); 192 193 if (NULL == payload || (der_end - payload) < 1 || payload_size != 1) { 194 return NULL; 195 } 196 197 if (boolean) 198 *boolean = (*payload != 0); 199 200 return payload + payload_size; 201} 202#endif 203 204bool SOSCCCircleIsOn_Artifact(void) { 205 bool circle_on = false; 206 CFDataRef accountStatus = NULL; 207 CFURLRef accountStatusFileURL = SecCopyURLForFileInKeychainDirectory(accountStatusFileName); 208 require_quiet(accountStatusFileURL && CFURLResourceIsReachable(accountStatusFileURL, NULL), xit); 209 accountStatus = (CFDataRef) CFPropertyListReadFromFile(accountStatusFileURL); 210 211 if(isData(accountStatus)) { 212 size_t size = CFDataGetLength(accountStatus); 213 const uint8_t *der = CFDataGetBytePtr(accountStatus); 214 const uint8_t *der_p = der; 215 216 const uint8_t *sequence_end; 217 der_p = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &sequence_end, der_p, der_p + size); 218 der_p = ccder_decode_bool(&circle_on, der_p, sequence_end); 219 (void) der_p; 220 } 221 222xit: 223 CFReleaseSafe(accountStatusFileURL); 224 CFReleaseSafe(accountStatus); 225 226 return circle_on; 227} 228 229#if 0 230static size_t ccder_sizeof_bool(bool value __unused, CFErrorRef *error) 231{ 232 return ccder_sizeof(CCDER_BOOLEAN, 1); 233} 234 235 236static uint8_t* ccder_encode_bool(bool value, const uint8_t *der, uint8_t *der_end) 237{ 238 uint8_t value_byte = value; 239 240 return ccder_encode_tl(CCDER_BOOLEAN, 1, der, 241 ccder_encode_body(1, &value_byte, der, der_end)); 242} 243#endif 244 245static void SOSCCCircleIsOn_SetArtifact(bool account_on) { 246 static CFDataRef sLastSavedAccountStatus = NULL; 247 CFErrorRef saveError = NULL; 248 size_t der_size = ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, ccder_sizeof_bool(account_on, NULL)); 249 uint8_t der[der_size]; 250 uint8_t *der_end = der + der_size; 251 der_end = ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der, 252 ccder_encode_bool(account_on, der, der_end)); 253 254 CFDataRef accountStatusAsData = CFDataCreate(kCFAllocatorDefault, der_end, der_size); 255 256 require_quiet(accountStatusAsData, exit); 257 if (sLastSavedAccountStatus && CFEqual(sLastSavedAccountStatus, accountStatusAsData)) goto exit; 258 259 CFURLRef accountStatusFileURL = SecCopyURLForFileInKeychainDirectory(accountStatusFileName); 260 CFPropertyListWriteToFile((CFPropertyListRef) accountStatusAsData, accountStatusFileURL); 261 CFReleaseSafe(accountStatusFileURL); 262 263 CFReleaseNull(sLastSavedAccountStatus); 264 sLastSavedAccountStatus = accountStatusAsData; 265 accountStatusAsData = NULL; 266 267exit: 268 CFReleaseNull(saveError); 269 CFReleaseNull(accountStatusAsData); 270} 271 272static void SOSCCCircleIsOn_UpdateArtifact(SOSCCStatus status) 273{ 274 switch (status) { 275 case kSOSCCCircleAbsent: 276 case kSOSCCNotInCircle: 277 SOSCCCircleIsOn_SetArtifact(false); 278 break; 279 case kSOSCCInCircle: 280 case kSOSCCRequestPending: 281 SOSCCCircleIsOn_SetArtifact(true); 282 break; 283 case kSOSCCError: 284 default: 285 // do nothing 286 break; 287 } 288} 289 290static void SOSKeychainAccountEnsureSaved(SOSAccountRef account) 291{ 292 static CFDataRef sLastSavedAccountData = NULL; 293 294 CFErrorRef saveError = NULL; 295 SOSCCCircleIsOn_UpdateArtifact(SOSAccountIsInCircles(account, NULL)); 296 297 CFDataRef accountAsData = SOSAccountCopyEncodedData(account, kCFAllocatorDefault, &saveError); 298 299 require_action_quiet(accountAsData, exit, secerror("Failed to transform account into data, error: %@", saveError)); 300 require_quiet(!CFEqualSafe(sLastSavedAccountData, accountAsData), exit); 301 302 if (!SOSItemUpdateOrAdd(kSOSAccountLabel, kSecAttrAccessibleAlwaysThisDeviceOnly, accountAsData, &saveError)) { 303 secerror("Can't save account: %@", saveError); 304 goto exit; 305 } 306 307 CFReleaseNull(sLastSavedAccountData); 308 sLastSavedAccountData = accountAsData; 309 accountAsData = NULL; 310 311exit: 312 CFReleaseNull(saveError); 313 CFReleaseNull(accountAsData); 314} 315 316 317/* 318 Stolen from keychain_sync.c 319 */ 320 321static bool clearAllKVS(CFErrorRef *error) 322{ 323 return true; 324 __block bool result = false; 325 const uint64_t maxTimeToWaitInSeconds = 30ull * NSEC_PER_SEC; 326 dispatch_queue_t processQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 327 dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0); 328 dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds); 329 330 SOSCloudKeychainClearAll(processQueue, ^(CFDictionaryRef returnedValues, CFErrorRef cerror) 331 { 332 result = (cerror != NULL); 333 dispatch_semaphore_signal(waitSemaphore); 334 }); 335 336 dispatch_semaphore_wait(waitSemaphore, finishTime); 337 dispatch_release(waitSemaphore); 338 339 return result; 340} 341 342static SOSAccountRef SOSKeychainAccountCreateSharedAccount(CFDictionaryRef our_gestalt) 343{ 344 secdebug("account", "Created account"); 345 346 CFDataRef savedAccount = SOSKeychainCopySavedAccountData(); 347 SOSAccountRef account = NULL; 348 SOSDataSourceFactoryRef factory = accountDataSourceOverride ? accountDataSourceOverride() 349 : SecItemDataSourceFactoryGetDefault(); 350 351 if (savedAccount) { 352 CFErrorRef inflationError = NULL; 353 354 account = SOSAccountCreateFromData(kCFAllocatorDefault, savedAccount, factory, &inflationError); 355 356 if (account){ 357 SOSAccountUpdateGestalt(account, our_gestalt); 358 } 359 else 360 secerror("Got error inflating account: %@", inflationError); 361 CFReleaseNull(inflationError); 362 } 363 CFReleaseSafe(savedAccount); 364 365 if (!account) { 366 account = SOSAccountCreate(kCFAllocatorDefault, our_gestalt, factory); 367 368 if (!account) 369 secerror("Got NULL creating account"); 370 } 371 372 return account; 373} 374 375// 376// Mark: Gestalt Handling 377// 378 379static CFStringRef CopyModelName(void) 380{ 381 static dispatch_once_t once; 382 static CFStringRef modelName = NULL; 383 dispatch_once(&once, ^{ 384#if TARGET_OS_EMBEDDED || TARGET_IPHONE_SIMULATOR 385 modelName = MGCopyAnswer(kMGQDeviceName, NULL); 386#else 387 modelName = ASI_CopyComputerModelName(FALSE); 388#endif 389 if (modelName == NULL) 390 modelName = CFSTR("Unknown model"); 391 }); 392 return CFStringCreateCopy(kCFAllocatorDefault, modelName); 393} 394 395static CFStringRef CopyComputerName(SCDynamicStoreRef store) 396{ 397 CFStringRef deviceName = SCDynamicStoreCopyComputerName(store, NULL); 398 if (deviceName == NULL) { 399 deviceName = CFSTR("Unknown name"); 400 } 401 return deviceName; 402} 403 404static bool _EngineMessageProtocolV2Enabled(void) 405{ 406#if DEBUG 407 //sudo rhr 408 static dispatch_once_t onceToken; 409 static bool v2_enabled = false; 410 dispatch_once(&onceToken, ^{ 411 CFTypeRef v2Pref = (CFNumberRef)CFPreferencesCopyValue(CFSTR("engineV2"), CFSTR("com.apple.security"), kCFPreferencesAnyUser, kCFPreferencesCurrentHost); 412 413 if (v2Pref && CFGetTypeID(v2Pref) == CFBooleanGetTypeID()) { 414 v2_enabled = CFBooleanGetValue((CFBooleanRef)v2Pref); 415 secnotice("server", "Engine v2 : %s", v2_enabled ? "enabled":"disabled"); 416 } 417 CFReleaseSafe(v2Pref); 418 }); 419 420 return v2_enabled; 421#else 422 return false; 423#endif 424} 425 426 427static CFDictionaryRef CFDictionaryCreateDeviceGestalt(SCDynamicStoreRef store, CFArrayRef keys, void *context) 428{ 429 CFStringRef modelName = CopyModelName(); 430 CFStringRef computerName = CopyComputerName(store); 431 SInt32 version = _EngineMessageProtocolV2Enabled() ? kEngineMessageProtocolVersion : 0; 432 CFNumberRef protocolVersion = CFNumberCreate(0, kCFNumberSInt32Type, &version); 433 434 435 CFDictionaryRef gestalt = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, 436 kPIUserDefinedDeviceName, computerName, 437 kPIDeviceModelName, modelName, 438 kPIMessageProtocolVersion, protocolVersion, 439 NULL); 440 CFReleaseSafe(modelName); 441 CFReleaseSafe(computerName); 442 CFReleaseSafe(protocolVersion); 443 444 return gestalt; 445} 446 447static void SOSCCProcessGestaltUpdate(SCDynamicStoreRef store, CFArrayRef keys, void *context) 448{ 449 do_with_account(^(SOSAccountRef account) { 450 if(account){ 451 CFDictionaryRef gestalt = CFDictionaryCreateDeviceGestalt(store, keys, context); 452 if (SOSAccountUpdateGestalt(account, gestalt)) { 453 notify_post(kSOSCCCircleChangedNotification); 454 } 455 CFReleaseSafe(gestalt); 456 } 457 }); 458} 459 460 461static CFDictionaryRef CFDictionaryCreateGestaltAndRegisterForUpdate(dispatch_queue_t queue, void *info) 462{ 463 SCDynamicStoreContext context = { .info = info }; 464 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("com.apple.securityd.cloudcircleserver"), SOSCCProcessGestaltUpdate, &context); 465 CFStringRef computerKey = SCDynamicStoreKeyCreateComputerName(NULL); 466 CFArrayRef keys = NULL; 467 CFDictionaryRef gestalt = NULL; 468 469 if (store == NULL || computerKey == NULL) { 470 goto done; 471 } 472 keys = CFArrayCreate(NULL, (const void **)&computerKey, 1, &kCFTypeArrayCallBacks); 473 if (keys == NULL) { 474 goto done; 475 } 476 gestalt = CFDictionaryCreateDeviceGestalt(store, keys, info); 477 SCDynamicStoreSetNotificationKeys(store, keys, NULL); 478 SCDynamicStoreSetDispatchQueue(store, queue); 479 480done: 481 if (store) CFRelease(store); 482 if (computerKey) CFRelease(computerKey); 483 if (keys) CFRelease(keys); 484 return gestalt; 485} 486 487static void do_with_account(void (^action)(SOSAccountRef account)); 488static void do_with_account_async(void (^action)(SOSAccountRef account)); 489 490static SOSAccountRef GetSharedAccount(void) { 491 static SOSAccountRef sSharedAccount = NULL; 492 static dispatch_once_t onceToken; 493 494#if !(TARGET_OS_EMBEDDED) 495 if(geteuid() == 0){ 496 secerror("Cannot inflate account object as root"); 497 return NULL; 498 } 499#endif 500 501 dispatch_once(&onceToken, ^{ 502 secdebug("account", "Account Creation start"); 503 504 CFDictionaryRef gestalt = CFDictionaryCreateGestaltAndRegisterForUpdate(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), NULL); 505 506 if (!gestalt) { 507#if TARGET_OS_IPHONE && TARGET_IPHONE_SIMULATOR 508 gestalt = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, NULL); 509#else 510 secerror("Didn't get machine gestalt! This is going to be ugly."); 511#endif 512 } 513 514 sSharedAccount = SOSKeychainAccountCreateSharedAccount(gestalt); 515 516 SOSCCSetThisDeviceDefinitelyNotActiveInCircle(SOSAccountIsInCircles(sSharedAccount, NULL)); 517 518 SOSAccountAddChangeBlock(sSharedAccount, ^(SOSCircleRef circle, 519 CFSetRef peer_additions, CFSetRef peer_removals, 520 CFSetRef applicant_additions, CFSetRef applicant_removals) { 521 CFErrorRef pi_error = NULL; 522 SOSPeerInfoRef me = SOSAccountGetMyPeerInCircle(sSharedAccount, circle, &pi_error); 523 if (!me) { 524 secerror("Error finding me for change: %@", pi_error); 525 } else { 526 if (SOSCircleHasPeer(circle, me, NULL) && CFSetGetCount(peer_additions) != 0) { 527 secnotice("updates", "Requesting Ensure Peer Registration."); 528 SOSCloudKeychainRequestEnsurePeerRegistration(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), NULL); 529 } 530 531 if (CFSetContainsValue(peer_additions, me)) { 532 SOSCCSyncWithAllPeers(); 533 } 534 } 535 536 CFReleaseNull(pi_error); 537 538 if (CFSetGetCount(peer_additions) != 0 || 539 CFSetGetCount(peer_removals) != 0 || 540 CFSetGetCount(applicant_additions) != 0 || 541 CFSetGetCount(applicant_removals) != 0) { 542 543 SOSCCSetThisDeviceDefinitelyNotActiveInCircle(SOSAccountIsInCircles(sSharedAccount, NULL)); 544 notify_post(kSOSCCCircleChangedNotification); 545 } 546 }); 547 548 SOSCloudKeychainSetItemsChangedBlock(^CFArrayRef(CFDictionaryRef changes) { 549 CFRetainSafe(changes); 550 __block CFMutableArrayRef handledKeys = NULL; 551 do_with_account(^(SOSAccountRef account) { 552 CFStringRef changeDescription = SOSChangesCopyDescription(changes, false); 553 secdebug(SOSCKCSCOPE, "Received: %@", changeDescription); 554 CFReleaseSafe(changeDescription); 555 556 CFErrorRef error = NULL; 557 handledKeys = SOSTransportDispatchMessages(account, changes, &error); 558 if (!handledKeys) { 559 secerror("Error handling updates: %@", error); 560 CFReleaseNull(error); 561 } 562 }); 563 CFReleaseSafe(changes); 564 return handledKeys; 565 }); 566 CFReleaseSafe(gestalt); 567 568 SOSCloudKeychainRequestEnsurePeerRegistration(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), NULL); 569 }); 570 571 572 return sSharedAccount; 573} 574 575static void do_with_account_dynamic(void (^action)(SOSAccountRef account), bool sync) { 576 SOSAccountRef account = GetSharedAccount(); 577 578 if(account){ 579 dispatch_block_t do_action_and_save = ^{ 580 action(account); 581 SOSKeychainAccountEnsureSaved(account); 582 }; 583 584 if (sync) { 585 dispatch_sync(SOSAccountGetQueue(account), do_action_and_save); 586 } else { 587 dispatch_async(SOSAccountGetQueue(account), do_action_and_save); 588 } 589 } 590} 591 592__unused static void do_with_account_async(void (^action)(SOSAccountRef account)) { 593 do_with_account_dynamic(action, false); 594} 595 596static void do_with_account(void (^action)(SOSAccountRef account)) { 597 do_with_account_dynamic(action, true); 598} 599 600#if TARGET_IPHONE_SIMULATOR 601#define MKBDeviceUnlockedSinceBoot() true 602#endif 603 604static bool do_if_after_first_unlock(CFErrorRef *error, dispatch_block_t action) 605{ 606 bool beenUnlocked = false; 607 require_quiet(SecAKSGetHasBeenUnlocked(&beenUnlocked, error), fail); 608 609 require_action_quiet(beenUnlocked, fail, 610 SOSCreateErrorWithFormat(kSOSErrorNotReady, NULL, error, NULL, 611 CFSTR("Keybag never unlocked, ask after first unlock"))); 612 613 action(); 614 return true; 615 616fail: 617 return false; 618} 619 620static bool do_with_account_if_after_first_unlock(CFErrorRef *error, bool (^action)(SOSAccountRef account, CFErrorRef* error)) 621{ 622 __block bool action_result = false; 623 624#if !(TARGET_OS_EMBEDDED) 625 if(geteuid() == 0){ 626 secerror("Cannot inflate account object as root"); 627 return false; 628 } 629#endif 630 return do_if_after_first_unlock(error, ^{ 631 do_with_account(^(SOSAccountRef account) { 632 action_result = action(account, error); 633 }); 634 635 }) && action_result; 636} 637 638static bool do_with_account_while_unlocked(CFErrorRef *error, bool (^action)(SOSAccountRef account, CFErrorRef* error)) 639{ 640 __block bool action_result = false; 641 642#if !(TARGET_OS_EMBEDDED) 643 if(geteuid() == 0){ 644 secerror("Cannot inflate account object as root"); 645 return false; 646 } 647#endif 648 649 return SecAKSDoWhileUserBagLocked(error, ^{ 650 do_with_account(^(SOSAccountRef account) { 651 action_result = action(account, error); 652 }); 653 654 }) && action_result; 655} 656 657SOSAccountRef SOSKeychainAccountGetSharedAccount() 658{ 659 __block SOSAccountRef result = NULL; 660 661 do_with_account(^(SOSAccountRef account) { 662 result = account; 663 }); 664 665 return result; 666} 667 668// 669// Mark: Credential processing 670// 671 672 673bool SOSCCTryUserCredentials_Server(CFStringRef user_label, CFDataRef user_password, CFErrorRef *error) 674{ 675 return do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { 676 return SOSAccountTryUserCredentials(account, user_label, user_password, block_error); 677 }); 678} 679 680#define kWAIT2MINID "EFRESH" 681 682static bool EnsureFreshParameters(SOSAccountRef account, CFErrorRef *error) { 683 dispatch_semaphore_t wait_for = dispatch_semaphore_create(0); 684 dispatch_retain(wait_for); // Both this scope and the block own it. 685 686 CFMutableArrayRef keysToGet = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); 687 CFArrayAppendValue(keysToGet, kSOSKVSKeyParametersKey); 688 689 __block CFDictionaryRef valuesToUpdate = NULL; 690 __block bool success = false; 691 692 secnoticeq("fresh", "%s calling SOSCloudKeychainSynchronizeAndWait", kWAIT2MINID); 693 694 SOSCloudKeychainSynchronizeAndWait(keysToGet, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFDictionaryRef returnedValues, CFErrorRef sync_error) { 695 696 if (sync_error) { 697 secerrorq("%s SOSCloudKeychainSynchronizeAndWait: %@", kWAIT2MINID, sync_error); 698 if (error) { 699 *error = sync_error; 700 CFRetainSafe(*error); 701 } 702 } else { 703 secnoticeq("fresh", "%s returned from call; in callback to SOSCloudKeychainSynchronizeAndWait: results: %@", kWAIT2MINID, returnedValues); 704 valuesToUpdate = returnedValues; 705 CFRetainSafe(valuesToUpdate); 706 success = true; 707 } 708 709 dispatch_semaphore_signal(wait_for); 710 dispatch_release(wait_for); 711 }); 712 713 dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER); 714 // TODO: Maybe we timeout here... used to dispatch_time(DISPATCH_TIME_NOW, 30ull * NSEC_PER_SEC)); 715 dispatch_release(wait_for); 716 CFMutableArrayRef handledKeys = NULL; 717 if ((valuesToUpdate) && (account)) { 718 handledKeys = SOSTransportDispatchMessages(account, valuesToUpdate, error); 719 if (!handledKeys) { 720 secerrorq("%s Freshness update failed: %@", kWAIT2MINID, error ? *error : NULL); 721 success = false; 722 } 723 } 724 CFReleaseNull(handledKeys); 725 CFReleaseNull(valuesToUpdate); 726 CFReleaseNull(keysToGet); 727 728 return success; 729} 730 731static bool Flush(CFErrorRef *error) { 732 __block bool success = false; 733 734 dispatch_semaphore_t wait_for = dispatch_semaphore_create(0); 735 dispatch_retain(wait_for); // Both this scope and the block own it. 736 737 secnotice("flush", "Starting"); 738 739 SOSCloudKeychainFlush(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFDictionaryRef returnedValues, CFErrorRef sync_error) { 740 success = (sync_error == NULL); 741 if (error) { 742 CFRetainAssign(*error, sync_error); 743 } 744 745 dispatch_semaphore_signal(wait_for); 746 dispatch_release(wait_for); 747 }); 748 749 dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER); 750 dispatch_release(wait_for); 751 752 secnotice("flush", "Returned %s", success? "Success": "Failure"); 753 754 return success; 755} 756 757bool SOSCCSetUserCredentials_Server(CFStringRef user_label, CFDataRef user_password, CFErrorRef *error) 758{ 759 secnotice("updates", "Setting credentials for %@", user_label); // TODO: remove this notice 760 bool result = do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { 761 if (!EnsureFreshParameters(account, block_error)) { 762 return false; 763 } 764 if (!SOSAccountAssertUserCredentials(account, user_label, user_password, block_error)) { 765 secnotice("updates", "EnsureFreshParameters/SOSAccountAssertUserCredentials error: %@", *block_error); 766 return false; 767 } 768 return true; 769 }); 770 771 return result && Flush(error); 772} 773 774bool SOSCCCanAuthenticate_Server(CFErrorRef *error) 775{ 776 bool result = do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { 777 return SOSAccountGetPrivateCredential(account, block_error) != NULL; 778 }); 779 780 if (!result && error && *error && CFErrorGetDomain(*error) == kSOSErrorDomain) { 781 CFIndex code = CFErrorGetCode(*error); 782 if (code == kSOSErrorPrivateKeyAbsent || code == kSOSErrorPublicKeyAbsent) { 783 CFReleaseNull(*error); 784 } 785 } 786 787 return result; 788} 789 790bool SOSCCPurgeUserCredentials_Server(CFErrorRef *error) 791{ 792 return do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { 793 SOSAccountPurgePrivateCredential(account); 794 return true; 795 }); 796} 797 798 799#if USE_BETTER 800static bool sAccountInCircleCache = false; 801 802static void do_with_not_in_circle_bool_queue(bool start_account, dispatch_block_t action) 803{ 804 static dispatch_queue_t account_start_queue; 805 static dispatch_queue_t not_in_circle_queue; 806 static bool account_started = false; 807 808 static dispatch_once_t onceToken; 809 dispatch_once(&onceToken, ^{ 810 not_in_circle_queue = dispatch_queue_create("nis queue", DISPATCH_QUEUE_SERIAL); 811 account_start_queue = dispatch_queue_create("init nis queue", DISPATCH_QUEUE_SERIAL);; 812 account_started = false; 813 }); 814 815 __block bool done = false; 816 dispatch_sync(not_in_circle_queue, ^{ 817 if (account_started) { 818 done = true; 819 action(); 820 } 821 }); 822 823 if (!done && start_account) { 824 dispatch_sync(account_start_queue, ^{ 825 __block bool do_start = false; 826 dispatch_sync(not_in_circle_queue, ^{ 827 do_start = !account_started; 828 account_started = true; 829 }); 830 if (do_start) 831 SOSCCThisDeviceIsInCircle(NULL); // Inflate account. 832 }); 833 834 dispatch_sync(not_in_circle_queue, action); 835 } 836} 837#endif 838 839bool SOSCCThisDeviceDefinitelyNotActiveInCircle() 840{ 841 return !SOSCCCircleIsOn_Artifact(); 842#if USE_BETTER 843 __block bool result = false; 844 do_with_not_in_circle_bool_queue(true, ^{ 845 result = sAccountInCircleCache; 846 }); 847 848 return result; 849#endif 850} 851 852void SOSCCSetThisDeviceDefinitelyNotActiveInCircle(SOSCCStatus currentStatus) 853{ 854 SOSCCCircleIsOn_UpdateArtifact(currentStatus); 855#if USE_BETTER 856 do_with_not_in_circle_bool_queue(false, ^{ 857 sAccountInCircleCache = notActive; 858 }); 859#endif 860} 861 862 863SOSCCStatus SOSCCThisDeviceIsInCircle_Server(CFErrorRef *error) 864{ 865 __block SOSCCStatus status; 866 867 return do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { 868 status = SOSAccountIsInCircles(account, block_error); 869 return true; 870 }) ? status : kSOSCCError; 871} 872 873bool SOSCCRequestToJoinCircle_Server(CFErrorRef* error) 874{ 875 __block bool result = true; 876 877 return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { 878 result = SOSAccountJoinCircles(account, block_error); 879 return result; 880 }); 881} 882 883bool SOSCCRequestToJoinCircleAfterRestore_Server(CFErrorRef* error) 884{ 885 __block bool result = true; 886 bool returned = false; 887 returned = do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { 888 SOSAccountEnsurePeerRegistration(account, block_error); 889 result = SOSAccountJoinCirclesAfterRestore(account, block_error); 890 return result; 891 }); 892 return returned; 893 894} 895 896bool SOSCCRequestEnsureFreshParameters_Server(CFErrorRef* error) 897{ 898 __block bool result = true; 899 bool returned = false; 900 returned = do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { 901 result = EnsureFreshParameters(account, NULL); 902 return result; 903 }); 904 return returned; 905} 906 907CFStringRef SOSCCRequestDeviceID_Server(CFErrorRef *error) 908{ 909 __block CFStringRef result = NULL; 910 911 (void) do_with_account_while_unlocked(error, ^bool(SOSAccountRef account, CFErrorRef *error) { 912 result = SOSAccountGetDeviceID(account, error); 913 return (!isNull(result)); 914 }); 915 return result; 916} 917 918bool SOSCCSetDeviceID_Server(CFStringRef IDS, CFErrorRef *error){ 919 __block bool result = true; 920 921 return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { 922 result = SOSAccountSetMyDSID(account, IDS, block_error); 923 return result; 924 }); 925 926} 927bool SOSCCResetToOffering_Server(CFErrorRef* error) 928{ 929 __block bool result = true; 930 931 return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { 932 clearAllKVS(NULL); 933 result = SOSAccountResetToOffering(account, block_error); 934 return result; 935 }); 936 937} 938 939bool SOSCCResetToEmpty_Server(CFErrorRef* error) 940{ 941 __block bool result = true; 942 943 return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { 944 result = SOSAccountResetToEmpty(account, block_error); 945 return result; 946 }); 947 948} 949 950bool SOSCCRemoveThisDeviceFromCircle_Server(CFErrorRef* error) 951{ 952 __block bool result = true; 953 954 return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { 955 result = SOSAccountLeaveCircles(account, block_error); 956 return result; 957 }); 958} 959 960bool SOSCCBailFromCircle_Server(uint64_t limit_in_seconds, CFErrorRef* error) 961{ 962 __block bool result = true; 963 964 return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { 965 result = SOSAccountBail(account, limit_in_seconds, block_error); 966 return result; 967 }); 968 969} 970 971CFArrayRef SOSCCCopyApplicantPeerInfo_Server(CFErrorRef* error) 972{ 973 __block CFArrayRef result = NULL; 974 975 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { 976 result = SOSAccountCopyApplicants(account, block_error); 977 return result != NULL; 978 }); 979 980 return result; 981} 982 983CFArrayRef SOSCCCopyGenerationPeerInfo_Server(CFErrorRef* error) 984{ 985 __block CFArrayRef result = NULL; 986 987 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { 988 result = SOSAccountCopyGeneration(account, block_error); 989 return result != NULL; 990 }); 991 992 return result; 993} 994 995CFArrayRef SOSCCCopyValidPeerPeerInfo_Server(CFErrorRef* error) 996{ 997 __block CFArrayRef result = NULL; 998 999 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { 1000 result = SOSAccountCopyValidPeers(account, block_error); 1001 return result != NULL; 1002 }); 1003 1004 return result; 1005} 1006 1007bool SOSCCValidateUserPublic_Server(CFErrorRef* error) 1008{ 1009 __block bool result = NULL; 1010 1011 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { 1012 result = SOSValidateUserPublic(account, block_error); 1013 return result; 1014 }); 1015 1016 return result; 1017} 1018 1019CFArrayRef SOSCCCopyNotValidPeerPeerInfo_Server(CFErrorRef* error) 1020{ 1021 __block CFArrayRef result = NULL; 1022 1023 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { 1024 result = SOSAccountCopyNotValidPeers(account, block_error); 1025 return result != NULL; 1026 }); 1027 1028 return result; 1029} 1030 1031CFArrayRef SOSCCCopyRetirementPeerInfo_Server(CFErrorRef* error) 1032{ 1033 __block CFArrayRef result = NULL; 1034 1035 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { 1036 result = SOSAccountCopyRetired(account, block_error); 1037 return result != NULL; 1038 }); 1039 1040 return result; 1041} 1042 1043bool SOSCCAcceptApplicants_Server(CFArrayRef applicants, CFErrorRef* error) 1044{ 1045 __block bool result = true; 1046 return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { 1047 result = SOSAccountAcceptApplicants(account, applicants, block_error); 1048 return result; 1049 }); 1050 1051} 1052 1053bool SOSCCRejectApplicants_Server(CFArrayRef applicants, CFErrorRef* error) 1054{ 1055 __block bool result = true; 1056 return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { 1057 result = SOSAccountRejectApplicants(account, applicants, block_error); 1058 return result; 1059 }); 1060} 1061 1062CFArrayRef SOSCCCopyPeerPeerInfo_Server(CFErrorRef* error) 1063{ 1064 __block CFArrayRef result = NULL; 1065 1066 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { 1067 result = SOSAccountCopyPeers(account, block_error); 1068 return result != NULL; 1069 }); 1070 1071 return result; 1072} 1073 1074CFArrayRef SOSCCCopyConcurringPeerPeerInfo_Server(CFErrorRef* error) 1075{ 1076 __block CFArrayRef result = NULL; 1077 1078 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { 1079 result = SOSAccountCopyConcurringPeers(account, block_error); 1080 return result != NULL; 1081 }); 1082 1083 return result; 1084} 1085 1086CFStringRef SOSCCCopyIncompatibilityInfo_Server(CFErrorRef* error) 1087{ 1088 __block CFStringRef result = NULL; 1089 1090 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { 1091 result = SOSAccountCopyIncompatibilityInfo(account, block_error); 1092 return result != NULL; 1093 }); 1094 1095 return result; 1096} 1097 1098enum DepartureReason SOSCCGetLastDepartureReason_Server(CFErrorRef* error) 1099{ 1100 __block enum DepartureReason result = kSOSDepartureReasonError; 1101 1102 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { 1103 result = SOSAccountGetLastDepartureReason(account, block_error); 1104 return result != kSOSDepartureReasonError; 1105 }); 1106 1107 return result; 1108} 1109 1110 1111bool SOSCCProcessEnsurePeerRegistration_Server(CFErrorRef* error) 1112{ 1113 secnotice("updates", "Request for registering peers"); 1114 return do_with_account_while_unlocked(error, ^bool(SOSAccountRef account, CFErrorRef *error) { 1115 return SOSAccountEnsurePeerRegistration(account, error); 1116 }); 1117} 1118 1119SyncWithAllPeersReason SOSCCProcessSyncWithAllPeers_Server(CFErrorRef* error) 1120{ 1121 /* 1122 #define kIOReturnLockedRead iokit_common_err(0x2c3) // device read locked 1123 #define kIOReturnLockedWrite iokit_common_err(0x2c4) // device write locked 1124 */ 1125 __block SyncWithAllPeersReason result = kSyncWithAllPeersSuccess; 1126 CFErrorRef action_error = NULL; 1127 1128 if (!do_with_account_while_unlocked(&action_error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { 1129 CFErrorRef localError = NULL; 1130 if (!SOSAccountSyncWithAllPeers(account, &localError)) { 1131 secerror("sync with all peers failed: %@", localError); 1132 CFReleaseSafe(localError); 1133 // This isn't a device-locked error, but returning false will 1134 // have CloudKeychainProxy ask us to try sync again after next unlock 1135 result = kSyncWithAllPeersOtherFail; 1136 return false; 1137 } 1138 return true; 1139 })) { 1140 if (action_error) { 1141 if (SecErrorGetOSStatus(action_error) == errSecInteractionNotAllowed) { 1142 secnotice("updates", "SOSAccountSyncWithAllPeers failed because device is locked; letting CloudKeychainProxy know"); 1143 result = kSyncWithAllPeersLocked; // tell CloudKeychainProxy to call us back when device unlocks 1144 CFReleaseNull(action_error); 1145 } else { 1146 secerror("Unexpected error: %@", action_error); 1147 } 1148 1149 if (error && *error == NULL) { 1150 *error = action_error; 1151 action_error = NULL; 1152 } 1153 1154 CFReleaseNull(action_error); 1155 } 1156 } 1157 1158 return result; 1159} 1160 1161void SOSCCSyncWithAllPeers(void) 1162{ 1163 SOSCloudKeychainRequestSyncWithAllPeers(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), NULL); 1164} 1165 1166CF_RETURNS_RETAINED CFArrayRef SOSCCHandleUpdateKeyParameter(CFDictionaryRef updates) 1167{ 1168 CFArrayRef result = NULL; 1169 SOSAccountRef account = SOSKeychainAccountGetSharedAccount(); //HACK to make sure itemsChangedBlock is set 1170 (account) ? (result = SOSCloudKeychainHandleUpdateKeyParameter(updates)) : (result = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault)); 1171 return result; 1172} 1173 1174CF_RETURNS_RETAINED CFArrayRef SOSCCHandleUpdateCircle(CFDictionaryRef updates) 1175{ 1176 CFArrayRef result = NULL; 1177 SOSAccountRef account = SOSKeychainAccountGetSharedAccount(); //HACK to make sure itemsChangedBlock is set 1178 (account) ? (result = SOSCloudKeychainHandleUpdateKeyParameter(updates)) : (result = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault)); 1179 return result; 1180} 1181 1182CF_RETURNS_RETAINED CFArrayRef SOSCCHandleUpdateMessage(CFDictionaryRef updates) 1183{ 1184 CFArrayRef result = NULL; 1185 SOSAccountRef account = SOSKeychainAccountGetSharedAccount(); //HACK to make sure itemsChangedBlock is set 1186 (account) ? (result = SOSCloudKeychainHandleUpdateKeyParameter(updates)) : (result = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault)); 1187 return result; 1188} 1189 1190 1191