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 <SecureObjectSync/SOSCloudCircle.h>
30 #include <SecureObjectSync/SOSCloudCircleInternal.h>
31 #include <SecureObjectSync/SOSCircle.h>
32 #include <SecureObjectSync/SOSAccount.h>
33 #include <SecureObjectSync/SOSAccountPriv.h>
34 #include <SecureObjectSync/SOSFullPeerInfo.h>
35 #include <SecureObjectSync/SOSPeerInfoInternal.h>
36 #include <SecureObjectSync/SOSInternal.h>
37 #include <SecureObjectSync/SOSUserKeygen.h>
38 #include <SecureObjectSync/SOSMessage.h>
39 #include <SecureObjectSync/SOSTransport.h>
40 #include <SecureObjectSync/SOSKVSKeys.h>
42 #include <utilities/SecCFWrappers.h>
43 #include <utilities/SecCFRelease.h>
44 #include <utilities/debugging.h>
45 #include <CKBridge/SOSCloudKeychainClient.h>
47 #include <corecrypto/ccrng.h>
48 #include <corecrypto/ccrng_pbkdf2_prng.h>
49 #include <corecrypto/ccec.h>
50 #include <corecrypto/ccdigest.h>
51 #include <corecrypto/ccsha2.h>
52 #include <CommonCrypto/CommonRandomSPI.h>
53 #include <Security/SecKeyPriv.h>
54 #include <Security/SecFramework.h>
56 #include <utilities/SecFileLocations.h>
57 #include <utilities/SecAKSWrappers.h>
58 #include <securityd/SecItemServer.h>
59 #include <Security/SecItemPriv.h>
61 #include <TargetConditionals.h>
63 #include <utilities/iCloudKeychainTrace.h>
65 #if TARGET_OS_EMBEDDED || TARGET_IPHONE_SIMULATOR
66 #include <MobileGestalt.h>
68 #include <AppleSystemInfo/AppleSystemInfo.h>
70 // We need authorization, but that doesn't exist
71 // on sec built for desktop (iOS in a process)
72 // Define AuthorizationRef here to make SystemConfiguration work
74 typedef const struct AuthorizationOpaqueRef
* AuthorizationRef
;
77 #define SOSCKCSCOPE "sync"
79 #define USE_SYSTEMCONFIGURATION_PRIVATE_HEADERS
80 #import <SystemConfiguration/SystemConfiguration.h>
84 static SOSCCAccountDataSourceFactoryBlock accountDataSourceOverride
= NULL
;
86 bool SOSKeychainAccountSetFactoryForAccount(SOSCCAccountDataSourceFactoryBlock block
)
88 accountDataSourceOverride
= Block_copy(block
);
97 static void do_with_account(void (^action
)(SOSAccountRef account
));
98 static void do_with_account_async(void (^action
)(SOSAccountRef account
));
103 CFStringRef kSOSInternalAccessGroup
= CFSTR("com.apple.security.sos");
105 CFStringRef kSOSAccountLabel
= CFSTR("iCloud Keychain Account Meta-data");
107 static CFStringRef accountFileName
= CFSTR("PersistedAccount.plist");
109 static CFDictionaryRef
SOSItemCopyQueryForSyncItems(CFStringRef service
, bool returnData
)
111 return CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
112 kSecClass
, kSecClassGenericPassword
,
113 kSecAttrService
, service
,
114 kSecAttrAccessGroup
, kSOSInternalAccessGroup
,
115 kSecReturnData
, returnData
? kCFBooleanTrue
: kCFBooleanFalse
,
119 CFDataRef
SOSItemCopy(CFStringRef service
, CFErrorRef
* error
)
121 CFDictionaryRef query
= SOSItemCopyQueryForSyncItems(service
, true);
123 CFDataRef result
= NULL
;
125 OSStatus copyResult
= SecItemCopyMatching(query
, (CFTypeRef
*) &result
);
127 CFReleaseNull(query
);
129 if (copyResult
!= noErr
) {
130 SecError(copyResult
, error
, CFSTR("Error %@ reading for service '%@'"), result
, service
);
131 CFReleaseNull(result
);
135 if (!isData(result
)) {
136 SOSCreateErrorWithFormat(kSOSErrorProcessingFailure
, NULL
, error
, NULL
, CFSTR("SecItemCopyMatching returned non-data in '%@'"), service
);
137 CFReleaseNull(result
);
144 static CFDataRef
SOSKeychainCopySavedAccountData()
146 CFErrorRef error
= NULL
;
147 CFDataRef accountData
= SOSItemCopy(kSOSAccountLabel
, &error
);
149 secnotice("account", "Failed to load account: %@", error
);
150 CFReleaseNull(error
);
155 bool SOSItemUpdateOrAdd(CFStringRef service
, CFStringRef accessibility
, CFDataRef data
, CFErrorRef
*error
)
157 CFDictionaryRef query
= SOSItemCopyQueryForSyncItems(service
, false);
159 CFDictionaryRef update
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
161 kSecAttrAccessible
, accessibility
,
163 OSStatus saveStatus
= SecItemUpdate(query
, update
);
165 if (errSecItemNotFound
== saveStatus
) {
166 CFMutableDictionaryRef add
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, query
);
167 CFDictionaryForEach(update
, ^(const void *key
, const void *value
) {
168 CFDictionaryAddValue(add
, key
, value
);
170 saveStatus
= SecItemAdd(add
, NULL
);
174 CFReleaseNull(query
);
175 CFReleaseNull(update
);
177 return SecError(saveStatus
, error
, CFSTR("Error saving %@ to service '%@'"), data
, service
);
180 static CFStringRef accountStatusFileName
= CFSTR("accountStatus.plist");
181 #include <utilities/der_plist.h>
182 #include <utilities/der_plist_internal.h>
183 #include <corecrypto/ccder.h>
185 static const uint8_t* ccder_decode_bool(bool* boolean
, const uint8_t* der
, const uint8_t *der_end
)
190 size_t payload_size
= 0;
191 const uint8_t *payload
= ccder_decode_tl(CCDER_BOOLEAN
, &payload_size
, der
, der_end
);
193 if (NULL
== payload
|| (der_end
- payload
) < 1 || payload_size
!= 1) {
198 *boolean
= (*payload
!= 0);
200 return payload
+ payload_size
;
204 bool SOSCCCircleIsOn_Artifact(void) {
205 bool circle_on
= false;
206 CFDataRef accountStatus
= NULL
;
207 CFURLRef accountStatusFileURL
= SecCopyURLForFileInKeychainDirectory(accountStatusFileName
);
208 require_quiet(accountStatusFileURL
&& CFURLResourceIsReachable(accountStatusFileURL
, NULL
), xit
);
209 accountStatus
= (CFDataRef
) CFPropertyListReadFromFile(accountStatusFileURL
);
211 if(isData(accountStatus
)) {
212 size_t size
= CFDataGetLength(accountStatus
);
213 const uint8_t *der
= CFDataGetBytePtr(accountStatus
);
214 const uint8_t *der_p
= der
;
216 const uint8_t *sequence_end
;
217 der_p
= ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE
, &sequence_end
, der_p
, der_p
+ size
);
218 der_p
= ccder_decode_bool(&circle_on
, der_p
, sequence_end
);
223 CFReleaseSafe(accountStatusFileURL
);
224 CFReleaseSafe(accountStatus
);
230 static size_t ccder_sizeof_bool(bool value __unused
, CFErrorRef
*error
)
232 return ccder_sizeof(CCDER_BOOLEAN
, 1);
236 static uint8_t* ccder_encode_bool(bool value
, const uint8_t *der
, uint8_t *der_end
)
238 uint8_t value_byte
= value
;
240 return ccder_encode_tl(CCDER_BOOLEAN
, 1, der
,
241 ccder_encode_body(1, &value_byte
, der
, der_end
));
245 static void SOSCCCircleIsOn_SetArtifact(bool account_on
) {
246 static CFDataRef sLastSavedAccountStatus
= NULL
;
247 CFErrorRef saveError
= NULL
;
248 size_t der_size
= ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE
, ccder_sizeof_bool(account_on
, NULL
));
249 uint8_t der
[der_size
];
250 uint8_t *der_end
= der
+ der_size
;
251 der_end
= ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE
, der_end
, der
,
252 ccder_encode_bool(account_on
, der
, der_end
));
254 CFDataRef accountStatusAsData
= CFDataCreate(kCFAllocatorDefault
, der_end
, der_size
);
256 require_quiet(accountStatusAsData
, exit
);
257 if (sLastSavedAccountStatus
&& CFEqual(sLastSavedAccountStatus
, accountStatusAsData
)) goto exit
;
259 CFURLRef accountStatusFileURL
= SecCopyURLForFileInKeychainDirectory(accountStatusFileName
);
260 CFPropertyListWriteToFile((CFPropertyListRef
) accountStatusAsData
, accountStatusFileURL
);
261 CFReleaseSafe(accountStatusFileURL
);
263 CFReleaseNull(sLastSavedAccountStatus
);
264 sLastSavedAccountStatus
= accountStatusAsData
;
265 accountStatusAsData
= NULL
;
268 CFReleaseNull(saveError
);
269 CFReleaseNull(accountStatusAsData
);
272 static void SOSCCCircleIsOn_UpdateArtifact(SOSCCStatus status
)
275 case kSOSCCCircleAbsent
:
276 case kSOSCCNotInCircle
:
277 SOSCCCircleIsOn_SetArtifact(false);
280 case kSOSCCRequestPending
:
281 SOSCCCircleIsOn_SetArtifact(true);
290 static void SOSKeychainAccountEnsureSaved(SOSAccountRef account
)
292 static CFDataRef sLastSavedAccountData
= NULL
;
294 CFErrorRef saveError
= NULL
;
295 SOSCCCircleIsOn_UpdateArtifact(SOSAccountIsInCircles(account
, NULL
));
297 CFDataRef accountAsData
= SOSAccountCopyEncodedData(account
, kCFAllocatorDefault
, &saveError
);
299 require_action_quiet(accountAsData
, exit
, secerror("Failed to transform account into data, error: %@", saveError
));
300 require_quiet(!CFEqualSafe(sLastSavedAccountData
, accountAsData
), exit
);
302 if (!SOSItemUpdateOrAdd(kSOSAccountLabel
, kSecAttrAccessibleAlwaysThisDeviceOnly
, accountAsData
, &saveError
)) {
303 secerror("Can't save account: %@", saveError
);
307 CFReleaseNull(sLastSavedAccountData
);
308 sLastSavedAccountData
= accountAsData
;
309 accountAsData
= NULL
;
312 CFReleaseNull(saveError
);
313 CFReleaseNull(accountAsData
);
318 Stolen from keychain_sync.c
321 static bool clearAllKVS(CFErrorRef
*error
)
324 __block
bool result
= false;
325 const uint64_t maxTimeToWaitInSeconds
= 30ull * NSEC_PER_SEC
;
326 dispatch_queue_t processQueue
= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0);
327 dispatch_semaphore_t waitSemaphore
= dispatch_semaphore_create(0);
328 dispatch_time_t finishTime
= dispatch_time(DISPATCH_TIME_NOW
, maxTimeToWaitInSeconds
);
330 SOSCloudKeychainClearAll(processQueue
, ^(CFDictionaryRef returnedValues
, CFErrorRef cerror
)
332 result
= (cerror
!= NULL
);
333 dispatch_semaphore_signal(waitSemaphore
);
336 dispatch_semaphore_wait(waitSemaphore
, finishTime
);
337 dispatch_release(waitSemaphore
);
342 static SOSAccountRef
SOSKeychainAccountCreateSharedAccount(CFDictionaryRef our_gestalt
)
344 secdebug("account", "Created account");
346 CFDataRef savedAccount
= SOSKeychainCopySavedAccountData();
347 SOSAccountRef account
= NULL
;
348 SOSDataSourceFactoryRef factory
= accountDataSourceOverride
? accountDataSourceOverride()
349 : SecItemDataSourceFactoryGetDefault();
352 CFErrorRef inflationError
= NULL
;
354 account
= SOSAccountCreateFromData(kCFAllocatorDefault
, savedAccount
, factory
, &inflationError
);
357 SOSAccountUpdateGestalt(account
, our_gestalt
);
360 secerror("Got error inflating account: %@", inflationError
);
361 CFReleaseNull(inflationError
);
363 CFReleaseSafe(savedAccount
);
366 account
= SOSAccountCreate(kCFAllocatorDefault
, our_gestalt
, factory
);
369 secerror("Got NULL creating account");
376 // Mark: Gestalt Handling
379 static CFStringRef
CopyModelName(void)
381 static dispatch_once_t once
;
382 static CFStringRef modelName
= NULL
;
383 dispatch_once(&once
, ^{
384 #if TARGET_OS_EMBEDDED || TARGET_IPHONE_SIMULATOR
385 modelName
= MGCopyAnswer(kMGQDeviceName
, NULL
);
387 modelName
= ASI_CopyComputerModelName(FALSE
);
389 if (modelName
== NULL
)
390 modelName
= CFSTR("Unknown model");
392 return CFStringCreateCopy(kCFAllocatorDefault
, modelName
);
395 static CFStringRef
CopyComputerName(SCDynamicStoreRef store
)
397 CFStringRef deviceName
= SCDynamicStoreCopyComputerName(store
, NULL
);
398 if (deviceName
== NULL
) {
399 deviceName
= CFSTR("Unknown name");
404 static bool _EngineMessageProtocolV2Enabled(void)
408 static dispatch_once_t onceToken
;
409 static bool v2_enabled
= false;
410 dispatch_once(&onceToken
, ^{
411 CFTypeRef v2Pref
= (CFNumberRef
)CFPreferencesCopyValue(CFSTR("engineV2"), CFSTR("com.apple.security"), kCFPreferencesAnyUser
, kCFPreferencesCurrentHost
);
413 if (v2Pref
&& CFGetTypeID(v2Pref
) == CFBooleanGetTypeID()) {
414 v2_enabled
= CFBooleanGetValue((CFBooleanRef
)v2Pref
);
415 secnotice("server", "Engine v2 : %s", v2_enabled
? "enabled":"disabled");
417 CFReleaseSafe(v2Pref
);
427 static CFDictionaryRef
CFDictionaryCreateDeviceGestalt(SCDynamicStoreRef store
, CFArrayRef keys
, void *context
)
429 CFStringRef modelName
= CopyModelName();
430 CFStringRef computerName
= CopyComputerName(store
);
431 SInt32 version
= _EngineMessageProtocolV2Enabled() ? kEngineMessageProtocolVersion
: 0;
432 CFNumberRef protocolVersion
= CFNumberCreate(0, kCFNumberSInt32Type
, &version
);
435 CFDictionaryRef gestalt
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
436 kPIUserDefinedDeviceName
, computerName
,
437 kPIDeviceModelName
, modelName
,
438 kPIMessageProtocolVersion
, protocolVersion
,
440 CFReleaseSafe(modelName
);
441 CFReleaseSafe(computerName
);
442 CFReleaseSafe(protocolVersion
);
447 static void SOSCCProcessGestaltUpdate(SCDynamicStoreRef store
, CFArrayRef keys
, void *context
)
449 do_with_account(^(SOSAccountRef account
) {
451 CFDictionaryRef gestalt
= CFDictionaryCreateDeviceGestalt(store
, keys
, context
);
452 if (SOSAccountUpdateGestalt(account
, gestalt
)) {
453 notify_post(kSOSCCCircleChangedNotification
);
455 CFReleaseSafe(gestalt
);
461 static CFDictionaryRef
CFDictionaryCreateGestaltAndRegisterForUpdate(dispatch_queue_t queue
, void *info
)
463 SCDynamicStoreContext context
= { .info
= info
};
464 SCDynamicStoreRef store
= SCDynamicStoreCreate(NULL
, CFSTR("com.apple.securityd.cloudcircleserver"), SOSCCProcessGestaltUpdate
, &context
);
465 CFStringRef computerKey
= SCDynamicStoreKeyCreateComputerName(NULL
);
466 CFArrayRef keys
= NULL
;
467 CFDictionaryRef gestalt
= NULL
;
469 if (store
== NULL
|| computerKey
== NULL
) {
472 keys
= CFArrayCreate(NULL
, (const void **)&computerKey
, 1, &kCFTypeArrayCallBacks
);
476 gestalt
= CFDictionaryCreateDeviceGestalt(store
, keys
, info
);
477 SCDynamicStoreSetNotificationKeys(store
, keys
, NULL
);
478 SCDynamicStoreSetDispatchQueue(store
, queue
);
481 if (store
) CFRelease(store
);
482 if (computerKey
) CFRelease(computerKey
);
483 if (keys
) CFRelease(keys
);
487 static void do_with_account(void (^action
)(SOSAccountRef account
));
488 static void do_with_account_async(void (^action
)(SOSAccountRef account
));
490 static SOSAccountRef
GetSharedAccount(void) {
491 static SOSAccountRef sSharedAccount
= NULL
;
492 static dispatch_once_t onceToken
;
494 #if !(TARGET_OS_EMBEDDED)
496 secerror("Cannot inflate account object as root");
501 dispatch_once(&onceToken
, ^{
502 secdebug("account", "Account Creation start");
504 CFDictionaryRef gestalt
= CFDictionaryCreateGestaltAndRegisterForUpdate(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), NULL
);
507 #if TARGET_OS_IPHONE && TARGET_IPHONE_SIMULATOR
508 gestalt
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, NULL
);
510 secerror("Didn't get machine gestalt! This is going to be ugly.");
514 sSharedAccount
= SOSKeychainAccountCreateSharedAccount(gestalt
);
516 SOSCCSetThisDeviceDefinitelyNotActiveInCircle(SOSAccountIsInCircles(sSharedAccount
, NULL
));
518 SOSAccountAddChangeBlock(sSharedAccount
, ^(SOSCircleRef circle
,
519 CFSetRef peer_additions
, CFSetRef peer_removals
,
520 CFSetRef applicant_additions
, CFSetRef applicant_removals
) {
521 CFErrorRef pi_error
= NULL
;
522 SOSPeerInfoRef me
= SOSAccountGetMyPeerInCircle(sSharedAccount
, circle
, &pi_error
);
524 secerror("Error finding me for change: %@", pi_error
);
526 if (SOSCircleHasPeer(circle
, me
, NULL
) && CFSetGetCount(peer_additions
) != 0) {
527 secnotice("updates", "Requesting Ensure Peer Registration.");
528 SOSCloudKeychainRequestEnsurePeerRegistration(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), NULL
);
531 if (CFSetContainsValue(peer_additions
, me
)) {
532 SOSCCSyncWithAllPeers();
536 CFReleaseNull(pi_error
);
538 if (CFSetGetCount(peer_additions
) != 0 ||
539 CFSetGetCount(peer_removals
) != 0 ||
540 CFSetGetCount(applicant_additions
) != 0 ||
541 CFSetGetCount(applicant_removals
) != 0) {
543 SOSCCSetThisDeviceDefinitelyNotActiveInCircle(SOSAccountIsInCircles(sSharedAccount
, NULL
));
544 notify_post(kSOSCCCircleChangedNotification
);
548 SOSCloudKeychainSetItemsChangedBlock(^CFArrayRef(CFDictionaryRef changes
) {
549 CFRetainSafe(changes
);
550 __block CFMutableArrayRef handledKeys
= NULL
;
551 do_with_account(^(SOSAccountRef account
) {
552 CFStringRef changeDescription
= SOSChangesCopyDescription(changes
, false);
553 secdebug(SOSCKCSCOPE
, "Received: %@", changeDescription
);
554 CFReleaseSafe(changeDescription
);
556 CFErrorRef error
= NULL
;
557 handledKeys
= SOSTransportDispatchMessages(account
, changes
, &error
);
559 secerror("Error handling updates: %@", error
);
560 CFReleaseNull(error
);
563 CFReleaseSafe(changes
);
566 CFReleaseSafe(gestalt
);
568 SOSCloudKeychainRequestEnsurePeerRegistration(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), NULL
);
572 return sSharedAccount
;
575 static void do_with_account_dynamic(void (^action
)(SOSAccountRef account
), bool sync
) {
576 SOSAccountRef account
= GetSharedAccount();
579 dispatch_block_t do_action_and_save
= ^{
581 SOSKeychainAccountEnsureSaved(account
);
585 dispatch_sync(SOSAccountGetQueue(account
), do_action_and_save
);
587 dispatch_async(SOSAccountGetQueue(account
), do_action_and_save
);
592 __unused
static void do_with_account_async(void (^action
)(SOSAccountRef account
)) {
593 do_with_account_dynamic(action
, false);
596 static void do_with_account(void (^action
)(SOSAccountRef account
)) {
597 do_with_account_dynamic(action
, true);
600 #if TARGET_IPHONE_SIMULATOR
601 #define MKBDeviceUnlockedSinceBoot() true
604 static bool do_if_after_first_unlock(CFErrorRef
*error
, dispatch_block_t action
)
606 bool beenUnlocked
= false;
607 require_quiet(SecAKSGetHasBeenUnlocked(&beenUnlocked
, error
), fail
);
609 require_action_quiet(beenUnlocked
, fail
,
610 SOSCreateErrorWithFormat(kSOSErrorNotReady
, NULL
, error
, NULL
,
611 CFSTR("Keybag never unlocked, ask after first unlock")));
620 static bool do_with_account_if_after_first_unlock(CFErrorRef
*error
, bool (^action
)(SOSAccountRef account
, CFErrorRef
* error
))
622 __block
bool action_result
= false;
624 #if !(TARGET_OS_EMBEDDED)
626 secerror("Cannot inflate account object as root");
630 return do_if_after_first_unlock(error
, ^{
631 do_with_account(^(SOSAccountRef account
) {
632 action_result
= action(account
, error
);
638 static bool do_with_account_while_unlocked(CFErrorRef
*error
, bool (^action
)(SOSAccountRef account
, CFErrorRef
* error
))
640 __block
bool action_result
= false;
642 #if !(TARGET_OS_EMBEDDED)
644 secerror("Cannot inflate account object as root");
649 return SecAKSDoWhileUserBagLocked(error
, ^{
650 do_with_account(^(SOSAccountRef account
) {
651 action_result
= action(account
, error
);
657 SOSAccountRef
SOSKeychainAccountGetSharedAccount()
659 __block SOSAccountRef result
= NULL
;
661 do_with_account(^(SOSAccountRef account
) {
669 // Mark: Credential processing
673 bool SOSCCTryUserCredentials_Server(CFStringRef user_label
, CFDataRef user_password
, CFErrorRef
*error
)
675 return do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
676 return SOSAccountTryUserCredentials(account
, user_label
, user_password
, block_error
);
680 #define kWAIT2MINID "EFRESH"
682 static bool EnsureFreshParameters(SOSAccountRef account
, CFErrorRef
*error
) {
683 dispatch_semaphore_t wait_for
= dispatch_semaphore_create(0);
684 dispatch_retain(wait_for
); // Both this scope and the block own it.
686 CFMutableArrayRef keysToGet
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
687 CFArrayAppendValue(keysToGet
, kSOSKVSKeyParametersKey
);
689 __block CFDictionaryRef valuesToUpdate
= NULL
;
690 __block
bool success
= false;
692 secnoticeq("fresh", "%s calling SOSCloudKeychainSynchronizeAndWait", kWAIT2MINID
);
694 SOSCloudKeychainSynchronizeAndWait(keysToGet
, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), ^(CFDictionaryRef returnedValues
, CFErrorRef sync_error
) {
697 secerrorq("%s SOSCloudKeychainSynchronizeAndWait: %@", kWAIT2MINID
, sync_error
);
700 CFRetainSafe(*error
);
703 secnoticeq("fresh", "%s returned from call; in callback to SOSCloudKeychainSynchronizeAndWait: results: %@", kWAIT2MINID
, returnedValues
);
704 valuesToUpdate
= returnedValues
;
705 CFRetainSafe(valuesToUpdate
);
709 dispatch_semaphore_signal(wait_for
);
710 dispatch_release(wait_for
);
713 dispatch_semaphore_wait(wait_for
, DISPATCH_TIME_FOREVER
);
714 // TODO: Maybe we timeout here... used to dispatch_time(DISPATCH_TIME_NOW, 30ull * NSEC_PER_SEC));
715 dispatch_release(wait_for
);
716 CFMutableArrayRef handledKeys
= NULL
;
717 if ((valuesToUpdate
) && (account
)) {
718 handledKeys
= SOSTransportDispatchMessages(account
, valuesToUpdate
, error
);
720 secerrorq("%s Freshness update failed: %@", kWAIT2MINID
, error
? *error
: NULL
);
724 CFReleaseNull(handledKeys
);
725 CFReleaseNull(valuesToUpdate
);
726 CFReleaseNull(keysToGet
);
731 static bool Flush(CFErrorRef
*error
) {
732 __block
bool success
= false;
734 dispatch_semaphore_t wait_for
= dispatch_semaphore_create(0);
735 dispatch_retain(wait_for
); // Both this scope and the block own it.
737 secnotice("flush", "Starting");
739 SOSCloudKeychainFlush(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), ^(CFDictionaryRef returnedValues
, CFErrorRef sync_error
) {
740 success
= (sync_error
== NULL
);
742 CFRetainAssign(*error
, sync_error
);
745 dispatch_semaphore_signal(wait_for
);
746 dispatch_release(wait_for
);
749 dispatch_semaphore_wait(wait_for
, DISPATCH_TIME_FOREVER
);
750 dispatch_release(wait_for
);
752 secnotice("flush", "Returned %s", success
? "Success": "Failure");
757 bool SOSCCSetUserCredentials_Server(CFStringRef user_label
, CFDataRef user_password
, CFErrorRef
*error
)
759 secnotice("updates", "Setting credentials for %@", user_label
); // TODO: remove this notice
760 bool result
= do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
761 if (!EnsureFreshParameters(account
, block_error
)) {
764 if (!SOSAccountAssertUserCredentials(account
, user_label
, user_password
, block_error
)) {
765 secnotice("updates", "EnsureFreshParameters/SOSAccountAssertUserCredentials error: %@", *block_error
);
771 return result
&& Flush(error
);
774 bool SOSCCCanAuthenticate_Server(CFErrorRef
*error
)
776 bool result
= do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
777 return SOSAccountGetPrivateCredential(account
, block_error
) != NULL
;
780 if (!result
&& error
&& *error
&& CFErrorGetDomain(*error
) == kSOSErrorDomain
) {
781 CFIndex code
= CFErrorGetCode(*error
);
782 if (code
== kSOSErrorPrivateKeyAbsent
|| code
== kSOSErrorPublicKeyAbsent
) {
783 CFReleaseNull(*error
);
790 bool SOSCCPurgeUserCredentials_Server(CFErrorRef
*error
)
792 return do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
793 SOSAccountPurgePrivateCredential(account
);
800 static bool sAccountInCircleCache
= false;
802 static void do_with_not_in_circle_bool_queue(bool start_account
, dispatch_block_t action
)
804 static dispatch_queue_t account_start_queue
;
805 static dispatch_queue_t not_in_circle_queue
;
806 static bool account_started
= false;
808 static dispatch_once_t onceToken
;
809 dispatch_once(&onceToken
, ^{
810 not_in_circle_queue
= dispatch_queue_create("nis queue", DISPATCH_QUEUE_SERIAL
);
811 account_start_queue
= dispatch_queue_create("init nis queue", DISPATCH_QUEUE_SERIAL
);;
812 account_started
= false;
815 __block
bool done
= false;
816 dispatch_sync(not_in_circle_queue
, ^{
817 if (account_started
) {
823 if (!done
&& start_account
) {
824 dispatch_sync(account_start_queue
, ^{
825 __block
bool do_start
= false;
826 dispatch_sync(not_in_circle_queue
, ^{
827 do_start
= !account_started
;
828 account_started
= true;
831 SOSCCThisDeviceIsInCircle(NULL
); // Inflate account.
834 dispatch_sync(not_in_circle_queue
, action
);
839 bool SOSCCThisDeviceDefinitelyNotActiveInCircle()
841 return !SOSCCCircleIsOn_Artifact();
843 __block
bool result
= false;
844 do_with_not_in_circle_bool_queue(true, ^{
845 result
= sAccountInCircleCache
;
852 void SOSCCSetThisDeviceDefinitelyNotActiveInCircle(SOSCCStatus currentStatus
)
854 SOSCCCircleIsOn_UpdateArtifact(currentStatus
);
856 do_with_not_in_circle_bool_queue(false, ^{
857 sAccountInCircleCache
= notActive
;
863 SOSCCStatus
SOSCCThisDeviceIsInCircle_Server(CFErrorRef
*error
)
865 __block SOSCCStatus status
;
867 return do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
868 status
= SOSAccountIsInCircles(account
, block_error
);
870 }) ? status
: kSOSCCError
;
873 bool SOSCCRequestToJoinCircle_Server(CFErrorRef
* error
)
875 __block
bool result
= true;
877 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
878 result
= SOSAccountJoinCircles(account
, block_error
);
883 bool SOSCCRequestToJoinCircleAfterRestore_Server(CFErrorRef
* error
)
885 __block
bool result
= true;
886 bool returned
= false;
887 returned
= do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
888 SOSAccountEnsurePeerRegistration(account
, block_error
);
889 result
= SOSAccountJoinCirclesAfterRestore(account
, block_error
);
896 bool SOSCCRequestEnsureFreshParameters_Server(CFErrorRef
* error
)
898 __block
bool result
= true;
899 bool returned
= false;
900 returned
= do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
901 result
= EnsureFreshParameters(account
, NULL
);
907 CFStringRef
SOSCCRequestDeviceID_Server(CFErrorRef
*error
)
909 __block CFStringRef result
= NULL
;
911 (void) do_with_account_while_unlocked(error
, ^bool(SOSAccountRef account
, CFErrorRef
*error
) {
912 result
= SOSAccountGetDeviceID(account
, error
);
913 return (!isNull(result
));
918 bool SOSCCSetDeviceID_Server(CFStringRef IDS
, CFErrorRef
*error
){
919 __block
bool result
= true;
921 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
922 result
= SOSAccountSetMyDSID(account
, IDS
, block_error
);
927 bool SOSCCResetToOffering_Server(CFErrorRef
* error
)
929 __block
bool result
= true;
931 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
933 result
= SOSAccountResetToOffering(account
, block_error
);
939 bool SOSCCResetToEmpty_Server(CFErrorRef
* error
)
941 __block
bool result
= true;
943 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
944 result
= SOSAccountResetToEmpty(account
, block_error
);
950 bool SOSCCRemoveThisDeviceFromCircle_Server(CFErrorRef
* error
)
952 __block
bool result
= true;
954 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
955 result
= SOSAccountLeaveCircles(account
, block_error
);
960 bool SOSCCBailFromCircle_Server(uint64_t limit_in_seconds
, CFErrorRef
* error
)
962 __block
bool result
= true;
964 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
965 result
= SOSAccountBail(account
, limit_in_seconds
, block_error
);
971 CFArrayRef
SOSCCCopyApplicantPeerInfo_Server(CFErrorRef
* error
)
973 __block CFArrayRef result
= NULL
;
975 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
976 result
= SOSAccountCopyApplicants(account
, block_error
);
977 return result
!= NULL
;
983 CFArrayRef
SOSCCCopyGenerationPeerInfo_Server(CFErrorRef
* error
)
985 __block CFArrayRef result
= NULL
;
987 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
988 result
= SOSAccountCopyGeneration(account
, block_error
);
989 return result
!= NULL
;
995 CFArrayRef
SOSCCCopyValidPeerPeerInfo_Server(CFErrorRef
* error
)
997 __block CFArrayRef result
= NULL
;
999 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1000 result
= SOSAccountCopyValidPeers(account
, block_error
);
1001 return result
!= NULL
;
1007 bool SOSCCValidateUserPublic_Server(CFErrorRef
* error
)
1009 __block
bool result
= NULL
;
1011 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1012 result
= SOSValidateUserPublic(account
, block_error
);
1019 CFArrayRef
SOSCCCopyNotValidPeerPeerInfo_Server(CFErrorRef
* error
)
1021 __block CFArrayRef result
= NULL
;
1023 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1024 result
= SOSAccountCopyNotValidPeers(account
, block_error
);
1025 return result
!= NULL
;
1031 CFArrayRef
SOSCCCopyRetirementPeerInfo_Server(CFErrorRef
* error
)
1033 __block CFArrayRef result
= NULL
;
1035 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1036 result
= SOSAccountCopyRetired(account
, block_error
);
1037 return result
!= NULL
;
1043 bool SOSCCAcceptApplicants_Server(CFArrayRef applicants
, CFErrorRef
* error
)
1045 __block
bool result
= true;
1046 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1047 result
= SOSAccountAcceptApplicants(account
, applicants
, block_error
);
1053 bool SOSCCRejectApplicants_Server(CFArrayRef applicants
, CFErrorRef
* error
)
1055 __block
bool result
= true;
1056 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1057 result
= SOSAccountRejectApplicants(account
, applicants
, block_error
);
1062 CFArrayRef
SOSCCCopyPeerPeerInfo_Server(CFErrorRef
* error
)
1064 __block CFArrayRef result
= NULL
;
1066 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1067 result
= SOSAccountCopyPeers(account
, block_error
);
1068 return result
!= NULL
;
1074 CFArrayRef
SOSCCCopyConcurringPeerPeerInfo_Server(CFErrorRef
* error
)
1076 __block CFArrayRef result
= NULL
;
1078 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1079 result
= SOSAccountCopyConcurringPeers(account
, block_error
);
1080 return result
!= NULL
;
1086 CFStringRef
SOSCCCopyIncompatibilityInfo_Server(CFErrorRef
* error
)
1088 __block CFStringRef result
= NULL
;
1090 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1091 result
= SOSAccountCopyIncompatibilityInfo(account
, block_error
);
1092 return result
!= NULL
;
1098 enum DepartureReason
SOSCCGetLastDepartureReason_Server(CFErrorRef
* error
)
1100 __block
enum DepartureReason result
= kSOSDepartureReasonError
;
1102 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1103 result
= SOSAccountGetLastDepartureReason(account
, block_error
);
1104 return result
!= kSOSDepartureReasonError
;
1111 bool SOSCCProcessEnsurePeerRegistration_Server(CFErrorRef
* error
)
1113 secnotice("updates", "Request for registering peers");
1114 return do_with_account_while_unlocked(error
, ^bool(SOSAccountRef account
, CFErrorRef
*error
) {
1115 return SOSAccountEnsurePeerRegistration(account
, error
);
1119 SyncWithAllPeersReason
SOSCCProcessSyncWithAllPeers_Server(CFErrorRef
* error
)
1122 #define kIOReturnLockedRead iokit_common_err(0x2c3) // device read locked
1123 #define kIOReturnLockedWrite iokit_common_err(0x2c4) // device write locked
1125 __block SyncWithAllPeersReason result
= kSyncWithAllPeersSuccess
;
1126 CFErrorRef action_error
= NULL
;
1128 if (!do_with_account_while_unlocked(&action_error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1129 CFErrorRef localError
= NULL
;
1130 if (!SOSAccountSyncWithAllPeers(account
, &localError
)) {
1131 secerror("sync with all peers failed: %@", localError
);
1132 CFReleaseSafe(localError
);
1133 // This isn't a device-locked error, but returning false will
1134 // have CloudKeychainProxy ask us to try sync again after next unlock
1135 result
= kSyncWithAllPeersOtherFail
;
1141 if (SecErrorGetOSStatus(action_error
) == errSecInteractionNotAllowed
) {
1142 secnotice("updates", "SOSAccountSyncWithAllPeers failed because device is locked; letting CloudKeychainProxy know");
1143 result
= kSyncWithAllPeersLocked
; // tell CloudKeychainProxy to call us back when device unlocks
1144 CFReleaseNull(action_error
);
1146 secerror("Unexpected error: %@", action_error
);
1149 if (error
&& *error
== NULL
) {
1150 *error
= action_error
;
1151 action_error
= NULL
;
1154 CFReleaseNull(action_error
);
1161 void SOSCCSyncWithAllPeers(void)
1163 SOSCloudKeychainRequestSyncWithAllPeers(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), NULL
);
1166 CF_RETURNS_RETAINED CFArrayRef
SOSCCHandleUpdateKeyParameter(CFDictionaryRef updates
)
1168 CFArrayRef result
= NULL
;
1169 SOSAccountRef account
= SOSKeychainAccountGetSharedAccount(); //HACK to make sure itemsChangedBlock is set
1170 (account
) ? (result
= SOSCloudKeychainHandleUpdateKeyParameter(updates
)) : (result
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
));
1174 CF_RETURNS_RETAINED CFArrayRef
SOSCCHandleUpdateCircle(CFDictionaryRef updates
)
1176 CFArrayRef result
= NULL
;
1177 SOSAccountRef account
= SOSKeychainAccountGetSharedAccount(); //HACK to make sure itemsChangedBlock is set
1178 (account
) ? (result
= SOSCloudKeychainHandleUpdateKeyParameter(updates
)) : (result
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
));
1182 CF_RETURNS_RETAINED CFArrayRef
SOSCCHandleUpdateMessage(CFDictionaryRef updates
)
1184 CFArrayRef result
= NULL
;
1185 SOSAccountRef account
= SOSKeychainAccountGetSharedAccount(); //HACK to make sure itemsChangedBlock is set
1186 (account
) ? (result
= SOSCloudKeychainHandleUpdateKeyParameter(updates
)) : (result
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
));