]> git.saurik.com Git - apple/security.git/blobdiff - OSX/sec/securityd/SOSCloudCircleServer.c
Security-57336.1.9.tar.gz
[apple/security.git] / OSX / sec / securityd / SOSCloudCircleServer.c
diff --git a/OSX/sec/securityd/SOSCloudCircleServer.c b/OSX/sec/securityd/SOSCloudCircleServer.c
new file mode 100644 (file)
index 0000000..438909d
--- /dev/null
@@ -0,0 +1,1553 @@
+/*
+ * 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 <AssertMacros.h>
+#include <CoreFoundation/CFURL.h>
+
+#include <securityd/SOSCloudCircleServer.h>
+#include <Security/SecureObjectSync/SOSCloudCircle.h>
+#include <Security/SecureObjectSync/SOSCloudCircleInternal.h>
+#include <Security/SecureObjectSync/SOSCircle.h>
+#include <Security/SecureObjectSync/SOSAccount.h>
+#include <Security/SecureObjectSync/SOSAccountPriv.h>
+#include <Security/SecureObjectSync/SOSFullPeerInfo.h>
+#include <Security/SecureObjectSync/SOSPeerInfoV2.h>
+
+#include <Security/SecureObjectSync/SOSPeerInfoInternal.h>
+#include <Security/SecureObjectSync/SOSInternal.h>
+#include <Security/SecureObjectSync/SOSUserKeygen.h>
+#include <Security/SecureObjectSync/SOSMessage.h>
+#include <Security/SecureObjectSync/SOSTransport.h>
+#include <Security/SecureObjectSync/SOSTransportMessageIDS.h>
+#include <Security/SecureObjectSync/SOSAccountHSAJoin.h>
+
+#include <Security/SecureObjectSync/SOSKVSKeys.h>
+
+#include <utilities/SecCFWrappers.h>
+#include <utilities/SecCFRelease.h>
+#include <utilities/debugging.h>
+#include <utilities/SecCoreCrypto.h>
+#include <SOSCircle/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 <Security/SecureObjectSync/SOSCloudCircleInternal.h>
+
+#include <TargetConditionals.h>
+
+#include <utilities/iCloudKeychainTrace.h>
+
+#if TARGET_OS_EMBEDDED || TARGET_IPHONE_SIMULATOR
+#include <MobileGestalt.h>
+#else
+#include <AppleSystemInfo/AppleSystemInfo.h>
+
+// 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 RUN_AS_ROOT_ERROR 550
+
+#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;
+}
+
+//
+// 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);
+        secerror("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 void SOSKeychainAccountEnsureSaved(SOSAccountRef account)
+{
+    static CFDataRef sLastSavedAccountData = NULL;
+
+    CFErrorRef saveError = NULL;
+    CFDataRef accountAsData = NULL;
+
+    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
+//
+
+CF_EXPORT CFDictionaryRef _CFCopySystemVersionDictionary(void);
+CF_EXPORT CFStringRef _kCFSystemVersionBuildVersionKey;
+
+CFStringRef CopyOSVersion(void)
+{
+    static dispatch_once_t once;
+    static CFStringRef osVersion = NULL;
+    dispatch_once(&once, ^{
+#if TARGET_OS_EMBEDDED || TARGET_IPHONE_SIMULATOR
+        osVersion = MGCopyAnswer(kMGQBuildVersion, NULL);
+#else
+        CFDictionaryRef versions = _CFCopySystemVersionDictionary();
+
+        if (versions) {
+            CFTypeRef versionValue = CFDictionaryGetValue(versions, _kCFSystemVersionBuildVersionKey);
+
+            if (isString(versionValue))
+                osVersion = CFRetainSafe((CFStringRef) versionValue);
+        }
+
+        CFReleaseNull(versions);
+#endif
+        // What to do on MacOS.
+        if (osVersion == NULL)
+            osVersion = CFSTR("Unknown model");
+    });
+    return CFRetainSafe(osVersion);
+}
+
+
+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);
+            secinfo("server", "Engine v2 : %s", v2_enabled ? "enabled":"disabled");
+        }
+        CFReleaseSafe(v2Pref);
+    });
+    
+    return v2_enabled;
+#else
+    return false;
+#endif
+}
+
+
+static CFDictionaryRef CreateDeviceGestaltDictionary(SCDynamicStoreRef store, CFArrayRef keys, void *context)
+{
+    CFStringRef modelName = CopyModelName();
+    CFStringRef computerName = CopyComputerName(store);
+    CFStringRef osVersion = CopyOSVersion();
+
+    SInt32 version = _EngineMessageProtocolV2Enabled() ? kEngineMessageProtocolVersion : 0;
+    CFNumberRef protocolVersion = CFNumberCreate(0, kCFNumberSInt32Type, &version);
+    
+    CFDictionaryRef gestalt = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
+                                                           kPIUserDefinedDeviceNameKey,  computerName,
+                                                           kPIDeviceModelNameKey,        modelName,
+                                                           kPIMessageProtocolVersionKey, protocolVersion,
+                                                           kPIOSVersionKey,              osVersion,
+                                                           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 = CreateDeviceGestaltDictionary(store, keys, context);
+            if (SOSAccountUpdateGestalt(account, gestalt)) {
+                notify_post(kSOSCCCircleChangedNotification);
+            }
+            CFReleaseSafe(gestalt);
+        }
+    });
+}
+
+
+static CFDictionaryRef CreateDeviceGestaltDictionaryAndRegisterForUpdate(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 = CreateDeviceGestaltDictionary(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 = CreateDeviceGestaltDictionaryAndRegisterForUpdate(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);
+
+        SOSAccountAddChangeBlock(sSharedAccount, ^(SOSCircleRef circle,
+                                                   CFSetRef peer_additions,      CFSetRef peer_removals,
+                                                   CFSetRef applicant_additions, CFSetRef applicant_removals) {
+            CFErrorRef pi_error = NULL;
+            SOSPeerInfoRef me = SOSFullPeerInfoGetPeerInfo(sSharedAccount->my_identity);
+            if (!me) {
+                secerror("Error finding me for change: %@", pi_error);
+            } else {
+                // TODO: Figure out why peer_additions isn't right in some cases (like when joining a v2 circle with a v0 peer.
+                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);
+                } else {
+                    secinfo("updates", "Not requesting Ensure Peer Registration, since it's not needed");
+                }
+                
+                if (CFSetContainsValue(peer_additions, me)) {
+                    // TODO: Potentially remove from here and move this to the engine
+                    // TODO: We also need to do this when our views change.
+                    SOSCCSyncWithAllPeers();
+                }
+            }
+            
+            CFReleaseNull(pi_error);
+
+            // TODO: We should notify the engine of these changes here
+            if (CFSetGetCount(peer_additions) != 0 ||
+                CFSetGetCount(peer_removals) != 0 ||
+                CFSetGetCount(applicant_additions) != 0 ||
+                CFSetGetCount(applicant_removals) != 0) {
+
+                notify_post(kSOSCCCircleChangedNotification);
+           }
+        });
+    
+        SOSCloudKeychainSetItemsChangedBlock(^CFArrayRef(CFDictionaryRef changes) {
+            CFRetainSafe(changes);
+            __block CFMutableArrayRef handledKeys = NULL;
+            do_with_account(^(SOSAccountRef account) {
+                CFStringRef changeDescription = SOSItemsChangedCopyDescription(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);
+
+        // TODO: We should not be doing extra work whenever securityd is launched, let's see if we can eliminate this call
+        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 =  ^{
+            SOSPeerInfoRef mpi = SOSAccountGetMyPeerInfo(account);
+            CFSetRef beforeViews = mpi ? SOSPeerInfoCopyEnabledViews(mpi) : NULL;
+
+            action(account);
+
+            // Fake transaction around using the account object
+            SOSAccountFinishTransaction(account);
+
+            mpi = SOSAccountGetMyPeerInfo(account); // Update the peer
+            CFSetRef afterViews = mpi ? SOSPeerInfoCopyEnabledViews(mpi) : NULL;
+
+            if(!CFEqualSafe(beforeViews, afterViews)) {
+                notify_post(kSOSCCViewMembershipChangedNotification);
+            }
+
+            CFReleaseNull(beforeViews);
+            CFReleaseNull(afterViews);
+
+            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);
+}
+
+static bool do_if_after_first_unlock(CFErrorRef *error, dispatch_block_t action)
+{
+#if TARGET_IPHONE_SIMULATOR
+    action();
+    return true;
+#else
+    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;
+#endif
+}
+
+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");
+        if(error)
+            *error = CFErrorCreate(kCFAllocatorDefault, CFSTR("com.apple.security"), RUN_AS_ROOT_ERROR, NULL);
+        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");
+        if(error)
+            *error = CFErrorCreate(kCFAllocatorDefault, CFSTR("com.apple.security"), RUN_AS_ROOT_ERROR, NULL);
+        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);
+    });
+}
+
+
+SOSViewResultCode SOSCCView_Server(CFStringRef viewname, SOSViewActionCode action, CFErrorRef *error) {
+    __block SOSViewResultCode status = kSOSCCGeneralViewError;
+
+    do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
+        switch(action) {
+        case kSOSCCViewQuery:
+            status = SOSAccountViewStatus(account, viewname, error);
+            break;
+        case kSOSCCViewEnable:
+        case kSOSCCViewDisable: // fallthrough
+            status = SOSAccountUpdateView(account, viewname, action, error);
+            secnotice("views", "HEY!!!!!! I'm Changing VIEWS- %d", (int) status);
+            break;
+        default:
+            secnotice("views", "Bad SOSViewActionCode - %d", (int) action);
+            return false;
+            break;
+        }
+        return true;
+    });
+    return status;
+}
+
+
+bool SOSCCViewSet_Server(CFSetRef enabledViews, CFSetRef disabledViews) {
+    __block bool status = false;
+    
+    do_with_account_if_after_first_unlock(NULL, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
+        status = SOSAccountUpdateViewSets(account, enabledViews, disabledViews);
+        return true;
+    });
+    return status;
+}
+
+
+
+SOSSecurityPropertyResultCode SOSCCSecurityProperty_Server(CFStringRef property, SOSSecurityPropertyActionCode action, CFErrorRef *error) {
+    
+    __block SOSViewResultCode status = kSOSCCGeneralSecurityPropertyError;
+    do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
+        switch(action) {
+            case kSOSCCSecurityPropertyQuery:
+                status = SOSAccountSecurityPropertyStatus(account, property, error);
+                break;
+            case kSOSCCSecurityPropertyEnable:
+            case kSOSCCSecurityPropertyDisable: // fallthrough
+                status = SOSAccountUpdateSecurityProperty(account, property, action, error);
+                secnotice("secprop", "HEY!!!!!! I'm Changing SecurityProperties- %d", (int) status);
+                break;
+            default:
+                secnotice("secprop", "Bad SOSSecurityPropertyActionCode - %d", (int) action);
+                return false;
+                break;
+        }
+        return true;
+    });
+    return status;
+}
+
+void sync_the_last_data_to_kvs(SOSAccountRef account){
+    
+    dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
+    dispatch_retain(wait_for); // Both this scope and the block own it.
+    
+    __block bool success = false;
+    
+    secnoticeq("force-push", "calling SOSCloudKeychainSynchronizeAndWait");
+    
+    CFMutableArrayRef keysToGet = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
+    
+    SOSCloudKeychainSynchronizeAndWait(keysToGet, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFDictionaryRef returnedValues, CFErrorRef sync_error) {
+        
+        if (sync_error) {
+            secerrorq("SOSCloudKeychainSynchronizeAndWait: %@", sync_error);
+        } else {
+            secnoticeq("force-push", "returned from call; in callback to SOSCloudKeychainSynchronizeAndWait: results: %@", returnedValues);
+            
+            success = true;
+        }
+        
+        dispatch_semaphore_signal(wait_for);
+        dispatch_release(wait_for);
+    });
+    
+    CFReleaseNull(keysToGet);
+    dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER);
+    dispatch_release(wait_for);
+}
+
+#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);
+    AppendCircleKeyName(keysToGet, SOSCircleGetName(account->trusted_circle));
+
+    __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;
+}
+
+static bool SOSCCAssertUserCredentialsAndOptionalDSID(CFStringRef user_label, CFDataRef user_password, CFStringRef dsid, CFErrorRef *error) {
+    secnotice("updates", "Setting credentials and dsid (%@) for %@", dsid, user_label);
+    bool result = do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
+        if (dsid != NULL && CFStringCompare(dsid, CFSTR(""), 0) != 0) {
+            CFStringRef accountDSID = SOSAccountGetValue(account, kSOSDSIDKey, NULL);
+            if( accountDSID == NULL){
+                SOSAccountUpdateDSID(account, dsid);
+                secdebug("updates", "Setting dsid, current dsid is empty for this account: %@", dsid);
+            }
+            else if(CFStringCompare(dsid, accountDSID, 0) != kCFCompareEqualTo){
+                secnotice("updates", "Changing DSID from: %@ to %@", accountDSID, dsid);
+
+                //DSID has changed, blast the account!
+                SOSAccountSetToNew(account);
+
+                //update DSID to the new DSID
+                SOSAccountUpdateDSID(account, dsid);
+            }
+            else {
+                secnotice("updates", "Not Changing DSID: %@ to %@", accountDSID, dsid);
+            }
+            
+        }
+
+        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;
+    });
+    
+    if (result && Flush(error)) {
+        result = do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
+            return SOSAccountGenerationSignatureUpdate(account, error);
+        });
+    }
+
+    return result;
+}
+
+bool SOSCCSetUserCredentialsAndDSID_Server(CFStringRef user_label, CFDataRef user_password, CFStringRef dsid, CFErrorRef *error)
+{
+    // TODO: Return error if DSID is NULL to insist our callers provide one?
+    return SOSCCAssertUserCredentialsAndOptionalDSID(user_label, user_password, dsid, error);
+}
+
+bool SOSCCSetUserCredentials_Server(CFStringRef user_label, CFDataRef user_password, CFErrorRef *error)
+{
+    return SOSCCAssertUserCredentialsAndOptionalDSID(user_label, user_password, NULL, 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;
+    });
+}
+
+SOSCCStatus SOSCCThisDeviceIsInCircle_Server(CFErrorRef *error)
+{
+    __block SOSCCStatus status;
+
+    return do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
+        status = SOSAccountGetCircleStatus(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;
+}
+
+bool SOSCCApplyToARing_Server(CFStringRef ringName, CFErrorRef *error){
+    __block bool result = true;
+    bool returned = false;
+    returned = do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
+        SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInfo(account);
+        SOSRingRef ring = SOSAccountGetRing(account, ringName, error);
+        if(fpi && ring)
+            result = SOSRingApply(ring, account->user_public, fpi , error);
+        return result;
+    });
+    return returned;
+}
+
+bool SOSCCWithdrawlFromARing_Server(CFStringRef ringName, CFErrorRef *error){
+    __block bool result = true;
+    bool returned = false;
+    returned = do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
+        SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInfo(account);
+        SOSRingRef ring = SOSAccountGetRing(account, ringName, error);
+        if(fpi && ring)
+            result = SOSRingWithdraw(ring, account->user_public, fpi , error);
+        return result;
+    });
+    return returned;
+}
+
+bool SOSCCEnableRing_Server(CFStringRef ringName, CFErrorRef *error){
+    __block bool result = true;
+    bool returned = false;
+    returned = do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
+        SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInfo(account);
+        SOSRingRef ring = SOSAccountGetRing(account, ringName, error);
+        if(fpi && ring)
+            result = SOSRingResetToOffering(ring, NULL, fpi, error); ;
+        return result;
+    });
+    return returned;
+}
+
+CFStringRef SOSCCGetAllTheRings_Server(CFErrorRef *error){
+    __block CFMutableDictionaryRef result = NULL;
+    __block CFMutableStringRef description = CFStringCreateMutable(kCFAllocatorDefault, 0);
+    
+    (void) do_with_account_while_unlocked(error, ^bool(SOSAccountRef account, CFErrorRef *error) {
+        result = SOSAccountGetRings(account, error);
+    
+        if(isDictionary(result)){
+            CFDictionaryForEach(result, ^(const void *key, const void *value) {
+                CFStringAppendFormat(description, NULL, CFSTR("%@"), value);
+            });
+        }
+        if(result)
+            return true;
+        return false;
+    });
+    
+    return description;
+}
+
+SOSRingStatus SOSCCRingStatus_Server(CFStringRef ringName, CFErrorRef *error){
+    __block bool result = true;
+    SOSRingStatus returned;
+    returned = do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
+        SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInfo(account);
+        SOSPeerInfoRef myPeer = SOSFullPeerInfoGetPeerInfo(fpi);
+        
+        SOSRingRef ring = SOSAccountGetRing(account, ringName, error);
+        if(myPeer && ring)
+            result = SOSRingDeviceIsInRing(ring, SOSPeerInfoGetPeerID(myPeer));
+        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 = SOSAccountCopyDeviceID(account, error);
+        return (!isNull(result));
+    });
+    return result;
+}
+
+bool SOSCCSetDeviceID_Server(CFStringRef IDS, CFErrorRef *error){
+    
+    bool didSetID = false;
+    __block bool result = false;
+    __block CFErrorRef blockError = NULL;
+
+    didSetID = do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
+        result = SOSAccountSetMyDSID(account, IDS, block_error);
+        if(block_error)
+            blockError = CFRetainSafe(*block_error);
+        return result;
+    });
+    
+    if(error){
+        *error = blockError;
+    }
+    return didSetID;
+}
+
+HandleIDSMessageReason SOSCCHandleIDSMessage_Server(CFDictionaryRef messageDict, CFErrorRef* error)
+{
+    // TODO: Locking flow:
+    /*
+     COMMON:
+        - Get PeerCoder instance from SOSPeerCoderManager(Currently Engine)
+            - Get Account lock and Initialize PeerCoder instance if it isn't valid yet.
+     INCOMING:
+        - Decode incoming msg on coder.
+        - Pass msg along to SOSPeerRef if decoding is done.
+        - Force reply from coder while in handshake mode. (or ask ckd to ask us later?)
+        - save coder state.
+
+        - Lookup SOSPeerRef in SOSEngineRef (getting engine lock temporarily to get peer.
+        - Ask peer to handle decoded message
+            - be notified of changed objects in all peers and update peer/engine states
+        - save peer/engine state
+
+     OUTGOING:
+        - Ask coder to send an outgoing message if it is negotiating
+        - Ask peer to create a message if needed
+        - Encode peer msg with coder
+        - save coder state
+        - send reply to ckd for transporting
+     */
+    
+    __block HandleIDSMessageReason result = kHandleIDSMessageSuccess;
+    CFErrorRef action_error = NULL;
+    
+    if (!do_with_account_while_unlocked(&action_error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
+        result = SOSTransportMessageIDSHandleMessage(account, messageDict, error);
+        return result;
+    })) {
+        if (action_error) {
+        if (SecErrorGetOSStatus(action_error) == errSecInteractionNotAllowed) {
+            secnotice("updates", "SOSCCHandleIDSMessage_Server failed because device is locked; letting IDSKeychainSyncingProxy know");
+            result = kHandleIDSMessageLocked;        // tell IDSKeychainSyncingProxy 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;
+}
+
+bool SOSCCIDSPingTest_Server(CFStringRef message, CFErrorRef *error){
+    bool didSendTestMessages = false;
+    __block bool result = true;
+    __block CFErrorRef blockError = NULL;
+    
+    didSendTestMessages = do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
+        result = SOSAccountStartPingTest(account, message, block_error);
+        if(block_error)
+            blockError = CFRetainSafe(*block_error);
+        return result;
+    });
+    if(blockError && error != NULL)
+        *error = blockError;
+    
+    return didSendTestMessages;
+}
+
+bool SOSCCIDSServiceRegistrationTest_Server(CFStringRef message, CFErrorRef *error){
+    bool didSendTestMessages = false;
+    __block bool result = true;
+    __block CFErrorRef blockError = NULL;
+    
+    didSendTestMessages = do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
+        result = SOSAccountSendIDSTestMessage(account, message, block_error);
+        if(block_error)
+            blockError = CFRetainSafe(*block_error);
+        return result;
+    });
+    if(blockError && error != NULL)
+        *error = blockError;
+    
+    return didSendTestMessages;
+}
+
+bool SOSCCIDSDeviceIDIsAvailableTest_Server(CFErrorRef *error){
+    bool didSendTestMessages = false;
+    __block bool result = true;
+    __block CFErrorRef blockError = NULL;
+    
+    didSendTestMessages = do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
+        result = SOSAccountRetrieveDeviceIDFromIDSKeychainSyncingProxy(account, block_error);
+        if(block_error)
+            blockError = CFRetainSafe(*block_error);
+        return result;
+    });
+    if(blockError && error != NULL)
+        *error = blockError;
+    
+    return didSendTestMessages;
+}
+
+bool SOSCCAccountSetToNew_Server(CFErrorRef *error)
+{
+       __block bool result = true;
+
+       return do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
+               clearAllKVS(NULL);
+               SOSAccountSetToNew(account);
+               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 = SOSAccountLeaveCircle(account, block_error);
+        return result;
+    });
+}
+
+bool SOSCCLoggedOutOfAccount_Server(CFErrorRef *error)
+{
+    __block bool result = true;
+    
+    return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
+        secnotice("sosops", "Signed out of account!");
+        result = SOSAccountLeaveCircle(account, block_error);
+
+        SOSAccountFinishTransaction(account); // Make sure this gets finished before we set to new.
+
+        SOSAccountSetToNew(account);
+        
+        sync_the_last_data_to_kvs(account);
+
+        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);
+       
+        SOSAccountFinishTransaction(account); // Make sure this gets finished before we set to new.
+                
+        sync_the_last_data_to_kvs(account);
+
+        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;
+}
+
+CFArrayRef SOSCCCopyEngineState_Server(CFErrorRef* error)
+{
+    CFArrayRef result = NULL;
+    SOSDataSourceFactoryRef dsf = SecItemDataSourceFactoryGetDefault();
+    SOSDataSourceRef ds = SOSDataSourceFactoryCreateDataSource(dsf, kSecAttrAccessibleWhenUnlocked, error);
+    if (ds) {
+        SOSEngineRef engine = SOSDataSourceGetSharedEngine(ds, error);
+        result = SOSEngineCopyPeerConfirmedDigests(engine, error);
+        SOSDataSourceRelease(ds, error);
+    }
+
+    return result;
+}
+
+bool SOSCCWaitForInitialSync_Server(CFErrorRef* error) {
+    __block dispatch_semaphore_t inSyncSema = NULL;
+
+    bool result = do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
+        bool alreadyInSync = SOSAccountCheckHasBeenInSync(account);
+        int token = -1;
+        if (!alreadyInSync) {
+            inSyncSema = dispatch_semaphore_create(0);
+            dispatch_retain(inSyncSema);
+            notify_register_dispatch(kSOSCCInitialSyncChangedNotification, &token,
+                                     dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(int token) {
+                                         dispatch_semaphore_signal(inSyncSema);
+                                         dispatch_release(inSyncSema);
+
+                                         notify_cancel(token);
+                                     });
+        }
+        return true;
+    });
+
+    if (result && inSyncSema != NULL) {
+        dispatch_semaphore_wait(inSyncSema, DISPATCH_TIME_FOREVER);
+        dispatch_release(inSyncSema);
+    }
+
+    return result;
+}
+
+static CFArrayRef SOSAccountCopyYetToSyncViews(SOSAccountRef account, CFErrorRef *error) {
+    CFArrayRef result = NULL;
+
+    CFTypeRef valueFetched = SOSAccountGetValue(account, kSOSUnsyncedViewsKey, error);
+    if (valueFetched == kCFBooleanTrue) {
+        SOSPeerInfoRef myPI = SOSAccountGetMyPeerInfo(account);
+        if (myPI) {
+            SOSPeerInfoWithEnabledViewSet(myPI, ^(CFSetRef enabled) {
+                CFSetCopyValues(enabled);
+            });
+        }
+    } else if (isSet(valueFetched)) {
+        result = CFSetCopyValues((CFSetRef)valueFetched);
+    }
+
+    if (result == NULL) {
+        result = CFArrayCreateForCFTypes(kCFAllocatorDefault, NULL);
+    }
+
+    return result;
+}
+
+CFArrayRef SOSCCCopyYetToSyncViewsList_Server(CFErrorRef* error) {
+
+    __block CFArrayRef views = NULL;
+
+    (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
+        views = SOSAccountCopyYetToSyncViews(account, error);
+
+        return true;
+    });
+
+    return views;
+}
+
+
+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;
+}
+
+SOSPeerInfoRef SOSCCCopyMyPeerInfo_Server(CFErrorRef* error)
+{
+    __block SOSPeerInfoRef result = NULL;
+
+    (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
+        // Create a copy to be DERed/sent back to client
+        result = SOSPeerInfoCreateCopy(kCFAllocatorDefault, SOSAccountGetMyPeerInfo(account), block_error);
+        return result != NULL;
+    });
+
+    return result;
+}
+
+SOSPeerInfoRef SOSCCSetNewPublicBackupKey_Server(CFDataRef newPublicBackup, CFErrorRef *error){
+    __block SOSPeerInfoRef result = NULL;
+
+    (void) do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
+        if(SOSAccountSetBackupPublicKey(account,newPublicBackup, error)){
+            // Create a copy to be DERed/sent back to client
+            result = SOSPeerInfoCreateCopy(kCFAllocatorDefault, SOSAccountGetMyPeerInfo(account), block_error);
+            secdebug("backup", "SOSCCSetNewPublicBackupKey_Server, new public backup is set");
+        }
+        else
+        {
+            secerror("SOSCCSetNewPublicBackupKey_Server, could not set new public backup");
+        }
+        return result != NULL;
+    });
+
+    return result;
+}
+
+bool SOSCCRegisterSingleRecoverySecret_Server(CFDataRef aks_bag, bool includeV0, CFErrorRef *error){
+    return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
+        return SOSAccountSetBSKBagForAllSlices(account, aks_bag, includeV0, error);
+    });
+}
+
+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 SOSCCSetLastDepartureReason_Server(enum DepartureReason reason, CFErrorRef *error){
+       __block bool result = true;
+
+       return do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
+               SOSAccountSetLastDepartureReason(account, reason);
+               return result;
+       });
+}
+
+bool SOSCCSetHSA2AutoAcceptInfo_Server(CFDataRef pubKey, CFErrorRef *error) {
+       __block bool result = true;
+
+       return do_with_account_if_after_first_unlock(error, ^(SOSAccountRef account,
+                       CFErrorRef *block_error) {
+               result = SOSAccountSetHSAPubKeyExpected(account, pubKey, error);
+               return (bool)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 SOSCCHandleUpdateMessage(CFDictionaryRef updates)
+{
+    CFArrayRef result = NULL;
+    SOSAccountRef account = SOSKeychainAccountGetSharedAccount();   //HACK to make sure itemsChangedBlock is set
+
+    (account) ? (result = SOSCloudKeychainHandleUpdateMessage(updates)) : (result = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault));
+    return result;
+}