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>
72 #if TARGET_OS_EMBEDDED || TARGET_IPHONE_SIMULATOR
73 #include <MobileGestalt.h>
75 #include <AppleSystemInfo/AppleSystemInfo.h>
77 // We need authorization, but that doesn't exist
78 // on sec built for desktop (iOS in a process)
79 // Define AuthorizationRef here to make SystemConfiguration work
81 typedef const struct AuthorizationOpaqueRef
* AuthorizationRef
;
84 #define SOSCKCSCOPE "sync"
85 #define RUN_AS_ROOT_ERROR 550
87 #define USE_SYSTEMCONFIGURATION_PRIVATE_HEADERS
88 #import <SystemConfiguration/SystemConfiguration.h>
92 static SOSCCAccountDataSourceFactoryBlock accountDataSourceOverride
= NULL
;
94 bool SOSKeychainAccountSetFactoryForAccount(SOSCCAccountDataSourceFactoryBlock block
)
96 accountDataSourceOverride
= Block_copy(block
);
105 static void do_with_account(void (^action
)(SOSAccountRef account
));
106 static void do_with_account_async(void (^action
)(SOSAccountRef account
));
111 CFStringRef kSOSInternalAccessGroup
= CFSTR("com.apple.security.sos");
113 CFStringRef kSOSAccountLabel
= CFSTR("iCloud Keychain Account Meta-data");
115 CFStringRef kSOSBurnedRecoveryAttemptCount
= CFSTR("Burned Recovery Attempt Count");
117 CFStringRef kSOSBurnedRecoveryAttemptAttestationDate
= CFSTR("Burned Recovery Attempt Attestation Date");
119 static CFStringRef accountFileName
= CFSTR("PersistedAccount.plist");
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 SOSAccountRef account
= GetSharedAccount();
537 dispatch_block_t do_action_and_save
= ^{
538 SOSPeerInfoRef mpi
= SOSAccountGetMyPeerInfo(account
);
539 bool wasInCircle
= SOSAccountIsInCircle(account
, NULL
);
540 CFSetRef beforeViews
= mpi
? SOSPeerInfoCopyEnabledViews(mpi
) : NULL
;
544 // Fake transaction around using the account object
545 SOSAccountFinishTransaction(account
);
547 mpi
= SOSAccountGetMyPeerInfo(account
); // Update the peer
548 bool isInCircle
= SOSAccountIsInCircle(account
, NULL
);
550 CFSetRef afterViews
= mpi
? SOSPeerInfoCopyEnabledViews(mpi
) : NULL
;
552 if(!CFEqualSafe(beforeViews
, afterViews
) || wasInCircle
!= isInCircle
) {
553 notify_post(kSOSCCViewMembershipChangedNotification
);
556 CFReleaseNull(beforeViews
);
557 CFReleaseNull(afterViews
);
559 SOSKeychainAccountEnsureSaved(account
);
563 dispatch_sync(SOSAccountGetQueue(account
), do_action_and_save
);
565 dispatch_async(SOSAccountGetQueue(account
), do_action_and_save
);
570 __unused
static void do_with_account_async(void (^action
)(SOSAccountRef account
)) {
571 do_with_account_dynamic(action
, false);
574 static void do_with_account(void (^action
)(SOSAccountRef account
)) {
575 do_with_account_dynamic(action
, true);
578 static bool do_if_after_first_unlock(CFErrorRef
*error
, dispatch_block_t action
)
580 #if TARGET_IPHONE_SIMULATOR
584 bool beenUnlocked
= false;
585 require_quiet(SecAKSGetHasBeenUnlocked(&beenUnlocked
, error
), fail
);
587 require_action_quiet(beenUnlocked
, fail
,
588 SOSCreateErrorWithFormat(kSOSErrorNotReady
, NULL
, error
, NULL
,
589 CFSTR("Keybag never unlocked, ask after first unlock")));
599 static bool do_with_account_if_after_first_unlock(CFErrorRef
*error
, bool (^action
)(SOSAccountRef account
, CFErrorRef
* error
))
601 __block
bool action_result
= false;
603 #if !(TARGET_OS_EMBEDDED)
605 secerror("Cannot inflate account object as root");
607 *error
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("com.apple.security"), RUN_AS_ROOT_ERROR
, NULL
);
611 return do_if_after_first_unlock(error
, ^{
612 do_with_account(^(SOSAccountRef account
) {
613 action_result
= action(account
, error
);
619 static bool do_with_account_while_unlocked(CFErrorRef
*error
, bool (^action
)(SOSAccountRef account
, CFErrorRef
* error
))
621 __block
bool action_result
= false;
623 #if !(TARGET_OS_EMBEDDED)
625 secerror("Cannot inflate account object as root");
627 *error
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("com.apple.security"), RUN_AS_ROOT_ERROR
, NULL
);
632 return SecAKSDoWhileUserBagLocked(error
, ^{
633 do_with_account(^(SOSAccountRef account
) {
634 action_result
= action(account
, error
);
640 SOSAccountRef
SOSKeychainAccountGetSharedAccount()
642 __block SOSAccountRef result
= NULL
;
644 do_with_account(^(SOSAccountRef account
) {
652 // Mark: Credential processing
656 bool SOSCCTryUserCredentials_Server(CFStringRef user_label
, CFDataRef user_password
, CFErrorRef
*error
)
658 return do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
659 return SOSAccountTryUserCredentials(account
, user_label
, user_password
, block_error
);
664 SOSViewResultCode
SOSCCView_Server(CFStringRef viewname
, SOSViewActionCode action
, CFErrorRef
*error
) {
665 __block SOSViewResultCode status
= kSOSCCGeneralViewError
;
667 do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
669 case kSOSCCViewQuery
:
670 status
= SOSAccountViewStatus(account
, viewname
, error
);
672 case kSOSCCViewEnable
:
673 case kSOSCCViewDisable
: // fallthrough
674 status
= SOSAccountUpdateView(account
, viewname
, action
, error
);
675 secnotice("views", "HEY!!!!!! I'm Changing VIEWS- %d", (int) status
);
678 secnotice("views", "Bad SOSViewActionCode - %d", (int) action
);
688 bool SOSCCViewSet_Server(CFSetRef enabledViews
, CFSetRef disabledViews
) {
689 __block
bool status
= false;
691 do_with_account_if_after_first_unlock(NULL
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
692 status
= SOSAccountUpdateViewSets(account
, enabledViews
, disabledViews
);
700 SOSSecurityPropertyResultCode
SOSCCSecurityProperty_Server(CFStringRef property
, SOSSecurityPropertyActionCode action
, CFErrorRef
*error
) {
702 __block SOSViewResultCode status
= kSOSCCGeneralSecurityPropertyError
;
703 do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
705 case kSOSCCSecurityPropertyQuery
:
706 status
= SOSAccountSecurityPropertyStatus(account
, property
, error
);
708 case kSOSCCSecurityPropertyEnable
:
709 case kSOSCCSecurityPropertyDisable
: // fallthrough
710 status
= SOSAccountUpdateSecurityProperty(account
, property
, action
, error
);
711 secnotice("secprop", "HEY!!!!!! I'm Changing SecurityProperties- %d", (int) status
);
714 secnotice("secprop", "Bad SOSSecurityPropertyActionCode - %d", (int) action
);
723 void sync_the_last_data_to_kvs(SOSAccountRef account
, bool waitForeverForSynchronization
){
725 dispatch_semaphore_t wait_for
= dispatch_semaphore_create(0);
726 dispatch_retain(wait_for
); // Both this scope and the block own it.
728 __block
bool success
= false;
730 secnoticeq("force-push", "calling SOSCloudKeychainSynchronizeAndWait");
732 CFMutableArrayRef keysToGet
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
734 SOSCloudKeychainSynchronizeAndWait(keysToGet
, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), ^(CFDictionaryRef returnedValues
, CFErrorRef sync_error
) {
737 secerrorq("SOSCloudKeychainSynchronizeAndWait: %@", sync_error
);
739 secnoticeq("force-push", "returned from call; in callback to SOSCloudKeychainSynchronizeAndWait: results: %@", returnedValues
);
744 dispatch_semaphore_signal(wait_for
);
745 dispatch_release(wait_for
);
748 CFReleaseNull(keysToGet
);
750 if(waitForeverForSynchronization
)
751 dispatch_semaphore_wait(wait_for
, DISPATCH_TIME_FOREVER
);
753 dispatch_semaphore_wait(wait_for
, dispatch_time(DISPATCH_TIME_NOW
, 60ull * NSEC_PER_SEC
));
755 dispatch_release(wait_for
);
758 #define kWAIT2MINID "EFRESH"
760 static bool EnsureFreshParameters(SOSAccountRef account
, CFErrorRef
*error
) {
761 dispatch_semaphore_t wait_for
= dispatch_semaphore_create(0);
762 dispatch_retain(wait_for
); // Both this scope and the block own it.
764 CFMutableArrayRef keysToGet
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
765 CFArrayAppendValue(keysToGet
, kSOSKVSKeyParametersKey
);
766 // Only get key parameters due to: <rdar://problem/22794892> Upgrading from Donner with an iCDP enabled account resets iCloud keychain on devices in circle
768 __block CFDictionaryRef valuesToUpdate
= NULL
;
769 __block
bool success
= false;
771 secnoticeq("fresh", "%s calling SOSCloudKeychainSynchronizeAndWait", kWAIT2MINID
);
773 SOSCloudKeychainSynchronizeAndWait(keysToGet
, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), ^(CFDictionaryRef returnedValues
, CFErrorRef sync_error
) {
776 secerrorq("%s SOSCloudKeychainSynchronizeAndWait: %@", kWAIT2MINID
, sync_error
);
779 CFRetainSafe(*error
);
782 secnoticeq("fresh", "%s returned from call; in callback to SOSCloudKeychainSynchronizeAndWait: results: %@", kWAIT2MINID
, returnedValues
);
783 valuesToUpdate
= returnedValues
;
784 CFRetainSafe(valuesToUpdate
);
788 dispatch_semaphore_signal(wait_for
);
789 dispatch_release(wait_for
);
792 dispatch_semaphore_wait(wait_for
, DISPATCH_TIME_FOREVER
);
793 // TODO: Maybe we timeout here... used to dispatch_time(DISPATCH_TIME_NOW, 30ull * NSEC_PER_SEC));
794 dispatch_release(wait_for
);
795 CFMutableArrayRef handledKeys
= NULL
;
796 if ((valuesToUpdate
) && (account
)) {
797 handledKeys
= SOSTransportDispatchMessages(account
, valuesToUpdate
, error
);
799 secerrorq("%s Freshness update failed: %@", kWAIT2MINID
, error
? *error
: NULL
);
803 CFReleaseNull(handledKeys
);
804 CFReleaseNull(valuesToUpdate
);
805 CFReleaseNull(keysToGet
);
810 static bool Flush(CFErrorRef
*error
) {
811 __block
bool success
= false;
813 dispatch_semaphore_t wait_for
= dispatch_semaphore_create(0);
814 dispatch_retain(wait_for
); // Both this scope and the block own it.
816 secnotice("flush", "Starting");
818 SOSCloudKeychainFlush(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), ^(CFDictionaryRef returnedValues
, CFErrorRef sync_error
) {
819 success
= (sync_error
== NULL
);
821 CFRetainAssign(*error
, sync_error
);
824 dispatch_semaphore_signal(wait_for
);
825 dispatch_release(wait_for
);
828 dispatch_semaphore_wait(wait_for
, DISPATCH_TIME_FOREVER
);
829 dispatch_release(wait_for
);
831 secnotice("flush", "Returned %s", success
? "Success": "Failure");
836 static bool SOSCCAssertUserCredentialsAndOptionalDSID(CFStringRef user_label
, CFDataRef user_password
, CFStringRef dsid
, CFErrorRef
*error
) {
837 secnotice("updates", "Setting credentials and dsid (%@) for %@", dsid
, user_label
);
838 bool result
= do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
839 if (dsid
!= NULL
&& CFStringCompare(dsid
, CFSTR(""), 0) != 0) {
840 CFStringRef accountDSID
= SOSAccountGetValue(account
, kSOSDSIDKey
, NULL
);
841 if( accountDSID
== NULL
){
842 SOSAccountUpdateDSID(account
, dsid
);
843 secdebug("updates", "Setting dsid, current dsid is empty for this account: %@", dsid
);
845 else if(CFStringCompare(dsid
, accountDSID
, 0) != kCFCompareEqualTo
){
846 secnotice("updates", "Changing DSID from: %@ to %@", accountDSID
, dsid
);
848 //DSID has changed, blast the account!
849 SOSAccountSetToNew(account
);
851 //update DSID to the new DSID
852 SOSAccountUpdateDSID(account
, dsid
);
855 secnotice("updates", "Not Changing DSID: %@ to %@", accountDSID
, dsid
);
860 // Short Circuit if this passes, return immediately.
861 if(SOSAccountTryUserCredentials(account
, user_label
, user_password
, NULL
)) {
865 if (!EnsureFreshParameters(account
, block_error
)) {
868 if (!SOSAccountAssertUserCredentials(account
, user_label
, user_password
, block_error
)) {
869 secnotice("updates", "EnsureFreshParameters/SOSAccountAssertUserCredentials error: %@", *block_error
);
875 if (result
&& Flush(error
)) {
876 result
= do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
877 return SOSAccountGenerationSignatureUpdate(account
, error
);
884 bool SOSCCSetUserCredentialsAndDSID_Server(CFStringRef user_label
, CFDataRef user_password
, CFStringRef dsid
, CFErrorRef
*error
)
886 // TODO: Return error if DSID is NULL to insist our callers provide one?
887 return SOSCCAssertUserCredentialsAndOptionalDSID(user_label
, user_password
, dsid
, error
);
890 bool SOSCCSetUserCredentials_Server(CFStringRef user_label
, CFDataRef user_password
, CFErrorRef
*error
)
892 return SOSCCAssertUserCredentialsAndOptionalDSID(user_label
, user_password
, NULL
, error
);
895 bool SOSCCCanAuthenticate_Server(CFErrorRef
*error
)
897 bool result
= do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
898 return SOSAccountGetPrivateCredential(account
, block_error
) != NULL
;
901 if (!result
&& error
&& *error
&& CFErrorGetDomain(*error
) == kSOSErrorDomain
) {
902 CFIndex code
= CFErrorGetCode(*error
);
903 if (code
== kSOSErrorPrivateKeyAbsent
|| code
== kSOSErrorPublicKeyAbsent
) {
904 CFReleaseNull(*error
);
911 bool SOSCCPurgeUserCredentials_Server(CFErrorRef
*error
)
913 return do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
914 SOSAccountPurgePrivateCredential(account
);
919 SOSCCStatus
SOSCCThisDeviceIsInCircle_Server(CFErrorRef
*error
)
921 __block SOSCCStatus status
;
923 return do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
924 status
= SOSAccountGetCircleStatus(account
, block_error
);
926 }) ? status
: kSOSCCError
;
929 bool SOSCCRequestToJoinCircle_Server(CFErrorRef
* error
)
931 __block
bool result
= true;
933 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
934 result
= SOSAccountJoinCircles(account
, block_error
);
939 bool SOSCCRequestToJoinCircleAfterRestore_Server(CFErrorRef
* error
)
941 __block
bool result
= true;
942 bool returned
= false;
943 returned
= do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
944 SOSAccountEnsurePeerRegistration(account
, block_error
);
945 result
= SOSAccountJoinCirclesAfterRestore(account
, block_error
);
952 bool SOSCCRequestEnsureFreshParameters_Server(CFErrorRef
* error
)
954 __block
bool result
= true;
955 bool returned
= false;
956 returned
= do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
957 result
= EnsureFreshParameters(account
, NULL
);
963 bool SOSCCApplyToARing_Server(CFStringRef ringName
, CFErrorRef
*error
){
964 __block
bool result
= true;
965 bool returned
= false;
966 returned
= do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
967 SOSFullPeerInfoRef fpi
= SOSAccountGetMyFullPeerInfo(account
);
968 SOSRingRef ring
= SOSAccountGetRing(account
, ringName
, error
);
970 result
= SOSRingApply(ring
, account
->user_public
, fpi
, error
);
976 bool SOSCCWithdrawlFromARing_Server(CFStringRef ringName
, CFErrorRef
*error
){
977 __block
bool result
= true;
978 bool returned
= false;
979 returned
= do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
980 SOSFullPeerInfoRef fpi
= SOSAccountGetMyFullPeerInfo(account
);
981 SOSRingRef ring
= SOSAccountGetRing(account
, ringName
, error
);
983 result
= SOSRingWithdraw(ring
, account
->user_public
, fpi
, error
);
989 bool SOSCCEnableRing_Server(CFStringRef ringName
, CFErrorRef
*error
){
990 __block
bool result
= true;
991 bool returned
= false;
992 returned
= do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
993 SOSFullPeerInfoRef fpi
= SOSAccountGetMyFullPeerInfo(account
);
994 SOSRingRef ring
= SOSAccountGetRing(account
, ringName
, error
);
996 result
= SOSRingResetToOffering(ring
, NULL
, fpi
, error
); ;
1002 CFStringRef
SOSCCGetAllTheRings_Server(CFErrorRef
*error
){
1003 __block CFMutableDictionaryRef result
= NULL
;
1004 __block CFMutableStringRef description
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
1006 (void) do_with_account_while_unlocked(error
, ^bool(SOSAccountRef account
, CFErrorRef
*error
) {
1007 result
= SOSAccountGetRings(account
, error
);
1009 if(isDictionary(result
)){
1010 CFDictionaryForEach(result
, ^(const void *key
, const void *value
) {
1011 CFStringAppendFormat(description
, NULL
, CFSTR("%@"), value
);
1022 SOSRingStatus
SOSCCRingStatus_Server(CFStringRef ringName
, CFErrorRef
*error
){
1023 __block
bool result
= true;
1024 SOSRingStatus returned
;
1025 returned
= do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1026 SOSFullPeerInfoRef fpi
= SOSAccountGetMyFullPeerInfo(account
);
1027 SOSPeerInfoRef myPeer
= SOSFullPeerInfoGetPeerInfo(fpi
);
1029 SOSRingRef ring
= SOSAccountGetRing(account
, ringName
, error
);
1031 result
= SOSRingDeviceIsInRing(ring
, SOSPeerInfoGetPeerID(myPeer
));
1037 CFStringRef
SOSCCCopyDeviceID_Server(CFErrorRef
*error
)
1039 __block CFStringRef result
= NULL
;
1041 (void) do_with_account_while_unlocked(error
, ^bool(SOSAccountRef account
, CFErrorRef
*error
) {
1042 result
= SOSAccountCopyDeviceID(account
, error
);
1043 return (!isNull(result
));
1048 bool SOSCCSetDeviceID_Server(CFStringRef IDS
, CFErrorRef
*error
){
1050 bool didSetID
= false;
1051 __block
bool result
= false;
1052 __block CFErrorRef blockError
= NULL
;
1054 didSetID
= do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1055 result
= SOSAccountSetMyDSID(account
, IDS
, block_error
);
1057 blockError
= CFRetainSafe(*block_error
);
1062 *error
= blockError
;
1067 HandleIDSMessageReason
SOSCCHandleIDSMessage_Server(CFDictionaryRef messageDict
, CFErrorRef
* error
)
1069 // TODO: Locking flow:
1072 - Get PeerCoder instance from SOSPeerCoderManager(Currently Engine)
1073 - Get Account lock and Initialize PeerCoder instance if it isn't valid yet.
1075 - Decode incoming msg on coder.
1076 - Pass msg along to SOSPeerRef if decoding is done.
1077 - Force reply from coder while in handshake mode. (or ask ckd to ask us later?)
1080 - Lookup SOSPeerRef in SOSEngineRef (getting engine lock temporarily to get peer.
1081 - Ask peer to handle decoded message
1082 - be notified of changed objects in all peers and update peer/engine states
1083 - save peer/engine state
1086 - Ask coder to send an outgoing message if it is negotiating
1087 - Ask peer to create a message if needed
1088 - Encode peer msg with coder
1090 - send reply to ckd for transporting
1093 __block HandleIDSMessageReason result
= kHandleIDSMessageSuccess
;
1094 CFErrorRef action_error
= NULL
;
1096 if (!do_with_account_while_unlocked(&action_error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1097 result
= SOSTransportMessageIDSHandleMessage(account
, messageDict
, error
);
1101 if (SecErrorGetOSStatus(action_error
) == errSecInteractionNotAllowed
) {
1102 secnotice("updates", "SOSCCHandleIDSMessage_Server failed because device is locked; letting IDSKeychainSyncingProxy know");
1103 result
= kHandleIDSMessageLocked
; // tell IDSKeychainSyncingProxy to call us back when device unlocks
1104 CFReleaseNull(action_error
);
1106 secerror("Unexpected error: %@", action_error
);
1109 if (error
&& *error
== NULL
) {
1110 *error
= action_error
;
1111 action_error
= NULL
;
1114 CFReleaseNull(action_error
);
1120 bool SOSCCIDSPingTest_Server(CFStringRef message
, CFErrorRef
*error
){
1121 bool didSendTestMessages
= false;
1122 __block
bool result
= true;
1123 __block CFErrorRef blockError
= NULL
;
1125 didSendTestMessages
= do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1126 result
= SOSAccountStartPingTest(account
, message
, block_error
);
1128 blockError
= CFRetainSafe(*block_error
);
1131 if(blockError
&& error
!= NULL
)
1132 *error
= blockError
;
1134 return didSendTestMessages
;
1137 bool SOSCCIDSServiceRegistrationTest_Server(CFStringRef message
, CFErrorRef
*error
){
1138 bool didSendTestMessages
= false;
1139 __block
bool result
= true;
1140 __block CFErrorRef blockError
= NULL
;
1142 didSendTestMessages
= do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1143 result
= SOSAccountSendIDSTestMessage(account
, message
, &blockError
);
1146 if(blockError
!= NULL
&& error
!= NULL
)
1147 *error
= blockError
;
1149 return didSendTestMessages
;
1152 bool SOSCCIDSDeviceIDIsAvailableTest_Server(CFErrorRef
*error
){
1153 bool didSendTestMessages
= false;
1154 __block
bool result
= true;
1155 __block CFErrorRef blockError
= NULL
;
1157 didSendTestMessages
= do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1158 result
= SOSAccountRetrieveDeviceIDFromIDSKeychainSyncingProxy(account
, &blockError
);
1161 if(blockError
!= NULL
&& error
!= NULL
)
1162 *error
= blockError
;
1165 return didSendTestMessages
;
1168 bool SOSCCAccountSetToNew_Server(CFErrorRef
*error
)
1170 __block
bool result
= true;
1172 return do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1174 SOSAccountSetToNew(account
);
1179 bool SOSCCResetToOffering_Server(CFErrorRef
* error
)
1181 __block
bool result
= true;
1183 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1185 result
= SOSAccountResetToOffering(account
, block_error
);
1191 bool SOSCCResetToEmpty_Server(CFErrorRef
* error
)
1193 __block
bool result
= true;
1195 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1196 result
= SOSAccountResetToEmpty(account
, block_error
);
1202 bool SOSCCRemoveThisDeviceFromCircle_Server(CFErrorRef
* error
)
1204 __block
bool result
= true;
1206 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1207 result
= SOSAccountLeaveCircle(account
, block_error
);
1212 bool SOSCCRemovePeersFromCircle_Server(CFArrayRef peers
, CFErrorRef
* error
)
1214 __block
bool result
= true;
1216 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1217 result
= SOSAccountRemovePeersFromCircle(account
, peers
, block_error
);
1223 bool SOSCCLoggedOutOfAccount_Server(CFErrorRef
*error
)
1225 __block
bool result
= true;
1227 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1228 secnotice("sosops", "Signed out of account!");
1230 bool waitForeverForSynchronization
= true;
1232 result
= SOSAccountLeaveCircle(account
, block_error
);
1234 SOSAccountFinishTransaction(account
); // Make sure this gets finished before we set to new.
1236 SOSAccountSetToNew(account
);
1238 sync_the_last_data_to_kvs(account
, waitForeverForSynchronization
);
1244 bool SOSCCBailFromCircle_Server(uint64_t limit_in_seconds
, CFErrorRef
* error
)
1246 __block
bool result
= true;
1248 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1249 bool waitForeverForSynchronization
= false;
1251 result
= SOSAccountBail(account
, limit_in_seconds
, block_error
);
1253 SOSAccountFinishTransaction(account
); // Make sure this gets finished before we set to new.
1255 sync_the_last_data_to_kvs(account
, waitForeverForSynchronization
);
1262 CFArrayRef
SOSCCCopyApplicantPeerInfo_Server(CFErrorRef
* error
)
1264 __block CFArrayRef result
= NULL
;
1266 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1267 result
= SOSAccountCopyApplicants(account
, block_error
);
1268 return result
!= NULL
;
1274 CFArrayRef
SOSCCCopyGenerationPeerInfo_Server(CFErrorRef
* error
)
1276 __block CFArrayRef result
= NULL
;
1278 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1279 result
= SOSAccountCopyGeneration(account
, block_error
);
1280 return result
!= NULL
;
1286 CFArrayRef
SOSCCCopyValidPeerPeerInfo_Server(CFErrorRef
* error
)
1288 __block CFArrayRef result
= NULL
;
1290 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1291 result
= SOSAccountCopyValidPeers(account
, block_error
);
1292 return result
!= NULL
;
1298 bool SOSCCValidateUserPublic_Server(CFErrorRef
* error
)
1300 __block
bool result
= NULL
;
1302 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1303 result
= SOSValidateUserPublic(account
, block_error
);
1310 CFArrayRef
SOSCCCopyNotValidPeerPeerInfo_Server(CFErrorRef
* error
)
1312 __block CFArrayRef result
= NULL
;
1314 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1315 result
= SOSAccountCopyNotValidPeers(account
, block_error
);
1316 return result
!= NULL
;
1322 CFArrayRef
SOSCCCopyRetirementPeerInfo_Server(CFErrorRef
* error
)
1324 __block CFArrayRef result
= NULL
;
1326 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1327 result
= SOSAccountCopyRetired(account
, block_error
);
1328 return result
!= NULL
;
1334 CFArrayRef
SOSCCCopyViewUnawarePeerInfo_Server(CFErrorRef
* error
)
1336 __block CFArrayRef result
= NULL
;
1338 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1339 result
= SOSAccountCopyViewUnaware(account
, block_error
);
1340 return result
!= NULL
;
1346 CFArrayRef
SOSCCCopyEngineState_Server(CFErrorRef
* error
)
1348 CFArrayRef result
= NULL
;
1349 SOSDataSourceFactoryRef dsf
= SecItemDataSourceFactoryGetDefault();
1350 SOSDataSourceRef ds
= SOSDataSourceFactoryCreateDataSource(dsf
, kSecAttrAccessibleWhenUnlocked
, error
);
1352 SOSEngineRef engine
= SOSDataSourceGetSharedEngine(ds
, error
);
1353 result
= SOSEngineCopyPeerConfirmedDigests(engine
, error
);
1354 SOSDataSourceRelease(ds
, error
);
1360 bool SOSCCWaitForInitialSync_Server(CFErrorRef
* error
) {
1361 __block dispatch_semaphore_t inSyncSema
= NULL
;
1363 bool result
= do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1364 bool alreadyInSync
= SOSAccountCheckHasBeenInSync(account
);
1366 if (!alreadyInSync
) {
1367 inSyncSema
= dispatch_semaphore_create(0);
1368 dispatch_retain(inSyncSema
);
1369 notify_register_dispatch(kSOSCCInitialSyncChangedNotification
, &token
,
1370 dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), ^(int token
) {
1371 dispatch_semaphore_signal(inSyncSema
);
1372 dispatch_release(inSyncSema
);
1374 notify_cancel(token
);
1380 if (result
&& inSyncSema
!= NULL
) {
1381 dispatch_semaphore_wait(inSyncSema
, DISPATCH_TIME_FOREVER
);
1382 dispatch_release(inSyncSema
);
1388 static CFArrayRef
SOSAccountCopyYetToSyncViews(SOSAccountRef account
, CFErrorRef
*error
) {
1389 CFArrayRef result
= NULL
;
1391 CFTypeRef valueFetched
= SOSAccountGetValue(account
, kSOSUnsyncedViewsKey
, error
);
1392 if (valueFetched
== kCFBooleanTrue
) {
1393 SOSPeerInfoRef myPI
= SOSAccountGetMyPeerInfo(account
);
1395 SOSPeerInfoWithEnabledViewSet(myPI
, ^(CFSetRef enabled
) {
1396 CFSetCopyValues(enabled
);
1399 } else if (isSet(valueFetched
)) {
1400 result
= CFSetCopyValues((CFSetRef
)valueFetched
);
1403 if (result
== NULL
) {
1404 result
= CFArrayCreateForCFTypes(kCFAllocatorDefault
, NULL
);
1410 CFArrayRef
SOSCCCopyYetToSyncViewsList_Server(CFErrorRef
* error
) {
1412 __block CFArrayRef views
= NULL
;
1414 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1415 views
= SOSAccountCopyYetToSyncViews(account
, error
);
1423 CFDictionaryRef
SOSCCCopyEscrowRecord_Server(CFErrorRef
*error
){
1425 __block CFDictionaryRef result
= NULL
;
1426 __block CFErrorRef block_error
= NULL
;
1428 (void) do_with_account_if_after_first_unlock(error
, ^bool(SOSAccountRef account
, CFErrorRef
*error
) {
1429 SOSCCStatus status
= SOSAccountGetCircleStatus(account
, &block_error
);
1430 CFStringRef dsid
= SOSAccountGetValue(account
, kSOSDSIDKey
, error
);
1431 CFDictionaryRef escrowRecords
= NULL
;
1432 CFDictionaryRef record
= NULL
;
1434 case kSOSCCInCircle
:
1435 //get the escrow record in the peer info!
1436 escrowRecords
= SOSPeerInfoCopyEscrowRecord(SOSAccountGetMyPeerInfo(account
));
1438 record
= CFDictionaryGetValue(escrowRecords
, dsid
);
1440 result
= CFRetainSafe(record
);
1442 CFReleaseNull(escrowRecords
);
1444 case kSOSCCRequestPending
:
1445 //set the escrow record in the peer info/application?
1447 case kSOSCCNotInCircle
:
1448 case kSOSCCCircleAbsent
:
1449 //set the escrow record in the account expansion!
1450 escrowRecords
= SOSAccountGetValue(account
, kSOSEscrowRecord
, error
);
1452 record
= CFDictionaryGetValue(escrowRecords
, dsid
);
1454 result
= CFRetainSafe(record
);
1458 secdebug("account", "no circle status!");
1467 bool SOSCCSetEscrowRecord_Server(CFStringRef escrow_label
, uint64_t tries
, CFErrorRef
*error
){
1469 __block
bool result
= true;
1470 __block CFErrorRef block_error
= NULL
;
1472 (void) do_with_account_if_after_first_unlock(error
, ^bool(SOSAccountRef account
, CFErrorRef
*error
) {
1473 SOSCCStatus status
= SOSAccountGetCircleStatus(account
, &block_error
);
1474 CFStringRef dsid
= SOSAccountGetValue(account
, kSOSDSIDKey
, error
);
1476 CFMutableStringRef timeDescription
= CFStringCreateMutableCopy(kCFAllocatorDefault
, 0, CFSTR("["));
1477 CFAbsoluteTime currentTimeAndDate
= CFAbsoluteTimeGetCurrent();
1479 withStringOfAbsoluteTime(currentTimeAndDate
, ^(CFStringRef decription
) {
1480 CFStringAppend(timeDescription
, decription
);
1482 CFStringAppend(timeDescription
, CFSTR("]"));
1484 CFNumberRef attempts
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberLongLongType
, (const void*)&tries
);
1486 CFMutableDictionaryRef escrowTimeAndTries
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1487 CFDictionaryAddValue(escrowTimeAndTries
, kSOSBurnedRecoveryAttemptCount
, attempts
);
1488 CFDictionaryAddValue(escrowTimeAndTries
, kSOSBurnedRecoveryAttemptAttestationDate
, timeDescription
);
1490 CFMutableDictionaryRef escrowRecord
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1491 CFDictionaryAddValue(escrowRecord
, escrow_label
, escrowTimeAndTries
);
1494 case kSOSCCInCircle
:
1495 //set the escrow record in the peer info!
1496 if(!SOSFullPeerInfoAddEscrowRecord(SOSAccountGetMyFullPeerInfo(account
), dsid
, escrowRecord
, error
)){
1497 secdebug("accout", "Could not set escrow record in the full peer info");
1501 case kSOSCCRequestPending
:
1502 //set the escrow record in the peer info/application?
1504 case kSOSCCNotInCircle
:
1505 case kSOSCCCircleAbsent
:
1506 //set the escrow record in the account expansion!
1508 if(!SOSAccountAddEscrowRecords(account
, dsid
, escrowRecord
, error
)) {
1509 secdebug("account", "Could not set escrow record in expansion data");
1514 secdebug("account", "no circle status!");
1517 CFReleaseNull(attempts
);
1518 CFReleaseNull(timeDescription
);
1519 CFReleaseNull(escrowTimeAndTries
);
1520 CFReleaseNull(escrowRecord
);
1528 bool SOSCCAcceptApplicants_Server(CFArrayRef applicants
, CFErrorRef
* error
)
1530 __block
bool result
= true;
1531 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1532 result
= SOSAccountAcceptApplicants(account
, applicants
, block_error
);
1538 bool SOSCCRejectApplicants_Server(CFArrayRef applicants
, CFErrorRef
* error
)
1540 __block
bool result
= true;
1541 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1542 result
= SOSAccountRejectApplicants(account
, applicants
, block_error
);
1547 CFArrayRef
SOSCCCopyPeerPeerInfo_Server(CFErrorRef
* error
)
1549 __block CFArrayRef result
= NULL
;
1551 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1552 result
= SOSAccountCopyPeers(account
, block_error
);
1553 return result
!= NULL
;
1559 CFArrayRef
SOSCCCopyConcurringPeerPeerInfo_Server(CFErrorRef
* error
)
1561 __block CFArrayRef result
= NULL
;
1563 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1564 result
= SOSAccountCopyConcurringPeers(account
, block_error
);
1565 return result
!= NULL
;
1571 SOSPeerInfoRef
SOSCCCopyMyPeerInfo_Server(CFErrorRef
* error
)
1573 __block SOSPeerInfoRef result
= NULL
;
1575 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1576 // Create a copy to be DERed/sent back to client
1577 result
= SOSPeerInfoCreateCopy(kCFAllocatorDefault
, SOSAccountGetMyPeerInfo(account
), block_error
);
1578 return result
!= NULL
;
1584 SOSPeerInfoRef
SOSCCSetNewPublicBackupKey_Server(CFDataRef newPublicBackup
, CFErrorRef
*error
){
1585 __block SOSPeerInfoRef result
= NULL
;
1587 (void) do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1588 if(SOSAccountSetBackupPublicKey(account
,newPublicBackup
, error
)){
1589 // Create a copy to be DERed/sent back to client
1590 result
= SOSPeerInfoCreateCopy(kCFAllocatorDefault
, SOSAccountGetMyPeerInfo(account
), block_error
);
1591 secdebug("backup", "SOSCCSetNewPublicBackupKey_Server, new public backup is set");
1595 secerror("SOSCCSetNewPublicBackupKey_Server, could not set new public backup");
1597 return result
!= NULL
;
1603 bool SOSCCRegisterSingleRecoverySecret_Server(CFDataRef aks_bag
, bool setupV0Only
, CFErrorRef
*error
){
1604 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1605 return SOSAccountSetBSKBagForAllSlices(account
, aks_bag
, setupV0Only
, error
);
1609 CFStringRef
SOSCCCopyIncompatibilityInfo_Server(CFErrorRef
* error
)
1611 __block CFStringRef result
= NULL
;
1613 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1614 result
= SOSAccountCopyIncompatibilityInfo(account
, block_error
);
1615 return result
!= NULL
;
1621 bool SOSCCCheckPeerAvailability_Server(CFErrorRef
*error
)
1623 __block
bool pingedPeersInCircle
= false;
1624 __block dispatch_semaphore_t peerSemaphore
= NULL
;
1625 __block
bool peerIsAvailable
= false;
1627 static dispatch_queue_t time_out
;
1628 static dispatch_once_t once
;
1629 dispatch_once(&once
, ^{
1630 time_out
= dispatch_queue_create("peersAvailableTimeout", DISPATCH_QUEUE_SERIAL
);
1632 __block
int token
= -1;
1634 bool result
= do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1636 peerSemaphore
= dispatch_semaphore_create(0);
1637 dispatch_retain(peerSemaphore
);
1638 notify_register_dispatch(kSOSCCPeerAvailable
, &token
, time_out
, ^(int token
) {
1639 if(peerSemaphore
!= NULL
){
1640 dispatch_semaphore_signal(peerSemaphore
);
1641 dispatch_release(peerSemaphore
);
1642 peerIsAvailable
= true;
1643 notify_cancel(token
);
1647 pingedPeersInCircle
= SOSAccountCheckPeerAvailability(account
, block_error
);
1648 return pingedPeersInCircle
;
1652 dispatch_semaphore_wait(peerSemaphore
, dispatch_time(DISPATCH_TIME_NOW
, 7ull * NSEC_PER_SEC
));
1655 if(peerSemaphore
!= NULL
)
1656 dispatch_release(peerSemaphore
);
1658 if(time_out
!= NULL
&& peerSemaphore
!= NULL
){
1659 dispatch_sync(time_out
, ^{
1660 if(!peerIsAvailable
){
1661 dispatch_release(peerSemaphore
);
1662 peerSemaphore
= NULL
;
1663 notify_cancel(token
);
1664 secnotice("peer available", "checking peer availability timed out, releasing semaphore");
1668 if(!peerIsAvailable
){
1669 CFStringRef errorMessage
= CFSTR("There are no peers in the circle currently available");
1670 CFDictionaryRef userInfo
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, kCFErrorLocalizedDescriptionKey
, errorMessage
, NULL
);
1672 *error
=CFErrorCreate(kCFAllocatorDefault
, CFSTR("com.apple.security.ids.error"), kSecIDSErrorNoPeersAvailable
, userInfo
);
1673 secerror("%@", *error
);
1675 CFReleaseNull(userInfo
);
1684 enum DepartureReason
SOSCCGetLastDepartureReason_Server(CFErrorRef
* error
)
1686 __block
enum DepartureReason result
= kSOSDepartureReasonError
;
1688 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1689 result
= SOSAccountGetLastDepartureReason(account
, block_error
);
1690 return result
!= kSOSDepartureReasonError
;
1696 bool SOSCCSetLastDepartureReason_Server(enum DepartureReason reason
, CFErrorRef
*error
){
1697 __block
bool result
= true;
1699 return do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1700 SOSAccountSetLastDepartureReason(account
, reason
);
1705 bool SOSCCSetHSA2AutoAcceptInfo_Server(CFDataRef pubKey
, CFErrorRef
*error
) {
1706 __block
bool result
= true;
1708 return do_with_account_if_after_first_unlock(error
, ^(SOSAccountRef account
,
1709 CFErrorRef
*block_error
) {
1710 result
= SOSAccountSetHSAPubKeyExpected(account
, pubKey
, error
);
1711 return (bool)result
;
1715 bool SOSCCProcessEnsurePeerRegistration_Server(CFErrorRef
* error
)
1717 secnotice("updates", "Request for registering peers");
1718 return do_with_account_while_unlocked(error
, ^bool(SOSAccountRef account
, CFErrorRef
*error
) {
1719 return SOSAccountEnsurePeerRegistration(account
, error
);
1723 SyncWithAllPeersReason
SOSCCProcessSyncWithAllPeers_Server(CFErrorRef
* error
)
1726 #define kIOReturnLockedRead iokit_common_err(0x2c3) // device read locked
1727 #define kIOReturnLockedWrite iokit_common_err(0x2c4) // device write locked
1729 __block SyncWithAllPeersReason result
= kSyncWithAllPeersSuccess
;
1730 CFErrorRef action_error
= NULL
;
1732 if (!do_with_account_while_unlocked(&action_error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1733 CFErrorRef localError
= NULL
;
1735 if (!SOSAccountSyncWithAllPeers(account
, &localError
)) {
1736 secerror("sync with all peers failed: %@", localError
);
1737 CFReleaseSafe(localError
);
1738 // This isn't a device-locked error, but returning false will
1739 // have CloudKeychainProxy ask us to try sync again after next unlock
1740 result
= kSyncWithAllPeersOtherFail
;
1746 if (SecErrorGetOSStatus(action_error
) == errSecInteractionNotAllowed
) {
1747 secnotice("updates", "SOSAccountSyncWithAllPeers failed because device is locked; letting CloudKeychainProxy know");
1748 result
= kSyncWithAllPeersLocked
; // tell CloudKeychainProxy to call us back when device unlocks
1749 CFReleaseNull(action_error
);
1751 secerror("Unexpected error: %@", action_error
);
1754 if (error
&& *error
== NULL
) {
1755 *error
= action_error
;
1756 action_error
= NULL
;
1759 CFReleaseNull(action_error
);
1766 void SOSCCSyncWithAllPeers(void)
1768 SOSCloudKeychainRequestSyncWithAllPeers(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), NULL
);
1771 CF_RETURNS_RETAINED CFArrayRef
SOSCCHandleUpdateMessage(CFDictionaryRef updates
)
1773 CFArrayRef result
= NULL
;
1774 SOSAccountRef account
= SOSKeychainAccountGetSharedAccount(); //HACK to make sure itemsChangedBlock is set
1776 (account
) ? (result
= SOSCloudKeychainHandleUpdateMessage(updates
)) : (result
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
));