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
;
296 if(SOSAccountGetMyFullPeerInCircleNamedIfPresent(account
, CFSTR("ak"), NULL
) == NULL
) {
300 SOSCCCircleIsOn_UpdateArtifact(SOSAccountIsInCircles(account
, NULL
));
302 CFDataRef accountAsData
= SOSAccountCopyEncodedData(account
, kCFAllocatorDefault
, &saveError
);
304 require_action_quiet(accountAsData
, exit
, secerror("Failed to transform account into data, error: %@", saveError
));
305 require_quiet(!CFEqualSafe(sLastSavedAccountData
, accountAsData
), exit
);
307 if (!SOSItemUpdateOrAdd(kSOSAccountLabel
, kSecAttrAccessibleAlwaysThisDeviceOnly
, accountAsData
, &saveError
)) {
308 secerror("Can't save account: %@", saveError
);
312 CFReleaseNull(sLastSavedAccountData
);
313 sLastSavedAccountData
= accountAsData
;
314 accountAsData
= NULL
;
317 CFReleaseNull(saveError
);
318 CFReleaseNull(accountAsData
);
323 Stolen from keychain_sync.c
326 static bool clearAllKVS(CFErrorRef
*error
)
329 __block
bool result
= false;
330 const uint64_t maxTimeToWaitInSeconds
= 30ull * NSEC_PER_SEC
;
331 dispatch_queue_t processQueue
= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0);
332 dispatch_semaphore_t waitSemaphore
= dispatch_semaphore_create(0);
333 dispatch_time_t finishTime
= dispatch_time(DISPATCH_TIME_NOW
, maxTimeToWaitInSeconds
);
335 SOSCloudKeychainClearAll(processQueue
, ^(CFDictionaryRef returnedValues
, CFErrorRef cerror
)
337 result
= (cerror
!= NULL
);
338 dispatch_semaphore_signal(waitSemaphore
);
341 dispatch_semaphore_wait(waitSemaphore
, finishTime
);
342 dispatch_release(waitSemaphore
);
347 static SOSAccountRef
SOSKeychainAccountCreateSharedAccount(CFDictionaryRef our_gestalt
)
349 secdebug("account", "Created account");
351 CFDataRef savedAccount
= SOSKeychainCopySavedAccountData();
352 SOSAccountRef account
= NULL
;
353 SOSDataSourceFactoryRef factory
= accountDataSourceOverride
? accountDataSourceOverride()
354 : SecItemDataSourceFactoryGetDefault();
357 CFErrorRef inflationError
= NULL
;
359 account
= SOSAccountCreateFromData(kCFAllocatorDefault
, savedAccount
, factory
, &inflationError
);
361 if(account
&& SOSAccountGetMyFullPeerInCircleNamedIfPresent(account
, CFSTR("ak"), NULL
) == NULL
) {
362 SOSAccountRef newAccount
= SOSAccountCreate(kCFAllocatorDefault
, our_gestalt
, factory
);
365 secnotice("repair_account", "Tried to repair bad account - got null account");
367 account
= newAccount
;
372 SOSAccountUpdateGestalt(account
, our_gestalt
);
374 secerror("Got error inflating account: %@", inflationError
);
377 CFReleaseNull(inflationError
);
379 CFReleaseSafe(savedAccount
);
382 account
= SOSAccountCreate(kCFAllocatorDefault
, our_gestalt
, factory
);
385 secerror("Got NULL creating account");
392 // Mark: Gestalt Handling
395 static CFStringRef
CopyModelName(void)
397 static dispatch_once_t once
;
398 static CFStringRef modelName
= NULL
;
399 dispatch_once(&once
, ^{
400 #if TARGET_OS_EMBEDDED || TARGET_IPHONE_SIMULATOR
401 modelName
= MGCopyAnswer(kMGQDeviceName
, NULL
);
403 modelName
= ASI_CopyComputerModelName(FALSE
);
405 if (modelName
== NULL
)
406 modelName
= CFSTR("Unknown model");
408 return CFStringCreateCopy(kCFAllocatorDefault
, modelName
);
411 static CFStringRef
CopyComputerName(SCDynamicStoreRef store
)
413 CFStringRef deviceName
= SCDynamicStoreCopyComputerName(store
, NULL
);
414 if (deviceName
== NULL
) {
415 deviceName
= CFSTR("Unknown name");
420 static bool _EngineMessageProtocolV2Enabled(void)
424 static dispatch_once_t onceToken
;
425 static bool v2_enabled
= false;
426 dispatch_once(&onceToken
, ^{
427 CFTypeRef v2Pref
= (CFNumberRef
)CFPreferencesCopyValue(CFSTR("engineV2"), CFSTR("com.apple.security"), kCFPreferencesAnyUser
, kCFPreferencesCurrentHost
);
429 if (v2Pref
&& CFGetTypeID(v2Pref
) == CFBooleanGetTypeID()) {
430 v2_enabled
= CFBooleanGetValue((CFBooleanRef
)v2Pref
);
431 secnotice("server", "Engine v2 : %s", v2_enabled
? "enabled":"disabled");
433 CFReleaseSafe(v2Pref
);
443 static CFDictionaryRef
CFDictionaryCreateDeviceGestalt(SCDynamicStoreRef store
, CFArrayRef keys
, void *context
)
445 CFStringRef modelName
= CopyModelName();
446 CFStringRef computerName
= CopyComputerName(store
);
447 SInt32 version
= _EngineMessageProtocolV2Enabled() ? kEngineMessageProtocolVersion
: 0;
448 CFNumberRef protocolVersion
= CFNumberCreate(0, kCFNumberSInt32Type
, &version
);
451 CFDictionaryRef gestalt
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
452 kPIUserDefinedDeviceName
, computerName
,
453 kPIDeviceModelName
, modelName
,
454 kPIMessageProtocolVersion
, protocolVersion
,
456 CFReleaseSafe(modelName
);
457 CFReleaseSafe(computerName
);
458 CFReleaseSafe(protocolVersion
);
463 static void SOSCCProcessGestaltUpdate(SCDynamicStoreRef store
, CFArrayRef keys
, void *context
)
465 do_with_account(^(SOSAccountRef account
) {
467 CFDictionaryRef gestalt
= CFDictionaryCreateDeviceGestalt(store
, keys
, context
);
468 if (SOSAccountUpdateGestalt(account
, gestalt
)) {
469 notify_post(kSOSCCCircleChangedNotification
);
471 CFReleaseSafe(gestalt
);
477 static CFDictionaryRef
CFDictionaryCreateGestaltAndRegisterForUpdate(dispatch_queue_t queue
, void *info
)
479 SCDynamicStoreContext context
= { .info
= info
};
480 SCDynamicStoreRef store
= SCDynamicStoreCreate(NULL
, CFSTR("com.apple.securityd.cloudcircleserver"), SOSCCProcessGestaltUpdate
, &context
);
481 CFStringRef computerKey
= SCDynamicStoreKeyCreateComputerName(NULL
);
482 CFArrayRef keys
= NULL
;
483 CFDictionaryRef gestalt
= NULL
;
485 if (store
== NULL
|| computerKey
== NULL
) {
488 keys
= CFArrayCreate(NULL
, (const void **)&computerKey
, 1, &kCFTypeArrayCallBacks
);
492 gestalt
= CFDictionaryCreateDeviceGestalt(store
, keys
, info
);
493 SCDynamicStoreSetNotificationKeys(store
, keys
, NULL
);
494 SCDynamicStoreSetDispatchQueue(store
, queue
);
497 if (store
) CFRelease(store
);
498 if (computerKey
) CFRelease(computerKey
);
499 if (keys
) CFRelease(keys
);
503 static void do_with_account(void (^action
)(SOSAccountRef account
));
504 static void do_with_account_async(void (^action
)(SOSAccountRef account
));
506 static SOSAccountRef
GetSharedAccount(void) {
507 static SOSAccountRef sSharedAccount
= NULL
;
508 static dispatch_once_t onceToken
;
510 #if !(TARGET_OS_EMBEDDED)
512 secerror("Cannot inflate account object as root");
517 dispatch_once(&onceToken
, ^{
518 secdebug("account", "Account Creation start");
520 CFDictionaryRef gestalt
= CFDictionaryCreateGestaltAndRegisterForUpdate(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), NULL
);
523 #if TARGET_OS_IPHONE && TARGET_IPHONE_SIMULATOR
524 gestalt
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, NULL
);
526 secerror("Didn't get machine gestalt! This is going to be ugly.");
530 sSharedAccount
= SOSKeychainAccountCreateSharedAccount(gestalt
);
532 SOSCCSetThisDeviceDefinitelyNotActiveInCircle(SOSAccountIsInCircles(sSharedAccount
, NULL
));
534 SOSAccountAddChangeBlock(sSharedAccount
, ^(SOSCircleRef circle
,
535 CFSetRef peer_additions
, CFSetRef peer_removals
,
536 CFSetRef applicant_additions
, CFSetRef applicant_removals
) {
537 CFErrorRef pi_error
= NULL
;
538 SOSPeerInfoRef me
= SOSAccountGetMyPeerInCircle(sSharedAccount
, circle
, &pi_error
);
540 secerror("Error finding me for change: %@", pi_error
);
542 if (SOSCircleHasPeer(circle
, me
, NULL
) && CFSetGetCount(peer_additions
) != 0) {
543 secnotice("updates", "Requesting Ensure Peer Registration.");
544 SOSCloudKeychainRequestEnsurePeerRegistration(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), NULL
);
547 if (CFSetContainsValue(peer_additions
, me
)) {
548 SOSCCSyncWithAllPeers();
552 CFReleaseNull(pi_error
);
554 if (CFSetGetCount(peer_additions
) != 0 ||
555 CFSetGetCount(peer_removals
) != 0 ||
556 CFSetGetCount(applicant_additions
) != 0 ||
557 CFSetGetCount(applicant_removals
) != 0) {
559 SOSCCSetThisDeviceDefinitelyNotActiveInCircle(SOSAccountIsInCircles(sSharedAccount
, NULL
));
560 notify_post(kSOSCCCircleChangedNotification
);
564 SOSCloudKeychainSetItemsChangedBlock(^CFArrayRef(CFDictionaryRef changes
) {
565 CFRetainSafe(changes
);
566 __block CFMutableArrayRef handledKeys
= NULL
;
567 do_with_account(^(SOSAccountRef account
) {
568 CFStringRef changeDescription
= SOSChangesCopyDescription(changes
, false);
569 secdebug(SOSCKCSCOPE
, "Received: %@", changeDescription
);
570 CFReleaseSafe(changeDescription
);
572 CFErrorRef error
= NULL
;
573 handledKeys
= SOSTransportDispatchMessages(account
, changes
, &error
);
575 secerror("Error handling updates: %@", error
);
576 CFReleaseNull(error
);
579 CFReleaseSafe(changes
);
582 CFReleaseSafe(gestalt
);
584 SOSCloudKeychainRequestEnsurePeerRegistration(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), NULL
);
588 return sSharedAccount
;
591 static void do_with_account_dynamic(void (^action
)(SOSAccountRef account
), bool sync
) {
592 SOSAccountRef account
= GetSharedAccount();
595 dispatch_block_t do_action_and_save
= ^{
597 SOSKeychainAccountEnsureSaved(account
);
601 dispatch_sync(SOSAccountGetQueue(account
), do_action_and_save
);
603 dispatch_async(SOSAccountGetQueue(account
), do_action_and_save
);
608 __unused
static void do_with_account_async(void (^action
)(SOSAccountRef account
)) {
609 do_with_account_dynamic(action
, false);
612 static void do_with_account(void (^action
)(SOSAccountRef account
)) {
613 do_with_account_dynamic(action
, true);
616 #if TARGET_IPHONE_SIMULATOR
617 #define MKBDeviceUnlockedSinceBoot() true
620 static bool do_if_after_first_unlock(CFErrorRef
*error
, dispatch_block_t action
)
622 bool beenUnlocked
= false;
623 require_quiet(SecAKSGetHasBeenUnlocked(&beenUnlocked
, error
), fail
);
625 require_action_quiet(beenUnlocked
, fail
,
626 SOSCreateErrorWithFormat(kSOSErrorNotReady
, NULL
, error
, NULL
,
627 CFSTR("Keybag never unlocked, ask after first unlock")));
636 static bool do_with_account_if_after_first_unlock(CFErrorRef
*error
, bool (^action
)(SOSAccountRef account
, CFErrorRef
* error
))
638 __block
bool action_result
= false;
640 #if !(TARGET_OS_EMBEDDED)
642 secerror("Cannot inflate account object as root");
646 return do_if_after_first_unlock(error
, ^{
647 do_with_account(^(SOSAccountRef account
) {
648 action_result
= action(account
, error
);
654 static bool do_with_account_while_unlocked(CFErrorRef
*error
, bool (^action
)(SOSAccountRef account
, CFErrorRef
* error
))
656 __block
bool action_result
= false;
658 #if !(TARGET_OS_EMBEDDED)
660 secerror("Cannot inflate account object as root");
665 return SecAKSDoWhileUserBagLocked(error
, ^{
666 do_with_account(^(SOSAccountRef account
) {
667 action_result
= action(account
, error
);
673 SOSAccountRef
SOSKeychainAccountGetSharedAccount()
675 __block SOSAccountRef result
= NULL
;
677 do_with_account(^(SOSAccountRef account
) {
685 // Mark: Credential processing
689 bool SOSCCTryUserCredentials_Server(CFStringRef user_label
, CFDataRef user_password
, CFErrorRef
*error
)
691 return do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
692 return SOSAccountTryUserCredentials(account
, user_label
, user_password
, block_error
);
696 #define kWAIT2MINID "EFRESH"
698 static bool EnsureFreshParameters(SOSAccountRef account
, CFErrorRef
*error
) {
699 dispatch_semaphore_t wait_for
= dispatch_semaphore_create(0);
700 dispatch_retain(wait_for
); // Both this scope and the block own it.
702 CFMutableArrayRef keysToGet
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
703 CFArrayAppendValue(keysToGet
, kSOSKVSKeyParametersKey
);
705 __block CFDictionaryRef valuesToUpdate
= NULL
;
706 __block
bool success
= false;
708 secnoticeq("fresh", "%s calling SOSCloudKeychainSynchronizeAndWait", kWAIT2MINID
);
710 SOSCloudKeychainSynchronizeAndWait(keysToGet
, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), ^(CFDictionaryRef returnedValues
, CFErrorRef sync_error
) {
713 secerrorq("%s SOSCloudKeychainSynchronizeAndWait: %@", kWAIT2MINID
, sync_error
);
716 CFRetainSafe(*error
);
719 secnoticeq("fresh", "%s returned from call; in callback to SOSCloudKeychainSynchronizeAndWait: results: %@", kWAIT2MINID
, returnedValues
);
720 valuesToUpdate
= returnedValues
;
721 CFRetainSafe(valuesToUpdate
);
725 dispatch_semaphore_signal(wait_for
);
726 dispatch_release(wait_for
);
729 dispatch_semaphore_wait(wait_for
, DISPATCH_TIME_FOREVER
);
730 // TODO: Maybe we timeout here... used to dispatch_time(DISPATCH_TIME_NOW, 30ull * NSEC_PER_SEC));
731 dispatch_release(wait_for
);
732 CFMutableArrayRef handledKeys
= NULL
;
733 if ((valuesToUpdate
) && (account
)) {
734 handledKeys
= SOSTransportDispatchMessages(account
, valuesToUpdate
, error
);
736 secerrorq("%s Freshness update failed: %@", kWAIT2MINID
, error
? *error
: NULL
);
740 CFReleaseNull(handledKeys
);
741 CFReleaseNull(valuesToUpdate
);
742 CFReleaseNull(keysToGet
);
747 static bool Flush(CFErrorRef
*error
) {
748 __block
bool success
= false;
750 dispatch_semaphore_t wait_for
= dispatch_semaphore_create(0);
751 dispatch_retain(wait_for
); // Both this scope and the block own it.
753 secnotice("flush", "Starting");
755 SOSCloudKeychainFlush(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), ^(CFDictionaryRef returnedValues
, CFErrorRef sync_error
) {
756 success
= (sync_error
== NULL
);
758 CFRetainAssign(*error
, sync_error
);
761 dispatch_semaphore_signal(wait_for
);
762 dispatch_release(wait_for
);
765 dispatch_semaphore_wait(wait_for
, DISPATCH_TIME_FOREVER
);
766 dispatch_release(wait_for
);
768 secnotice("flush", "Returned %s", success
? "Success": "Failure");
773 bool SOSCCSetUserCredentials_Server(CFStringRef user_label
, CFDataRef user_password
, CFErrorRef
*error
)
775 secnotice("updates", "Setting credentials for %@", user_label
); // TODO: remove this notice
776 bool result
= do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
777 if (!EnsureFreshParameters(account
, block_error
)) {
780 if (!SOSAccountAssertUserCredentials(account
, user_label
, user_password
, block_error
)) {
781 secnotice("updates", "EnsureFreshParameters/SOSAccountAssertUserCredentials error: %@", *block_error
);
787 return result
&& Flush(error
);
790 bool SOSCCCanAuthenticate_Server(CFErrorRef
*error
)
792 bool result
= do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
793 return SOSAccountGetPrivateCredential(account
, block_error
) != NULL
;
796 if (!result
&& error
&& *error
&& CFErrorGetDomain(*error
) == kSOSErrorDomain
) {
797 CFIndex code
= CFErrorGetCode(*error
);
798 if (code
== kSOSErrorPrivateKeyAbsent
|| code
== kSOSErrorPublicKeyAbsent
) {
799 CFReleaseNull(*error
);
806 bool SOSCCPurgeUserCredentials_Server(CFErrorRef
*error
)
808 return do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
809 SOSAccountPurgePrivateCredential(account
);
816 static bool sAccountInCircleCache
= false;
818 static void do_with_not_in_circle_bool_queue(bool start_account
, dispatch_block_t action
)
820 static dispatch_queue_t account_start_queue
;
821 static dispatch_queue_t not_in_circle_queue
;
822 static bool account_started
= false;
824 static dispatch_once_t onceToken
;
825 dispatch_once(&onceToken
, ^{
826 not_in_circle_queue
= dispatch_queue_create("nis queue", DISPATCH_QUEUE_SERIAL
);
827 account_start_queue
= dispatch_queue_create("init nis queue", DISPATCH_QUEUE_SERIAL
);;
828 account_started
= false;
831 __block
bool done
= false;
832 dispatch_sync(not_in_circle_queue
, ^{
833 if (account_started
) {
839 if (!done
&& start_account
) {
840 dispatch_sync(account_start_queue
, ^{
841 __block
bool do_start
= false;
842 dispatch_sync(not_in_circle_queue
, ^{
843 do_start
= !account_started
;
844 account_started
= true;
847 SOSCCThisDeviceIsInCircle(NULL
); // Inflate account.
850 dispatch_sync(not_in_circle_queue
, action
);
855 bool SOSCCThisDeviceDefinitelyNotActiveInCircle()
857 return !SOSCCCircleIsOn_Artifact();
859 __block
bool result
= false;
860 do_with_not_in_circle_bool_queue(true, ^{
861 result
= sAccountInCircleCache
;
868 void SOSCCSetThisDeviceDefinitelyNotActiveInCircle(SOSCCStatus currentStatus
)
870 SOSCCCircleIsOn_UpdateArtifact(currentStatus
);
872 do_with_not_in_circle_bool_queue(false, ^{
873 sAccountInCircleCache
= notActive
;
879 SOSCCStatus
SOSCCThisDeviceIsInCircle_Server(CFErrorRef
*error
)
881 __block SOSCCStatus status
;
883 return do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
884 status
= SOSAccountIsInCircles(account
, block_error
);
886 }) ? status
: kSOSCCError
;
889 bool SOSCCRequestToJoinCircle_Server(CFErrorRef
* error
)
891 __block
bool result
= true;
893 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
894 result
= SOSAccountJoinCircles(account
, block_error
);
899 bool SOSCCRequestToJoinCircleAfterRestore_Server(CFErrorRef
* error
)
901 __block
bool result
= true;
902 bool returned
= false;
903 returned
= do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
904 SOSAccountEnsurePeerRegistration(account
, block_error
);
905 result
= SOSAccountJoinCirclesAfterRestore(account
, block_error
);
912 bool SOSCCRequestEnsureFreshParameters_Server(CFErrorRef
* error
)
914 __block
bool result
= true;
915 bool returned
= false;
916 returned
= do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
917 result
= EnsureFreshParameters(account
, NULL
);
923 CFStringRef
SOSCCRequestDeviceID_Server(CFErrorRef
*error
)
925 __block CFStringRef result
= NULL
;
927 (void) do_with_account_while_unlocked(error
, ^bool(SOSAccountRef account
, CFErrorRef
*error
) {
928 result
= SOSAccountGetDeviceID(account
, error
);
929 return (!isNull(result
));
934 bool SOSCCSetDeviceID_Server(CFStringRef IDS
, CFErrorRef
*error
){
935 __block
bool result
= true;
937 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
938 result
= SOSAccountSetMyDSID(account
, IDS
, block_error
);
943 bool SOSCCResetToOffering_Server(CFErrorRef
* error
)
945 __block
bool result
= true;
947 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
949 result
= SOSAccountResetToOffering(account
, block_error
);
955 bool SOSCCResetToEmpty_Server(CFErrorRef
* error
)
957 __block
bool result
= true;
959 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
960 result
= SOSAccountResetToEmpty(account
, block_error
);
966 bool SOSCCRemoveThisDeviceFromCircle_Server(CFErrorRef
* error
)
968 __block
bool result
= true;
970 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
971 result
= SOSAccountLeaveCircles(account
, block_error
);
976 bool SOSCCBailFromCircle_Server(uint64_t limit_in_seconds
, CFErrorRef
* error
)
978 __block
bool result
= true;
980 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
981 result
= SOSAccountBail(account
, limit_in_seconds
, block_error
);
987 CFArrayRef
SOSCCCopyApplicantPeerInfo_Server(CFErrorRef
* error
)
989 __block CFArrayRef result
= NULL
;
991 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
992 result
= SOSAccountCopyApplicants(account
, block_error
);
993 return result
!= NULL
;
999 CFArrayRef
SOSCCCopyGenerationPeerInfo_Server(CFErrorRef
* error
)
1001 __block CFArrayRef result
= NULL
;
1003 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1004 result
= SOSAccountCopyGeneration(account
, block_error
);
1005 return result
!= NULL
;
1011 CFArrayRef
SOSCCCopyValidPeerPeerInfo_Server(CFErrorRef
* error
)
1013 __block CFArrayRef result
= NULL
;
1015 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1016 result
= SOSAccountCopyValidPeers(account
, block_error
);
1017 return result
!= NULL
;
1023 bool SOSCCValidateUserPublic_Server(CFErrorRef
* error
)
1025 __block
bool result
= NULL
;
1027 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1028 result
= SOSValidateUserPublic(account
, block_error
);
1035 CFArrayRef
SOSCCCopyNotValidPeerPeerInfo_Server(CFErrorRef
* error
)
1037 __block CFArrayRef result
= NULL
;
1039 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1040 result
= SOSAccountCopyNotValidPeers(account
, block_error
);
1041 return result
!= NULL
;
1047 CFArrayRef
SOSCCCopyRetirementPeerInfo_Server(CFErrorRef
* error
)
1049 __block CFArrayRef result
= NULL
;
1051 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1052 result
= SOSAccountCopyRetired(account
, block_error
);
1053 return result
!= NULL
;
1059 bool SOSCCAcceptApplicants_Server(CFArrayRef applicants
, CFErrorRef
* error
)
1061 __block
bool result
= true;
1062 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1063 result
= SOSAccountAcceptApplicants(account
, applicants
, block_error
);
1069 bool SOSCCRejectApplicants_Server(CFArrayRef applicants
, CFErrorRef
* error
)
1071 __block
bool result
= true;
1072 return do_with_account_while_unlocked(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1073 result
= SOSAccountRejectApplicants(account
, applicants
, block_error
);
1078 CFArrayRef
SOSCCCopyPeerPeerInfo_Server(CFErrorRef
* error
)
1080 __block CFArrayRef result
= NULL
;
1082 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1083 result
= SOSAccountCopyPeers(account
, block_error
);
1084 return result
!= NULL
;
1090 CFArrayRef
SOSCCCopyConcurringPeerPeerInfo_Server(CFErrorRef
* error
)
1092 __block CFArrayRef result
= NULL
;
1094 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1095 result
= SOSAccountCopyConcurringPeers(account
, block_error
);
1096 return result
!= NULL
;
1102 CFStringRef
SOSCCCopyIncompatibilityInfo_Server(CFErrorRef
* error
)
1104 __block CFStringRef result
= NULL
;
1106 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1107 result
= SOSAccountCopyIncompatibilityInfo(account
, block_error
);
1108 return result
!= NULL
;
1114 enum DepartureReason
SOSCCGetLastDepartureReason_Server(CFErrorRef
* error
)
1116 __block
enum DepartureReason result
= kSOSDepartureReasonError
;
1118 (void) do_with_account_if_after_first_unlock(error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1119 result
= SOSAccountGetLastDepartureReason(account
, block_error
);
1120 return result
!= kSOSDepartureReasonError
;
1127 bool SOSCCProcessEnsurePeerRegistration_Server(CFErrorRef
* error
)
1129 secnotice("updates", "Request for registering peers");
1130 return do_with_account_while_unlocked(error
, ^bool(SOSAccountRef account
, CFErrorRef
*error
) {
1131 return SOSAccountEnsurePeerRegistration(account
, error
);
1135 SyncWithAllPeersReason
SOSCCProcessSyncWithAllPeers_Server(CFErrorRef
* error
)
1138 #define kIOReturnLockedRead iokit_common_err(0x2c3) // device read locked
1139 #define kIOReturnLockedWrite iokit_common_err(0x2c4) // device write locked
1141 __block SyncWithAllPeersReason result
= kSyncWithAllPeersSuccess
;
1142 CFErrorRef action_error
= NULL
;
1144 if (!do_with_account_while_unlocked(&action_error
, ^bool (SOSAccountRef account
, CFErrorRef
* block_error
) {
1145 CFErrorRef localError
= NULL
;
1146 if (!SOSAccountSyncWithAllPeers(account
, &localError
)) {
1147 secerror("sync with all peers failed: %@", localError
);
1148 CFReleaseSafe(localError
);
1149 // This isn't a device-locked error, but returning false will
1150 // have CloudKeychainProxy ask us to try sync again after next unlock
1151 result
= kSyncWithAllPeersOtherFail
;
1157 if (SecErrorGetOSStatus(action_error
) == errSecInteractionNotAllowed
) {
1158 secnotice("updates", "SOSAccountSyncWithAllPeers failed because device is locked; letting CloudKeychainProxy know");
1159 result
= kSyncWithAllPeersLocked
; // tell CloudKeychainProxy to call us back when device unlocks
1160 CFReleaseNull(action_error
);
1162 secerror("Unexpected error: %@", action_error
);
1165 if (error
&& *error
== NULL
) {
1166 *error
= action_error
;
1167 action_error
= NULL
;
1170 CFReleaseNull(action_error
);
1177 void SOSCCSyncWithAllPeers(void)
1179 SOSCloudKeychainRequestSyncWithAllPeers(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), NULL
);
1182 CF_RETURNS_RETAINED CFArrayRef
SOSCCHandleUpdateKeyParameter(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
));
1190 CF_RETURNS_RETAINED CFArrayRef
SOSCCHandleUpdateCircle(CFDictionaryRef updates
)
1192 CFArrayRef result
= NULL
;
1193 SOSAccountRef account
= SOSKeychainAccountGetSharedAccount(); //HACK to make sure itemsChangedBlock is set
1194 (account
) ? (result
= SOSCloudKeychainHandleUpdateKeyParameter(updates
)) : (result
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
));
1198 CF_RETURNS_RETAINED CFArrayRef
SOSCCHandleUpdateMessage(CFDictionaryRef updates
)
1200 CFArrayRef result
= NULL
;
1201 SOSAccountRef account
= SOSKeychainAccountGetSharedAccount(); //HACK to make sure itemsChangedBlock is set
1202 (account
) ? (result
= SOSCloudKeychainHandleUpdateKeyParameter(updates
)) : (result
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
));