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/CoreFoundation.h>
27 #include <CoreFoundation/CFPriv.h>
29 #include <Security/SecureObjectSync/SOSAccountTransaction.h>
31 #include <securityd/SOSCloudCircleServer.h>
32 #include <Security/SecureObjectSync/SOSCloudCircle.h>
33 #include <Security/SecureObjectSync/SOSCloudCircleInternal.h>
34 #include <Security/SecureObjectSync/SOSCircle.h>
35 #include <Security/SecureObjectSync/SOSAccount.h>
36 #include <Security/SecureObjectSync/SOSAccountPriv.h>
37 #include <Security/SecureObjectSync/SOSTransport.h>
38 #include <Security/SecureObjectSync/SOSFullPeerInfo.h>
39 #include <Security/SecureObjectSync/SOSPeerInfoV2.h>
41 #include <Security/SecureObjectSync/SOSPeerInfoPriv.h>
42 #include <Security/SecureObjectSync/SOSPeerInfoInternal.h>
43 #include <Security/SecureObjectSync/SOSInternal.h>
44 #include <Security/SecureObjectSync/SOSUserKeygen.h>
45 #include <Security/SecureObjectSync/SOSMessage.h>
46 #include <Security/SecureObjectSync/SOSBackupInformation.h>
47 #include <Security/SecureObjectSync/SOSDataSource.h>
48 #include <Security/SecureObjectSync/SOSKVSKeys.h>
49 #import <Security/SecureObjectSync/SOSAccountTrustClassic.h>
50 #import <Security/SecureObjectSync/SOSAccountTrustClassic+Circle.h>
51 #import <Security/SecureObjectSync/SOSAccountTrustClassic+Expansion.h>
52 #import <Security/SecureObjectSync/SOSAuthKitHelpers.h>
55 #include <utilities/SecADWrapper.h>
56 #include <utilities/SecCFWrappers.h>
57 #include <utilities/SecCFRelease.h>
59 #include <utilities/SecCFError.h>
60 #include <utilities/debugging.h>
61 #include <utilities/SecCoreCrypto.h>
62 #include <utilities/SecTrace.h>
64 #include <SOSCircle/CKBridge/SOSCloudKeychainClient.h>
66 #include <corecrypto/ccrng.h>
67 #include <corecrypto/ccrng_pbkdf2_prng.h>
68 #include <corecrypto/ccec.h>
69 #include <corecrypto/ccdigest.h>
70 #include <corecrypto/ccsha2.h>
71 #include <CommonCrypto/CommonRandomSPI.h>
72 #include <Security/SecKeyPriv.h>
73 #include <Security/SecFramework.h>
75 #include <utilities/SecFileLocations.h>
76 #include <utilities/SecAKSWrappers.h>
77 #include <securityd/SecItemServer.h>
78 #include <Security/SecItemPriv.h>
80 #include <TargetConditionals.h>
82 #include <utilities/iCloudKeychainTrace.h>
83 #include <Security/SecAccessControlPriv.h>
84 #include <securityd/SecDbKeychainItem.h>
86 #include <os/activity.h>
87 #include <os/state_private.h>
89 #if TARGET_OS_EMBEDDED || TARGET_IPHONE_SIMULATOR
90 #include <MobileGestalt.h>
92 #include <AppleSystemInfo/AppleSystemInfo.h>
95 #define SOSCKCSCOPE "sync"
96 #define RUN_AS_ROOT_ERROR 550
98 #define USE_SYSTEMCONFIGURATION_PRIVATE_HEADERS
99 #import <SystemConfiguration/SystemConfiguration.h>
103 static int64_t getTimeDifference(time_t start);
104 CFStringRef const SOSAggdSyncCompletionKey = CFSTR("com.apple.security.sos.synccompletion");
105 CFStringRef const SOSAggdSyncTimeoutKey = CFSTR("com.apple.security.sos.timeout");
107 typedef SOSDataSourceFactoryRef (^SOSCCAccountDataSourceFactoryBlock)(void);
109 static SOSCCAccountDataSourceFactoryBlock accountDataSourceOverride = NULL;
115 static void do_with_account(void (^action)(SOSAccountTransaction* txn));
121 CFStringRef kSOSAccountLabel = CFSTR("iCloud Keychain Account Meta-data");
123 CFStringRef kSOSBurnedRecoveryAttemptCount = CFSTR("Burned Recovery Attempt Count");
125 CFStringRef kSOSBurnedRecoveryAttemptAttestationDate = CFSTR("Burned Recovery Attempt Attestation Date");
127 static CFDictionaryRef SOSItemCopyQueryForSyncItems(CFStringRef service, bool returnData)
129 return CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
130 kSecClass, kSecClassGenericPassword,
131 kSecAttrService, service,
132 kSecAttrAccessGroup, kSOSInternalAccessGroup,
133 kSecReturnData, returnData ? kCFBooleanTrue : kCFBooleanFalse,
137 CFDataRef SOSItemCopy(CFStringRef service, CFErrorRef* error)
139 CFDictionaryRef query = SOSItemCopyQueryForSyncItems(service, true);
141 CFDataRef result = NULL;
143 OSStatus copyResult = SecItemCopyMatching(query, (CFTypeRef*) &result);
145 CFReleaseNull(query);
147 if (copyResult != noErr) {
148 SecError(copyResult, error, CFSTR("Error %@ reading for service '%@'"), result, service);
149 CFReleaseNull(result);
153 if (!isData(result)) {
154 SOSCreateErrorWithFormat(kSOSErrorProcessingFailure, NULL, error, NULL, CFSTR("SecItemCopyMatching returned non-data in '%@'"), service);
155 CFReleaseNull(result);
162 static CFDataRef SOSKeychainCopySavedAccountData()
164 CFErrorRef error = NULL;
165 CFDataRef accountData = SOSItemCopy(kSOSAccountLabel, &error);
167 secnotice("account", "Failed to load account: %@", error);
168 secerror("Failed to load account: %@", error);
170 CFReleaseNull(error);
175 bool SOSItemUpdateOrAdd(CFStringRef service, CFStringRef accessibility, CFDataRef data, CFErrorRef *error)
177 CFDictionaryRef query = SOSItemCopyQueryForSyncItems(service, false);
179 CFDictionaryRef update = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
181 kSecAttrAccessible, accessibility,
183 OSStatus saveStatus = SecItemUpdate(query, update);
185 if (errSecItemNotFound == saveStatus) {
186 CFMutableDictionaryRef add = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, query);
187 CFDictionaryForEach(update, ^(const void *key, const void *value) {
188 CFDictionaryAddValue(add, key, value);
190 saveStatus = SecItemAdd(add, NULL);
194 CFReleaseNull(query);
195 CFReleaseNull(update);
197 return SecError(saveStatus, error, CFSTR("Error saving %@ to service '%@'"), data, service);
200 static void SOSKeychainAccountEnsureSaved(CFDataRef accountAsData)
202 static CFDataRef sLastSavedAccountData = NULL;
204 CFErrorRef saveError = NULL;
205 require_quiet(!CFEqualSafe(sLastSavedAccountData, accountAsData), exit);
207 if (!SOSItemUpdateOrAdd(kSOSAccountLabel, kSecAttrAccessibleAlwaysThisDeviceOnlyPrivate, accountAsData, &saveError)) {
208 secerror("Can't save account: %@", saveError);
212 CFAssignRetained(sLastSavedAccountData, CFRetainSafe(accountAsData));
215 CFReleaseNull(saveError);
218 static SOSAccount* SOSKeychainAccountCreateSharedAccount(CFDictionaryRef our_gestalt)
220 secdebug("account", "Created account");
222 CFDataRef savedAccount = SOSKeychainCopySavedAccountData();
223 SOSAccount* account = NULL;
225 SOSDataSourceFactoryRef factory = accountDataSourceOverride ? accountDataSourceOverride()
226 : SecItemDataSourceFactoryGetDefault();
228 require_quiet(factory, done);
231 NSError* inflationError = NULL;
233 account = [SOSAccount accountFromData:(__bridge NSData*) savedAccount
235 error:&inflationError];
238 [account.trust updateGestalt:account newGestalt:our_gestalt];
240 secerror("Got error inflating account: %@", inflationError);
244 CFReleaseNull(savedAccount);
247 account = SOSAccountCreate(kCFAllocatorDefault, our_gestalt, factory);
250 secerror("Got NULL creating account");
254 CFReleaseNull(savedAccount);
259 // Mark: Gestalt Handling
262 CFStringRef SOSGestaltVersion = NULL;
263 CFStringRef SOSGestaltModel = NULL;
264 CFStringRef SOSGestaltDeviceName = NULL;
267 SOSCCSetGestalt_Server(CFStringRef deviceName,
272 SOSGestaltDeviceName = CFRetainSafe(deviceName);
273 SOSGestaltVersion = CFRetainSafe(version);
274 SOSGestaltModel = CFRetainSafe(model);
275 SOSGestaltSerial = CFRetainSafe(serial);
278 CFStringRef SOSCCCopyOSVersion(void)
280 static dispatch_once_t once;
281 dispatch_once(&once, ^{
282 if (SOSGestaltVersion == NULL) {
283 CFDictionaryRef versions = _CFCopySystemVersionDictionary();
285 CFTypeRef versionValue = CFDictionaryGetValue(versions, _kCFSystemVersionBuildVersionKey);
286 if (isString(versionValue))
287 SOSGestaltVersion = CFRetainSafe((CFStringRef) versionValue);
290 CFReleaseNull(versions);
291 if (SOSGestaltVersion == NULL) {
292 SOSGestaltVersion = CFSTR("Unknown model");
296 return CFRetainSafe(SOSGestaltVersion);
300 static CFStringRef CopyModelName(void)
302 static dispatch_once_t once;
303 dispatch_once(&once, ^{
304 if (SOSGestaltModel == NULL) {
305 #if TARGET_OS_EMBEDDED || TARGET_IPHONE_SIMULATOR
306 SOSGestaltModel = MGCopyAnswer(kMGQDeviceName, NULL);
308 SOSGestaltModel = ASI_CopyComputerModelName(FALSE);
310 if (SOSGestaltModel == NULL)
311 SOSGestaltModel = CFSTR("Unknown model");
314 return CFStringCreateCopy(kCFAllocatorDefault, SOSGestaltModel);
317 static CFStringRef CopyComputerName(SCDynamicStoreRef store)
319 if (SOSGestaltDeviceName == NULL) {
320 CFStringRef deviceName = SCDynamicStoreCopyComputerName(store, NULL);
321 if (deviceName == NULL) {
322 deviceName = CFSTR("Unknown name");
326 return SOSGestaltDeviceName;
329 static bool _EngineMessageProtocolV2Enabled(void)
333 static dispatch_once_t onceToken;
334 static bool v2_enabled = false;
335 dispatch_once(&onceToken, ^{
336 CFTypeRef v2Pref = (CFNumberRef)CFPreferencesCopyValue(CFSTR("engineV2"), CFSTR("com.apple.security"), kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
338 if (v2Pref && CFGetTypeID(v2Pref) == CFBooleanGetTypeID()) {
339 v2_enabled = CFBooleanGetValue((CFBooleanRef)v2Pref);
340 secinfo("server", "Engine v2 : %s", v2_enabled ? "enabled":"disabled");
342 CFReleaseSafe(v2Pref);
352 static CFDictionaryRef CreateDeviceGestaltDictionary(SCDynamicStoreRef store, CFArrayRef keys, void *context)
354 CFStringRef modelName = CopyModelName();
355 CFStringRef computerName = CopyComputerName(store);
356 CFStringRef osVersion = SOSCCCopyOSVersion();
358 SInt32 version = _EngineMessageProtocolV2Enabled() ? kEngineMessageProtocolVersion : 0;
359 CFNumberRef protocolVersion = CFNumberCreate(0, kCFNumberSInt32Type, &version);
361 CFDictionaryRef gestalt = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
362 kPIUserDefinedDeviceNameKey, computerName,
363 kPIDeviceModelNameKey, modelName,
364 kPIMessageProtocolVersionKey, protocolVersion,
365 kPIOSVersionKey, osVersion,
367 CFReleaseSafe(osVersion);
368 CFReleaseSafe(modelName);
369 CFReleaseSafe(computerName);
370 CFReleaseSafe(protocolVersion);
375 static void SOSCCProcessGestaltUpdate(SCDynamicStoreRef store, CFArrayRef keys, void *context)
377 do_with_account(^(SOSAccountTransaction* txn) {
379 CFDictionaryRef gestalt = CreateDeviceGestaltDictionary(store, keys, context);
380 if ([txn.account.trust updateGestalt:txn.account newGestalt:gestalt]) {
381 secnotice("circleOps", "Changed our peer's gestalt information. This is not a circle change.");
383 CFReleaseSafe(gestalt);
389 static CFDictionaryRef CreateDeviceGestaltDictionaryAndRegisterForUpdate(dispatch_queue_t queue, void *info)
391 SCDynamicStoreContext context = { .info = info };
392 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("com.apple.securityd.cloudcircleserver"), SOSCCProcessGestaltUpdate, &context);
393 CFStringRef computerKey = SCDynamicStoreKeyCreateComputerName(NULL);
394 CFArrayRef keys = NULL;
395 CFDictionaryRef gestalt = NULL;
397 if (store == NULL || computerKey == NULL) {
400 keys = CFArrayCreate(NULL, (const void **)&computerKey, 1, &kCFTypeArrayCallBacks);
404 gestalt = CreateDeviceGestaltDictionary(store, keys, info);
405 SCDynamicStoreSetNotificationKeys(store, keys, NULL);
406 SCDynamicStoreSetDispatchQueue(store, queue);
409 if (store) CFRelease(store);
410 if (computerKey) CFRelease(computerKey);
411 if (keys) CFRelease(keys);
415 os_state_block_t accountStateBlock = ^os_state_data_t(os_state_hints_t hints) {
416 os_state_data_t retval = NULL;
417 CFDataRef savedAccount = NULL;
418 if(hints->osh_api != OS_STATE_API_REQUEST) return NULL;
420 /* Get account DER */
421 savedAccount = SOSKeychainCopySavedAccountData();
422 require_quiet(savedAccount, errOut);
424 /* make a os_state_data_t object to return. */
425 size_t statelen = CFDataGetLength(savedAccount);
426 retval = (os_state_data_t)calloc(1, OS_STATE_DATA_SIZE_NEEDED(statelen));
427 require_quiet(retval, errOut);
429 retval->osd_type = OS_STATE_DATA_PROTOCOL_BUFFER;
430 memcpy(retval->osd_data, CFDataGetBytePtr(savedAccount), statelen);
431 retval->osd_size = statelen;
432 strlcpy(retval->osd_title, "CloudCircle Account Object", sizeof(retval->osd_title));
435 CFReleaseNull(savedAccount);
441 static SOSAccount* GetSharedAccount(void) {
442 static SOSAccount* sSharedAccount = NULL;
443 static dispatch_once_t onceToken;
445 #if !(TARGET_OS_EMBEDDED)
447 secerror("Cannot inflate account object as root");
452 dispatch_once(&onceToken, ^{
453 secdebug("account", "Account Creation start");
455 CFDictionaryRef gestalt = CreateDeviceGestaltDictionaryAndRegisterForUpdate(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), NULL);
458 #if TARGET_OS_IPHONE && TARGET_IPHONE_SIMULATOR
459 gestalt = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, NULL);
461 secerror("Didn't get machine gestalt! This is going to be ugly.");
465 sSharedAccount = SOSKeychainAccountCreateSharedAccount(gestalt);
467 SOSAccountAddChangeBlock(sSharedAccount, ^(SOSAccount *account, SOSCircleRef circle,
468 CFSetRef peer_additions, CFSetRef peer_removals,
469 CFSetRef applicant_additions, CFSetRef applicant_removals) {
470 CFErrorRef pi_error = NULL;
471 SOSPeerInfoRef me = account.peerInfo;
473 secinfo("circleOps", "Change block called with no peerInfo");
477 if(!SOSCircleHasPeer(circle, me, NULL)) {
478 secinfo("circleOps", "Change block called while not in circle");
482 // TODO: Figure out why peer_additions isn't right in some cases (like when joining a v2 circle with a v0 peer.
483 if (CFSetGetCount(peer_additions) != 0) {
484 secnotice("updates", "Requesting Ensure Peer Registration.");
485 SOSCloudKeychainRequestEnsurePeerRegistration(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), NULL);
487 secinfo("updates", "Not requesting Ensure Peer Registration, since it's not needed");
490 if (CFSetContainsValue(peer_additions, me)) {
491 // TODO: Potentially remove from here and move this to the engine
492 // TODO: We also need to do this when our views change.
493 CFMutableSetRef peers = SOSCircleCopyPeers(circle, kCFAllocatorDefault);
494 CFSetRemoveValue(peers, me);
495 if (!CFSetIsEmpty(peers)) {
496 SOSCCRequestSyncWithPeers(peers);
498 CFReleaseNull(peers);
501 CFReleaseNull(pi_error);
503 if (CFSetGetCount(peer_additions) != 0 ||
504 CFSetGetCount(peer_removals) != 0 ||
505 CFSetGetCount(applicant_additions) != 0 ||
506 CFSetGetCount(applicant_removals) != 0) {
508 if(CFSetGetCount(peer_removals) != 0)
510 CFErrorRef localError = NULL;
511 CFMutableArrayRef removed = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
512 CFSetForEach(peer_removals, ^(const void *value) {
513 CFArrayAppendValue(removed, value);
515 SOSAccountRemoveBackupPeers(account, removed, &localError);
517 secerror("Had trouble removing: %@, error: %@", removed, localError);
518 CFReleaseNull(localError);
519 CFReleaseNull(removed);
521 secnotice("circleOps", "peer counts changed, posting kSOSCCCircleChangedNotification");
522 account.notifyCircleChangeOnExit = true;
526 SOSCloudKeychainSetItemsChangedBlock(^CFArrayRef(CFDictionaryRef changes) {
527 CFRetainSafe(changes);
528 __block CFMutableArrayRef handledKeys = NULL;
529 do_with_account(^(SOSAccountTransaction* txn) {
530 CFStringRef changeDescription = SOSItemsChangedCopyDescription(changes, false);
531 secdebug(SOSCKCSCOPE, "Received: %@", changeDescription);
532 CFReleaseSafe(changeDescription);
534 CFErrorRef error = NULL;
535 handledKeys = SOSTransportDispatchMessages(txn, changes, &error);
536 if (!handledKeys || error) {
537 secerror("Error handling updates: %@", error);
539 CFReleaseNull(error);
541 CFReleaseSafe(changes);
544 CFReleaseSafe(gestalt);
546 sSharedAccount.saveBlock = ^(CFDataRef flattenedAccount, CFErrorRef flattenFailError) {
547 if (flattenedAccount) {
548 SOSKeychainAccountEnsureSaved(flattenedAccount);
550 secerror("Failed to transform account into data, error: %@", flattenFailError);
553 // TODO: We should not be doing extra work whenever securityd is launched, let's see if we can eliminate this call
554 SOSCloudKeychainRequestEnsurePeerRegistration(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), NULL);
556 // provide state handler to sysdiagnose and logging
557 os_state_add_handler(dispatch_get_global_queue(0, 0), accountStateBlock);
562 return sSharedAccount;
565 CFTypeRef GetSharedAccountRef(void)
567 return (__bridge CFTypeRef)GetSharedAccount();
570 static void do_with_account(void (^action)(SOSAccountTransaction* txn)) {
572 SOSAccount* account = GetSharedAccount();
575 [account performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
582 static bool isValidUser(CFErrorRef* error) {
583 #if !(TARGET_OS_EMBEDDED)
585 secerror("Cannot inflate account object as root");
586 SOSErrorCreate(kSOSErrorUnsupported, error, NULL, CFSTR("Cannot inflate account object as root"));
594 static bool do_if_after_first_unlock(CFErrorRef *error, dispatch_block_t action)
596 #if TARGET_IPHONE_SIMULATOR
600 bool beenUnlocked = false;
601 require_quiet(SecAKSGetHasBeenUnlocked(&beenUnlocked, error), fail);
603 require_action_quiet(beenUnlocked, fail,
604 SOSCreateErrorWithFormat(kSOSErrorNotReady, NULL, error, NULL,
605 CFSTR("Keybag never unlocked, ask after first unlock")));
616 static bool do_with_account_if_after_first_unlock(CFErrorRef *error, bool (^action)(SOSAccountTransaction* txn, CFErrorRef* error))
618 static dispatch_once_t initialNotificationToken;
619 __block bool action_result = false;
621 return isValidUser(error) && do_if_after_first_unlock(error, ^{
622 do_with_account(^(SOSAccountTransaction* txn) {
623 action_result = action(txn, error);
625 // The first time we run do_with_account_if_after_first_unlock and the bag is unlocked,
626 // if we are now 'in circle', notify our listeners of a 'circle change' and a 'view change'.
627 // This will prompt them to refetch circle status, and we can actually respond correctly now!
628 // If we are out of circle, don't send the notification--all clients 'know' we're out of circle anyway.
629 dispatch_once(&initialNotificationToken, ^{
630 CFErrorRef cferror = NULL;
632 SOSCCStatus status = [txn.account getCircleStatus:&cferror];
634 secerror("error getting circle status on first unlock: %@", cferror);
635 } else if (status == kSOSCCInCircle) {
636 secnotice("secdNotify", "Notified clients of kSOSCCCircleChangedNotification && kSOSCCViewMembershipChangedNotification for in-circle initial unlock");
637 txn.account.notifyCircleChangeOnExit = true;
638 txn.account.notifyViewChangeOnExit = true;
640 CFReleaseNull(cferror);
647 static bool isAssertionLockAcquireError(CFErrorRef error) {
648 return (CFErrorGetCode(error) == kIOReturnNotPermitted) && (CFEqualSafe(CFErrorGetDomain(error), kSecKernDomain));
651 static bool do_with_account_while_unlocked(CFErrorRef *error, bool (^action)(SOSAccountTransaction* txn, CFErrorRef* error))
655 CFErrorRef statusError = NULL;
657 __block bool action_result = false;
658 __block bool attempted_action = false;
659 __block CFErrorRef localError = NULL;
662 if(!isValidUser(error)){
663 if (error && !*error && localError) {
664 CFTransferRetained(*error, localError);
666 CFReleaseNull(localError);
667 CFReleaseNull(statusError);
672 result = SecAKSDoWhileUserBagLocked(&localError, ^{
673 do_with_account(^(SOSAccountTransaction* txn) {
674 SOSAccount *account = txn.account;
675 if(![SOSAuthKitHelpers peerinfoHasMID: account]) {
676 // This is the first good opportunity to update our FullPeerInfo and
677 // push the resulting circle.
678 [SOSAuthKitHelpers updateMIDInPeerInfo: account];
680 attempted_action = true;
681 action_result = action(txn, error);
685 // For <rdar://problem/24355048> 13E196: Circle join fails after successful recovery with a mach error if performed while device is locked
686 // If we fail with an error attempting to get an assertion while someone else has one and the system is unlocked, it must be trying to lock.
687 // we assume our caller will hold the lock assertion for us to finsh our job.
688 // to be extra paranoid we track if we tried the caller's block. If we did we don't do it again.
690 if(result || !isAssertionLockAcquireError(localError)){
691 if (error && !*error && localError) {
692 CFTransferRetained(*error, localError);
694 CFReleaseNull(localError);
695 CFReleaseNull(statusError);
697 return (result && action_result);
699 if(attempted_action){
700 if (error && !*error && localError) {
701 CFTransferRetained(*error, localError);
703 CFReleaseNull(localError);
704 CFReleaseNull(statusError);
706 return (result && action_result);
709 bool isUnlocked = false;
710 (void) SecAKSGetIsUnlocked(&isUnlocked, &statusError);
712 secnotice("while-unlocked-hack", "Not trying action, aks bag locked (%@)", statusError);
713 if (error && !*error && localError) {
714 CFTransferRetained(*error, localError);
716 CFReleaseNull(localError);
717 CFReleaseNull(statusError);
719 return result && action_result;
722 CFReleaseNull(localError);
724 secnotice("while-unlocked-hack", "Trying action while unlocked without assertion");
727 do_with_account(^(SOSAccountTransaction* txn) {
728 action_result = action(txn, &localError);
731 secnotice("while-unlocked-hack", "Action %s (%@)", action_result ? "succeeded" : "failed", localError);
733 if (error && !*error && localError) {
734 CFTransferRetained(*error, localError);
736 CFReleaseNull(localError);
737 CFReleaseNull(statusError);
739 return result && action_result;
742 CFTypeRef SOSKeychainAccountGetSharedAccount()
744 __block SOSAccount* result = NULL;
746 do_with_account(^(SOSAccountTransaction* txn) {
747 result = txn.account;
750 return (__bridge CFTypeRef)result;
754 // Mark: Credential processing
757 bool SOSCCTryUserCredentials_Server(CFStringRef user_label, CFDataRef user_password, CFStringRef dsid, CFErrorRef *error) {
758 secnotice("updates", "Trying credentials and dsid (%@) for %@", dsid, user_label);
760 return do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
761 if (dsid != NULL && CFStringCompare(dsid, CFSTR(""), 0) != 0) {
762 SOSAccountAssertDSID(txn.account, dsid);
764 return SOSAccountTryUserCredentials(txn.account, user_label, user_password, block_error);
768 SOSViewResultCode SOSCCView_Server(CFStringRef viewname, SOSViewActionCode action, CFErrorRef *error) {
769 __block SOSViewResultCode status = kSOSCCGeneralViewError;
771 do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
775 case kSOSCCViewQuery:
776 status = [txn.account.trust viewStatus:txn.account name:viewname err:error];
779 case kSOSCCViewEnable:
780 status = [txn.account.trust updateView:txn.account name:viewname code:action err:error];
784 case kSOSCCViewDisable:
785 status = [txn.account.trust updateView:txn.account name:viewname code:action err:error];
789 secnotice("views", "Bad SOSViewActionCode - %d", (int) action);
798 bool SOSCCViewSetWithAnalytics_Server(CFSetRef enabledViews, CFSetRef disabledViews, CFDataRef parentEvent) {
799 __block bool status = false;
801 do_with_account_if_after_first_unlock(NULL, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
802 status = [txn.account.trust updateViewSetsWithAnalytics:txn.account enabled:enabledViews disabled:disabledViews parentEvent:(__bridge NSData*)parentEvent];
808 bool SOSCCViewSet_Server(CFSetRef enabledViews, CFSetRef disabledViews) {
809 __block bool status = false;
811 do_with_account_if_after_first_unlock(NULL, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
812 status = [txn.account.trust updateViewSets:txn.account enabled:enabledViews disabled:disabledViews];
819 void sync_the_last_data_to_kvs(CFTypeRef account, bool waitForeverForSynchronization){
821 dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
823 secnoticeq("force-push", "calling SOSCloudKeychainSynchronizeAndWait");
825 SOSCloudKeychainSynchronizeAndWait(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFDictionaryRef returnedValues, CFErrorRef sync_error) {
827 secerrorq("SOSCloudKeychainSynchronizeAndWait: %@", sync_error);
829 secnoticeq("force-push", "returned from call; in callback to SOSCloudKeychainSynchronizeAndWait: results: %@", returnedValues);
832 dispatch_semaphore_signal(wait_for);
835 if(waitForeverForSynchronization)
836 dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER);
838 dispatch_semaphore_wait(wait_for, dispatch_time(DISPATCH_TIME_NOW, 60ull * NSEC_PER_SEC));
843 #define kWAIT2MINID "EFRESH"
845 static bool SyncKVSAndWait(CFErrorRef *error) {
846 dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
848 __block bool success = false;
850 secnoticeq("fresh", "EFP calling SOSCloudKeychainSynchronizeAndWait");
852 os_activity_initiate("CloudCircle EFRESH", OS_ACTIVITY_FLAG_DEFAULT, ^(void) {
853 SOSCloudKeychainSynchronizeAndWait(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(__unused CFDictionaryRef returnedValues, CFErrorRef sync_error) {
854 secnotice("fresh", "EFP returned, callback error: %@", sync_error);
856 success = (sync_error == NULL);
858 CFRetainAssign(*error, sync_error);
861 dispatch_semaphore_signal(wait_for);
865 dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER);
866 secnotice("fresh", "EFP complete: %s %@", success ? "success" : "failure", error ? *error : NULL);
872 static bool Flush(CFErrorRef *error) {
873 __block bool success = false;
875 dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
876 secnotice("flush", "Starting");
878 SOSCloudKeychainFlush(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFDictionaryRef returnedValues, CFErrorRef sync_error) {
879 success = (sync_error == NULL);
881 CFRetainAssign(*error, sync_error);
884 dispatch_semaphore_signal(wait_for);
887 dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER);
889 secnotice("flush", "Returned %s", success? "Success": "Failure");
894 static bool SOSCCAssertUserCredentialsAndOptionalDSID(CFStringRef user_label, CFDataRef user_password, CFStringRef dsid, CFErrorRef *error) {
895 secnotice("updates", "Setting credentials and dsid (%@) for %@", dsid, user_label);
897 bool result = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
898 if (dsid != NULL && CFStringCompare(dsid, CFSTR(""), 0) != 0) {
899 SOSAccountAssertDSID(txn.account, dsid);
904 require_quiet(result, done);
906 require_quiet(SyncKVSAndWait(error), done); // Make sure we've seen what the server has
907 require_quiet(Flush(error), done); // And processed it already...before asserting
909 result = do_with_account_while_unlocked(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *block_error) {
910 return SOSAccountAssertUserCredentials(txn.account, user_label, user_password, block_error);
913 require_quiet(result, done);
914 require_quiet(Flush(error), done); // Process any incoming information..circles et.al. before fixing our signature
916 result = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
917 return SOSAccountGenerationSignatureUpdate(txn.account, error);
924 static bool SOSCCAssertUserCredentialsAndOptionalDSIDWithAnalytics(CFStringRef user_label, CFDataRef user_password, CFStringRef dsid, NSData* parentEvent, CFErrorRef *error) {
925 secnotice("updates", "Setting credentials and dsid (%@) for %@", dsid, user_label);
927 NSError* localError = nil;
928 SFSignInAnalytics* parent = [NSKeyedUnarchiver unarchivedObjectOfClass:[SFSignInAnalytics class] fromData:parentEvent error:&localError];
930 SFSignInAnalytics *syncAndWaitEvent = nil;
931 SFSignInAnalytics *flushEvent = nil;
932 SFSignInAnalytics *secondFlushEvent = nil;
933 SFSignInAnalytics *generationSignatureUpdateEvent = nil;
935 bool result = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
936 if (dsid != NULL && CFStringCompare(dsid, CFSTR(""), 0) != 0) {
937 SOSAccountAssertDSID(txn.account, dsid);
942 require_quiet(result, done);
943 syncAndWaitEvent = [parent newSubTaskForEvent:@"syncAndWaitEvent"];
944 require_quiet(SyncKVSAndWait(error), done); // Make sure we've seen what the server has
945 [syncAndWaitEvent stopWithAttributes:nil];
947 flushEvent = [parent newSubTaskForEvent:@"flushEvent"];
948 require_quiet(Flush(error), done); // And processed it already...before asserting
949 [flushEvent stopWithAttributes:nil];
951 result = do_with_account_while_unlocked(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *block_error) {
952 return SOSAccountAssertUserCredentials(txn.account, user_label, user_password, block_error);
955 require_quiet(result, done);
956 secondFlushEvent = [parent newSubTaskForEvent:@"secondFlushEvent"];
957 require_quiet(Flush(error), done); // Process any incoming information..circles et.al. before fixing our signature
958 [secondFlushEvent stopWithAttributes:nil];
960 generationSignatureUpdateEvent = [parent newSubTaskForEvent:@"generationSignatureUpdateEvent"];
961 result = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
962 return SOSAccountGenerationSignatureUpdate(txn.account, error);
964 [generationSignatureUpdateEvent stopWithAttributes:nil];
968 if(syncAndWaitEvent){
969 [syncAndWaitEvent stopWithAttributes:nil];
972 [flushEvent stopWithAttributes:nil];
974 if(secondFlushEvent){
975 [secondFlushEvent stopWithAttributes:nil];
977 if(generationSignatureUpdateEvent){
978 [generationSignatureUpdateEvent stopWithAttributes:nil];
983 bool SOSCCSetUserCredentialsAndDSID_Server(CFStringRef user_label, CFDataRef user_password, CFStringRef dsid, CFErrorRef *error)
985 // TODO: Return error if DSID is NULL to insist our callers provide one?
986 return SOSCCAssertUserCredentialsAndOptionalDSID(user_label, user_password, dsid, error);
989 bool SOSCCSetUserCredentialsAndDSIDWithAnalytics_Server(CFStringRef user_label, CFDataRef user_password, CFStringRef dsid, CFDataRef parentEvent, CFErrorRef *error)
991 return SOSCCAssertUserCredentialsAndOptionalDSIDWithAnalytics(user_label, user_password, dsid, (__bridge NSData*)parentEvent, error);
993 bool SOSCCSetUserCredentials_Server(CFStringRef user_label, CFDataRef user_password, CFErrorRef *error)
995 return SOSCCAssertUserCredentialsAndOptionalDSID(user_label, user_password, NULL, error);
998 bool SOSCCCanAuthenticate_Server(CFErrorRef *error)
1000 bool result = do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1001 // If we reply that yes we can authenticate, then let's make sure we can authenticate for a while yet.
1002 // <rdar://problem/32732066>
1003 SOSAccountRestartPrivateCredentialTimer(txn.account);
1004 return SOSAccountGetPrivateCredential(txn.account, block_error) != NULL;
1007 if (!result && error && *error && CFErrorGetDomain(*error) == kSOSErrorDomain) {
1008 CFIndex code = CFErrorGetCode(*error);
1009 if (code == kSOSErrorPrivateKeyAbsent || code == kSOSErrorPublicKeyAbsent) {
1010 CFReleaseNull(*error);
1017 bool SOSCCPurgeUserCredentials_Server(CFErrorRef *error)
1019 return do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1020 SOSAccountPurgePrivateCredential(txn.account);
1025 SOSCCStatus SOSCCThisDeviceIsInCircle_Server(CFErrorRef *error)
1027 __block SOSCCStatus status;
1029 return do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1030 status = [txn.account getCircleStatus:block_error];
1033 }) ? status : kSOSCCError;
1036 bool SOSCCRequestToJoinCircle_Server(CFErrorRef* error)
1038 __block bool result = true;
1040 return do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1041 result = SOSAccountJoinCircles(txn, block_error);
1046 bool SOSCCRequestToJoinCircleWithAnalytics_Server(CFDataRef parentEvent, CFErrorRef* error)
1048 __block bool result = true;
1050 return do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1051 result = SOSAccountJoinCirclesWithAnalytics(txn, (__bridge NSData*)parentEvent, block_error);
1056 bool SOSCCAccountHasPublicKey_Server(CFErrorRef *error)
1058 __block bool result = true;
1059 __block CFErrorRef localError = NULL;
1061 bool hasPublicKey = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1062 result = SOSAccountHasPublicKey(txn.account, &localError);
1066 if(error != NULL && localError != NULL)
1067 *error = localError;
1069 return hasPublicKey;
1072 bool SOSCCAccountIsNew_Server(CFErrorRef *error)
1074 __block bool result = true;
1075 __block CFErrorRef localError = NULL;
1077 (void) do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1078 result = SOSAccountIsNew(txn.account, &localError);
1082 if(error != NULL && localError != NULL)
1083 *error = localError;
1087 bool SOSCCRequestToJoinCircleAfterRestore_Server(CFErrorRef* error)
1089 __block bool result = true;
1090 bool returned = false;
1091 returned = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1092 SOSAccountEnsurePeerRegistration(txn.account, block_error);
1093 result = SOSAccountJoinCirclesAfterRestore(txn, block_error);
1100 bool SOSCCRequestToJoinCircleAfterRestoreWithAnalytics_Server(CFDataRef parentEvent, CFErrorRef* error)
1102 __block bool result = true;
1103 bool returned = false;
1104 returned = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1105 NSError* localError = nil;
1106 SFSignInAnalytics* parent = [NSKeyedUnarchiver unarchivedObjectOfClass:[SFSignInAnalytics class] fromData:(__bridge NSData*)parentEvent error:&localError];
1108 SFSignInAnalytics *ensurePeerRegistrationEvent = [parent newSubTaskForEvent:@"ensurePeerRegistrationEvent"];
1109 SOSAccountEnsurePeerRegistration(txn.account, block_error);
1110 if(block_error && *block_error){
1111 NSError* blockError = (__bridge NSError*)*block_error;
1113 [ensurePeerRegistrationEvent logRecoverableError:blockError];
1114 secerror("ensure peer registration error: %@", blockError);
1117 [ensurePeerRegistrationEvent stopWithAttributes:nil];
1118 result = SOSAccountJoinCirclesAfterRestoreWithAnalytics(txn, (__bridge NSData*)parentEvent, block_error);
1125 bool SOSCCRequestEnsureFreshParameters_Server(CFErrorRef* error)
1127 bool returned = false;
1128 returned = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1129 return SyncKVSAndWait(block_error);
1132 returned = Flush(error);
1137 bool SOSCCApplyToARing_Server(CFStringRef ringName, CFErrorRef *error){
1138 __block bool result = true;
1139 bool returned = false;
1140 returned = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1141 SOSFullPeerInfoRef fpi = txn.account.fullPeerInfo;
1142 SOSRingRef ring = [txn.account.trust copyRing:ringName err:error];
1145 result = SOSRingApply(ring, txn.account.accountKey, fpi , error);
1147 CFReleaseNull(ring);
1153 bool SOSCCWithdrawlFromARing_Server(CFStringRef ringName, CFErrorRef *error){
1154 __block bool result = true;
1155 bool returned = false;
1156 returned = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1157 SOSFullPeerInfoRef fpi = txn.account.fullPeerInfo;
1158 SOSRingRef ring = [txn.account.trust copyRing:ringName err:error];
1160 result = SOSRingWithdraw(ring, txn.account.accountKey, fpi , error);
1162 CFReleaseNull(ring);
1168 bool SOSCCEnableRing_Server(CFStringRef ringName, CFErrorRef *error){
1169 __block bool result = true;
1170 bool returned = false;
1171 returned = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1172 SOSFullPeerInfoRef fpi = txn.account.fullPeerInfo;
1173 SOSRingRef ring = [txn.account.trust copyRing:ringName err:error];
1175 result = SOSRingResetToOffering(ring, NULL, fpi, error);
1177 CFReleaseNull(ring);
1183 CFStringRef SOSCCGetAllTheRings_Server(CFErrorRef *error){
1184 __block CFMutableDictionaryRef result = NULL;
1185 __block CFMutableStringRef description = CFStringCreateMutable(kCFAllocatorDefault, 0);
1187 (void) do_with_account_while_unlocked(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
1188 SOSAccountForEachRing(txn.account, ^SOSRingRef(CFStringRef name, SOSRingRef ring) {
1189 CFStringAppendFormat(description, NULL, CFSTR("%@\n"), ring);
1200 SOSRingStatus SOSCCRingStatus_Server(CFStringRef ringName, CFErrorRef *error){
1201 __block bool result = true;
1202 SOSRingStatus returned;
1203 returned = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1204 SOSFullPeerInfoRef fpi = txn.account.fullPeerInfo;
1205 SOSPeerInfoRef myPeer = SOSFullPeerInfoGetPeerInfo(fpi);
1207 SOSRingRef ring = [txn.account.trust copyRing:ringName err:error];
1208 if(myPeer && ring) {
1209 result = SOSRingDeviceIsInRing(ring, SOSPeerInfoGetPeerID(myPeer));
1211 CFReleaseNull(ring);
1218 bool SOSCCAccountSetToNew_Server(CFErrorRef *error)
1220 return do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1221 SOSAccountSetToNew(txn.account);
1226 bool SOSCCResetToOffering_Server(CFErrorRef* error)
1228 return do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1229 SecKeyRef user_key = SOSAccountGetPrivateCredential(txn.account, error);
1232 return [txn.account.trust resetToOffering:txn key:user_key err:block_error];
1237 bool SOSCCResetToEmpty_Server(CFErrorRef* error)
1239 return do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1240 if (!SOSAccountHasPublicKey(txn.account, error))
1242 return [txn.account.trust resetAccountToEmpty:txn.account transport:txn.account.circle_transport err:block_error];
1247 bool SOSCCResetToEmptyWithAnalytics_Server(CFDataRef parentEvent, CFErrorRef* error)
1249 return do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1250 if (!SOSAccountHasPublicKey(txn.account, error))
1252 return [txn.account.trust resetAccountToEmptyWithAnalytics:txn.account transport:txn.account.circle_transport parentEvent:(__bridge NSData*)parentEvent err:block_error];
1257 bool SOSCCRemoveThisDeviceFromCircleWithAnalytics_Server(CFDataRef parentEvent, CFErrorRef* error)
1259 return do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1260 return [txn.account.trust leaveCircleWithAccount:txn.account withAnalytics:(__bridge NSData*)parentEvent err:error];
1264 bool SOSCCRemoveThisDeviceFromCircle_Server(CFErrorRef* error)
1266 return do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1267 return [txn.account.trust leaveCircle:txn.account err:block_error];
1271 bool SOSCCRemovePeersFromCircle_Server(CFArrayRef peers, CFErrorRef* error)
1273 return do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1274 return SOSAccountRemovePeersFromCircle(txn.account, peers, block_error);
1278 bool SOSCCRemovePeersFromCircleWithAnalytics_Server(CFArrayRef peers, CFDataRef parentEvent, CFErrorRef* error)
1280 return do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1281 return SOSAccountRemovePeersFromCircleWithAnalytics(txn.account, peers, (__bridge NSData*)parentEvent, block_error);
1285 bool SOSCCLoggedOutOfAccount_Server(CFErrorRef *error)
1287 return do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1288 secnotice("circleOps", "Signed out of account!");
1290 bool waitForeverForSynchronization = true;
1292 bool result = [txn.account.trust leaveCircle:txn.account err:block_error];
1294 [txn restart]; // Make sure this gets finished before we set to new.
1296 sync_the_last_data_to_kvs((__bridge CFTypeRef)(txn.account), waitForeverForSynchronization);
1298 SOSAccountSetToNew(txn.account);
1304 bool SOSCCBailFromCircle_Server(uint64_t limit_in_seconds, CFErrorRef* error)
1306 return do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1307 bool waitForeverForSynchronization = false;
1309 bool result = SOSAccountBail(txn.account, limit_in_seconds, block_error);
1311 [txn restart]; // Make sure this gets finished before we set to new.
1313 sync_the_last_data_to_kvs((__bridge CFTypeRef)(txn.account), waitForeverForSynchronization);
1320 CFArrayRef SOSCCCopyApplicantPeerInfo_Server(CFErrorRef* error)
1322 __block CFArrayRef result = NULL;
1324 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1325 result = SOSAccountCopyApplicants(txn.account, block_error);
1326 return result != NULL;
1332 CFArrayRef SOSCCCopyGenerationPeerInfo_Server(CFErrorRef* error)
1334 __block CFArrayRef result = NULL;
1336 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1337 result = SOSAccountCopyGeneration(txn.account, block_error);
1338 return result != NULL;
1344 CFArrayRef SOSCCCopyValidPeerPeerInfo_Server(CFErrorRef* error)
1346 __block CFArrayRef result = NULL;
1350 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1352 result = SOSAccountCopyValidPeers(txn.account, block_error);
1354 return result != NULL;
1361 bool SOSCCValidateUserPublic_Server(CFErrorRef* error)
1363 __block bool result = NULL;
1365 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1366 result = SOSValidateUserPublic(txn.account, block_error);
1373 CFArrayRef SOSCCCopyNotValidPeerPeerInfo_Server(CFErrorRef* error)
1375 __block CFArrayRef result = NULL;
1377 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1378 result = SOSAccountCopyNotValidPeers(txn.account, block_error);
1379 return result != NULL;
1385 CFArrayRef SOSCCCopyRetirementPeerInfo_Server(CFErrorRef* error)
1387 __block CFArrayRef result = NULL;
1389 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1390 result = SOSAccountCopyRetired(txn.account, block_error);
1391 return result != NULL;
1397 CFArrayRef SOSCCCopyViewUnawarePeerInfo_Server(CFErrorRef* error)
1399 __block CFArrayRef result = NULL;
1401 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1402 result = SOSAccountCopyViewUnaware(txn.account, block_error);
1403 return result != NULL;
1409 CFArrayRef SOSCCCopyEngineState_Server(CFErrorRef* error)
1411 CFArrayRef result = NULL;
1412 SOSDataSourceFactoryRef dsf = SecItemDataSourceFactoryGetDefault();
1413 SOSDataSourceRef ds = SOSDataSourceFactoryCreateDataSource(dsf, kSecAttrAccessibleWhenUnlocked, error);
1415 SOSEngineRef engine = SOSDataSourceGetSharedEngine(ds, error);
1416 result = SOSEngineCopyPeerConfirmedDigests(engine, error);
1417 SOSDataSourceRelease(ds, error);
1423 static int64_t getTimeDifference(time_t start)
1430 duration = stop - start;
1432 return SecBucket1Significant(duration);
1435 static uint64_t initialSyncTimeoutFromDefaultsWrite(void)
1437 uint64_t timeout = 10;
1439 //sudo defaults write /Library/Preferences/com.apple.authd enforceEntitlement -bool true
1440 CFTypeRef initialSyncTimeout = (CFNumberRef)CFPreferencesCopyValue(CFSTR("InitialSync.WaitPeriod"), CFSTR("com.apple.security"), kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
1442 if (isNumber(initialSyncTimeout)) {
1443 CFNumberGetValue((CFNumberRef)initialSyncTimeout, kCFNumberSInt64Type, &timeout);
1445 CFReleaseNull(initialSyncTimeout);
1449 bool SOSCCWaitForInitialSyncWithAnalytics_Server(CFDataRef parentEvent, CFErrorRef* error) {
1450 __block dispatch_semaphore_t inSyncSema = NULL;
1451 __block bool result = false;
1452 __block bool synced = false;
1453 bool timed_out = false;
1454 __block CFStringRef inSyncCallID = NULL;
1455 __block time_t start;
1456 __block CFBooleanRef shouldUseInitialSyncV0 = false;
1457 SFSignInAnalytics* syncingEvent = nil;
1459 NSError* localError = nil;
1460 SFSignInAnalytics* parent = [NSKeyedUnarchiver unarchivedObjectOfClass:[SFSignInAnalytics class] fromData:(__bridge NSData*)parentEvent error:&localError];
1462 secnotice("initial sync", "Wait for initial sync start!");
1464 result = do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1465 shouldUseInitialSyncV0 = (CFBooleanRef)SOSAccountGetValue(txn.account, kSOSInitialSyncTimeoutV0, error);
1466 bool alreadyInSync = (SOSAccountHasCompletedInitialSync(txn.account));
1468 if (!alreadyInSync) {
1469 txn.account.isInitialSyncing = true;
1471 inSyncSema = dispatch_semaphore_create(0);
1473 SFSignInAnalytics* callWhenInSyncEvent = [parent newSubTaskForEvent:@"callWhenInSync"];
1474 inSyncCallID = SOSAccountCallWhenInSync(txn.account, ^bool(SOSAccount* mightBeSynced) {
1478 dispatch_semaphore_signal(inSyncSema);
1479 NSDictionary* attributes = @{@"finishedSyncing" : @YES};
1480 [syncingEvent stopWithAttributes:attributes];
1484 NSDictionary* attributes = @{};
1485 [callWhenInSyncEvent stopWithAttributes:attributes];
1493 require_quiet(result, fail);
1497 syncingEvent = [parent newSubTaskForEvent:@"initialSyncEvent"];
1498 if(shouldUseInitialSyncV0){
1499 secnotice("piggy","setting initial sync timeout to 5 minutes");
1500 timed_out = dispatch_semaphore_wait(inSyncSema, dispatch_time(DISPATCH_TIME_NOW, 300ull * NSEC_PER_SEC));
1503 uint64_t timeoutFromDefaultsWrite = initialSyncTimeoutFromDefaultsWrite();
1504 secnotice("piggy","setting initial sync timeout to %llu seconds", timeoutFromDefaultsWrite);
1505 timed_out = dispatch_semaphore_wait(inSyncSema, dispatch_time(DISPATCH_TIME_NOW, timeoutFromDefaultsWrite * NSEC_PER_SEC));
1508 if (timed_out && shouldUseInitialSyncV0) {
1509 do_with_account(^(SOSAccountTransaction* txn) {
1510 if (SOSAccountUnregisterCallWhenInSync(txn.account, inSyncCallID)) {
1512 inSyncSema = NULL; // We've canceled the timeout so we must be the last.
1516 NSError* error = [NSError errorWithDomain:@"securityd" code:errSecTimedOut userInfo:@{NSLocalizedDescriptionKey: @"timed out waiting for initial sync"}];
1517 [syncingEvent logUnrecoverableError:error];
1518 NSDictionary* attributes = @{@"finishedSyncing" : @NO, @"legacyPiggybacking" : @YES};
1519 [syncingEvent stopWithAttributes:attributes];
1522 require_quiet(result, fail);
1526 SecADClientPushValueForDistributionKey(SOSAggdSyncCompletionKey, getTimeDifference(start));
1530 SecADAddValueForScalarKey(SOSAggdSyncTimeoutKey, 1);
1533 (void)do_with_account(^(SOSAccountTransaction *txn) {
1534 txn.account.isInitialSyncing = false;
1537 secnotice("initial sync", "Finished!: %d", result);
1540 CFReleaseNull(inSyncCallID);
1544 bool SOSCCWaitForInitialSync_Server(CFErrorRef* error) {
1546 __block dispatch_semaphore_t inSyncSema = NULL;
1547 __block bool result = false;
1548 __block bool synced = false;
1549 bool timed_out = false;
1550 __block CFStringRef inSyncCallID = NULL;
1551 __block time_t start;
1552 __block CFBooleanRef shouldUseInitialSyncV0 = false;
1554 secnotice("initial sync", "Wait for initial sync start!");
1556 result = do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1557 shouldUseInitialSyncV0 = (CFBooleanRef)SOSAccountGetValue(txn.account, kSOSInitialSyncTimeoutV0, error);
1558 bool alreadyInSync = (SOSAccountHasCompletedInitialSync(txn.account));
1560 if (!alreadyInSync) {
1561 txn.account.isInitialSyncing = true;
1563 inSyncSema = dispatch_semaphore_create(0);
1565 inSyncCallID = SOSAccountCallWhenInSync(txn.account, ^bool(SOSAccount* mightBeSynced) {
1569 dispatch_semaphore_signal(inSyncSema);
1581 require_quiet(result, fail);
1584 if(shouldUseInitialSyncV0){
1585 secnotice("piggy","setting initial sync timeout to 5 minutes");
1586 timed_out = dispatch_semaphore_wait(inSyncSema, dispatch_time(DISPATCH_TIME_NOW, 300ull * NSEC_PER_SEC));
1589 uint64_t timeoutFromDefaultsWrite = initialSyncTimeoutFromDefaultsWrite();
1590 secnotice("piggy","setting initial sync timeout to %llu seconds", timeoutFromDefaultsWrite);
1591 timed_out = dispatch_semaphore_wait(inSyncSema, dispatch_time(DISPATCH_TIME_NOW, timeoutFromDefaultsWrite * NSEC_PER_SEC));
1594 if (timed_out && shouldUseInitialSyncV0) {
1595 do_with_account(^(SOSAccountTransaction* txn) {
1596 if (SOSAccountUnregisterCallWhenInSync(txn.account, inSyncCallID)) {
1598 inSyncSema = NULL; // We've canceled the timeout so we must be the last.
1604 require_quiet(result, fail);
1608 SecADClientPushValueForDistributionKey(SOSAggdSyncCompletionKey, getTimeDifference(start));
1612 SecADAddValueForScalarKey(SOSAggdSyncTimeoutKey, 1);
1615 (void)do_with_account(^(SOSAccountTransaction *txn) {
1616 txn.account.isInitialSyncing = false;
1619 secnotice("initial sync", "Finished!: %d", result);
1622 CFReleaseNull(inSyncCallID);
1627 static CFArrayRef SOSAccountCopyYetToSyncViews(SOSAccount* account, CFErrorRef *error) {
1628 __block CFArrayRef result = NULL;
1630 CFTypeRef valueFetched = SOSAccountGetValue(account, kSOSUnsyncedViewsKey, error);
1631 if (valueFetched == kCFBooleanTrue) {
1632 SOSPeerInfoRef myPI = account.peerInfo;
1634 SOSPeerInfoWithEnabledViewSet(myPI, ^(CFSetRef enabled) {
1635 result = CFSetCopyValues(enabled);
1638 } else if (isSet(valueFetched)) {
1639 result = CFSetCopyValues((CFSetRef)valueFetched);
1642 if (result == NULL) {
1643 result = CFArrayCreateForCFTypes(kCFAllocatorDefault, NULL);
1649 CFArrayRef SOSCCCopyYetToSyncViewsList_Server(CFErrorRef* error) {
1651 __block CFArrayRef views = NULL;
1653 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1654 views = SOSAccountCopyYetToSyncViews(txn.account, error);
1662 bool SOSWrapToBackupSliceKeyBagForView_Server(CFStringRef viewName, CFDataRef input, CFDataRef* output, CFDataRef* bskbEncoded, CFErrorRef* error) {
1663 CFErrorRef localerror = NULL;
1664 SOSBackupSliceKeyBagRef bskb = SOSBackupSliceKeyBagForView(viewName, &localerror);
1666 if(bskbEncoded && bskb) {
1667 *bskbEncoded = SOSBSKBCopyEncoded(bskb, &localerror);
1671 *output = SOSWrapToBackupSliceKeyBag(bskb, input, &localerror);
1675 *error = localerror;
1677 return localerror == NULL;
1680 SOSBackupSliceKeyBagRef SOSBackupSliceKeyBagForView(CFStringRef viewName, CFErrorRef* error){
1681 __block SOSBackupSliceKeyBagRef bskb = NULL;
1682 (void) do_with_account(^ (SOSAccountTransaction* txn) {
1683 bskb = SOSAccountBackupSliceKeyBagForView(txn.account, viewName, error);
1688 CFDataRef SOSWrapToBackupSliceKeyBag(SOSBackupSliceKeyBagRef bskb, CFDataRef input, CFErrorRef* error) {
1689 CFDataRef encrypted = NULL;
1690 bskb_keybag_handle_t bskb_handle = 0;
1692 require_quiet(bskb, exit);
1694 bskb_handle = SOSBSKBLoadLocked(bskb, error);
1695 require_quiet(bskb_handle, exit);
1697 SecAccessControlRef access = NULL;
1698 require_quiet(access = SecAccessControlCreate(kCFAllocatorDefault, error), exit);
1699 require_quiet(SecAccessControlSetProtection(access, kSecAttrAccessibleWhenUnlocked, error), exit);
1701 // ks_encrypt_data takes a dictionary as its plaintext.
1702 CFMutableDictionaryRef plaintext = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1703 CFDictionarySetValue(plaintext, CFSTR("data"), input);
1705 require_quiet(ks_encrypt_data_legacy(bskb_handle, access, NULL, plaintext, NULL, &encrypted, false, error), exit);
1708 CFReleaseNull(bskb);
1709 if(bskb_handle != 0) {
1710 ks_close_keybag(bskb_handle, error);
1712 if(error && *error) {
1713 secnotice("backup", "Failed to wrap to a BKSB: %@", *error);
1719 CFDictionaryRef SOSCCCopyEscrowRecord_Server(CFErrorRef *error){
1721 __block CFDictionaryRef result = NULL;
1723 (void) do_with_account_if_after_first_unlock(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
1724 CFErrorRef localError = NULL;
1725 SOSCCStatus status = [txn.account getCircleStatus:&localError];
1727 CFStringRef dsid = SOSAccountGetValue(txn.account, kSOSDSIDKey, error);
1728 CFDictionaryRef escrowRecords = NULL;
1729 CFDictionaryRef record = NULL;
1731 case kSOSCCInCircle:
1732 //get the escrow record in the peer info!
1733 escrowRecords = SOSPeerInfoCopyEscrowRecord(txn.account.peerInfo);
1735 record = CFDictionaryGetValue(escrowRecords, dsid);
1737 result = CFRetainSafe(record);
1739 CFReleaseNull(escrowRecords);
1741 case kSOSCCRequestPending:
1742 //set the escrow record in the peer info/application?
1744 case kSOSCCNotInCircle:
1745 case kSOSCCCircleAbsent:
1746 //set the escrow record in the account expansion!
1747 escrowRecords = SOSAccountGetValue(txn.account, kSOSEscrowRecord, error);
1749 record = CFDictionaryGetValue(escrowRecords, dsid);
1751 result = CFRetainSafe(record);
1755 secdebug("account", "no circle status!");
1764 CFDictionaryRef SOSCCCopyBackupInformation_Server(CFErrorRef *error) {
1765 __block CFDictionaryRef result = NULL;
1767 (void) do_with_account_if_after_first_unlock(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
1768 result = SOSBackupInformation(txn, error);
1774 bool SOSCCSetEscrowRecord_Server(CFStringRef escrow_label, uint64_t tries, CFErrorRef *error){
1776 if (escrow_label == NULL) {
1780 __block bool result = true;
1781 __block CFErrorRef block_error = NULL;
1783 (void) do_with_account_if_after_first_unlock(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
1784 SOSCCStatus status = [txn.account getCircleStatus:&block_error];
1786 CFStringRef dsid = SOSAccountGetValue(txn.account, kSOSDSIDKey, error);
1788 CFMutableStringRef timeDescription = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, CFSTR("["));
1789 CFAbsoluteTime currentTimeAndDate = CFAbsoluteTimeGetCurrent();
1791 withStringOfAbsoluteTime(currentTimeAndDate, ^(CFStringRef decription) {
1792 CFStringAppend(timeDescription, decription);
1794 CFStringAppend(timeDescription, CFSTR("]"));
1796 CFNumberRef attempts = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, (const void*)&tries);
1798 CFMutableDictionaryRef escrowTimeAndTries = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1799 CFDictionaryAddValue(escrowTimeAndTries, kSOSBurnedRecoveryAttemptCount, attempts);
1800 CFDictionaryAddValue(escrowTimeAndTries, kSOSBurnedRecoveryAttemptAttestationDate, timeDescription);
1802 CFMutableDictionaryRef escrowRecord = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1803 CFDictionaryAddValue(escrowRecord, escrow_label, escrowTimeAndTries);
1806 case kSOSCCInCircle:
1807 //set the escrow record in the peer info!
1808 if(!SOSFullPeerInfoAddEscrowRecord(txn.account.fullPeerInfo, dsid, escrowRecord, error)){
1809 secdebug("accout", "Could not set escrow record in the full peer info");
1813 case kSOSCCRequestPending:
1814 //set the escrow record in the peer info/application?
1816 case kSOSCCNotInCircle:
1817 case kSOSCCCircleAbsent:
1818 //set the escrow record in the account expansion!
1820 if(!SOSAccountAddEscrowRecords(txn.account, dsid, escrowRecord, error)) {
1821 secdebug("account", "Could not set escrow record in expansion data");
1826 secdebug("account", "no circle status!");
1829 CFReleaseNull(attempts);
1830 CFReleaseNull(timeDescription);
1831 CFReleaseNull(escrowTimeAndTries);
1832 CFReleaseNull(escrowRecord);
1840 bool SOSCCAcceptApplicants_Server(CFArrayRef applicants, CFErrorRef* error)
1842 return do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1843 return SOSAccountAcceptApplicants(txn.account, applicants, block_error);
1848 bool SOSCCRejectApplicants_Server(CFArrayRef applicants, CFErrorRef* error)
1850 return do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1851 return SOSAccountRejectApplicants(txn.account, applicants, block_error);
1855 CFArrayRef SOSCCCopyPeerPeerInfo_Server(CFErrorRef* error)
1857 __block CFArrayRef result = NULL;
1859 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1860 result = SOSAccountCopyPeers(txn.account, block_error);
1861 return result != NULL;
1867 CFArrayRef SOSCCCopyConcurringPeerPeerInfo_Server(CFErrorRef* error)
1869 __block CFArrayRef result = NULL;
1871 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1872 result = SOSAccountCopyConcurringPeers(txn.account, block_error);
1873 return result != NULL;
1879 SOSPeerInfoRef SOSCCCopyMyPeerInfo_Server(CFErrorRef* error)
1881 __block SOSPeerInfoRef result = NULL;
1883 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1884 // Create a copy to be DERed/sent back to client
1885 result = SOSPeerInfoCreateCopy(kCFAllocatorDefault, txn.account.peerInfo, block_error);
1886 return result != NULL;
1892 CFDataRef SOSCCCopyAccountState_Server(CFErrorRef* error)
1894 __block CFDataRef accountState = NULL;
1896 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1897 // Copy account state from the keychain
1898 accountState = SOSAccountCopyAccountStateFromKeychain(block_error);
1899 return accountState != NULL;
1902 return accountState;
1905 bool SOSCCDeleteAccountState_Server(CFErrorRef* error)
1907 __block bool result = NULL;
1909 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1910 // Delete account state from the keychain
1911 result = SOSAccountDeleteAccountStateFromKeychain(block_error);
1918 CFDataRef SOSCCCopyEngineData_Server(CFErrorRef* error)
1920 __block CFDataRef engineState = NULL;
1922 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1923 // Copy engine state from the keychain
1924 engineState = SOSAccountCopyEngineStateFromKeychain(block_error);
1925 return engineState != NULL;
1931 bool SOSCCDeleteEngineState_Server(CFErrorRef* error)
1933 __block bool result = NULL;
1935 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1936 // Delete engine state from the keychain
1937 result = SOSAccountDeleteEngineStateFromKeychain(block_error);
1946 SOSPeerInfoRef SOSCCSetNewPublicBackupKey_Server(CFDataRef newPublicBackup, CFErrorRef *error){
1947 __block SOSPeerInfoRef result = NULL;
1949 secnotice("devRecovery", "SOSCCSetNewPublicBackupKey_Server acquiring account lock");
1950 (void) do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1951 secnotice("devRecovery", "SOSCCSetNewPublicBackupKey_Server acquired account lock");
1952 if(SOSAccountSetBackupPublicKey(txn,newPublicBackup, error)){
1953 secnotice("devRecovery", "SOSCCSetNewPublicBackupKey_Server, new public backup is set in account");
1954 [txn restart]; // Finish the transaction to update any changes to the peer info.
1956 // Create a copy to be DERed/sent back to client
1957 result = SOSPeerInfoCreateCopy(kCFAllocatorDefault, txn.account.peerInfo, block_error);
1958 secnotice("devRecovery", "SOSCCSetNewPublicBackupKey_Server, new public backup is set and pushed");
1962 secnotice("devRecovery", "SOSCCSetNewPublicBackupKey_Server, could not set new public backup");
1964 return result != NULL;
1970 bool SOSCCRegisterSingleRecoverySecret_Server(CFDataRef aks_bag, bool setupV0Only, CFErrorRef *error){
1971 return do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1972 return SOSAccountSetBSKBagForAllSlices(txn.account, aks_bag, setupV0Only, error);
1976 CFStringRef SOSCCCopyIncompatibilityInfo_Server(CFErrorRef* error)
1978 __block CFStringRef result = NULL;
1980 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1981 result = SOSAccountCopyIncompatibilityInfo(txn.account, block_error);
1982 return result != NULL;
1988 bool SOSCCkSecXPCOpIsThisDeviceLastBackup_Server(CFErrorRef *error) {
1989 bool result = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1990 return SOSAccountIsLastBackupPeer(txn.account, block_error);
1995 enum DepartureReason SOSCCGetLastDepartureReason_Server(CFErrorRef* error)
1997 __block enum DepartureReason result = kSOSDepartureReasonError;
1999 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
2000 result = SOSAccountGetLastDepartureReason(txn.account, block_error);
2001 return result != kSOSDepartureReasonError;
2007 bool SOSCCSetLastDepartureReason_Server(enum DepartureReason reason, CFErrorRef *error){
2008 return do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
2009 SOSAccountSetLastDepartureReason(txn.account, reason);
2014 bool SOSCCProcessEnsurePeerRegistration_Server(CFErrorRef* error)
2016 secnotice("updates", "Request for registering peers");
2017 return do_with_account_while_unlocked(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
2018 return SOSAccountEnsurePeerRegistration(txn.account, error);
2022 CF_RETURNS_RETAINED CFSetRef SOSCCProcessSyncWithPeers_Server(CFSetRef peers, CFSetRef backupPeers, CFErrorRef *error) {
2023 __block CFSetRef result = NULL;
2024 if (!do_with_account_while_unlocked(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
2025 result = SOSAccountProcessSyncWithPeers(txn, peers, backupPeers, error);
2026 return result != NULL;
2028 // Be sure we don't return a result if we got an error
2029 CFReleaseNull(result);
2035 SyncWithAllPeersReason SOSCCProcessSyncWithAllPeers_Server(CFErrorRef* error)
2038 #define kIOReturnLockedRead iokit_common_err(0x2c3) // device read locked
2039 #define kIOReturnLockedWrite iokit_common_err(0x2c4) // device write locked
2041 __block SyncWithAllPeersReason result = kSyncWithAllPeersSuccess;
2043 CFErrorRef action_error = NULL;
2045 if (!do_with_account_while_unlocked(&action_error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
2046 return SOSAccountRequestSyncWithAllPeers(txn, block_error);
2049 if (SecErrorGetOSStatus(action_error) == errSecInteractionNotAllowed) {
2050 secnotice("updates", "SOSAccountSyncWithAllKVSPeers failed because device is locked; letting CloudKeychainProxy know");
2051 result = kSyncWithAllPeersLocked; // tell CloudKeychainProxy to call us back when device unlocks
2052 CFReleaseNull(action_error);
2054 secerror("Unexpected error: %@", action_error);
2058 SecErrorPropagate(action_error, error);
2068 void SOSCCRequestSyncWithPeer(CFStringRef peerID) {
2069 CFArrayRef peers = CFArrayCreateForCFTypes(kCFAllocatorDefault, peerID, NULL);
2071 SOSCCRequestSyncWithPeersList(peers);
2073 CFReleaseNull(peers);
2076 void SOSCCRequestSyncWithPeers(CFSetRef /*SOSPeerInfoRef/CFStringRef*/ peerIDs) {
2077 CFMutableArrayRef peerIDArray = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
2079 CFSetForEach(peerIDs, ^(const void *value) {
2080 if (isString(value)) {
2081 CFArrayAppendValue(peerIDArray, value);
2082 } else if (isSOSPeerInfo(value)) {
2083 SOSPeerInfoRef peer = asSOSPeerInfo(value);
2084 CFArrayAppendValue(peerIDArray, SOSPeerInfoGetPeerID(peer));
2086 secerror("Bad element, skipping: %@", value);
2090 SOSCCRequestSyncWithPeersList(peerIDArray);
2092 CFReleaseNull(peerIDArray);
2095 void SOSCCRequestSyncWithPeersList(CFArrayRef /*CFStringRef*/ peerIDs) {
2096 os_activity_initiate("CloudCircle RequestSyncWithPeersList", OS_ACTIVITY_FLAG_DEFAULT, ^(void) {
2097 CFArrayRef empty = CFArrayCreateForCFTypes(kCFAllocatorDefault, NULL);
2099 CFStringArrayPerformWithDescription(peerIDs, ^(CFStringRef description) {
2100 secnotice("syncwith", "Request Sync With: %@", description);
2103 SOSCloudKeychainRequestSyncWithPeers(peerIDs, empty,
2104 dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), NULL);
2105 CFReleaseNull(empty);
2109 void SOSCCRequestSyncWithBackupPeer(CFStringRef backupPeerId) {
2110 os_activity_initiate("CloudCircle RequestSyncWithPeersList", OS_ACTIVITY_FLAG_DEFAULT, ^(void) {
2111 CFArrayRef empty = CFArrayCreateForCFTypes(kCFAllocatorDefault, NULL);
2112 CFArrayRef backupPeerList = CFArrayCreateForCFTypes(kCFAllocatorDefault, backupPeerId, NULL);
2114 CFStringArrayPerformWithDescription(backupPeerList, ^(CFStringRef description) {
2115 secnotice("syncwith", "Request backup sync With: %@", description);
2118 SOSCloudKeychainRequestSyncWithPeers(empty, backupPeerList,
2119 dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), NULL);
2121 CFReleaseNull(empty);
2122 CFReleaseNull(backupPeerList);
2126 bool SOSCCIsSyncPendingFor(CFStringRef peerID, CFErrorRef *error) {
2127 return SOSCloudKeychainHasPendingSyncWithPeer(peerID, error);
2130 void SOSCCEnsurePeerRegistration(void)
2132 os_activity_initiate("CloudCircle EnsurePeerRegistration", OS_ACTIVITY_FLAG_DEFAULT, ^(void) {
2134 SOSCloudKeychainRequestEnsurePeerRegistration(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), NULL);
2139 CF_RETURNS_RETAINED CFArrayRef SOSCCHandleUpdateMessage(CFDictionaryRef updates)
2141 CFArrayRef result = NULL;
2142 SOSAccount* account = (__bridge SOSAccount *)(SOSKeychainAccountGetSharedAccount()); //HACK to make sure itemsChangedBlock is set
2144 (account) ? (result = SOSCloudKeychainHandleUpdateMessage(updates)) : (result = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault));
2148 SOSPeerInfoRef SOSCCCopyApplication_Server(CFErrorRef *error) {
2149 __block SOSPeerInfoRef application = NULL;
2150 do_with_account_while_unlocked(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
2151 application = SOSAccountCopyApplication(txn.account, error);
2152 return application != NULL;
2158 bool SOSCCCleanupKVSKeys_Server(CFErrorRef *error) {
2159 bool result = do_with_account_while_unlocked(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
2160 return SOSAccountCleanupAllKVSKeys(txn.account, error);
2162 if(result && error && *error) {
2163 CFReleaseNull(*error);
2168 bool SOSCCTestPopulateKVSWithBadKeys_Server(CFErrorRef *error)
2170 __block bool result = false;
2171 do_with_account_while_unlocked(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
2172 return SOSAccountPopulateKVSWithBadKeys(txn.account, error);
2176 CFDataRef SOSCCCopyCircleJoiningBlob_Server(SOSPeerInfoRef applicant, CFErrorRef *error) {
2177 __block CFDataRef pbblob = NULL;
2178 do_with_account_while_unlocked(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
2179 pbblob = SOSAccountCopyCircleJoiningBlob(txn.account, applicant, error);
2180 return pbblob != NULL;
2185 CFDataRef SOSCCCopyInitialSyncData_Server(CFErrorRef *error) {
2186 __block CFDataRef pbblob = NULL;
2187 do_with_account_while_unlocked(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
2188 pbblob = SOSAccountCopyInitialSyncData(txn.account, error);
2189 return pbblob != NULL;
2194 bool SOSCCJoinWithCircleJoiningBlob_Server(CFDataRef joiningBlob, PiggyBackProtocolVersion version, CFErrorRef *error) {
2195 return do_with_account_while_unlocked(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
2196 return SOSAccountJoinWithCircleJoiningBlob(txn.account, joiningBlob, version, error);
2201 CFBooleanRef SOSCCPeersHaveViewsEnabled_Server(CFArrayRef viewNames, CFErrorRef *error) {
2202 __block CFBooleanRef result = NULL;
2203 do_with_account_if_after_first_unlock(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
2204 result = SOSAccountPeersHaveViewsEnabled(txn.account, viewNames, error);
2205 return result != NULL;
2211 bool SOSCCRegisterRecoveryPublicKey_Server(CFDataRef recovery_key, CFErrorRef *error){
2212 return do_with_account_if_after_first_unlock(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
2213 if(recovery_key != NULL && CFDataGetLength(recovery_key) != 0)
2214 return SOSAccountRegisterRecoveryPublicKey(txn, recovery_key, error);
2216 return SOSAccountClearRecoveryPublicKey(txn, recovery_key, error);
2220 CFDataRef SOSCCCopyRecoveryPublicKey_Server(CFErrorRef *error){
2222 __block CFDataRef result = NULL;
2223 do_with_account_if_after_first_unlock(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
2224 result = SOSAccountCopyRecoveryPublicKey(txn, error);
2225 return result != NULL;
2231 bool SOSCCMessageFromPeerIsPending_Server(SOSPeerInfoRef peer, CFErrorRef *error) {
2232 return do_with_account_if_after_first_unlock(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
2233 return SOSAccountMessageFromPeerIsPending(txn, peer, error);
2237 bool SOSCCSendToPeerIsPending_Server(SOSPeerInfoRef peer, CFErrorRef *error) {
2238 return do_with_account_if_after_first_unlock(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
2239 return SOSAccountSendToPeerIsPending(txn, peer, error);
2243 void SOSCCResetOTRNegotiation_Server(CFStringRef peerid)
2245 CFErrorRef localError = NULL;
2246 do_with_account_while_unlocked(&localError, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
2247 SOSAccountResetOTRNegotiationCoder(txn.account, peerid);
2252 secerror("error resetting otr negotation: %@", localError);
2256 void SOSCCPeerRateLimiterSendNextMessage_Server(CFStringRef peerid, CFStringRef accessGroup)
2258 CFErrorRef localError = NULL;
2259 do_with_account_while_unlocked(&localError, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
2260 SOSAccountTimerFiredSendNextMessage(txn, (__bridge NSString*)peerid, (__bridge NSString*)accessGroup);
2265 secerror("error sending next message: %@", localError);
2269 void SOSCCPerformWithOctagonSigningKey(void (^action)(SecKeyRef octagonPrivSigningKey, CFErrorRef error))
2271 CFErrorRef error = NULL;
2272 do_with_account_if_after_first_unlock(&error, ^bool(SOSAccountTransaction *txn, CFErrorRef *err) {
2273 SOSFullPeerInfoRef fpi = txn.account.trust.fullPeerInfo;
2274 SecKeyRef signingKey = SOSFullPeerInfoCopyOctagonSigningKey(fpi, err);
2275 CFErrorRef errorArg = err ? *err : NULL;
2276 action(signingKey, errorArg);
2277 CFReleaseNull(signingKey);
2280 CFReleaseNull(error);
2283 void SOSCCPerformWithOctagonSigningPublicKey(void (^action)(SecKeyRef octagonPublicKey, CFErrorRef error))
2285 CFErrorRef error = NULL;
2286 do_with_account_if_after_first_unlock(&error, ^bool(SOSAccountTransaction *txn, CFErrorRef *err) {
2287 SOSFullPeerInfoRef fpi = txn.account.trust.fullPeerInfo;
2288 SecKeyRef signingKey = SOSFullPeerInfoCopyOctagonPublicSigningKey(fpi, err);
2289 CFErrorRef errorArg = err ? *err : NULL;
2290 action(signingKey, errorArg);
2291 CFReleaseNull(signingKey);
2294 CFReleaseNull(error);
2297 void SOSCCPerformWithOctagonEncryptionKey(void (^action)(SecKeyRef octagonPrivEncryptionKey, CFErrorRef error))
2299 CFErrorRef error = NULL;
2300 do_with_account_if_after_first_unlock(&error, ^bool(SOSAccountTransaction *txn, CFErrorRef *err) {
2301 SOSFullPeerInfoRef fpi = txn.account.trust.fullPeerInfo;
2302 SecKeyRef signingKey = SOSFullPeerInfoCopyOctagonEncryptionKey(fpi, err);
2303 CFErrorRef errorArg = err ? *err : NULL;
2304 action(signingKey, errorArg);
2305 CFReleaseNull(signingKey);
2308 CFReleaseNull(error);
2311 void SOSCCPerformWithOctagonEncryptionPublicKey(void (^action)(SecKeyRef octagonPublicEncryptionKey, CFErrorRef error))
2313 CFErrorRef error = NULL;
2314 do_with_account_if_after_first_unlock(&error, ^bool(SOSAccountTransaction *txn, CFErrorRef *err) {
2315 SOSFullPeerInfoRef fpi = txn.account.trust.fullPeerInfo;
2316 SecKeyRef signingKey = SOSFullPeerInfoCopyOctagonPublicEncryptionKey(fpi, err);
2317 CFErrorRef errorArg = err ? *err : NULL;
2318 action(signingKey, errorArg);
2319 CFReleaseNull(signingKey);
2322 CFReleaseNull(error);
2325 void SOSCCPerformWithAllOctagonKeys(void (^action)(SecKeyRef octagonEncryptionKey, SecKeyRef octagonSigningKey, CFErrorRef error))
2327 CFErrorRef localError = NULL;
2328 do_with_account_if_after_first_unlock(&localError, ^bool(SOSAccountTransaction *txn, CFErrorRef *err) {
2329 SecKeyRef encryptionKey = NULL;
2330 SecKeyRef signingKey = NULL;
2331 CFErrorRef errorArg = err ? *err : NULL;
2333 SOSFullPeerInfoRef fpi = txn.account.trust.fullPeerInfo;
2334 require_action_quiet(fpi, fail, secerror("device does not have a peer"); SOSCreateError(kSOSErrorPeerNotFound, CFSTR("No Peer for Account"), NULL, &errorArg));
2336 signingKey = SOSFullPeerInfoCopyOctagonSigningKey(fpi, &errorArg);
2337 require_action_quiet(signingKey && !errorArg, fail, secerror("SOSCCPerformWithAllOctagonKeys signing key error: %@", errorArg));
2338 CFReleaseNull(errorArg);
2340 encryptionKey = SOSFullPeerInfoCopyOctagonEncryptionKey(fpi, &errorArg);
2341 require_action_quiet(encryptionKey && !errorArg, fail, secerror("SOSCCPerformWithAllOctagonKeys encryption key error: %@", errorArg));
2343 action(encryptionKey, signingKey, errorArg);
2344 CFReleaseNull(signingKey);
2345 CFReleaseNull(encryptionKey);
2346 CFReleaseNull(errorArg);
2349 action(NULL, NULL, errorArg);
2350 CFReleaseNull(errorArg);
2351 CFReleaseNull(signingKey);
2352 CFReleaseNull(encryptionKey);
2355 CFReleaseNull(localError);
2357 void SOSCCPerformWithTrustedPeers(void (^action)(CFSetRef sosPeerInfoRefs, CFErrorRef error))
2359 CFErrorRef cfAccountError = NULL;
2360 do_with_account_if_after_first_unlock(&cfAccountError, ^bool(SOSAccountTransaction *txn, CFErrorRef *cferror) {
2361 CFSetRef sosPeerSet = [txn.account.trust copyPeerSetMatching:^bool(SOSPeerInfoRef peer) {
2365 CFErrorRef errorArg = cferror ? *cferror : NULL;
2366 action(sosPeerSet, errorArg);
2367 CFReleaseNull(sosPeerSet);
2370 CFReleaseNull(cfAccountError);
2373 void SOSCCPerformWithPeerID(void (^action)(CFStringRef peerID, CFErrorRef error))
2375 CFErrorRef cfAccountError = NULL;
2376 do_with_account_if_after_first_unlock(&cfAccountError, ^bool(SOSAccountTransaction *txn, CFErrorRef *cferror) {
2377 SOSAccount* account = txn.account;
2378 NSString* peerID = nil;
2379 CFErrorRef localError = nil;
2381 if([account getCircleStatus:nil] == kSOSCCInCircle){
2382 peerID = [txn.account peerID];
2385 SOSErrorCreate(kSOSErrorNoCircle, &localError, NULL, CFSTR("Not in circle"));
2387 action((__bridge CFStringRef)peerID, localError);
2388 CFReleaseNull(localError);
2391 CFReleaseNull(cfAccountError);