SOSCloudCircleServer.c [plain text]
#include <AssertMacros.h>
#include <CoreFoundation/CFURL.h>
#include <securityd/SOSCloudCircleServer.h>
#include <SecureObjectSync/SOSCloudCircle.h>
#include <SecureObjectSync/SOSCloudCircleInternal.h>
#include <SecureObjectSync/SOSCircle.h>
#include <SecureObjectSync/SOSAccount.h>
#include <SecureObjectSync/SOSAccountPriv.h>
#include <SecureObjectSync/SOSFullPeerInfo.h>
#include <SecureObjectSync/SOSPeerInfoInternal.h>
#include <SecureObjectSync/SOSInternal.h>
#include <SecureObjectSync/SOSUserKeygen.h>
#include <SecureObjectSync/SOSMessage.h>
#include <SecureObjectSync/SOSTransport.h>
#include <SecureObjectSync/SOSKVSKeys.h>
#include <utilities/SecCFWrappers.h>
#include <utilities/SecCFRelease.h>
#include <utilities/debugging.h>
#include <CKBridge/SOSCloudKeychainClient.h>
#include <corecrypto/ccrng.h>
#include <corecrypto/ccrng_pbkdf2_prng.h>
#include <corecrypto/ccec.h>
#include <corecrypto/ccdigest.h>
#include <corecrypto/ccsha2.h>
#include <CommonCrypto/CommonRandomSPI.h>
#include <Security/SecKeyPriv.h>
#include <Security/SecFramework.h>
#include <utilities/SecFileLocations.h>
#include <utilities/SecAKSWrappers.h>
#include <securityd/SecItemServer.h>
#include <Security/SecItemPriv.h>
#include <TargetConditionals.h>
#include <utilities/iCloudKeychainTrace.h>
#if TARGET_OS_EMBEDDED || TARGET_IPHONE_SIMULATOR
#include <MobileGestalt.h>
#else
#include <AppleSystemInfo/AppleSystemInfo.h>
typedef const struct AuthorizationOpaqueRef * AuthorizationRef;
#endif
#define SOSCKCSCOPE "sync"
#define USE_SYSTEMCONFIGURATION_PRIVATE_HEADERS
#import <SystemConfiguration/SystemConfiguration.h>
#include <notify.h>
static SOSCCAccountDataSourceFactoryBlock accountDataSourceOverride = NULL;
bool SOSKeychainAccountSetFactoryForAccount(SOSCCAccountDataSourceFactoryBlock block)
{
accountDataSourceOverride = Block_copy(block);
return true;
}
static void do_with_account(void (^action)(SOSAccountRef account));
static void do_with_account_async(void (^action)(SOSAccountRef account));
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 <utilities/der_plist.h>
#include <utilities/der_plist_internal.h>
#include <corecrypto/ccder.h>
#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:
break;
}
}
static void SOSKeychainAccountEnsureSaved(SOSAccountRef account)
{
static CFDataRef sLastSavedAccountData = NULL;
CFErrorRef saveError = NULL;
if(SOSAccountGetMyFullPeerInCircleNamedIfPresent(account, CFSTR("ak"), NULL) == NULL) {
return;
}
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);
}
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 && SOSAccountGetMyFullPeerInCircleNamedIfPresent(account, CFSTR("ak"), NULL) == NULL) {
SOSAccountRef newAccount = SOSAccountCreate(kCFAllocatorDefault, our_gestalt, factory);
if (!newAccount) {
secnotice("repair_account", "Tried to repair bad account - got null account");
} else {
account = newAccount;
}
}
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;
}
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
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;
}
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);
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);
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);
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); 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); });
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)
{
__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);
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; 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(); (account) ? (result = SOSCloudKeychainHandleUpdateKeyParameter(updates)) : (result = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault));
return result;
}
CF_RETURNS_RETAINED CFArrayRef SOSCCHandleUpdateCircle(CFDictionaryRef updates)
{
CFArrayRef result = NULL;
SOSAccountRef account = SOSKeychainAccountGetSharedAccount(); (account) ? (result = SOSCloudKeychainHandleUpdateKeyParameter(updates)) : (result = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault));
return result;
}
CF_RETURNS_RETAINED CFArrayRef SOSCCHandleUpdateMessage(CFDictionaryRef updates)
{
CFArrayRef result = NULL;
SOSAccountRef account = SOSKeychainAccountGetSharedAccount(); (account) ? (result = SOSCloudKeychainHandleUpdateKeyParameter(updates)) : (result = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault));
return result;
}