2 // SOSCloudCircleServer.c
5 // Created by Mitch Adler on 11/15/12.
9 #include <AssertMacros.h>
10 #include <CoreFoundation/CFURL.h>
12 #include <securityd/SOSCloudCircleServer.h>
13 #include <SecureObjectSync/SOSCloudCircle.h>
14 #include <SecureObjectSync/SOSCloudCircleInternal.h>
15 #include <SecureObjectSync/SOSCircle.h>
16 #include <SecureObjectSync/SOSAccount.h>
17 #include <SecureObjectSync/SOSFullPeerInfo.h>
18 #include <SecureObjectSync/SOSPeerInfoInternal.h>
19 #include <SecureObjectSync/SOSInternal.h>
20 #include <SecureObjectSync/SOSUserKeygen.h>
22 #include <utilities/SecCFWrappers.h>
23 #include <utilities/SecCFRelease.h>
24 #include <utilities/debugging.h>
25 #include "SOSCloudKeychainClient.h"
27 #include <corecrypto/ccrng.h>
28 #include <corecrypto/ccrng_pbkdf2_prng.h>
29 #include <corecrypto/ccec.h>
30 #include <corecrypto/ccdigest.h>
31 #include <corecrypto/ccsha2.h>
32 #include <CommonCrypto/CommonRandomSPI.h>
33 #include <Security/SecKeyPriv.h>
34 #include <Security/SecFramework.h>
36 #include <utilities/SecFileLocations.h>
37 #include <utilities/SecAKSWrappers.h>
38 #include <SecItemServer.h>
39 #include <SecItemPriv.h>
41 #include <TargetConditionals.h>
43 #include <utilities/iCloudKeychainTrace.h>
45 #if TARGET_OS_EMBEDDED || TARGET_IPHONE_SIMULATOR
46 #include <MobileGestalt.h>
48 #include <AppleSystemInfo/AppleSystemInfo.h>
50 // We need authorization, but that doesn't exist
51 // on sec built for desktop (iOS in a process)
52 // Define AuthorizationRef here to make SystemConfiguration work
54 typedef const struct AuthorizationOpaqueRef
* AuthorizationRef
;
57 #define SOSCKCSCOPE "sync"
59 #define USE_SYSTEMCONFIGURATION_PRIVATE_HEADERS
60 #import <SystemConfiguration/SystemConfiguration.h>
64 CloudKeychainReplyBlock log_error
= ^(CFDictionaryRef returnedValues __unused
, CFErrorRef error
) {
66 secerror("Error putting: %@", error
);
71 static SOSCCAccountDataSourceFactoryBlock accountDataSourceOverride
= NULL
;
73 bool SOSKeychainAccountSetFactoryForAccount(SOSCCAccountDataSourceFactoryBlock block
)
75 accountDataSourceOverride
= Block_copy(block
);
80 static void do_with_account(void (^action
)(SOSAccountRef account
));
86 CFStringRef kSOSInternalAccessGroup
= CFSTR("com.apple.security.sos");
88 CFStringRef kSOSAccountLabel
= CFSTR("iCloud Keychain Account Meta-data");
89 CFStringRef kSOSPeerDataLabel
= CFSTR("iCloud Peer Data Meta-data");
91 static CFStringRef accountFileName
= CFSTR("PersistedAccount.plist");
93 static CFDictionaryRef
SOSItemCopyQueryForSyncItems(CFStringRef service
, bool returnData
)
95 return CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
96 kSecClass
, kSecClassGenericPassword
,
97 kSecAttrService
, service
,
98 kSecAttrAccessGroup
, kSOSInternalAccessGroup
,
99 kSecReturnData
, returnData
? kCFBooleanTrue
: kCFBooleanFalse
,
103 CFDataRef
SOSItemGet(CFStringRef service
, CFErrorRef
* error
)
105 CFDictionaryRef query
= SOSItemCopyQueryForSyncItems(service
, true);
107 CFDataRef result
= NULL
;
109 OSStatus copyResult
= SecItemCopyMatching(query
, (CFTypeRef
*) &result
);
111 CFReleaseNull(query
);
113 if (copyResult
!= noErr
) {
114 SecError(copyResult
, error
, CFSTR("Error %@ reading for service '%@'"), result
, service
);
115 CFReleaseNull(result
);
119 if (!isData(result
)) {
120 SOSCreateErrorWithFormat(kSOSErrorProcessingFailure
, NULL
, error
, NULL
, CFSTR("SecItemCopyMatching returned non-data in '%@'"), service
);
127 static CFDataRef
SOSKeychainCopySavedAccountData()
129 CFErrorRef error
= NULL
;
130 CFDataRef accountData
= SOSItemGet(kSOSAccountLabel
, &error
);
132 secnotice("account", "Failed to load account: %@", error
);
133 CFReleaseNull(error
);
139 bool SOSItemUpdateOrAdd(CFStringRef service
, CFStringRef accessibility
, CFDataRef data
, CFErrorRef
*error
)
141 CFDictionaryRef query
= SOSItemCopyQueryForSyncItems(service
, false);
143 CFDictionaryRef update
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
145 kSecAttrAccessible
, accessibility
,
147 OSStatus saveStatus
= SecItemUpdate(query
, update
);
149 if (errSecItemNotFound
== saveStatus
) {
150 CFMutableDictionaryRef add
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, query
);
151 CFDictionaryForEach(update
, ^(const void *key
, const void *value
) {
152 CFDictionaryAddValue(add
, key
, value
);
154 saveStatus
= SecItemAdd(add
, NULL
);
158 CFReleaseNull(query
);
159 CFReleaseNull(update
);
161 return SecError(saveStatus
, error
, CFSTR("Error saving %@ to service '%@'"), data
, service
);
164 static CFStringRef accountStatusFileName
= CFSTR("accountStatus.plist");
165 #include <utilities/der_plist.h>
166 #include <utilities/der_plist_internal.h>
167 #include <corecrypto/ccder.h>
169 static const uint8_t* ccder_decode_bool(bool* boolean
, const uint8_t* der
, const uint8_t *der_end
)
174 size_t payload_size
= 0;
175 const uint8_t *payload
= ccder_decode_tl(CCDER_BOOLEAN
, &payload_size
, der
, der_end
);
177 if (NULL
== payload
|| (der_end
- payload
) < 1 || payload_size
!= 1) {
182 *boolean
= (*payload
!= 0);
184 return payload
+ payload_size
;
187 bool SOSCCCircleIsOn_Artifact(void) {
188 CFURLRef accountStatusFileURL
= SecCopyURLForFileInKeychainDirectory(accountStatusFileName
);
189 CFDataRef accountStatus
= (CFDataRef
) CFPropertyListReadFromFile(accountStatusFileURL
);
190 CFReleaseSafe(accountStatusFileURL
);
191 bool circle_on
= false;
193 if (accountStatus
&& !isData(accountStatus
)) {
194 CFReleaseNull(accountStatus
);
195 } else if(accountStatus
) {
196 size_t size
= CFDataGetLength(accountStatus
);
197 const uint8_t *der
= CFDataGetBytePtr(accountStatus
);
198 const uint8_t *der_p
= der
;
200 const uint8_t *sequence_end
;
201 der_p
= ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE
, &sequence_end
, der_p
, der_p
+ size
);
202 der_p
= ccder_decode_bool(&circle_on
, der_p
, sequence_end
);
210 static size_t ccder_sizeof_bool(bool value __unused
, CFErrorRef
*error
)
212 return ccder_sizeof(CCDER_BOOLEAN
, 1);
216 static uint8_t* ccder_encode_bool(bool value
, const uint8_t *der
, uint8_t *der_end
)
218 uint8_t value_byte
= value
;
220 return ccder_encode_tl(CCDER_BOOLEAN
, 1, der
,
221 ccder_encode_body(1, &value_byte
, der
, der_end
));
224 static void SOSCCCircleIsOn_SetArtifact(bool account_on
) {
225 static CFDataRef sLastSavedAccountStatus
= NULL
;
226 CFErrorRef saveError
= NULL
;
227 size_t der_size
= ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE
, ccder_sizeof_bool(account_on
, NULL
));
228 uint8_t der
[der_size
];
229 uint8_t *der_end
= der
+ der_size
;
230 der_end
= ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE
, der_end
, der
,
231 ccder_encode_bool(account_on
, der
, der_end
));
233 CFDataRef accountStatusAsData
= CFDataCreate(kCFAllocatorDefault
, der_end
, der_size
);
235 require_quiet(accountStatusAsData
, exit
);
236 if (sLastSavedAccountStatus
&& CFEqual(sLastSavedAccountStatus
, accountStatusAsData
)) goto exit
;
238 CFURLRef accountStatusFileURL
= SecCopyURLForFileInKeychainDirectory(accountStatusFileName
);
239 CFPropertyListWriteToFile((CFPropertyListRef
) accountStatusAsData
, accountStatusFileURL
);
240 CFReleaseSafe(accountStatusFileURL
);
242 CFReleaseNull(sLastSavedAccountStatus
);
243 sLastSavedAccountStatus
= accountStatusAsData
;
244 accountStatusAsData
= NULL
;
247 CFReleaseNull(saveError
);
248 CFReleaseNull(accountStatusAsData
);
251 static void SOSCCCircleIsOn_UpdateArtifact(SOSCCStatus status
)
254 case kSOSCCCircleAbsent
:
255 case kSOSCCNotInCircle
:
256 SOSCCCircleIsOn_SetArtifact(false);
259 case kSOSCCRequestPending
:
260 SOSCCCircleIsOn_SetArtifact(true);
269 static void SOSKeychainAccountEnsureSaved(SOSAccountRef account
)
271 static CFDataRef sLastSavedAccountData
= NULL
;
273 CFErrorRef saveError
= NULL
;
274 SOSCCCircleIsOn_UpdateArtifact(SOSAccountIsInCircles(account
, NULL
));
276 CFDataRef accountAsData
= SOSAccountCopyEncodedData(account
, kCFAllocatorDefault
, &saveError
);
278 require_action_quiet(accountAsData
, exit
, secerror("Failed to transform account into data, error: %@", saveError
));
279 require_quiet(!CFEqualSafe(sLastSavedAccountData
, accountAsData
), exit
);
281 if (!SOSItemUpdateOrAdd(kSOSAccountLabel
, kSecAttrAccessibleAlwaysThisDeviceOnly
, accountAsData
, &saveError
)) {
282 secerror("Can't save account: %@", saveError
);
286 CFReleaseNull(sLastSavedAccountData
);
287 sLastSavedAccountData
= accountAsData
;
288 accountAsData
= NULL
;
291 CFReleaseNull(saveError
);
292 CFReleaseNull(accountAsData
);
297 Stolen from keychain_sync.c
300 static bool clearAllKVS(CFErrorRef
*error
)
303 __block
bool result
= false;
304 const uint64_t maxTimeToWaitInSeconds
= 30ull * NSEC_PER_SEC
;
305 dispatch_queue_t processQueue
= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0);
306 dispatch_semaphore_t waitSemaphore
= dispatch_semaphore_create(0);
307 dispatch_time_t finishTime
= dispatch_time(DISPATCH_TIME_NOW
, maxTimeToWaitInSeconds
);
309 SOSCloudKeychainClearAll(processQueue
, ^(CFDictionaryRef returnedValues
, CFErrorRef cerror
)
311 result
= (cerror
!= NULL
);
312 dispatch_semaphore_signal(waitSemaphore
);
315 dispatch_semaphore_wait(waitSemaphore
, finishTime
);
316 dispatch_release(waitSemaphore
);
321 static SOSAccountRef
SOSKeychainAccountCreateSharedAccount(CFDictionaryRef our_gestalt
)
323 SOSAccountKeyInterestBlock updateKVSKeys
= ^(bool getNewKeysOnly
, CFArrayRef alwaysKeys
, CFArrayRef afterFirstUnlockKeys
, CFArrayRef unlockedKeys
) {
324 CFErrorRef error
= NULL
;
326 if (!SOSCloudKeychainUpdateKeys(getNewKeysOnly
, alwaysKeys
, afterFirstUnlockKeys
, unlockedKeys
, &error
))
328 secerror("Error updating keys: %@", error
);
329 // TODO: propagate error(s) to callers.
331 if (CFArrayGetCount(unlockedKeys
) == 0) {
332 secnotice(SOSCKCSCOPE
, "Unlocked keys were empty!");
334 // This leaks 3 CFStringRefs in DEBUG builds.
335 CFStringRef alwaysKeysDesc
= SOSInterestListCopyDescription(alwaysKeys
);
336 CFStringRef afterFirstUnlockKeysDesc
= SOSInterestListCopyDescription(afterFirstUnlockKeys
);
337 CFStringRef unlockedKeysDesc
= SOSInterestListCopyDescription(unlockedKeys
);
338 secdebug(SOSCKCSCOPE
, "Updating interest: always: %@,\nfirstUnlock: %@,\nunlockedKeys: %@",
340 afterFirstUnlockKeysDesc
,
342 CFReleaseNull(alwaysKeysDesc
);
343 CFReleaseNull(afterFirstUnlockKeysDesc
);
344 CFReleaseNull(unlockedKeysDesc
);
347 CFReleaseNull(error
);
350 SOSAccountDataUpdateBlock updateKVS
= ^ bool (CFDictionaryRef changes
, CFErrorRef
*error
) {
351 SOSCloudKeychainPutObjectsInCloud(changes
, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), log_error
);
353 CFStringRef changeDescription
= SOSChangesCopyDescription(changes
, true);
354 secnotice("account", "Keys Sent: %@", changeDescription
);
355 CFReleaseSafe(changeDescription
);
360 secdebug("account", "Created account");
362 CFDataRef savedAccount
= SOSKeychainCopySavedAccountData();
364 // At this point we might have an account structure from keychain that may or may not match the account we're building this for
365 // murf ZZZ we should probably make sure this is a good thing before using it.
367 SOSAccountRef account
= NULL
;
370 CFErrorRef inflationError
= NULL
;
371 SOSDataSourceFactoryRef factory
= accountDataSourceOverride
? accountDataSourceOverride() : SecItemDataSourceFactoryCreateDefault();
373 account
= SOSAccountCreateFromData(kCFAllocatorDefault
, savedAccount
, factory
, updateKVSKeys
, updateKVS
, &inflationError
);
376 SOSAccountUpdateGestalt(account
, our_gestalt
);
378 secerror("Got error inflating account: %@", inflationError
);
379 CFReleaseNull(inflationError
);
382 CFReleaseSafe(savedAccount
);
385 // If we get here then we are creating a new accout and so increment the peer count for ourselves.
386 SOSDataSourceFactoryRef factory
= accountDataSourceOverride
? accountDataSourceOverride() : SecItemDataSourceFactoryCreateDefault();
388 account
= SOSAccountCreate(kCFAllocatorDefault
, our_gestalt
, factory
, updateKVSKeys
, updateKVS
);
391 secerror("Got NULL creating account");
398 // Mark: Gestalt Handling
401 static CFStringRef
CopyModelName(void)
403 static dispatch_once_t once
;
404 static CFStringRef modelName
= NULL
;
405 dispatch_once(&once
, ^{
406 #if TARGET_OS_EMBEDDED || TARGET_IPHONE_SIMULATOR
407 modelName
= MGCopyAnswer(kMGQDeviceName
, NULL
);
409 modelName
= ASI_CopyComputerModelName(FALSE
);
411 if (modelName
== NULL
)
412 modelName
= CFSTR("Unknown model");
414 return CFStringCreateCopy(kCFAllocatorDefault
, modelName
);
417 static CFStringRef
CopyComputerName(SCDynamicStoreRef store
)
419 CFStringRef deviceName
= SCDynamicStoreCopyComputerName(store
, NULL
);
420 if (deviceName
== NULL
) {
421 deviceName
= CFSTR("Unknown name");
426 static CFDictionaryRef
GatherDeviceGestalt(SCDynamicStoreRef store
, CFArrayRef keys
, void *context
)
428 CFStringRef modelName
= CopyModelName();
429 CFStringRef computerName
= CopyComputerName(store
);
431 CFDictionaryRef gestalt
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
432 kPIUserDefinedDeviceName
, computerName
,
433 kPIDeviceModelName
, modelName
,
435 CFRelease(modelName
);
436 CFRelease(computerName
);
441 static void SOSCCProcessGestaltUpdate(SCDynamicStoreRef store
, CFArrayRef keys
, void *context
)
443 do_with_account(^(SOSAccountRef account
) {
444 CFDictionaryRef gestalt
= GatherDeviceGestalt(store
, keys
, context
);
445 if (SOSAccountUpdateGestalt(account
, gestalt
)) {
446 notify_post(kSOSCCCircleChangedNotification
);
448 CFReleaseSafe(gestalt
);
453 static CFDictionaryRef
RegisterForGestaltUpdate(dispatch_queue_t queue
, void *info
)
455 SCDynamicStoreContext context
= { .info
= info
};
456 SCDynamicStoreRef store
= SCDynamicStoreCreate(NULL
, CFSTR("com.apple.securityd.cloudcircleserver"), SOSCCProcessGestaltUpdate
, &context
);
457 CFStringRef computerKey
= SCDynamicStoreKeyCreateComputerName(NULL
);
458 CFArrayRef keys
= NULL
;
459 CFDictionaryRef gestalt
= NULL
;
461 if (store
== NULL
|| computerKey
== NULL
) {
464 keys
= CFArrayCreate(NULL
, (const void **)&computerKey
, 1, &kCFTypeArrayCallBacks
);
468 gestalt
= GatherDeviceGestalt(store
, keys
, info
);
469 SCDynamicStoreSetNotificationKeys(store
, keys
, NULL
);
470 SCDynamicStoreSetDispatchQueue(store
, queue
);
473 if (store
) CFRelease(store
);
474 if (computerKey
) CFRelease(computerKey
);
475 if (keys
) CFRelease(keys
);
479 static void do_with_account(void (^action
)(SOSAccountRef account
));
480 static void do_with_account_async(void (^action
)(SOSAccountRef account
));
482 static void do_with_account_dynamic(void (^action
)(SOSAccountRef account
), bool sync
) {
483 static SOSAccountRef sSharedAccount
;
484 static dispatch_once_t onceToken
;
486 dispatch_once(&onceToken
, ^{
487 secdebug(SOSCKCSCOPE
, "Account Creation start");
489 CFDictionaryRef gestalt
= RegisterForGestaltUpdate(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), NULL
);
492 #if TARGET_OS_IPHONE && TARGET_IPHONE_SIMULATOR
493 gestalt
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, NULL
);
495 secerror("Didn't get machine gestalt! This is going to be ugly.");
499 sSharedAccount
= SOSKeychainAccountCreateSharedAccount(gestalt
);
501 CFReleaseSafe(gestalt
);
503 SOSCCSetThisDeviceDefinitelyNotActiveInCircle(SOSAccountIsInCircles(sSharedAccount
, NULL
));
505 SOSAccountAddChangeBlock(sSharedAccount
, ^(SOSCircleRef circle
,
506 CFArrayRef peer_additions
, CFArrayRef peer_removals
,
507 CFArrayRef applicant_additions
, CFArrayRef applicant_removals
) {
508 CFErrorRef pi_error
= NULL
;
509 SOSPeerInfoRef me
= SOSAccountGetMyPeerInCircle(sSharedAccount
, circle
, &pi_error
);
511 secerror("Error finding me for change: %@", pi_error
);
512 CFReleaseNull(pi_error
);
514 CFReleaseSafe(pi_error
);
516 if (CFArrayContainsValue(peer_additions
, CFRangeMake(0, CFArrayGetCount(peer_additions
)), me
)) {
517 SOSCCSyncWithAllPeers();
521 if (CFArrayGetCount(peer_additions
) != 0 ||
522 CFArrayGetCount(peer_removals
) != 0 ||
523 CFArrayGetCount(applicant_additions
) != 0 ||
524 CFArrayGetCount(applicant_removals
) != 0) {
526 SOSCCSetThisDeviceDefinitelyNotActiveInCircle(SOSAccountIsInCircles(sSharedAccount
, NULL
));
527 notify_post(kSOSCCCircleChangedNotification
);
531 SOSCloudKeychainSetItemsChangedBlock(^(CFDictionaryRef changes
) {
532 CFRetainSafe(changes
);
533 do_with_account_async(^(SOSAccountRef account
) {
534 CFStringRef changeDescription
= SOSChangesCopyDescription(changes
, false);
535 secdebug(SOSCKCSCOPE
, "Received: %@", changeDescription
);
536 CFReleaseSafe(changeDescription
);
538 CFErrorRef error
= NULL
;
539 if (!SOSAccountHandleUpdates(account
, changes
, &error
)) {
540 secerror("Error handling updates: %@", error
);
541 CFReleaseNull(error
);
544 CFReleaseNull(error
);
545 CFReleaseSafe(changes
);
551 dispatch_block_t do_action_and_save
= ^{
552 action(sSharedAccount
);
553 SOSKeychainAccountEnsureSaved(sSharedAccount
);
557 dispatch_sync(SOSAccountGetQueue(sSharedAccount
), do_action_and_save
);
559 dispatch_async(SOSAccountGetQueue(sSharedAccount
), do_action_and_save
);
563 static void do_with_account_async(void (^action
)(SOSAccountRef account
)) {
564 do_with_account_dynamic(action
, false);
567 static void do_with_account(void (^action
)(SOSAccountRef account
)) {
568 do_with_account_dynamic(action
, true);
571 #if TARGET_IPHONE_SIMULATOR
572 #define MKBDeviceUnlockedSinceBoot() true
575 static bool do_if_after_first_unlock(CFErrorRef
*error
, dispatch_block_t action
)
577 bool beenUnlocked
= false;
578 require_quiet(SecAKSGetHasBeenUnlocked(&beenUnlocked
, error
), fail
);
580 require_action_quiet(beenUnlocked
, fail
,
581 SOSCreateErrorWithFormat(kSOSErrorNotReady
, NULL
, error
, NULL
,
582 CFSTR("Keybag never unlocked, ask after first unlock")));
591 static bool do_with_account_if_after_first_unlock(CFErrorRef
*error
, bool (^action
)(SOSAccountRef account
, CFErrorRef
* error
))
593 __block
bool action_result
= false;
595 return do_if_after_first_unlock(error
, ^{
596 do_with_account(^(SOSAccountRef account
) {
597 action_result
= action(account
, error
);
603 static bool do_with_account_while_unlocked(CFErrorRef
*error
, bool (^action
)(SOSAccountRef account
, CFErrorRef
* error
))
605 __block
bool action_result
= false;
607 return SecAKSDoWhileUserBagLocked(error
, ^{
608 do_with_account(^(SOSAccountRef account
) {
609 action_result
= action(account
, error
);
615 SOSAccountRef
SOSKeychainAccountGetSharedAccount()
617 __block SOSAccountRef result
= NULL
;
619 do_with_account(^(SOSAccountRef account
) {
627 // Mark: Credential processing
631 bool SOSCCTryUserCredentials_Server(CFStringRef user_label
, CFDataRef user_password
, CFErrorRef
*error
)
633 return do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
634 return SOSAccountTryUserCredentials(account
, user_label
, user_password
, block_error
);
639 static bool EnsureFreshParameters(SOSAccountRef account
, CFErrorRef
*error
) {
640 dispatch_semaphore_t wait_for
= dispatch_semaphore_create(0);
641 dispatch_retain(wait_for
); // Both this scope and the block own it.
643 CFMutableArrayRef keysToGet
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
644 CFArrayAppendValue(keysToGet
, kSOSKVSKeyParametersKey
);
646 __block CFDictionaryRef valuesToUpdate
= NULL
;
647 __block
bool success
= false;
649 SOSAccountForEachCircle(account
, ^(SOSCircleRef circle
) {
650 CFStringRef circle_key
= SOSCircleKeyCreateWithName(SOSCircleGetName(circle
), NULL
);
651 CFArrayAppendValue(keysToGet
, circle_key
);
652 CFReleaseNull(circle_key
);
655 secnotice("updates", "***** EnsureFreshParameters *****");
657 SOSCloudKeychainSynchronizeAndWait(keysToGet
, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), ^(CFDictionaryRef returnedValues
, CFErrorRef sync_error
) {
660 secerror("SOSCloudKeychainSynchronizeAndWait: %@", sync_error
);
663 CFRetainSafe(*error
);
666 secnotice("updates", "SOSCloudKeychainSynchronizeAndWait: results: %@", returnedValues
);
667 valuesToUpdate
= returnedValues
;
668 CFRetainSafe(valuesToUpdate
);
672 dispatch_semaphore_signal(wait_for
);
673 dispatch_release(wait_for
);
676 dispatch_semaphore_wait(wait_for
, DISPATCH_TIME_FOREVER
);
677 // TODO: Maybe we timeout here... used to dispatch_time(DISPATCH_TIME_NOW, 30ull * NSEC_PER_SEC));
678 dispatch_release(wait_for
);
680 if ((valuesToUpdate
) && (account
)) {
681 if (!SOSAccountHandleUpdates(account
, valuesToUpdate
, error
)) {
682 secerror("Freshness update failed: %@", *error
);
688 CFReleaseNull(valuesToUpdate
);
689 CFReleaseNull(keysToGet
);
694 static bool EnsureFreshParameters_once(SOSAccountRef account
, CFErrorRef
*error
) {
695 static dispatch_once_t once
;
696 __block
bool retval
= false;
697 dispatch_once(&once
, ^{
698 retval
= EnsureFreshParameters(account
, error
);
703 bool SOSCCSetUserCredentials_Server(CFStringRef user_label
, CFDataRef user_password
, CFErrorRef
*error
)
705 return do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
706 if (!EnsureFreshParameters(account
, block_error
)) {
707 secnotice("updates", "EnsureFreshParameters error: %@", *block_error
);
710 if (!SOSAccountAssertUserCredentials(account
, user_label
, user_password
, block_error
)) {
711 secnotice("updates", "EnsureFreshParameters/SOSAccountAssertUserCredentials error: %@", *block_error
);
720 bool SOSCCCanAuthenticate_Server(CFErrorRef
*error
)
722 bool result
= do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
723 return SOSAccountGetPrivateCredential(account
, block_error
) != NULL
;
726 if (!result
&& error
&& *error
&& CFErrorGetDomain(*error
) == kSOSErrorDomain
) {
727 CFIndex code
= CFErrorGetCode(*error
);
728 if (code
== kSOSErrorPrivateKeyAbsent
|| code
== kSOSErrorPublicKeyAbsent
) {
729 CFReleaseNull(*error
);
736 bool SOSCCPurgeUserCredentials_Server(CFErrorRef
*error
)
738 return do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
739 SOSAccountPurgePrivateCredential(account
);
746 static bool sAccountInCircleCache
= false;
748 static void do_with_not_in_circle_bool_queue(bool start_account
, dispatch_block_t action
)
750 static dispatch_queue_t account_start_queue
;
751 static dispatch_queue_t not_in_circle_queue
;
752 static bool account_started
= false;
754 static dispatch_once_t onceToken
;
755 dispatch_once(&onceToken
, ^{
756 not_in_circle_queue
= dispatch_queue_create("nis queue", DISPATCH_QUEUE_SERIAL
);
757 account_start_queue
= dispatch_queue_create("init nis queue", DISPATCH_QUEUE_SERIAL
);;
758 account_started
= false;
761 __block
bool done
= false;
762 dispatch_sync(not_in_circle_queue
, ^{
763 if (account_started
) {
769 if (!done
&& start_account
) {
770 dispatch_sync(account_start_queue
, ^{
771 __block
bool do_start
= false;
772 dispatch_sync(not_in_circle_queue
, ^{
773 do_start
= !account_started
;
774 account_started
= true;
777 SOSCCThisDeviceIsInCircle(NULL
); // Inflate account.
780 dispatch_sync(not_in_circle_queue
, action
);
785 bool SOSCCThisDeviceDefinitelyNotActiveInCircle()
787 return !SOSCCCircleIsOn_Artifact();
789 __block
bool result
= false;
790 do_with_not_in_circle_bool_queue(true, ^{
791 result
= sAccountInCircleCache
;
798 void SOSCCSetThisDeviceDefinitelyNotActiveInCircle(SOSCCStatus currentStatus
)
800 SOSCCCircleIsOn_UpdateArtifact(currentStatus
);
802 do_with_not_in_circle_bool_queue(false, ^{
803 sAccountInCircleCache
= notActive
;
809 SOSCCStatus
SOSCCThisDeviceIsInCircle_Server(CFErrorRef
*error
)
811 __block SOSCCStatus status
;
813 return do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
814 EnsureFreshParameters_once(account
, NULL
);
815 status
= SOSAccountIsInCircles(account
, block_error
);
817 }) ? status
: kSOSCCError
;
820 bool SOSCCRequestToJoinCircle_Server(CFErrorRef
* error
)
822 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
823 EnsureFreshParameters_once(account
, NULL
);
824 return SOSAccountJoinCircles(account
, block_error
);
828 bool SOSCCRequestToJoinCircleAfterRestore_Server(CFErrorRef
* error
)
830 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
831 EnsureFreshParameters_once(account
, NULL
);
832 return SOSAccountJoinCirclesAfterRestore(account
, block_error
);
836 bool SOSCCResetToOffering_Server(CFErrorRef
* error
)
838 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
839 EnsureFreshParameters_once(account
, NULL
);
841 return SOSAccountResetToOffering(account
, block_error
);
845 bool SOSCCResetToEmpty_Server(CFErrorRef
* error
)
847 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
848 return SOSAccountResetToEmpty(account
, block_error
);
852 bool SOSCCRemoveThisDeviceFromCircle_Server(CFErrorRef
* error
)
854 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
855 bool result
= SOSAccountLeaveCircles(account
, block_error
);
860 bool SOSCCBailFromCircle_Server(uint64_t limit_in_seconds
, CFErrorRef
* error
)
862 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
863 return SOSAccountBail(account
, limit_in_seconds
, block_error
);
867 CFArrayRef
SOSCCCopyApplicantPeerInfo_Server(CFErrorRef
* error
)
869 __block CFArrayRef result
= NULL
;
871 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
872 EnsureFreshParameters_once(account
, NULL
);
873 result
= SOSAccountCopyApplicants(account
, block_error
);
874 return result
!= NULL
;
881 bool SOSCCAcceptApplicants_Server(CFArrayRef applicants
, CFErrorRef
* error
)
883 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
884 EnsureFreshParameters_once(account
, NULL
);
885 return SOSAccountAcceptApplicants(account
, applicants
, block_error
);
889 bool SOSCCRejectApplicants_Server(CFArrayRef applicants
, CFErrorRef
* error
)
891 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
892 EnsureFreshParameters_once(account
, NULL
);
893 return SOSAccountRejectApplicants(account
, applicants
, block_error
);
897 CFArrayRef
SOSCCCopyPeerPeerInfo_Server(CFErrorRef
* error
)
899 __block CFArrayRef result
= NULL
;
901 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
902 EnsureFreshParameters_once(account
, NULL
);
903 result
= SOSAccountCopyPeers(account
, block_error
);
904 return result
!= NULL
;
910 CFArrayRef
SOSCCCopyConcurringPeerPeerInfo_Server(CFErrorRef
* error
)
912 __block CFArrayRef result
= NULL
;
914 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
915 EnsureFreshParameters_once(account
, NULL
);
916 result
= SOSAccountCopyConcurringPeers(account
, block_error
);
917 return result
!= NULL
;
923 CFStringRef
SOSCCCopyIncompatibilityInfo_Server(CFErrorRef
* error
)
925 __block CFStringRef result
= NULL
;
927 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
928 EnsureFreshParameters_once(account
, NULL
);
929 result
= SOSAccountCopyIncompatibilityInfo(account
, block_error
);
930 return result
!= NULL
;
936 enum DepartureReason
SOSCCGetLastDepartureReason_Server(CFErrorRef
* error
)
938 __block
enum DepartureReason result
= kSOSDepartureReasonError
;
940 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
941 EnsureFreshParameters_once(account
, NULL
);
942 result
= SOSAccountGetLastDepartureReason(account
, block_error
);
943 return result
!= kSOSDepartureReasonError
;
949 SyncWithAllPeersReason
SOSCCProcessSyncWithAllPeers_Server(CFErrorRef
* error
)
952 #define kIOReturnLockedRead iokit_common_err(0x2c3) // device read locked
953 #define kIOReturnLockedWrite iokit_common_err(0x2c4) // device write locked
955 __block SyncWithAllPeersReason result
= kSyncWithAllPeersSuccess
;
956 CFErrorRef action_error
= NULL
;
958 if (!do_with_account_while_unlocked(&action_error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
959 CFErrorRef localError
= NULL
;
960 if (!SOSAccountSyncWithAllPeers(account
, &localError
)) {
961 secerror("sync with all peers failed: %@", localError
);
962 CFReleaseSafe(localError
);
963 // This isn't a device-locked error, but returning false will
964 // have CloudKeychainProxy ask us to try sync again after next unlock
965 result
= kSyncWithAllPeersOtherFail
;
971 if (SecErrorGetOSStatus(action_error
) == errSecInteractionNotAllowed
) {
972 secnotice("updates", "SOSAccountSyncWithAllPeers failed because device is locked; letting CloudKeychainProxy know");
973 result
= kSyncWithAllPeersLocked
; // tell CloudKeychainProxy to call us back when device unlocks
974 CFReleaseNull(action_error
);
976 secerror("Unexpected error: %@", action_error
);
979 if (error
&& *error
== NULL
) {
980 *error
= action_error
;
984 CFReleaseNull(action_error
);
991 void SOSCCSyncWithAllPeers(void)
993 SOSCloudKeychainRequestSyncWithAllPeers(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), NULL
);
996 void SOSCCHandleUpdate(CFDictionaryRef updates
)
998 SOSCloudKeychainHandleUpdate(updates
);