/* * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if TARGET_OS_EMBEDDED || TARGET_IPHONE_SIMULATOR #include #else #include // We need authorization, but that doesn't exist // on sec built for desktop (iOS in a process) // Define AuthorizationRef here to make SystemConfiguration work // as if it's on iOS. typedef const struct AuthorizationOpaqueRef * AuthorizationRef; #endif #define SOSCKCSCOPE "sync" #define USE_SYSTEMCONFIGURATION_PRIVATE_HEADERS #import #include static SOSCCAccountDataSourceFactoryBlock accountDataSourceOverride = NULL; bool SOSKeychainAccountSetFactoryForAccount(SOSCCAccountDataSourceFactoryBlock block) { accountDataSourceOverride = Block_copy(block); return true; } // // Forward declared // static void do_with_account(void (^action)(SOSAccountRef account)); static void do_with_account_async(void (^action)(SOSAccountRef account)); // // Constants // CFStringRef kSOSInternalAccessGroup = CFSTR("com.apple.security.sos"); CFStringRef kSOSAccountLabel = CFSTR("iCloud Keychain Account Meta-data"); static CFStringRef accountFileName = CFSTR("PersistedAccount.plist"); static CFDictionaryRef SOSItemCopyQueryForSyncItems(CFStringRef service, bool returnData) { return CFDictionaryCreateForCFTypes(kCFAllocatorDefault, kSecClass, kSecClassGenericPassword, kSecAttrService, service, kSecAttrAccessGroup, kSOSInternalAccessGroup, kSecReturnData, returnData ? kCFBooleanTrue : kCFBooleanFalse, NULL); } CFDataRef SOSItemCopy(CFStringRef service, CFErrorRef* error) { CFDictionaryRef query = SOSItemCopyQueryForSyncItems(service, true); CFDataRef result = NULL; OSStatus copyResult = SecItemCopyMatching(query, (CFTypeRef*) &result); CFReleaseNull(query); if (copyResult != noErr) { SecError(copyResult, error, CFSTR("Error %@ reading for service '%@'"), result, service); CFReleaseNull(result); return NULL; } if (!isData(result)) { SOSCreateErrorWithFormat(kSOSErrorProcessingFailure, NULL, error, NULL, CFSTR("SecItemCopyMatching returned non-data in '%@'"), service); CFReleaseNull(result); return NULL; } return result; } static CFDataRef SOSKeychainCopySavedAccountData() { CFErrorRef error = NULL; CFDataRef accountData = SOSItemCopy(kSOSAccountLabel, &error); if (!accountData) secnotice("account", "Failed to load account: %@", error); CFReleaseNull(error); return accountData; } bool SOSItemUpdateOrAdd(CFStringRef service, CFStringRef accessibility, CFDataRef data, CFErrorRef *error) { CFDictionaryRef query = SOSItemCopyQueryForSyncItems(service, false); CFDictionaryRef update = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, kSecValueData, data, kSecAttrAccessible, accessibility, NULL); OSStatus saveStatus = SecItemUpdate(query, update); if (errSecItemNotFound == saveStatus) { CFMutableDictionaryRef add = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, query); CFDictionaryForEach(update, ^(const void *key, const void *value) { CFDictionaryAddValue(add, key, value); }); saveStatus = SecItemAdd(add, NULL); CFReleaseNull(add); } CFReleaseNull(query); CFReleaseNull(update); return SecError(saveStatus, error, CFSTR("Error saving %@ to service '%@'"), data, service); } static CFStringRef accountStatusFileName = CFSTR("accountStatus.plist"); #include #include #include #if 0 static const uint8_t* ccder_decode_bool(bool* boolean, const uint8_t* der, const uint8_t *der_end) { if (NULL == der) return NULL; size_t payload_size = 0; const uint8_t *payload = ccder_decode_tl(CCDER_BOOLEAN, &payload_size, der, der_end); if (NULL == payload || (der_end - payload) < 1 || payload_size != 1) { return NULL; } if (boolean) *boolean = (*payload != 0); return payload + payload_size; } #endif bool SOSCCCircleIsOn_Artifact(void) { bool circle_on = false; CFDataRef accountStatus = NULL; CFURLRef accountStatusFileURL = SecCopyURLForFileInKeychainDirectory(accountStatusFileName); require_quiet(accountStatusFileURL && CFURLResourceIsReachable(accountStatusFileURL, NULL), xit); accountStatus = (CFDataRef) CFPropertyListReadFromFile(accountStatusFileURL); if(isData(accountStatus)) { size_t size = CFDataGetLength(accountStatus); const uint8_t *der = CFDataGetBytePtr(accountStatus); const uint8_t *der_p = der; const uint8_t *sequence_end; der_p = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &sequence_end, der_p, der_p + size); der_p = ccder_decode_bool(&circle_on, der_p, sequence_end); (void) der_p; } xit: CFReleaseSafe(accountStatusFileURL); CFReleaseSafe(accountStatus); return circle_on; } #if 0 static size_t ccder_sizeof_bool(bool value __unused, CFErrorRef *error) { return ccder_sizeof(CCDER_BOOLEAN, 1); } static uint8_t* ccder_encode_bool(bool value, const uint8_t *der, uint8_t *der_end) { uint8_t value_byte = value; return ccder_encode_tl(CCDER_BOOLEAN, 1, der, ccder_encode_body(1, &value_byte, der, der_end)); } #endif static void SOSCCCircleIsOn_SetArtifact(bool account_on) { static CFDataRef sLastSavedAccountStatus = NULL; CFErrorRef saveError = NULL; size_t der_size = ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, ccder_sizeof_bool(account_on, NULL)); uint8_t der[der_size]; uint8_t *der_end = der + der_size; der_end = ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der, ccder_encode_bool(account_on, der, der_end)); CFDataRef accountStatusAsData = CFDataCreate(kCFAllocatorDefault, der_end, der_size); require_quiet(accountStatusAsData, exit); if (sLastSavedAccountStatus && CFEqual(sLastSavedAccountStatus, accountStatusAsData)) goto exit; CFURLRef accountStatusFileURL = SecCopyURLForFileInKeychainDirectory(accountStatusFileName); CFPropertyListWriteToFile((CFPropertyListRef) accountStatusAsData, accountStatusFileURL); CFReleaseSafe(accountStatusFileURL); CFReleaseNull(sLastSavedAccountStatus); sLastSavedAccountStatus = accountStatusAsData; accountStatusAsData = NULL; exit: CFReleaseNull(saveError); CFReleaseNull(accountStatusAsData); } static void SOSCCCircleIsOn_UpdateArtifact(SOSCCStatus status) { switch (status) { case kSOSCCCircleAbsent: case kSOSCCNotInCircle: SOSCCCircleIsOn_SetArtifact(false); break; case kSOSCCInCircle: case kSOSCCRequestPending: SOSCCCircleIsOn_SetArtifact(true); break; case kSOSCCError: default: // do nothing break; } } static void SOSKeychainAccountEnsureSaved(SOSAccountRef account) { static CFDataRef sLastSavedAccountData = NULL; CFErrorRef saveError = NULL; SOSCCCircleIsOn_UpdateArtifact(SOSAccountIsInCircles(account, NULL)); CFDataRef accountAsData = SOSAccountCopyEncodedData(account, kCFAllocatorDefault, &saveError); require_action_quiet(accountAsData, exit, secerror("Failed to transform account into data, error: %@", saveError)); require_quiet(!CFEqualSafe(sLastSavedAccountData, accountAsData), exit); if (!SOSItemUpdateOrAdd(kSOSAccountLabel, kSecAttrAccessibleAlwaysThisDeviceOnly, accountAsData, &saveError)) { secerror("Can't save account: %@", saveError); goto exit; } CFReleaseNull(sLastSavedAccountData); sLastSavedAccountData = accountAsData; accountAsData = NULL; exit: CFReleaseNull(saveError); CFReleaseNull(accountAsData); } /* Stolen from keychain_sync.c */ static bool clearAllKVS(CFErrorRef *error) { return true; __block bool result = false; const uint64_t maxTimeToWaitInSeconds = 30ull * NSEC_PER_SEC; dispatch_queue_t processQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0); dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds); SOSCloudKeychainClearAll(processQueue, ^(CFDictionaryRef returnedValues, CFErrorRef cerror) { result = (cerror != NULL); dispatch_semaphore_signal(waitSemaphore); }); dispatch_semaphore_wait(waitSemaphore, finishTime); dispatch_release(waitSemaphore); return result; } static SOSAccountRef SOSKeychainAccountCreateSharedAccount(CFDictionaryRef our_gestalt) { secdebug("account", "Created account"); CFDataRef savedAccount = SOSKeychainCopySavedAccountData(); SOSAccountRef account = NULL; SOSDataSourceFactoryRef factory = accountDataSourceOverride ? accountDataSourceOverride() : SecItemDataSourceFactoryGetDefault(); if (savedAccount) { CFErrorRef inflationError = NULL; account = SOSAccountCreateFromData(kCFAllocatorDefault, savedAccount, factory, &inflationError); if (account){ SOSAccountUpdateGestalt(account, our_gestalt); } else secerror("Got error inflating account: %@", inflationError); CFReleaseNull(inflationError); } CFReleaseSafe(savedAccount); if (!account) { account = SOSAccountCreate(kCFAllocatorDefault, our_gestalt, factory); if (!account) secerror("Got NULL creating account"); } return account; } // // Mark: Gestalt Handling // static CFStringRef CopyModelName(void) { static dispatch_once_t once; static CFStringRef modelName = NULL; dispatch_once(&once, ^{ #if TARGET_OS_EMBEDDED || TARGET_IPHONE_SIMULATOR modelName = MGCopyAnswer(kMGQDeviceName, NULL); #else modelName = ASI_CopyComputerModelName(FALSE); #endif if (modelName == NULL) modelName = CFSTR("Unknown model"); }); return CFStringCreateCopy(kCFAllocatorDefault, modelName); } static CFStringRef CopyComputerName(SCDynamicStoreRef store) { CFStringRef deviceName = SCDynamicStoreCopyComputerName(store, NULL); if (deviceName == NULL) { deviceName = CFSTR("Unknown name"); } return deviceName; } static bool _EngineMessageProtocolV2Enabled(void) { #if DEBUG //sudo rhr static dispatch_once_t onceToken; static bool v2_enabled = false; dispatch_once(&onceToken, ^{ CFTypeRef v2Pref = (CFNumberRef)CFPreferencesCopyValue(CFSTR("engineV2"), CFSTR("com.apple.security"), kCFPreferencesAnyUser, kCFPreferencesCurrentHost); if (v2Pref && CFGetTypeID(v2Pref) == CFBooleanGetTypeID()) { v2_enabled = CFBooleanGetValue((CFBooleanRef)v2Pref); secnotice("server", "Engine v2 : %s", v2_enabled ? "enabled":"disabled"); } CFReleaseSafe(v2Pref); }); return v2_enabled; #else return false; #endif } static CFDictionaryRef CFDictionaryCreateDeviceGestalt(SCDynamicStoreRef store, CFArrayRef keys, void *context) { CFStringRef modelName = CopyModelName(); CFStringRef computerName = CopyComputerName(store); SInt32 version = _EngineMessageProtocolV2Enabled() ? kEngineMessageProtocolVersion : 0; CFNumberRef protocolVersion = CFNumberCreate(0, kCFNumberSInt32Type, &version); CFDictionaryRef gestalt = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, kPIUserDefinedDeviceName, computerName, kPIDeviceModelName, modelName, kPIMessageProtocolVersion, protocolVersion, NULL); CFReleaseSafe(modelName); CFReleaseSafe(computerName); CFReleaseSafe(protocolVersion); return gestalt; } static void SOSCCProcessGestaltUpdate(SCDynamicStoreRef store, CFArrayRef keys, void *context) { do_with_account(^(SOSAccountRef account) { if(account){ CFDictionaryRef gestalt = CFDictionaryCreateDeviceGestalt(store, keys, context); if (SOSAccountUpdateGestalt(account, gestalt)) { notify_post(kSOSCCCircleChangedNotification); } CFReleaseSafe(gestalt); } }); } static CFDictionaryRef CFDictionaryCreateGestaltAndRegisterForUpdate(dispatch_queue_t queue, void *info) { SCDynamicStoreContext context = { .info = info }; SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("com.apple.securityd.cloudcircleserver"), SOSCCProcessGestaltUpdate, &context); CFStringRef computerKey = SCDynamicStoreKeyCreateComputerName(NULL); CFArrayRef keys = NULL; CFDictionaryRef gestalt = NULL; if (store == NULL || computerKey == NULL) { goto done; } keys = CFArrayCreate(NULL, (const void **)&computerKey, 1, &kCFTypeArrayCallBacks); if (keys == NULL) { goto done; } gestalt = CFDictionaryCreateDeviceGestalt(store, keys, info); SCDynamicStoreSetNotificationKeys(store, keys, NULL); SCDynamicStoreSetDispatchQueue(store, queue); done: if (store) CFRelease(store); if (computerKey) CFRelease(computerKey); if (keys) CFRelease(keys); return gestalt; } static void do_with_account(void (^action)(SOSAccountRef account)); static void do_with_account_async(void (^action)(SOSAccountRef account)); static SOSAccountRef GetSharedAccount(void) { static SOSAccountRef sSharedAccount = NULL; static dispatch_once_t onceToken; #if !(TARGET_OS_EMBEDDED) if(geteuid() == 0){ secerror("Cannot inflate account object as root"); return NULL; } #endif dispatch_once(&onceToken, ^{ secdebug("account", "Account Creation start"); CFDictionaryRef gestalt = CFDictionaryCreateGestaltAndRegisterForUpdate(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), NULL); if (!gestalt) { #if TARGET_OS_IPHONE && TARGET_IPHONE_SIMULATOR gestalt = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, NULL); #else secerror("Didn't get machine gestalt! This is going to be ugly."); #endif } sSharedAccount = SOSKeychainAccountCreateSharedAccount(gestalt); SOSCCSetThisDeviceDefinitelyNotActiveInCircle(SOSAccountIsInCircles(sSharedAccount, NULL)); SOSAccountAddChangeBlock(sSharedAccount, ^(SOSCircleRef circle, CFSetRef peer_additions, CFSetRef peer_removals, CFSetRef applicant_additions, CFSetRef applicant_removals) { CFErrorRef pi_error = NULL; SOSPeerInfoRef me = SOSAccountGetMyPeerInCircle(sSharedAccount, circle, &pi_error); if (!me) { secerror("Error finding me for change: %@", pi_error); } else { if (SOSCircleHasPeer(circle, me, NULL) && CFSetGetCount(peer_additions) != 0) { secnotice("updates", "Requesting Ensure Peer Registration."); SOSCloudKeychainRequestEnsurePeerRegistration(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), NULL); } if (CFSetContainsValue(peer_additions, me)) { SOSCCSyncWithAllPeers(); } } CFReleaseNull(pi_error); if (CFSetGetCount(peer_additions) != 0 || CFSetGetCount(peer_removals) != 0 || CFSetGetCount(applicant_additions) != 0 || CFSetGetCount(applicant_removals) != 0) { SOSCCSetThisDeviceDefinitelyNotActiveInCircle(SOSAccountIsInCircles(sSharedAccount, NULL)); notify_post(kSOSCCCircleChangedNotification); } }); SOSCloudKeychainSetItemsChangedBlock(^CFArrayRef(CFDictionaryRef changes) { CFRetainSafe(changes); __block CFMutableArrayRef handledKeys = NULL; do_with_account(^(SOSAccountRef account) { CFStringRef changeDescription = SOSChangesCopyDescription(changes, false); secdebug(SOSCKCSCOPE, "Received: %@", changeDescription); CFReleaseSafe(changeDescription); CFErrorRef error = NULL; handledKeys = SOSTransportDispatchMessages(account, changes, &error); if (!handledKeys) { secerror("Error handling updates: %@", error); CFReleaseNull(error); } }); CFReleaseSafe(changes); return handledKeys; }); CFReleaseSafe(gestalt); SOSCloudKeychainRequestEnsurePeerRegistration(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), NULL); }); return sSharedAccount; } static void do_with_account_dynamic(void (^action)(SOSAccountRef account), bool sync) { SOSAccountRef account = GetSharedAccount(); if(account){ dispatch_block_t do_action_and_save = ^{ action(account); SOSKeychainAccountEnsureSaved(account); }; if (sync) { dispatch_sync(SOSAccountGetQueue(account), do_action_and_save); } else { dispatch_async(SOSAccountGetQueue(account), do_action_and_save); } } } __unused static void do_with_account_async(void (^action)(SOSAccountRef account)) { do_with_account_dynamic(action, false); } static void do_with_account(void (^action)(SOSAccountRef account)) { do_with_account_dynamic(action, true); } #if TARGET_IPHONE_SIMULATOR #define MKBDeviceUnlockedSinceBoot() true #endif static bool do_if_after_first_unlock(CFErrorRef *error, dispatch_block_t action) { bool beenUnlocked = false; require_quiet(SecAKSGetHasBeenUnlocked(&beenUnlocked, error), fail); require_action_quiet(beenUnlocked, fail, SOSCreateErrorWithFormat(kSOSErrorNotReady, NULL, error, NULL, CFSTR("Keybag never unlocked, ask after first unlock"))); action(); return true; fail: return false; } static bool do_with_account_if_after_first_unlock(CFErrorRef *error, bool (^action)(SOSAccountRef account, CFErrorRef* error)) { __block bool action_result = false; #if !(TARGET_OS_EMBEDDED) if(geteuid() == 0){ secerror("Cannot inflate account object as root"); return false; } #endif return do_if_after_first_unlock(error, ^{ do_with_account(^(SOSAccountRef account) { action_result = action(account, error); }); }) && action_result; } static bool do_with_account_while_unlocked(CFErrorRef *error, bool (^action)(SOSAccountRef account, CFErrorRef* error)) { __block bool action_result = false; #if !(TARGET_OS_EMBEDDED) if(geteuid() == 0){ secerror("Cannot inflate account object as root"); return false; } #endif return SecAKSDoWhileUserBagLocked(error, ^{ do_with_account(^(SOSAccountRef account) { action_result = action(account, error); }); }) && action_result; } SOSAccountRef SOSKeychainAccountGetSharedAccount() { __block SOSAccountRef result = NULL; do_with_account(^(SOSAccountRef account) { result = account; }); return result; } // // Mark: Credential processing // bool SOSCCTryUserCredentials_Server(CFStringRef user_label, CFDataRef user_password, CFErrorRef *error) { return do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { return SOSAccountTryUserCredentials(account, user_label, user_password, block_error); }); } #define kWAIT2MINID "EFRESH" static bool EnsureFreshParameters(SOSAccountRef account, CFErrorRef *error) { dispatch_semaphore_t wait_for = dispatch_semaphore_create(0); dispatch_retain(wait_for); // Both this scope and the block own it. CFMutableArrayRef keysToGet = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); CFArrayAppendValue(keysToGet, kSOSKVSKeyParametersKey); __block CFDictionaryRef valuesToUpdate = NULL; __block bool success = false; secnoticeq("fresh", "%s calling SOSCloudKeychainSynchronizeAndWait", kWAIT2MINID); SOSCloudKeychainSynchronizeAndWait(keysToGet, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFDictionaryRef returnedValues, CFErrorRef sync_error) { if (sync_error) { secerrorq("%s SOSCloudKeychainSynchronizeAndWait: %@", kWAIT2MINID, sync_error); if (error) { *error = sync_error; CFRetainSafe(*error); } } else { secnoticeq("fresh", "%s returned from call; in callback to SOSCloudKeychainSynchronizeAndWait: results: %@", kWAIT2MINID, returnedValues); valuesToUpdate = returnedValues; CFRetainSafe(valuesToUpdate); success = true; } dispatch_semaphore_signal(wait_for); dispatch_release(wait_for); }); dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER); // TODO: Maybe we timeout here... used to dispatch_time(DISPATCH_TIME_NOW, 30ull * NSEC_PER_SEC)); dispatch_release(wait_for); CFMutableArrayRef handledKeys = NULL; if ((valuesToUpdate) && (account)) { handledKeys = SOSTransportDispatchMessages(account, valuesToUpdate, error); if (!handledKeys) { secerrorq("%s Freshness update failed: %@", kWAIT2MINID, error ? *error : NULL); success = false; } } CFReleaseNull(handledKeys); CFReleaseNull(valuesToUpdate); CFReleaseNull(keysToGet); return success; } static bool Flush(CFErrorRef *error) { __block bool success = false; dispatch_semaphore_t wait_for = dispatch_semaphore_create(0); dispatch_retain(wait_for); // Both this scope and the block own it. secnotice("flush", "Starting"); SOSCloudKeychainFlush(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFDictionaryRef returnedValues, CFErrorRef sync_error) { success = (sync_error == NULL); if (error) { CFRetainAssign(*error, sync_error); } dispatch_semaphore_signal(wait_for); dispatch_release(wait_for); }); dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER); dispatch_release(wait_for); secnotice("flush", "Returned %s", success? "Success": "Failure"); return success; } bool SOSCCSetUserCredentials_Server(CFStringRef user_label, CFDataRef user_password, CFErrorRef *error) { secnotice("updates", "Setting credentials for %@", user_label); // TODO: remove this notice bool result = do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { if (!EnsureFreshParameters(account, block_error)) { return false; } if (!SOSAccountAssertUserCredentials(account, user_label, user_password, block_error)) { secnotice("updates", "EnsureFreshParameters/SOSAccountAssertUserCredentials error: %@", *block_error); return false; } return true; }); return result && Flush(error); } bool SOSCCCanAuthenticate_Server(CFErrorRef *error) { bool result = do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { return SOSAccountGetPrivateCredential(account, block_error) != NULL; }); if (!result && error && *error && CFErrorGetDomain(*error) == kSOSErrorDomain) { CFIndex code = CFErrorGetCode(*error); if (code == kSOSErrorPrivateKeyAbsent || code == kSOSErrorPublicKeyAbsent) { CFReleaseNull(*error); } } return result; } bool SOSCCPurgeUserCredentials_Server(CFErrorRef *error) { return do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { SOSAccountPurgePrivateCredential(account); return true; }); } #if USE_BETTER static bool sAccountInCircleCache = false; static void do_with_not_in_circle_bool_queue(bool start_account, dispatch_block_t action) { static dispatch_queue_t account_start_queue; static dispatch_queue_t not_in_circle_queue; static bool account_started = false; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ not_in_circle_queue = dispatch_queue_create("nis queue", DISPATCH_QUEUE_SERIAL); account_start_queue = dispatch_queue_create("init nis queue", DISPATCH_QUEUE_SERIAL);; account_started = false; }); __block bool done = false; dispatch_sync(not_in_circle_queue, ^{ if (account_started) { done = true; action(); } }); if (!done && start_account) { dispatch_sync(account_start_queue, ^{ __block bool do_start = false; dispatch_sync(not_in_circle_queue, ^{ do_start = !account_started; account_started = true; }); if (do_start) SOSCCThisDeviceIsInCircle(NULL); // Inflate account. }); dispatch_sync(not_in_circle_queue, action); } } #endif bool SOSCCThisDeviceDefinitelyNotActiveInCircle() { return !SOSCCCircleIsOn_Artifact(); #if USE_BETTER __block bool result = false; do_with_not_in_circle_bool_queue(true, ^{ result = sAccountInCircleCache; }); return result; #endif } void SOSCCSetThisDeviceDefinitelyNotActiveInCircle(SOSCCStatus currentStatus) { SOSCCCircleIsOn_UpdateArtifact(currentStatus); #if USE_BETTER do_with_not_in_circle_bool_queue(false, ^{ sAccountInCircleCache = notActive; }); #endif } SOSCCStatus SOSCCThisDeviceIsInCircle_Server(CFErrorRef *error) { __block SOSCCStatus status; return do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { status = SOSAccountIsInCircles(account, block_error); return true; }) ? status : kSOSCCError; } bool SOSCCRequestToJoinCircle_Server(CFErrorRef* error) { __block bool result = true; return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { result = SOSAccountJoinCircles(account, block_error); return result; }); } bool SOSCCRequestToJoinCircleAfterRestore_Server(CFErrorRef* error) { __block bool result = true; bool returned = false; returned = do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { SOSAccountEnsurePeerRegistration(account, block_error); result = SOSAccountJoinCirclesAfterRestore(account, block_error); return result; }); return returned; } bool SOSCCRequestEnsureFreshParameters_Server(CFErrorRef* error) { __block bool result = true; bool returned = false; returned = do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { result = EnsureFreshParameters(account, NULL); return result; }); return returned; } CFStringRef SOSCCRequestDeviceID_Server(CFErrorRef *error) { __block CFStringRef result = NULL; (void) do_with_account_while_unlocked(error, ^bool(SOSAccountRef account, CFErrorRef *error) { result = SOSAccountGetDeviceID(account, error); return (!isNull(result)); }); return result; } bool SOSCCSetDeviceID_Server(CFStringRef IDS, CFErrorRef *error){ __block bool result = true; return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { result = SOSAccountSetMyDSID(account, IDS, block_error); return result; }); } bool SOSCCResetToOffering_Server(CFErrorRef* error) { __block bool result = true; return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { clearAllKVS(NULL); result = SOSAccountResetToOffering(account, block_error); return result; }); } bool SOSCCResetToEmpty_Server(CFErrorRef* error) { __block bool result = true; return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { result = SOSAccountResetToEmpty(account, block_error); return result; }); } bool SOSCCRemoveThisDeviceFromCircle_Server(CFErrorRef* error) { __block bool result = true; return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { result = SOSAccountLeaveCircles(account, block_error); return result; }); } bool SOSCCBailFromCircle_Server(uint64_t limit_in_seconds, CFErrorRef* error) { __block bool result = true; return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { result = SOSAccountBail(account, limit_in_seconds, block_error); return result; }); } CFArrayRef SOSCCCopyApplicantPeerInfo_Server(CFErrorRef* error) { __block CFArrayRef result = NULL; (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { result = SOSAccountCopyApplicants(account, block_error); return result != NULL; }); return result; } CFArrayRef SOSCCCopyGenerationPeerInfo_Server(CFErrorRef* error) { __block CFArrayRef result = NULL; (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { result = SOSAccountCopyGeneration(account, block_error); return result != NULL; }); return result; } CFArrayRef SOSCCCopyValidPeerPeerInfo_Server(CFErrorRef* error) { __block CFArrayRef result = NULL; (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { result = SOSAccountCopyValidPeers(account, block_error); return result != NULL; }); return result; } bool SOSCCValidateUserPublic_Server(CFErrorRef* error) { __block bool result = NULL; (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { result = SOSValidateUserPublic(account, block_error); return result; }); return result; } CFArrayRef SOSCCCopyNotValidPeerPeerInfo_Server(CFErrorRef* error) { __block CFArrayRef result = NULL; (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { result = SOSAccountCopyNotValidPeers(account, block_error); return result != NULL; }); return result; } CFArrayRef SOSCCCopyRetirementPeerInfo_Server(CFErrorRef* error) { __block CFArrayRef result = NULL; (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { result = SOSAccountCopyRetired(account, block_error); return result != NULL; }); return result; } bool SOSCCAcceptApplicants_Server(CFArrayRef applicants, CFErrorRef* error) { __block bool result = true; return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { result = SOSAccountAcceptApplicants(account, applicants, block_error); return result; }); } bool SOSCCRejectApplicants_Server(CFArrayRef applicants, CFErrorRef* error) { __block bool result = true; return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { result = SOSAccountRejectApplicants(account, applicants, block_error); return result; }); } CFArrayRef SOSCCCopyPeerPeerInfo_Server(CFErrorRef* error) { __block CFArrayRef result = NULL; (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { result = SOSAccountCopyPeers(account, block_error); return result != NULL; }); return result; } CFArrayRef SOSCCCopyConcurringPeerPeerInfo_Server(CFErrorRef* error) { __block CFArrayRef result = NULL; (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { result = SOSAccountCopyConcurringPeers(account, block_error); return result != NULL; }); return result; } CFStringRef SOSCCCopyIncompatibilityInfo_Server(CFErrorRef* error) { __block CFStringRef result = NULL; (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { result = SOSAccountCopyIncompatibilityInfo(account, block_error); return result != NULL; }); return result; } enum DepartureReason SOSCCGetLastDepartureReason_Server(CFErrorRef* error) { __block enum DepartureReason result = kSOSDepartureReasonError; (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { result = SOSAccountGetLastDepartureReason(account, block_error); return result != kSOSDepartureReasonError; }); return result; } bool SOSCCProcessEnsurePeerRegistration_Server(CFErrorRef* error) { secnotice("updates", "Request for registering peers"); return do_with_account_while_unlocked(error, ^bool(SOSAccountRef account, CFErrorRef *error) { return SOSAccountEnsurePeerRegistration(account, error); }); } SyncWithAllPeersReason SOSCCProcessSyncWithAllPeers_Server(CFErrorRef* error) { /* #define kIOReturnLockedRead iokit_common_err(0x2c3) // device read locked #define kIOReturnLockedWrite iokit_common_err(0x2c4) // device write locked */ __block SyncWithAllPeersReason result = kSyncWithAllPeersSuccess; CFErrorRef action_error = NULL; if (!do_with_account_while_unlocked(&action_error, ^bool (SOSAccountRef account, CFErrorRef* block_error) { CFErrorRef localError = NULL; if (!SOSAccountSyncWithAllPeers(account, &localError)) { secerror("sync with all peers failed: %@", localError); CFReleaseSafe(localError); // This isn't a device-locked error, but returning false will // have CloudKeychainProxy ask us to try sync again after next unlock result = kSyncWithAllPeersOtherFail; return false; } return true; })) { if (action_error) { if (SecErrorGetOSStatus(action_error) == errSecInteractionNotAllowed) { secnotice("updates", "SOSAccountSyncWithAllPeers failed because device is locked; letting CloudKeychainProxy know"); result = kSyncWithAllPeersLocked; // tell CloudKeychainProxy to call us back when device unlocks CFReleaseNull(action_error); } else { secerror("Unexpected error: %@", action_error); } if (error && *error == NULL) { *error = action_error; action_error = NULL; } CFReleaseNull(action_error); } } return result; } void SOSCCSyncWithAllPeers(void) { SOSCloudKeychainRequestSyncWithAllPeers(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), NULL); } CF_RETURNS_RETAINED CFArrayRef SOSCCHandleUpdateKeyParameter(CFDictionaryRef updates) { CFArrayRef result = NULL; SOSAccountRef account = SOSKeychainAccountGetSharedAccount(); //HACK to make sure itemsChangedBlock is set (account) ? (result = SOSCloudKeychainHandleUpdateKeyParameter(updates)) : (result = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault)); return result; } CF_RETURNS_RETAINED CFArrayRef SOSCCHandleUpdateCircle(CFDictionaryRef updates) { CFArrayRef result = NULL; SOSAccountRef account = SOSKeychainAccountGetSharedAccount(); //HACK to make sure itemsChangedBlock is set (account) ? (result = SOSCloudKeychainHandleUpdateKeyParameter(updates)) : (result = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault)); return result; } CF_RETURNS_RETAINED CFArrayRef SOSCCHandleUpdateMessage(CFDictionaryRef updates) { CFArrayRef result = NULL; SOSAccountRef account = SOSKeychainAccountGetSharedAccount(); //HACK to make sure itemsChangedBlock is set (account) ? (result = SOSCloudKeychainHandleUpdateKeyParameter(updates)) : (result = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault)); return result; }