2 * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 #include <AssertMacros.h>
26 #include <CoreFoundation/CFURL.h>
28 #include <securityd/SOSCloudCircleServer.h>
29 #include <Security/SecureObjectSync/SOSCloudCircle.h>
30 #include <Security/SecureObjectSync/SOSCloudCircleInternal.h>
31 #include <Security/SecureObjectSync/SOSCircle.h>
32 #include <Security/SecureObjectSync/SOSAccount.h>
33 #include <Security/SecureObjectSync/SOSAccountPriv.h>
34 #include <Security/SecureObjectSync/SOSFullPeerInfo.h>
35 #include <Security/SecureObjectSync/SOSPeerInfoV2.h>
37 #include <Security/SecureObjectSync/SOSPeerInfoInternal.h>
38 #include <Security/SecureObjectSync/SOSInternal.h>
39 #include <Security/SecureObjectSync/SOSUserKeygen.h>
40 #include <Security/SecureObjectSync/SOSMessage.h>
41 #include <Security/SecureObjectSync/SOSTransport.h>
42 #include <Security/SecureObjectSync/SOSTransportMessageIDS.h>
43 #include <Security/SecureObjectSync/SOSAccountHSAJoin.h>
45 #include <Security/SecureObjectSync/SOSKVSKeys.h>
47 #include <utilities/SecCFWrappers.h>
48 #include <utilities/SecCFRelease.h>
49 #include <utilities/debugging.h>
50 #include <utilities/SecCoreCrypto.h>
51 #include <SOSCircle/CKBridge/SOSCloudKeychainClient.h>
53 #include <corecrypto/ccrng.h>
54 #include <corecrypto/ccrng_pbkdf2_prng.h>
55 #include <corecrypto/ccec.h>
56 #include <corecrypto/ccdigest.h>
57 #include <corecrypto/ccsha2.h>
58 #include <CommonCrypto/CommonRandomSPI.h>
59 #include <Security/SecKeyPriv.h>
60 #include <Security/SecFramework.h>
62 #include <utilities/SecFileLocations.h>
63 #include <utilities/SecAKSWrappers.h>
64 #include <securityd/SecItemServer.h>
65 #include <Security/SecItemPriv.h>
66 #include <Security/SecureObjectSync/SOSCloudCircleInternal.h>
68 #include <TargetConditionals.h>
70 #include <utilities/iCloudKeychainTrace.h>
71 #include <Security/SecAccessControlPriv.h>
72 #include <securityd/SecDbKeychainItem.h>
74 #if TARGET_OS_EMBEDDED || TARGET_IPHONE_SIMULATOR
75 #include <MobileGestalt.h>
77 #include <AppleSystemInfo/AppleSystemInfo.h>
79 // We need authorization, but that doesn't exist
80 // on sec built for desktop (iOS in a process)
81 // Define AuthorizationRef here to make SystemConfiguration work
83 typedef const struct AuthorizationOpaqueRef
* AuthorizationRef
;
86 #define SOSCKCSCOPE "sync"
87 #define RUN_AS_ROOT_ERROR 550
89 #define USE_SYSTEMCONFIGURATION_PRIVATE_HEADERS
90 #import <SystemConfiguration/SystemConfiguration.h>
94 static SOSCCAccountDataSourceFactoryBlock accountDataSourceOverride
= NULL
;
96 bool SOSKeychainAccountSetFactoryForAccount(SOSCCAccountDataSourceFactoryBlock block
)
98 accountDataSourceOverride
= Block_copy(block
);
107 static void do_with_account(void (^action
)(SOSAccountRef account
));
108 static void do_with_account_async(void (^action
)(SOSAccountRef account
));
113 CFStringRef kSOSInternalAccessGroup
= CFSTR("com.apple.security.sos");
115 CFStringRef kSOSAccountLabel
= CFSTR("iCloud Keychain Account Meta-data");
117 CFStringRef kSOSBurnedRecoveryAttemptCount
= CFSTR("Burned Recovery Attempt Count");
119 CFStringRef kSOSBurnedRecoveryAttemptAttestationDate
= CFSTR("Burned Recovery Attempt Attestation Date");
121 static CFDictionaryRef
SOSItemCopyQueryForSyncItems(CFStringRef service
, bool returnData
)
123 return CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
124 kSecClass
, kSecClassGenericPassword
,
125 kSecAttrService
, service
,
126 kSecAttrAccessGroup
, kSOSInternalAccessGroup
,
127 kSecReturnData
, returnData
? kCFBooleanTrue
: kCFBooleanFalse
,
131 CFDataRef
SOSItemCopy(CFStringRef service
, CFErrorRef
* error
)
133 CFDictionaryRef query
= SOSItemCopyQueryForSyncItems(service
, true);
135 CFDataRef result
= NULL
;
137 OSStatus copyResult
= SecItemCopyMatching(query
, (CFTypeRef
*) &result
);
139 CFReleaseNull(query
);
141 if (copyResult
!= noErr
) {
142 SecError(copyResult
, error
, CFSTR("Error %@ reading for service '%@'"), result
, service
);
143 CFReleaseNull(result
);
147 if (!isData(result
)) {
148 SOSCreateErrorWithFormat(kSOSErrorProcessingFailure
, NULL
, error
, NULL
, CFSTR("SecItemCopyMatching returned non-data in '%@'"), service
);
149 CFReleaseNull(result
);
156 static CFDataRef
SOSKeychainCopySavedAccountData()
158 CFErrorRef error
= NULL
;
159 CFDataRef accountData
= SOSItemCopy(kSOSAccountLabel
, &error
);
161 secnotice("account", "Failed to load account: %@", error
);
162 secerror("Failed to load account: %@", error
);
164 CFReleaseNull(error
);
169 bool SOSItemUpdateOrAdd(CFStringRef service
, CFStringRef accessibility
, CFDataRef data
, CFErrorRef
*error
)
171 CFDictionaryRef query
= SOSItemCopyQueryForSyncItems(service
, false);
173 CFDictionaryRef update
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
175 kSecAttrAccessible
, accessibility
,
177 OSStatus saveStatus
= SecItemUpdate(query
, update
);
179 if (errSecItemNotFound
== saveStatus
) {
180 CFMutableDictionaryRef add
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, query
);
181 CFDictionaryForEach(update
, ^(const void *key
, const void *value
) {
182 CFDictionaryAddValue(add
, key
, value
);
184 saveStatus
= SecItemAdd(add
, NULL
);
188 CFReleaseNull(query
);
189 CFReleaseNull(update
);
191 return SecError(saveStatus
, error
, CFSTR("Error saving %@ to service '%@'"), data
, service
);
194 static void SOSKeychainAccountEnsureSaved(SOSAccountRef account
)
196 static CFDataRef sLastSavedAccountData
= NULL
;
198 CFErrorRef saveError
= NULL
;
199 CFDataRef accountAsData
= NULL
;
201 accountAsData
= SOSAccountCopyEncodedData(account
, kCFAllocatorDefault
, &saveError
);
203 require_action_quiet(accountAsData
, exit
, secerror("Failed to transform account into data, error: %@", saveError
));
204 require_quiet(!CFEqualSafe(sLastSavedAccountData
, accountAsData
), exit
);
206 if (!SOSItemUpdateOrAdd(kSOSAccountLabel
, kSecAttrAccessibleAlwaysThisDeviceOnly
, accountAsData
, &saveError
)) {
207 secerror("Can't save account: %@", saveError
);
211 CFReleaseNull(sLastSavedAccountData
);
212 sLastSavedAccountData
= accountAsData
;
213 accountAsData
= NULL
;
216 CFReleaseNull(saveError
);
217 CFReleaseNull(accountAsData
);
222 Stolen from keychain_sync.c
225 static bool clearAllKVS(CFErrorRef
*error
)
228 __block
bool result
= false;
229 const uint64_t maxTimeToWaitInSeconds
= 30ull * NSEC_PER_SEC
;
230 dispatch_queue_t processQueue
= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0);
231 dispatch_semaphore_t waitSemaphore
= dispatch_semaphore_create(0);
232 dispatch_time_t finishTime
= dispatch_time(DISPATCH_TIME_NOW
, maxTimeToWaitInSeconds
);
234 SOSCloudKeychainClearAll(processQueue
, ^(CFDictionaryRef returnedValues
, CFErrorRef cerror
)
236 result
= (cerror
!= NULL
);
237 dispatch_semaphore_signal(waitSemaphore
);
240 dispatch_semaphore_wait(waitSemaphore
, finishTime
);
241 dispatch_release(waitSemaphore
);
246 static SOSAccountRef
SOSKeychainAccountCreateSharedAccount(CFDictionaryRef our_gestalt
)
248 secdebug("account", "Created account");
250 CFDataRef savedAccount
= SOSKeychainCopySavedAccountData();
251 SOSAccountRef account
= NULL
;
252 SOSDataSourceFactoryRef factory
= accountDataSourceOverride
? accountDataSourceOverride()
253 : SecItemDataSourceFactoryGetDefault();
256 CFErrorRef inflationError
= NULL
;
258 account
= SOSAccountCreateFromData(kCFAllocatorDefault
, savedAccount
, factory
, &inflationError
);
261 SOSAccountUpdateGestalt(account
, our_gestalt
);
263 secerror("Got error inflating account: %@", inflationError
);
266 CFReleaseNull(inflationError
);
269 CFReleaseSafe(savedAccount
);
272 account
= SOSAccountCreate(kCFAllocatorDefault
, our_gestalt
, factory
);
275 secerror("Got NULL creating account");
282 // Mark: Gestalt Handling
285 CF_EXPORT CFDictionaryRef
_CFCopySystemVersionDictionary(void);
286 CF_EXPORT CFStringRef _kCFSystemVersionBuildVersionKey
;
288 CFStringRef
CopyOSVersion(void)
290 static dispatch_once_t once
;
291 static CFStringRef osVersion
= NULL
;
292 dispatch_once(&once
, ^{
293 #if TARGET_OS_EMBEDDED || TARGET_IPHONE_SIMULATOR
294 osVersion
= MGCopyAnswer(kMGQBuildVersion
, NULL
);
296 CFDictionaryRef versions
= _CFCopySystemVersionDictionary();
299 CFTypeRef versionValue
= CFDictionaryGetValue(versions
, _kCFSystemVersionBuildVersionKey
);
301 if (isString(versionValue
))
302 osVersion
= CFRetainSafe((CFStringRef
) versionValue
);
305 CFReleaseNull(versions
);
307 // What to do on MacOS.
308 if (osVersion
== NULL
)
309 osVersion
= CFSTR("Unknown model");
311 return CFRetainSafe(osVersion
);
315 static CFStringRef
CopyModelName(void)
317 static dispatch_once_t once
;
318 static CFStringRef modelName
= NULL
;
319 dispatch_once(&once
, ^{
320 #if TARGET_OS_EMBEDDED || TARGET_IPHONE_SIMULATOR
321 modelName
= MGCopyAnswer(kMGQDeviceName
, NULL
);
323 modelName
= ASI_CopyComputerModelName(FALSE
);
325 if (modelName
== NULL
)
326 modelName
= CFSTR("Unknown model");
328 return CFStringCreateCopy(kCFAllocatorDefault
, modelName
);
331 static CFStringRef
CopyComputerName(SCDynamicStoreRef store
)
333 CFStringRef deviceName
= SCDynamicStoreCopyComputerName(store
, NULL
);
334 if (deviceName
== NULL
) {
335 deviceName
= CFSTR("Unknown name");
340 static bool _EngineMessageProtocolV2Enabled(void)
344 static dispatch_once_t onceToken
;
345 static bool v2_enabled
= false;
346 dispatch_once(&onceToken
, ^{
347 CFTypeRef v2Pref
= (CFNumberRef
)CFPreferencesCopyValue(CFSTR("engineV2"), CFSTR("com.apple.security"), kCFPreferencesAnyUser
, kCFPreferencesCurrentHost
);
349 if (v2Pref
&& CFGetTypeID(v2Pref
) == CFBooleanGetTypeID()) {
350 v2_enabled
= CFBooleanGetValue((CFBooleanRef
)v2Pref
);
351 secinfo("server", "Engine v2 : %s", v2_enabled
? "enabled":"disabled");
353 CFReleaseSafe(v2Pref
);
363 static CFDictionaryRef
CreateDeviceGestaltDictionary(SCDynamicStoreRef store
, CFArrayRef keys
, void *context
)
365 CFStringRef modelName
= CopyModelName();
366 CFStringRef computerName
= CopyComputerName(store
);
367 CFStringRef osVersion
= CopyOSVersion();
369 SInt32 version
= _EngineMessageProtocolV2Enabled() ? kEngineMessageProtocolVersion
: 0;
370 CFNumberRef protocolVersion
= CFNumberCreate(0, kCFNumberSInt32Type
, &version
);
372 CFDictionaryRef gestalt
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
373 kPIUserDefinedDeviceNameKey
, computerName
,
374 kPIDeviceModelNameKey
, modelName
,
375 kPIMessageProtocolVersionKey
, protocolVersion
,
376 kPIOSVersionKey
, osVersion
,
378 CFReleaseSafe(modelName
);
379 CFReleaseSafe(computerName
);
380 CFReleaseSafe(protocolVersion
);
385 static void SOSCCProcessGestaltUpdate(SCDynamicStoreRef store
, CFArrayRef keys
, void *context
)
387 do_with_account(^(SOSAccountRef account
) {
389 CFDictionaryRef gestalt
= CreateDeviceGestaltDictionary(store
, keys
, context
);
390 if (SOSAccountUpdateGestalt(account
, gestalt
)) {
391 notify_post(kSOSCCCircleChangedNotification
);
393 CFReleaseSafe(gestalt
);
399 static CFDictionaryRef
CreateDeviceGestaltDictionaryAndRegisterForUpdate(dispatch_queue_t queue
, void *info
)
401 SCDynamicStoreContext context
= { .info
= info
};
402 SCDynamicStoreRef store
= SCDynamicStoreCreate(NULL
, CFSTR("com.apple.securityd.cloudcircleserver"), SOSCCProcessGestaltUpdate
, &context
);
403 CFStringRef computerKey
= SCDynamicStoreKeyCreateComputerName(NULL
);
404 CFArrayRef keys
= NULL
;
405 CFDictionaryRef gestalt
= NULL
;
407 if (store
== NULL
|| computerKey
== NULL
) {
410 keys
= CFArrayCreate(NULL
, (const void **)&computerKey
, 1, &kCFTypeArrayCallBacks
);
414 gestalt
= CreateDeviceGestaltDictionary(store
, keys
, info
);
415 SCDynamicStoreSetNotificationKeys(store
, keys
, NULL
);
416 SCDynamicStoreSetDispatchQueue(store
, queue
);
419 if (store
) CFRelease(store
);
420 if (computerKey
) CFRelease(computerKey
);
421 if (keys
) CFRelease(keys
);
425 static void do_with_account(void (^action
)(SOSAccountRef account
));
426 static void do_with_account_async(void (^action
)(SOSAccountRef account
));
428 static SOSAccountRef
GetSharedAccount(void) {
429 static SOSAccountRef sSharedAccount
= NULL
;
430 static dispatch_once_t onceToken
;
432 #if !(TARGET_OS_EMBEDDED)
434 secerror("Cannot inflate account object as root");
439 dispatch_once(&onceToken
, ^{
440 secdebug("account", "Account Creation start");
442 CFDictionaryRef gestalt
= CreateDeviceGestaltDictionaryAndRegisterForUpdate(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), NULL
);
445 #if TARGET_OS_IPHONE && TARGET_IPHONE_SIMULATOR
446 gestalt
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, NULL
);
448 secerror("Didn't get machine gestalt! This is going to be ugly.");
452 sSharedAccount
= SOSKeychainAccountCreateSharedAccount(gestalt
);
454 SOSAccountAddChangeBlock(sSharedAccount
, ^(SOSCircleRef circle
,
455 CFSetRef peer_additions
, CFSetRef peer_removals
,
456 CFSetRef applicant_additions
, CFSetRef applicant_removals
) {
457 CFErrorRef pi_error
= NULL
;
458 SOSPeerInfoRef me
= SOSFullPeerInfoGetPeerInfo(sSharedAccount
->my_identity
);
460 secerror("Error finding me for change: %@", pi_error
);
462 // TODO: Figure out why peer_additions isn't right in some cases (like when joining a v2 circle with a v0 peer.
463 if (SOSCircleHasPeer(circle
, me
, NULL
) /* && CFSetGetCount(peer_additions) != 0 */) {
464 secnotice("updates", "Requesting Ensure Peer Registration.");
465 SOSCloudKeychainRequestEnsurePeerRegistration(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), NULL
);
467 secinfo("updates", "Not requesting Ensure Peer Registration, since it's not needed");
470 if (CFSetContainsValue(peer_additions
, me
)) {
471 // TODO: Potentially remove from here and move this to the engine
472 // TODO: We also need to do this when our views change.
473 SOSCCSyncWithAllPeers();
477 CFReleaseNull(pi_error
);
479 // TODO: We should notify the engine of these changes here
480 if (CFSetGetCount(peer_additions
) != 0 ||
481 CFSetGetCount(peer_removals
) != 0 ||
482 CFSetGetCount(applicant_additions
) != 0 ||
483 CFSetGetCount(applicant_removals
) != 0) {
485 if(CFSetGetCount(peer_removals
) != 0)
487 CFErrorRef localError
= NULL
;
488 CFMutableArrayRef removed
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
489 CFSetForEach(peer_removals
, ^(const void *value
) {
490 CFArrayAppendValue(removed
, value
);
492 SOSAccountRemoveBackupPeers(sSharedAccount
, removed
, &localError
);
494 secerror("Had trouble removing: %@, error: %@", removed
, localError
);
495 CFReleaseNull(localError
);
496 CFReleaseNull(removed
);
498 notify_post(kSOSCCCircleChangedNotification
);
499 // This might be a bit chatty for now, but it will get things moving for clients.
500 notify_post(kSOSCCViewMembershipChangedNotification
);
505 SOSCloudKeychainSetItemsChangedBlock(^CFArrayRef(CFDictionaryRef changes
) {
506 CFRetainSafe(changes
);
507 __block CFMutableArrayRef handledKeys
= NULL
;
508 do_with_account(^(SOSAccountRef account
) {
509 CFStringRef changeDescription
= SOSItemsChangedCopyDescription(changes
, false);
510 secdebug(SOSCKCSCOPE
, "Received: %@", changeDescription
);
511 CFReleaseSafe(changeDescription
);
513 CFErrorRef error
= NULL
;
514 handledKeys
= SOSTransportDispatchMessages(account
, changes
, &error
);
516 secerror("Error handling updates: %@", error
);
517 CFReleaseNull(error
);
520 CFReleaseSafe(changes
);
523 CFReleaseSafe(gestalt
);
525 // TODO: We should not be doing extra work whenever securityd is launched, let's see if we can eliminate this call
526 SOSCloudKeychainRequestEnsurePeerRegistration(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), NULL
);
530 return sSharedAccount
;
533 static void do_with_account_dynamic(void (^action
)(SOSAccountRef account
), bool sync
) {
534 Boolean keyExistsAndHasValue
= false;
535 whichTransportType
= CFPreferencesGetAppIntegerValue(CFSTR("Transport"), CFSTR("com.apple.security"), &keyExistsAndHasValue
);
536 SOSAccountRef account
= GetSharedAccount();
539 dispatch_block_t do_action_and_save
= ^{
540 SOSPeerInfoRef mpi
= SOSAccountGetMyPeerInfo(account
);
541 bool wasInCircle
= SOSAccountIsInCircle(account
, NULL
);
542 CFSetRef beforeViews
= mpi
? SOSPeerInfoCopyEnabledViews(mpi
) : NULL
;
546 // Fake transaction around using the account object
547 SOSAccountFinishTransaction(account
);
549 mpi
= SOSAccountGetMyPeerInfo(account
); // Update the peer
550 bool isInCircle
= SOSAccountIsInCircle(account
, NULL
);
552 CFSetRef afterViews
= mpi
? SOSPeerInfoCopyEnabledViews(mpi
) : NULL
;
554 if(!CFEqualSafe(beforeViews
, afterViews
) || wasInCircle
!= isInCircle
) {
555 notify_post(kSOSCCViewMembershipChangedNotification
);
558 CFReleaseNull(beforeViews
);
559 CFReleaseNull(afterViews
);
561 SOSKeychainAccountEnsureSaved(account
);
565 dispatch_sync(SOSAccountGetQueue(account
), do_action_and_save
);
567 dispatch_async(SOSAccountGetQueue(account
), do_action_and_save
);
572 __unused
static void do_with_account_async(void (^action
)(SOSAccountRef account
)) {
573 do_with_account_dynamic(action
, false);
576 static void do_with_account(void (^action
)(SOSAccountRef account
)) {
577 do_with_account_dynamic(action
, true);
580 static bool do_if_after_first_unlock(CFErrorRef
*error
, dispatch_block_t action
)
582 #if TARGET_IPHONE_SIMULATOR
586 bool beenUnlocked
= false;
587 require_quiet(SecAKSGetHasBeenUnlocked(&beenUnlocked
, error
), fail
);
589 require_action_quiet(beenUnlocked
, fail
,
590 SOSCreateErrorWithFormat(kSOSErrorNotReady
, NULL
, error
, NULL
,
591 CFSTR("Keybag never unlocked, ask after first unlock")));
601 static bool do_with_account_if_after_first_unlock(CFErrorRef
*error
, bool (^action
)(SOSAccountRef account
, CFErrorRef
* error
))
603 __block
bool action_result
= false;
605 #if !(TARGET_OS_EMBEDDED)
607 secerror("Cannot inflate account object as root");
609 *error
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("com.apple.security"), RUN_AS_ROOT_ERROR
, NULL
);
613 return do_if_after_first_unlock(error
, ^{
614 do_with_account(^(SOSAccountRef account
) {
615 action_result
= action(account
, error
);
621 static bool do_with_account_while_unlocked(CFErrorRef
*error
, bool (^action
)(SOSAccountRef account
, CFErrorRef
* error
))
623 __block
bool action_result
= false;
625 #if !(TARGET_OS_EMBEDDED)
627 secerror("Cannot inflate account object as root");
629 *error
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("com.apple.security"), RUN_AS_ROOT_ERROR
, NULL
);
634 return SecAKSDoWhileUserBagLocked(error
, ^{
635 do_with_account(^(SOSAccountRef account
) {
636 action_result
= action(account
, error
);
642 SOSAccountRef
SOSKeychainAccountGetSharedAccount()
644 __block SOSAccountRef result
= NULL
;
646 do_with_account(^(SOSAccountRef account
) {
654 // Mark: Credential processing
658 bool SOSCCTryUserCredentials_Server(CFStringRef user_label
, CFDataRef user_password
, CFErrorRef
*error
)
660 return do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
661 return SOSAccountTryUserCredentials(account
, user_label
, user_password
, block_error
);
666 SOSViewResultCode
SOSCCView_Server(CFStringRef viewname
, SOSViewActionCode action
, CFErrorRef
*error
) {
667 __block SOSViewResultCode status
= kSOSCCGeneralViewError
;
669 do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
671 case kSOSCCViewQuery
:
672 status
= SOSAccountViewStatus(account
, viewname
, error
);
674 case kSOSCCViewEnable
:
675 case kSOSCCViewDisable
: // fallthrough
676 status
= SOSAccountUpdateView(account
, viewname
, action
, error
);
677 secnotice("views", "HEY!!!!!! I'm Changing VIEWS- %d", (int) status
);
680 secnotice("views", "Bad SOSViewActionCode - %d", (int) action
);
690 bool SOSCCViewSet_Server(CFSetRef enabledViews
, CFSetRef disabledViews
) {
691 __block
bool status
= false;
693 do_with_account_if_after_first_unlock(NULL
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
694 status
= SOSAccountUpdateViewSets(account
, enabledViews
, disabledViews
);
702 SOSSecurityPropertyResultCode
SOSCCSecurityProperty_Server(CFStringRef property
, SOSSecurityPropertyActionCode action
, CFErrorRef
*error
) {
704 __block SOSViewResultCode status
= kSOSCCGeneralSecurityPropertyError
;
705 do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
707 case kSOSCCSecurityPropertyQuery
:
708 status
= SOSAccountSecurityPropertyStatus(account
, property
, error
);
710 case kSOSCCSecurityPropertyEnable
:
711 case kSOSCCSecurityPropertyDisable
: // fallthrough
712 status
= SOSAccountUpdateSecurityProperty(account
, property
, action
, error
);
713 secnotice("secprop", "HEY!!!!!! I'm Changing SecurityProperties- %d", (int) status
);
716 secnotice("secprop", "Bad SOSSecurityPropertyActionCode - %d", (int) action
);
725 void sync_the_last_data_to_kvs(SOSAccountRef account
, bool waitForeverForSynchronization
){
727 dispatch_semaphore_t wait_for
= dispatch_semaphore_create(0);
728 dispatch_retain(wait_for
); // Both this scope and the block own it.
730 __block
bool success
= false;
732 secnoticeq("force-push", "calling SOSCloudKeychainSynchronizeAndWait");
734 CFMutableArrayRef keysToGet
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
736 SOSCloudKeychainSynchronizeAndWait(keysToGet
, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), ^(CFDictionaryRef returnedValues
, CFErrorRef sync_error
) {
739 secerrorq("SOSCloudKeychainSynchronizeAndWait: %@", sync_error
);
741 secnoticeq("force-push", "returned from call; in callback to SOSCloudKeychainSynchronizeAndWait: results: %@", returnedValues
);
746 dispatch_semaphore_signal(wait_for
);
747 dispatch_release(wait_for
);
750 CFReleaseNull(keysToGet
);
752 if(waitForeverForSynchronization
)
753 dispatch_semaphore_wait(wait_for
, DISPATCH_TIME_FOREVER
);
755 dispatch_semaphore_wait(wait_for
, dispatch_time(DISPATCH_TIME_NOW
, 60ull * NSEC_PER_SEC
));
757 dispatch_release(wait_for
);
760 #define kWAIT2MINID "EFRESH"
762 static bool EnsureFreshParameters(SOSAccountRef account
, CFErrorRef
*error
) {
763 dispatch_semaphore_t wait_for
= dispatch_semaphore_create(0);
764 dispatch_retain(wait_for
); // Both this scope and the block own it.
766 CFMutableArrayRef keysToGet
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
767 CFArrayAppendValue(keysToGet
, kSOSKVSKeyParametersKey
);
768 // Only get key parameters due to: <rdar://problem/22794892> Upgrading from Donner with an iCDP enabled account resets iCloud keychain on devices in circle
770 __block CFDictionaryRef valuesToUpdate
= NULL
;
771 __block
bool success
= false;
773 secnoticeq("fresh", "%s calling SOSCloudKeychainSynchronizeAndWait", kWAIT2MINID
);
775 SOSCloudKeychainSynchronizeAndWait(keysToGet
, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), ^(CFDictionaryRef returnedValues
, CFErrorRef sync_error
) {
778 secerrorq("%s SOSCloudKeychainSynchronizeAndWait: %@", kWAIT2MINID
, sync_error
);
781 CFRetainSafe(*error
);
784 secnoticeq("fresh", "%s returned from call; in callback to SOSCloudKeychainSynchronizeAndWait: results: %@", kWAIT2MINID
, returnedValues
);
785 valuesToUpdate
= returnedValues
;
786 CFRetainSafe(valuesToUpdate
);
790 dispatch_semaphore_signal(wait_for
);
791 dispatch_release(wait_for
);
794 dispatch_semaphore_wait(wait_for
, DISPATCH_TIME_FOREVER
);
795 // TODO: Maybe we timeout here... used to dispatch_time(DISPATCH_TIME_NOW, 30ull * NSEC_PER_SEC));
796 dispatch_release(wait_for
);
797 CFMutableArrayRef handledKeys
= NULL
;
798 if ((valuesToUpdate
) && (account
)) {
799 handledKeys
= SOSTransportDispatchMessages(account
, valuesToUpdate
, error
);
801 secerrorq("%s Freshness update failed: %@", kWAIT2MINID
, error
? *error
: NULL
);
805 CFReleaseNull(handledKeys
);
806 CFReleaseNull(valuesToUpdate
);
807 CFReleaseNull(keysToGet
);
812 static bool Flush(CFErrorRef
*error
) {
813 __block
bool success
= false;
815 dispatch_semaphore_t wait_for
= dispatch_semaphore_create(0);
816 dispatch_retain(wait_for
); // Both this scope and the block own it.
818 secnotice("flush", "Starting");
820 SOSCloudKeychainFlush(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), ^(CFDictionaryRef returnedValues
, CFErrorRef sync_error
) {
821 success
= (sync_error
== NULL
);
823 CFRetainAssign(*error
, sync_error
);
826 dispatch_semaphore_signal(wait_for
);
827 dispatch_release(wait_for
);
830 dispatch_semaphore_wait(wait_for
, DISPATCH_TIME_FOREVER
);
831 dispatch_release(wait_for
);
833 secnotice("flush", "Returned %s", success
? "Success": "Failure");
838 static bool SOSCCAssertUserCredentialsAndOptionalDSID(CFStringRef user_label
, CFDataRef user_password
, CFStringRef dsid
, CFErrorRef
*error
) {
839 secnotice("updates", "Setting credentials and dsid (%@) for %@", dsid
, user_label
);
840 bool result
= do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
841 if (dsid
!= NULL
&& CFStringCompare(dsid
, CFSTR(""), 0) != 0) {
842 CFStringRef accountDSID
= SOSAccountGetValue(account
, kSOSDSIDKey
, NULL
);
843 if( accountDSID
== NULL
){
844 SOSAccountUpdateDSID(account
, dsid
);
845 secdebug("updates", "Setting dsid, current dsid is empty for this account: %@", dsid
);
847 else if(CFStringCompare(dsid
, accountDSID
, 0) != kCFCompareEqualTo
){
848 secnotice("updates", "Changing DSID from: %@ to %@", accountDSID
, dsid
);
850 //DSID has changed, blast the account!
851 SOSAccountSetToNew(account
);
853 //update DSID to the new DSID
854 SOSAccountUpdateDSID(account
, dsid
);
857 secnotice("updates", "Not Changing DSID: %@ to %@", accountDSID
, dsid
);
862 // Short Circuit if this passes, return immediately.
863 if(SOSAccountTryUserCredentials(account
, user_label
, user_password
, NULL
)) {
867 if (!EnsureFreshParameters(account
, block_error
)) {
870 if (!SOSAccountAssertUserCredentials(account
, user_label
, user_password
, block_error
)) {
871 secnotice("updates", "EnsureFreshParameters/SOSAccountAssertUserCredentials error: %@", *block_error
);
877 if (result
&& Flush(error
)) {
878 result
= do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
879 return SOSAccountGenerationSignatureUpdate(account
, error
);
886 bool SOSCCSetUserCredentialsAndDSID_Server(CFStringRef user_label
, CFDataRef user_password
, CFStringRef dsid
, CFErrorRef
*error
)
888 // TODO: Return error if DSID is NULL to insist our callers provide one?
889 return SOSCCAssertUserCredentialsAndOptionalDSID(user_label
, user_password
, dsid
, error
);
892 bool SOSCCSetUserCredentials_Server(CFStringRef user_label
, CFDataRef user_password
, CFErrorRef
*error
)
894 return SOSCCAssertUserCredentialsAndOptionalDSID(user_label
, user_password
, NULL
, error
);
897 bool SOSCCCanAuthenticate_Server(CFErrorRef
*error
)
899 bool result
= do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
900 return SOSAccountGetPrivateCredential(account
, block_error
) != NULL
;
903 if (!result
&& error
&& *error
&& CFErrorGetDomain(*error
) == kSOSErrorDomain
) {
904 CFIndex code
= CFErrorGetCode(*error
);
905 if (code
== kSOSErrorPrivateKeyAbsent
|| code
== kSOSErrorPublicKeyAbsent
) {
906 CFReleaseNull(*error
);
913 bool SOSCCPurgeUserCredentials_Server(CFErrorRef
*error
)
915 return do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
916 SOSAccountPurgePrivateCredential(account
);
921 SOSCCStatus
SOSCCThisDeviceIsInCircle_Server(CFErrorRef
*error
)
923 __block SOSCCStatus status
;
925 return do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
926 status
= SOSAccountGetCircleStatus(account
, block_error
);
928 }) ? status
: kSOSCCError
;
931 bool SOSCCRequestToJoinCircle_Server(CFErrorRef
* error
)
933 __block
bool result
= true;
935 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
936 result
= SOSAccountJoinCircles(account
, block_error
);
941 bool SOSCCRequestToJoinCircleAfterRestore_Server(CFErrorRef
* error
)
943 __block
bool result
= true;
944 bool returned
= false;
945 returned
= do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
946 SOSAccountEnsurePeerRegistration(account
, block_error
);
947 result
= SOSAccountJoinCirclesAfterRestore(account
, block_error
);
954 bool SOSCCRequestEnsureFreshParameters_Server(CFErrorRef
* error
)
956 __block
bool result
= true;
957 bool returned
= false;
958 returned
= do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
959 result
= EnsureFreshParameters(account
, NULL
);
965 bool SOSCCApplyToARing_Server(CFStringRef ringName
, CFErrorRef
*error
){
966 __block
bool result
= true;
967 bool returned
= false;
968 returned
= do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
969 SOSFullPeerInfoRef fpi
= SOSAccountGetMyFullPeerInfo(account
);
970 SOSRingRef ring
= SOSAccountGetRing(account
, ringName
, error
);
972 result
= SOSRingApply(ring
, account
->user_public
, fpi
, error
);
978 bool SOSCCWithdrawlFromARing_Server(CFStringRef ringName
, CFErrorRef
*error
){
979 __block
bool result
= true;
980 bool returned
= false;
981 returned
= do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
982 SOSFullPeerInfoRef fpi
= SOSAccountGetMyFullPeerInfo(account
);
983 SOSRingRef ring
= SOSAccountGetRing(account
, ringName
, error
);
985 result
= SOSRingWithdraw(ring
, account
->user_public
, fpi
, error
);
991 bool SOSCCEnableRing_Server(CFStringRef ringName
, CFErrorRef
*error
){
992 __block
bool result
= true;
993 bool returned
= false;
994 returned
= do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
995 SOSFullPeerInfoRef fpi
= SOSAccountGetMyFullPeerInfo(account
);
996 SOSRingRef ring
= SOSAccountGetRing(account
, ringName
, error
);
998 result
= SOSRingResetToOffering(ring
, NULL
, fpi
, error
); ;
1004 CFStringRef
SOSCCGetAllTheRings_Server(CFErrorRef
*error
){
1005 __block CFMutableDictionaryRef result
= NULL
;
1006 __block CFMutableStringRef description
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
1008 (void) do_with_account_while_unlocked(error
, ^bool(SOSAccountRef account
, CFErrorRef
*error
) {
1009 result
= SOSAccountGetRings(account
, error
);
1011 if(isDictionary(result
)){
1012 CFDictionaryForEach(result
, ^(const void *key
, const void *value
) {
1013 CFStringAppendFormat(description
, NULL
, CFSTR("%@"), value
);
1024 SOSRingStatus
SOSCCRingStatus_Server(CFStringRef ringName
, CFErrorRef
*error
){
1025 __block
bool result
= true;
1026 SOSRingStatus returned
;
1027 returned
= do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1028 SOSFullPeerInfoRef fpi
= SOSAccountGetMyFullPeerInfo(account
);
1029 SOSPeerInfoRef myPeer
= SOSFullPeerInfoGetPeerInfo(fpi
);
1031 SOSRingRef ring
= SOSAccountGetRing(account
, ringName
, error
);
1033 result
= SOSRingDeviceIsInRing(ring
, SOSPeerInfoGetPeerID(myPeer
));
1039 CFStringRef
SOSCCCopyDeviceID_Server(CFErrorRef
*error
)
1041 __block CFStringRef result
= NULL
;
1043 (void) do_with_account_while_unlocked(error
, ^bool(SOSAccountRef account
, CFErrorRef
*error
) {
1044 result
= SOSAccountCopyDeviceID(account
, error
);
1045 return (!isNull(result
));
1050 bool SOSCCSetDeviceID_Server(CFStringRef IDS
, CFErrorRef
*error
){
1052 bool didSetID
= false;
1053 __block
bool result
= false;
1054 __block CFErrorRef blockError
= NULL
;
1056 didSetID
= do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1057 result
= SOSAccountSetMyDSID(account
, IDS
, block_error
);
1059 blockError
= CFRetainSafe(*block_error
);
1064 *error
= blockError
;
1069 HandleIDSMessageReason
SOSCCHandleIDSMessage_Server(CFDictionaryRef messageDict
, CFErrorRef
* error
)
1071 // TODO: Locking flow:
1074 - Get PeerCoder instance from SOSPeerCoderManager(Currently Engine)
1075 - Get Account lock and Initialize PeerCoder instance if it isn't valid yet.
1077 - Decode incoming msg on coder.
1078 - Pass msg along to SOSPeerRef if decoding is done.
1079 - Force reply from coder while in handshake mode. (or ask ckd to ask us later?)
1082 - Lookup SOSPeerRef in SOSEngineRef (getting engine lock temporarily to get peer.
1083 - Ask peer to handle decoded message
1084 - be notified of changed objects in all peers and update peer/engine states
1085 - save peer/engine state
1088 - Ask coder to send an outgoing message if it is negotiating
1089 - Ask peer to create a message if needed
1090 - Encode peer msg with coder
1092 - send reply to ckd for transporting
1095 __block HandleIDSMessageReason result
= kHandleIDSMessageSuccess
;
1096 CFErrorRef action_error
= NULL
;
1098 if (!do_with_account_while_unlocked(&action_error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1099 result
= SOSTransportMessageIDSHandleMessage(account
, messageDict
, error
);
1103 if (SecErrorGetOSStatus(action_error
) == errSecInteractionNotAllowed
) {
1104 secnotice("updates", "SOSCCHandleIDSMessage_Server failed because device is locked; letting IDSKeychainSyncingProxy know");
1105 result
= kHandleIDSMessageLocked
; // tell IDSKeychainSyncingProxy to call us back when device unlocks
1106 CFReleaseNull(action_error
);
1108 secerror("Unexpected error: %@", action_error
);
1111 if (error
&& *error
== NULL
) {
1112 *error
= action_error
;
1113 action_error
= NULL
;
1116 CFReleaseNull(action_error
);
1122 bool SOSCCIDSPingTest_Server(CFStringRef message
, CFErrorRef
*error
){
1123 bool didSendTestMessages
= false;
1124 __block
bool result
= true;
1125 __block CFErrorRef blockError
= NULL
;
1127 didSendTestMessages
= do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1128 result
= SOSAccountStartPingTest(account
, message
, block_error
);
1130 blockError
= CFRetainSafe(*block_error
);
1133 if(blockError
&& error
!= NULL
)
1134 *error
= blockError
;
1136 return didSendTestMessages
;
1139 bool SOSCCIDSServiceRegistrationTest_Server(CFStringRef message
, CFErrorRef
*error
){
1140 bool didSendTestMessages
= false;
1141 __block
bool result
= true;
1142 __block CFErrorRef blockError
= NULL
;
1144 didSendTestMessages
= do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1145 result
= SOSAccountSendIDSTestMessage(account
, message
, &blockError
);
1148 if(blockError
!= NULL
&& error
!= NULL
)
1149 *error
= blockError
;
1151 return didSendTestMessages
;
1154 bool SOSCCIDSDeviceIDIsAvailableTest_Server(CFErrorRef
*error
){
1155 bool didSendTestMessages
= false;
1156 __block
bool result
= true;
1157 __block CFErrorRef blockError
= NULL
;
1159 didSendTestMessages
= do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1160 result
= SOSAccountRetrieveDeviceIDFromIDSKeychainSyncingProxy(account
, &blockError
);
1163 if(blockError
&& error
!= NULL
)
1164 *error
= blockError
;
1166 return didSendTestMessages
;
1169 bool SOSCCAccountSetToNew_Server(CFErrorRef
*error
)
1171 __block
bool result
= true;
1173 return do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1175 SOSAccountSetToNew(account
);
1180 bool SOSCCResetToOffering_Server(CFErrorRef
* error
)
1182 __block
bool result
= true;
1184 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1186 result
= SOSAccountResetToOffering(account
, block_error
);
1192 bool SOSCCResetToEmpty_Server(CFErrorRef
* error
)
1194 __block
bool result
= true;
1196 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1197 result
= SOSAccountResetToEmpty(account
, block_error
);
1203 bool SOSCCRemoveThisDeviceFromCircle_Server(CFErrorRef
* error
)
1205 __block
bool result
= true;
1207 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1208 result
= SOSAccountLeaveCircle(account
, block_error
);
1213 bool SOSCCRemovePeersFromCircle_Server(CFArrayRef peers
, CFErrorRef
* error
)
1215 __block
bool result
= true;
1217 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1218 result
= SOSAccountRemovePeersFromCircle(account
, peers
, block_error
);
1224 bool SOSCCLoggedOutOfAccount_Server(CFErrorRef
*error
)
1226 __block
bool result
= true;
1228 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1229 secnotice("sosops", "Signed out of account!");
1231 bool waitForeverForSynchronization
= true;
1233 result
= SOSAccountLeaveCircle(account
, block_error
);
1235 SOSAccountFinishTransaction(account
); // Make sure this gets finished before we set to new.
1237 SOSAccountSetToNew(account
);
1239 sync_the_last_data_to_kvs(account
, waitForeverForSynchronization
);
1245 bool SOSCCBailFromCircle_Server(uint64_t limit_in_seconds
, CFErrorRef
* error
)
1247 __block
bool result
= true;
1249 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1250 bool waitForeverForSynchronization
= false;
1252 result
= SOSAccountBail(account
, limit_in_seconds
, block_error
);
1254 SOSAccountFinishTransaction(account
); // Make sure this gets finished before we set to new.
1256 sync_the_last_data_to_kvs(account
, waitForeverForSynchronization
);
1263 CFArrayRef
SOSCCCopyApplicantPeerInfo_Server(CFErrorRef
* error
)
1265 __block CFArrayRef result
= NULL
;
1267 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1268 result
= SOSAccountCopyApplicants(account
, block_error
);
1269 return result
!= NULL
;
1275 CFArrayRef
SOSCCCopyGenerationPeerInfo_Server(CFErrorRef
* error
)
1277 __block CFArrayRef result
= NULL
;
1279 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1280 result
= SOSAccountCopyGeneration(account
, block_error
);
1281 return result
!= NULL
;
1287 CFArrayRef
SOSCCCopyValidPeerPeerInfo_Server(CFErrorRef
* error
)
1289 __block CFArrayRef result
= NULL
;
1291 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1292 result
= SOSAccountCopyValidPeers(account
, block_error
);
1293 return result
!= NULL
;
1299 bool SOSCCValidateUserPublic_Server(CFErrorRef
* error
)
1301 __block
bool result
= NULL
;
1303 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1304 result
= SOSValidateUserPublic(account
, block_error
);
1311 CFArrayRef
SOSCCCopyNotValidPeerPeerInfo_Server(CFErrorRef
* error
)
1313 __block CFArrayRef result
= NULL
;
1315 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1316 result
= SOSAccountCopyNotValidPeers(account
, block_error
);
1317 return result
!= NULL
;
1323 CFArrayRef
SOSCCCopyRetirementPeerInfo_Server(CFErrorRef
* error
)
1325 __block CFArrayRef result
= NULL
;
1327 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1328 result
= SOSAccountCopyRetired(account
, block_error
);
1329 return result
!= NULL
;
1335 CFArrayRef
SOSCCCopyViewUnawarePeerInfo_Server(CFErrorRef
* error
)
1337 __block CFArrayRef result
= NULL
;
1339 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1340 result
= SOSAccountCopyViewUnaware(account
, block_error
);
1341 return result
!= NULL
;
1347 CFArrayRef
SOSCCCopyEngineState_Server(CFErrorRef
* error
)
1349 CFArrayRef result
= NULL
;
1350 SOSDataSourceFactoryRef dsf
= SecItemDataSourceFactoryGetDefault();
1351 SOSDataSourceRef ds
= SOSDataSourceFactoryCreateDataSource(dsf
, kSecAttrAccessibleWhenUnlocked
, error
);
1353 SOSEngineRef engine
= SOSDataSourceGetSharedEngine(ds
, error
);
1354 result
= SOSEngineCopyPeerConfirmedDigests(engine
, error
);
1355 SOSDataSourceRelease(ds
, error
);
1361 static CFStringRef
CreateUUIDString() {
1362 CFUUIDRef uuid
= CFUUIDCreate(kCFAllocatorDefault
);
1363 CFStringRef result
= CFUUIDCreateString(kCFAllocatorDefault
, uuid
);
1364 CFReleaseNull(uuid
);
1368 static CFStringRef
SOSAccountCallWhenInSync(SOSAccountRef account
, SOSAccountWaitForInitialSyncBlock syncBlock
) {
1369 //if we are not initially synced
1370 CFStringRef id
= NULL
;
1371 CFTypeRef unSyncedViews
= SOSAccountGetValue(account
, kSOSUnsyncedViewsKey
, NULL
);
1372 if (unSyncedViews
!= NULL
) {
1373 id
= CreateUUIDString();
1374 secnotice("initial-sync", "adding sync block [%@] to array!", id
);
1375 SOSAccountWaitForInitialSyncBlock copy
= Block_copy(syncBlock
);
1376 CFDictionarySetValue(account
->waitForInitialSync_blocks
, id
, copy
);
1377 Block_release(copy
);
1385 static bool SOSAccountUnregisterCallWhenInSync(SOSAccountRef account
, CFStringRef id
) {
1386 bool removed
= CFDictionaryGetValueIfPresent(account
->waitForInitialSync_blocks
, id
, NULL
);
1387 CFDictionaryRemoveValue(account
->waitForInitialSync_blocks
, id
);
1391 bool SOSCCWaitForInitialSync_Server(CFErrorRef
* error
) {
1393 __block dispatch_semaphore_t inSyncSema
= NULL
;
1394 __block
bool result
= false;
1395 __block
bool synced
= false;
1396 bool timed_out
= false;
1398 __block CFStringRef inSyncCallID
= NULL
;
1400 secnotice("initial sync", "Wait for initial sync start!");
1402 result
= do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1403 bool alreadyInSync
= SOSAccountCheckHasBeenInSync(account
);
1405 if (!alreadyInSync
) {
1406 inSyncSema
= dispatch_semaphore_create(0);
1407 dispatch_retain(inSyncSema
); // For the block
1409 inSyncCallID
= SOSAccountCallWhenInSync(account
, ^bool(SOSAccountRef mightBeSynced
) {
1410 secerror("might be synced!");
1411 synced
= SOSAccountCheckHasBeenInSync(mightBeSynced
);
1413 dispatch_semaphore_signal(inSyncSema
);
1414 dispatch_release(inSyncSema
);
1415 SOSAccountEnsureBackupStarts(account
);
1420 SOSAccountEnsureBackupStarts(account
);
1426 require_quiet(result
, fail
);
1428 timed_out
= dispatch_semaphore_wait(inSyncSema
, dispatch_time(DISPATCH_TIME_NOW
, 300ull * NSEC_PER_SEC
));
1431 do_with_account(^(SOSAccountRef account
) {
1432 if (SOSAccountUnregisterCallWhenInSync(account
, inSyncCallID
)) {
1433 dispatch_release(inSyncSema
); // if we unregistered we release the sema
1438 secerror("waiting for initial sync timed out, resetting account");
1441 SOSCCLoggedOutOfAccount_Server(error
);
1442 SOSErrorCreate(kSOSInitialSyncFailed
, error
, NULL
, CFSTR("InitialSyncTimedOut"));
1446 dispatch_release(inSyncSema
);
1447 inSyncSema
= NULL
; // We've canceled the timeout so we must be the last.
1449 require_quiet(result
, fail
);
1452 xpc_transaction_begin();
1454 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), ^{
1455 result
= do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1456 result
= SOSAccountIsInCircle(account
, NULL
);
1457 xpc_transaction_end();
1463 secerror("waiting for initial sync: left circle");
1466 SOSErrorCreate(kSOSInitialSyncFailed
, error
, NULL
, CFSTR("Left circle before initial sync."));
1469 secnotice("initial sync", "Finished!: %d", result
);
1471 CFReleaseNull(inSyncCallID
);
1476 static CFArrayRef
SOSAccountCopyYetToSyncViews(SOSAccountRef account
, CFErrorRef
*error
) {
1477 __block CFArrayRef result
= NULL
;
1479 CFTypeRef valueFetched
= SOSAccountGetValue(account
, kSOSUnsyncedViewsKey
, error
);
1480 if (valueFetched
== kCFBooleanTrue
) {
1481 SOSPeerInfoRef myPI
= SOSAccountGetMyPeerInfo(account
);
1483 SOSPeerInfoWithEnabledViewSet(myPI
, ^(CFSetRef enabled
) {
1484 result
= CFSetCopyValues(enabled
);
1487 } else if (isSet(valueFetched
)) {
1488 result
= CFSetCopyValues((CFSetRef
)valueFetched
);
1491 if (result
== NULL
) {
1492 result
= CFArrayCreateForCFTypes(kCFAllocatorDefault
, NULL
);
1498 CFArrayRef
SOSCCCopyYetToSyncViewsList_Server(CFErrorRef
* error
) {
1500 __block CFArrayRef views
= NULL
;
1502 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1503 views
= SOSAccountCopyYetToSyncViews(account
, error
);
1511 bool SOSWrapToBackupSliceKeyBagForView_Server(CFStringRef viewName
, CFDataRef input
, CFDataRef
* output
, CFDataRef
* bskbEncoded
, CFErrorRef
* error
) {
1512 CFErrorRef localerror
= NULL
;
1513 SOSBackupSliceKeyBagRef bskb
= SOSBackupSliceKeyBagForView(viewName
, &localerror
);
1515 if(bskbEncoded
&& bskb
) {
1516 *bskbEncoded
= SOSBSKBCopyEncoded(bskb
, &localerror
);
1520 *output
= SOSWrapToBackupSliceKeyBag(bskb
, input
, &localerror
);
1524 *error
= localerror
;
1526 return localerror
== NULL
;
1529 SOSBackupSliceKeyBagRef
SOSBackupSliceKeyBagForView(CFStringRef viewName
, CFErrorRef
* error
){
1530 __block SOSBackupSliceKeyBagRef bskb
= NULL
;
1531 (void) do_with_account(^ (SOSAccountRef account
) {
1532 bskb
= SOSAccountBackupSliceKeyBagForView(account
, viewName
, error
);
1537 CFDataRef
SOSWrapToBackupSliceKeyBag(SOSBackupSliceKeyBagRef bskb
, CFDataRef input
, CFErrorRef
* error
) {
1538 CFDataRef encrypted
= NULL
;
1539 bskb_keybag_handle_t bskb_handle
= 0;
1541 require_quiet(bskb
, exit
);
1543 bskb_handle
= SOSBSKBLoadLocked(bskb
, error
);
1544 require_quiet(bskb_handle
, exit
);
1546 SecAccessControlRef access
= NULL
;
1547 require_quiet(access
= SecAccessControlCreate(kCFAllocatorDefault
, error
), exit
);
1548 require_quiet(SecAccessControlSetProtection(access
, kSecAttrAccessibleWhenUnlocked
, error
), exit
);
1550 // ks_encrypt_data takes a dictionary as its plaintext.
1551 CFMutableDictionaryRef plaintext
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1552 CFDictionarySetValue(plaintext
, CFSTR("data"), input
);
1554 require_quiet(ks_encrypt_data(bskb_handle
, access
, NULL
, plaintext
, NULL
, &encrypted
, error
), exit
);
1557 CFReleaseNull(bskb
);
1558 if(bskb_handle
!= 0) {
1559 ks_close_keybag(bskb_handle
, error
);
1561 if(error
&& *error
) {
1562 secnotice("backup", "Failed to wrap to a BKSB: %@", *error
);
1568 CFDictionaryRef
SOSCCCopyEscrowRecord_Server(CFErrorRef
*error
){
1570 __block CFDictionaryRef result
= NULL
;
1571 __block CFErrorRef block_error
= NULL
;
1573 (void) do_with_account_if_after_first_unlock(error
, ^bool(SOSAccountRef account
, CFErrorRef
*error
) {
1574 SOSCCStatus status
= SOSAccountGetCircleStatus(account
, &block_error
);
1575 CFStringRef dsid
= SOSAccountGetValue(account
, kSOSDSIDKey
, error
);
1576 CFDictionaryRef escrowRecords
= NULL
;
1577 CFDictionaryRef record
= NULL
;
1579 case kSOSCCInCircle
:
1580 //get the escrow record in the peer info!
1581 escrowRecords
= SOSPeerInfoCopyEscrowRecord(SOSAccountGetMyPeerInfo(account
));
1583 record
= CFDictionaryGetValue(escrowRecords
, dsid
);
1585 result
= CFRetainSafe(record
);
1587 CFReleaseNull(escrowRecords
);
1589 case kSOSCCRequestPending
:
1590 //set the escrow record in the peer info/application?
1592 case kSOSCCNotInCircle
:
1593 case kSOSCCCircleAbsent
:
1594 //set the escrow record in the account expansion!
1595 escrowRecords
= SOSAccountGetValue(account
, kSOSEscrowRecord
, error
);
1597 record
= CFDictionaryGetValue(escrowRecords
, dsid
);
1599 result
= CFRetainSafe(record
);
1603 secdebug("account", "no circle status!");
1612 bool SOSCCSetEscrowRecord_Server(CFStringRef escrow_label
, uint64_t tries
, CFErrorRef
*error
){
1614 __block
bool result
= true;
1615 __block CFErrorRef block_error
= NULL
;
1617 (void) do_with_account_if_after_first_unlock(error
, ^bool(SOSAccountRef account
, CFErrorRef
*error
) {
1618 SOSCCStatus status
= SOSAccountGetCircleStatus(account
, &block_error
);
1619 CFStringRef dsid
= SOSAccountGetValue(account
, kSOSDSIDKey
, error
);
1621 CFMutableStringRef timeDescription
= CFStringCreateMutableCopy(kCFAllocatorDefault
, 0, CFSTR("["));
1622 CFAbsoluteTime currentTimeAndDate
= CFAbsoluteTimeGetCurrent();
1624 withStringOfAbsoluteTime(currentTimeAndDate
, ^(CFStringRef decription
) {
1625 CFStringAppend(timeDescription
, decription
);
1627 CFStringAppend(timeDescription
, CFSTR("]"));
1629 CFNumberRef attempts
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberLongLongType
, (const void*)&tries
);
1631 CFMutableDictionaryRef escrowTimeAndTries
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1632 CFDictionaryAddValue(escrowTimeAndTries
, kSOSBurnedRecoveryAttemptCount
, attempts
);
1633 CFDictionaryAddValue(escrowTimeAndTries
, kSOSBurnedRecoveryAttemptAttestationDate
, timeDescription
);
1635 CFMutableDictionaryRef escrowRecord
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1636 CFDictionaryAddValue(escrowRecord
, escrow_label
, escrowTimeAndTries
);
1639 case kSOSCCInCircle
:
1640 //set the escrow record in the peer info!
1641 if(!SOSFullPeerInfoAddEscrowRecord(SOSAccountGetMyFullPeerInfo(account
), dsid
, escrowRecord
, error
)){
1642 secdebug("accout", "Could not set escrow record in the full peer info");
1646 case kSOSCCRequestPending
:
1647 //set the escrow record in the peer info/application?
1649 case kSOSCCNotInCircle
:
1650 case kSOSCCCircleAbsent
:
1651 //set the escrow record in the account expansion!
1653 if(!SOSAccountAddEscrowRecords(account
, dsid
, escrowRecord
, error
)) {
1654 secdebug("account", "Could not set escrow record in expansion data");
1659 secdebug("account", "no circle status!");
1662 CFReleaseNull(attempts
);
1663 CFReleaseNull(timeDescription
);
1664 CFReleaseNull(escrowTimeAndTries
);
1665 CFReleaseNull(escrowRecord
);
1673 bool SOSCCAcceptApplicants_Server(CFArrayRef applicants
, CFErrorRef
* error
)
1675 __block
bool result
= true;
1676 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1677 result
= SOSAccountAcceptApplicants(account
, applicants
, block_error
);
1683 bool SOSCCRejectApplicants_Server(CFArrayRef applicants
, CFErrorRef
* error
)
1685 __block
bool result
= true;
1686 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1687 result
= SOSAccountRejectApplicants(account
, applicants
, block_error
);
1692 CFArrayRef
SOSCCCopyPeerPeerInfo_Server(CFErrorRef
* error
)
1694 __block CFArrayRef result
= NULL
;
1696 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1697 result
= SOSAccountCopyPeers(account
, block_error
);
1698 return result
!= NULL
;
1704 CFArrayRef
SOSCCCopyConcurringPeerPeerInfo_Server(CFErrorRef
* error
)
1706 __block CFArrayRef result
= NULL
;
1708 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1709 result
= SOSAccountCopyConcurringPeers(account
, block_error
);
1710 return result
!= NULL
;
1716 SOSPeerInfoRef
SOSCCCopyMyPeerInfo_Server(CFErrorRef
* error
)
1718 __block SOSPeerInfoRef result
= NULL
;
1720 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1721 // Create a copy to be DERed/sent back to client
1722 result
= SOSPeerInfoCreateCopy(kCFAllocatorDefault
, SOSAccountGetMyPeerInfo(account
), block_error
);
1723 return result
!= NULL
;
1729 CFDataRef
SOSCCCopyAccountState_Server(CFErrorRef
* error
)
1731 __block CFDataRef accountState
= NULL
;
1733 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1734 // Copy account state from the keychain
1735 accountState
= SOSAccountCopyAccountStateFromKeychain(block_error
);
1736 return accountState
!= NULL
;
1739 return accountState
;
1742 bool SOSCCDeleteAccountState_Server(CFErrorRef
* error
)
1744 __block
bool result
= NULL
;
1746 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1747 // Delete account state from the keychain
1748 result
= SOSAccountDeleteAccountStateFromKeychain(block_error
);
1755 CFDataRef
SOSCCCopyEngineData_Server(CFErrorRef
* error
)
1757 __block CFDataRef engineState
= NULL
;
1759 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1760 // Copy engine state from the keychain
1761 engineState
= SOSAccountCopyEngineStateFromKeychain(block_error
);
1762 return engineState
!= NULL
;
1768 bool SOSCCDeleteEngineState_Server(CFErrorRef
* error
)
1770 __block
bool result
= NULL
;
1772 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1773 // Delete engine state from the keychain
1774 result
= SOSAccountDeleteEngineStateFromKeychain(block_error
);
1783 SOSPeerInfoRef
SOSCCSetNewPublicBackupKey_Server(CFDataRef newPublicBackup
, CFErrorRef
*error
){
1784 __block SOSPeerInfoRef result
= NULL
;
1786 (void) do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1787 if(SOSAccountSetBackupPublicKey(account
,newPublicBackup
, error
)){
1788 // Create a copy to be DERed/sent back to client
1789 result
= SOSPeerInfoCreateCopy(kCFAllocatorDefault
, SOSAccountGetMyPeerInfo(account
), block_error
);
1790 secdebug("backup", "SOSCCSetNewPublicBackupKey_Server, new public backup is set");
1794 secerror("SOSCCSetNewPublicBackupKey_Server, could not set new public backup");
1796 return result
!= NULL
;
1802 bool SOSCCRegisterSingleRecoverySecret_Server(CFDataRef aks_bag
, bool setupV0Only
, CFErrorRef
*error
){
1803 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1804 return SOSAccountSetBSKBagForAllSlices(account
, aks_bag
, setupV0Only
, error
);
1808 CFStringRef
SOSCCCopyIncompatibilityInfo_Server(CFErrorRef
* error
)
1810 __block CFStringRef result
= NULL
;
1812 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1813 result
= SOSAccountCopyIncompatibilityInfo(account
, block_error
);
1814 return result
!= NULL
;
1820 bool SOSCCCheckPeerAvailability_Server(CFErrorRef
*error
)
1822 __block
bool pingedPeersInCircle
= false;
1823 __block dispatch_semaphore_t peerSemaphore
= NULL
;
1824 __block
bool peerIsAvailable
= false;
1826 static dispatch_queue_t time_out
;
1827 static dispatch_once_t once
;
1828 dispatch_once(&once
, ^{
1829 time_out
= dispatch_queue_create("peersAvailableTimeout", DISPATCH_QUEUE_SERIAL
);
1831 __block
int token
= -1;
1833 bool result
= do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1835 peerSemaphore
= dispatch_semaphore_create(0);
1836 dispatch_retain(peerSemaphore
);
1837 notify_register_dispatch(kSOSCCPeerAvailable
, &token
, time_out
, ^(int token
) {
1838 if(peerSemaphore
!= NULL
){
1839 dispatch_semaphore_signal(peerSemaphore
);
1840 dispatch_release(peerSemaphore
);
1841 peerIsAvailable
= true;
1842 notify_cancel(token
);
1846 pingedPeersInCircle
= SOSAccountCheckPeerAvailability(account
, block_error
);
1847 return pingedPeersInCircle
;
1851 dispatch_semaphore_wait(peerSemaphore
, dispatch_time(DISPATCH_TIME_NOW
, 7ull * NSEC_PER_SEC
));
1854 if(peerSemaphore
!= NULL
)
1855 dispatch_release(peerSemaphore
);
1857 if(time_out
!= NULL
&& peerSemaphore
!= NULL
){
1858 dispatch_sync(time_out
, ^{
1859 if(!peerIsAvailable
){
1860 dispatch_release(peerSemaphore
);
1861 peerSemaphore
= NULL
;
1862 notify_cancel(token
);
1863 secnotice("peer available", "checking peer availability timed out, releasing semaphore");
1867 if(!peerIsAvailable
){
1868 CFStringRef errorMessage
= CFSTR("There are no peers in the circle currently available");
1869 CFDictionaryRef userInfo
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, kCFErrorLocalizedDescriptionKey
, errorMessage
, NULL
);
1871 *error
=CFErrorCreate(kCFAllocatorDefault
, CFSTR("com.apple.security.ids.error"), kSecIDSErrorNoPeersAvailable
, userInfo
);
1872 secerror("%@", *error
);
1874 CFReleaseNull(userInfo
);
1883 enum DepartureReason
SOSCCGetLastDepartureReason_Server(CFErrorRef
* error
)
1885 __block
enum DepartureReason result
= kSOSDepartureReasonError
;
1887 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1888 result
= SOSAccountGetLastDepartureReason(account
, block_error
);
1889 return result
!= kSOSDepartureReasonError
;
1895 bool SOSCCSetLastDepartureReason_Server(enum DepartureReason reason
, CFErrorRef
*error
){
1896 __block
bool result
= true;
1898 return do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1899 SOSAccountSetLastDepartureReason(account
, reason
);
1904 bool SOSCCSetHSA2AutoAcceptInfo_Server(CFDataRef pubKey
, CFErrorRef
*error
) {
1905 __block
bool result
= true;
1907 return do_with_account_if_after_first_unlock(error
, ^(SOSAccountRef account
,
1908 CFErrorRef
*block_error
) {
1909 result
= SOSAccountSetHSAPubKeyExpected(account
, pubKey
, error
);
1910 return (bool)result
;
1914 bool SOSCCProcessEnsurePeerRegistration_Server(CFErrorRef
* error
)
1916 secnotice("updates", "Request for registering peers");
1917 return do_with_account_while_unlocked(error
, ^bool(SOSAccountRef account
, CFErrorRef
*error
) {
1918 return SOSAccountEnsurePeerRegistration(account
, error
);
1922 SyncWithAllPeersReason
SOSCCProcessSyncWithAllPeers_Server(CFErrorRef
* error
)
1925 #define kIOReturnLockedRead iokit_common_err(0x2c3) // device read locked
1926 #define kIOReturnLockedWrite iokit_common_err(0x2c4) // device write locked
1928 __block SyncWithAllPeersReason result
= kSyncWithAllPeersSuccess
;
1929 CFErrorRef action_error
= NULL
;
1931 if (!do_with_account_while_unlocked(&action_error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1932 CFErrorRef localError
= NULL
;
1934 if (!SOSAccountSyncWithAllPeers(account
, &localError
)) {
1935 secerror("sync with all peers failed: %@", localError
);
1936 CFReleaseSafe(localError
);
1937 // This isn't a device-locked error, but returning false will
1938 // have CloudKeychainProxy ask us to try sync again after next unlock
1939 result
= kSyncWithAllPeersOtherFail
;
1945 if (SecErrorGetOSStatus(action_error
) == errSecInteractionNotAllowed
) {
1946 secnotice("updates", "SOSAccountSyncWithAllPeers failed because device is locked; letting CloudKeychainProxy know");
1947 result
= kSyncWithAllPeersLocked
; // tell CloudKeychainProxy to call us back when device unlocks
1948 CFReleaseNull(action_error
);
1950 secerror("Unexpected error: %@", action_error
);
1953 if (error
&& *error
== NULL
) {
1954 *error
= action_error
;
1955 action_error
= NULL
;
1958 CFReleaseNull(action_error
);
1965 void SOSCCSyncWithAllPeers(void)
1967 SOSCloudKeychainRequestSyncWithAllPeers(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), NULL
);
1970 CF_RETURNS_RETAINED CFArrayRef
SOSCCHandleUpdateMessage(CFDictionaryRef updates
)
1972 CFArrayRef result
= NULL
;
1973 SOSAccountRef account
= SOSKeychainAccountGetSharedAccount(); //HACK to make sure itemsChangedBlock is set
1975 (account
) ? (result
= SOSCloudKeychainHandleUpdateMessage(updates
)) : (result
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
));