]> git.saurik.com Git - apple/security.git/blob - keychain/securityd/SOSCloudCircleServer.m
Security-59754.41.1.tar.gz
[apple/security.git] / keychain / securityd / SOSCloudCircleServer.m
1 /*
2 * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25 #include <AssertMacros.h>
26 #include <CoreFoundation/CoreFoundation.h>
27 #include <CoreFoundation/CFPriv.h>
28
29 #import "keychain/SecureObjectSync/SOSAccountTransaction.h"
30
31 #include "keychain/securityd/SOSCloudCircleServer.h"
32 #include <Security/SecureObjectSync/SOSCloudCircle.h>
33 #include <Security/SecureObjectSync/SOSCloudCircleInternal.h>
34
35 #include "keychain/SecureObjectSync/SOSCircle.h"
36 #include "keychain/SecureObjectSync/SOSAccount.h"
37 #include "keychain/SecureObjectSync/SOSAccountPriv.h"
38 #include "keychain/SecureObjectSync/SOSAccountGhost.h"
39
40 #include "keychain/SecureObjectSync/SOSTransport.h"
41 #include "keychain/SecureObjectSync/SOSFullPeerInfo.h"
42 #include "keychain/SecureObjectSync/SOSPeerInfoV2.h"
43 #include "keychain/SecureObjectSync/SOSPeerInfoPriv.h"
44 #include "keychain/SecureObjectSync/SOSPeerInfoInternal.h"
45 #include "keychain/SecureObjectSync/SOSInternal.h"
46 #include "keychain/SecureObjectSync/SOSUserKeygen.h"
47 #include "keychain/SecureObjectSync/SOSMessage.h"
48 #include "keychain/SecureObjectSync/SOSDataSource.h"
49 #include "keychain/SecureObjectSync/SOSKVSKeys.h"
50 #import "keychain/SecureObjectSync/SOSAccountTrustClassic.h"
51 #import "keychain/SecureObjectSync/SOSAccountTrustClassic+Circle.h"
52 #import "keychain/SecureObjectSync/SOSAccountTrustClassic+Expansion.h"
53 #import "keychain/SecureObjectSync/SOSAuthKitHelpers.h"
54 #import "keychain/ot/OTManager.h"
55 #import "keychain/SigninMetrics/OctagonSignPosts.h"
56 #import "NSError+UsefulConstructors.h"
57
58 #import "utilities/SecCoreAnalytics.h"
59 #include <utilities/SecCFWrappers.h>
60 #include <utilities/SecCFRelease.h>
61
62 #include <utilities/SecCFError.h>
63 #include <utilities/debugging.h>
64 #include <utilities/SecCoreCrypto.h>
65 #include <utilities/SecTrace.h>
66
67 #include "keychain/SecureObjectSync/CKBridge/SOSCloudKeychainClient.h"
68
69 #include <corecrypto/ccrng.h>
70 #include <corecrypto/ccrng_pbkdf2_prng.h>
71 #include <corecrypto/ccec.h>
72 #include <corecrypto/ccdigest.h>
73 #include <corecrypto/ccsha2.h>
74 #include <Security/SecKeyPriv.h>
75 #include <Security/SecFramework.h>
76
77 #include <utilities/SecFileLocations.h>
78 #include <utilities/SecAKSWrappers.h>
79 #include "keychain/securityd/SecItemServer.h"
80 #include <Security/SecItemPriv.h>
81
82 #include <TargetConditionals.h>
83
84 #include <utilities/iCloudKeychainTrace.h>
85 #include <Security/SecAccessControlPriv.h>
86 #include "keychain/securityd/SecDbKeychainItem.h"
87
88 #include <os/activity.h>
89 #include <xpc/private.h>
90
91 #include <os/state_private.h>
92
93 #if TARGET_OS_IPHONE
94 #include <MobileGestalt.h>
95 #else
96 #include <AppleSystemInfo/AppleSystemInfo.h>
97 #endif
98
99 #define SOSCKCSCOPE "sync"
100 #define RUN_AS_ROOT_ERROR 550
101
102 #define USE_SYSTEMCONFIGURATION_PRIVATE_HEADERS
103 #import <SystemConfiguration/SystemConfiguration.h>
104
105 #include <notify.h>
106
107 static int64_t getTimeDifference(time_t start);
108 CFStringRef const SOSAggdSyncCompletionKey = CFSTR("com.apple.security.sos.synccompletion");
109 CFStringRef const SOSAggdSyncTimeoutKey = CFSTR("com.apple.security.sos.timeout");
110
111 typedef SOSDataSourceFactoryRef (^SOSCCAccountDataSourceFactoryBlock)(void);
112
113 static SOSCCAccountDataSourceFactoryBlock accountDataSourceOverride = NULL;
114
115
116
117 //
118 // Forward declared
119 //
120
121 static void do_with_account(void (^action)(SOSAccountTransaction* txn));
122
123 //
124 // Constants
125 //
126
127 CFStringRef kSOSAccountLabel = CFSTR("iCloud Keychain Account Meta-data");
128
129 CFStringRef kSOSBurnedRecoveryAttemptCount = CFSTR("Burned Recovery Attempt Count");
130
131 CFStringRef kSOSBurnedRecoveryAttemptAttestationDate = CFSTR("Burned Recovery Attempt Attestation Date");
132
133 static CFDictionaryRef SOSItemCopyQueryForSyncItems(CFStringRef service, bool returnData)
134 {
135 return CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
136 kSecClass, kSecClassGenericPassword,
137 kSecAttrService, service,
138 kSecAttrAccessGroup, kSOSInternalAccessGroup,
139 kSecReturnData, returnData ? kCFBooleanTrue : kCFBooleanFalse,
140 NULL);
141 }
142
143 CFDataRef SOSItemCopy(CFStringRef service, CFErrorRef* error)
144 {
145 CFDictionaryRef query = SOSItemCopyQueryForSyncItems(service, true);
146
147 CFDataRef result = NULL;
148
149 OSStatus copyResult = SecItemCopyMatching(query, (CFTypeRef*) &result);
150
151 CFReleaseNull(query);
152
153 if (copyResult != noErr) {
154 SecError(copyResult, error, CFSTR("Error %@ reading for service '%@'"), result, service);
155 CFReleaseNull(result);
156 return NULL;
157 }
158
159 if (!isData(result)) {
160 SOSCreateErrorWithFormat(kSOSErrorProcessingFailure, NULL, error, NULL, CFSTR("SecItemCopyMatching returned non-data in '%@'"), service);
161 CFReleaseNull(result);
162 return NULL;
163 }
164
165 return result;
166 }
167
168 static CFDataRef SOSKeychainCopySavedAccountData()
169 {
170 CFErrorRef error = NULL;
171 CFDataRef accountData = SOSItemCopy(kSOSAccountLabel, &error);
172 if (!accountData) {
173 secnotice("account", "Failed to load account: %@", error);
174 secerror("Failed to load account: %@", error);
175 }
176 CFReleaseNull(error);
177
178 return accountData;
179 }
180
181 bool SOSItemUpdateOrAdd(CFStringRef service, CFStringRef accessibility, CFDataRef data, CFErrorRef *error)
182 {
183 CFDictionaryRef query = SOSItemCopyQueryForSyncItems(service, false);
184
185 CFDictionaryRef update = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
186 kSecValueData, data,
187 kSecAttrAccessible, accessibility,
188 NULL);
189 OSStatus saveStatus = SecItemUpdate(query, update);
190
191 if (errSecItemNotFound == saveStatus) {
192 CFMutableDictionaryRef add = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, query);
193 CFDictionaryForEach(update, ^(const void *key, const void *value) {
194 CFDictionaryAddValue(add, key, value);
195 });
196 saveStatus = SecItemAdd(add, NULL);
197 CFReleaseNull(add);
198 }
199
200 CFReleaseNull(query);
201 CFReleaseNull(update);
202
203 return SecError(saveStatus, error, CFSTR("Error saving %@ to service '%@'"), data, service);
204 }
205
206 static void SOSKeychainAccountEnsureSaved(CFDataRef accountAsData)
207 {
208 static CFDataRef sLastSavedAccountData = NULL;
209
210 CFErrorRef saveError = NULL;
211 require_quiet(!CFEqualSafe(sLastSavedAccountData, accountAsData), exit);
212
213 if (!SOSItemUpdateOrAdd(kSOSAccountLabel, kSecAttrAccessibleAlwaysThisDeviceOnlyPrivate, accountAsData, &saveError)) {
214 secerror("Can't save account: %@", saveError);
215 goto exit;
216 }
217
218 CFAssignRetained(sLastSavedAccountData, CFRetainSafe(accountAsData));
219
220 exit:
221 CFReleaseNull(saveError);
222 }
223
224 static SOSAccount* SOSKeychainAccountCreateSharedAccount(CFDictionaryRef our_gestalt)
225 {
226 secdebug("account", "Create account for UID %d EUID %d", getuid(), geteuid());
227
228 CFDataRef savedAccount = SOSKeychainCopySavedAccountData();
229 SOSAccount* account = NULL;
230
231 SOSDataSourceFactoryRef factory = accountDataSourceOverride ? accountDataSourceOverride()
232 : SecItemDataSourceFactoryGetDefault();
233
234 require_quiet(factory, done);
235
236 if (savedAccount) {
237 NSError* inflationError = NULL;
238
239 account = [SOSAccount accountFromData:(__bridge NSData*) savedAccount
240 factory:factory
241 error:&inflationError];
242
243 if (account){
244 [account.trust updateGestalt:account newGestalt:our_gestalt];
245 } else {
246 secnotice("account", "Got error inflating account: %@", inflationError);
247 }
248
249 }
250 CFReleaseNull(savedAccount);
251
252 if (!account) {
253 account = SOSAccountCreate(kCFAllocatorDefault, our_gestalt, factory);
254
255 if (!account)
256 secnotice("account", "Got NULL creating account");
257 }
258
259 [account startStateMachine];
260
261 done:
262 CFReleaseNull(savedAccount);
263 return account;
264 }
265
266 //
267 // Mark: Gestalt Handling
268 //
269
270 CFStringRef SOSGestaltVersion = NULL;
271 CFStringRef SOSGestaltModel = NULL;
272 CFStringRef SOSGestaltDeviceName = NULL;
273
274 void
275 SOSCCSetGestalt_Server(CFStringRef deviceName,
276 CFStringRef version,
277 CFStringRef model,
278 CFStringRef serial)
279 {
280 SOSGestaltDeviceName = CFRetainSafe(deviceName);
281 SOSGestaltVersion = CFRetainSafe(version);
282 SOSGestaltModel = CFRetainSafe(model);
283 SOSGestaltSerial = CFRetainSafe(serial);
284 }
285
286 CFStringRef SOSCCCopyOSVersion(void)
287 {
288 static dispatch_once_t once;
289 dispatch_once(&once, ^{
290 if (SOSGestaltVersion == NULL) {
291 CFDictionaryRef versions = _CFCopySystemVersionDictionary();
292 if (versions) {
293 CFTypeRef versionValue = CFDictionaryGetValue(versions, _kCFSystemVersionBuildVersionKey);
294 if (isString(versionValue))
295 SOSGestaltVersion = CFRetainSafe((CFStringRef) versionValue);
296 }
297
298 CFReleaseNull(versions);
299 if (SOSGestaltVersion == NULL) {
300 SOSGestaltVersion = CFSTR("Unknown model");
301 }
302 }
303 });
304 return CFRetainSafe(SOSGestaltVersion);
305 }
306
307
308 static CFStringRef CopyModelName(void)
309 {
310 static dispatch_once_t once;
311 dispatch_once(&once, ^{
312 if (SOSGestaltModel == NULL) {
313 #if TARGET_OS_IPHONE
314 SOSGestaltModel = MGCopyAnswer(kMGQDeviceName, NULL);
315 #else
316 SOSGestaltModel = ASI_CopyComputerModelName(FALSE);
317 #endif
318 if (SOSGestaltModel == NULL)
319 SOSGestaltModel = CFSTR("Unknown model");
320 }
321 });
322 return CFStringCreateCopy(kCFAllocatorDefault, SOSGestaltModel);
323 }
324
325 static CFStringRef CopyComputerName(SCDynamicStoreRef store)
326 {
327 if (SOSGestaltDeviceName == NULL) {
328 CFStringRef deviceName = SCDynamicStoreCopyComputerName(store, NULL);
329 if (deviceName == NULL) {
330 deviceName = CFSTR("Unknown name");
331 }
332 return deviceName;
333 }
334 return SOSGestaltDeviceName;
335 }
336
337 static bool _EngineMessageProtocolV2Enabled(void)
338 {
339 #if DEBUG
340 //sudo rhr
341 static dispatch_once_t onceToken;
342 static bool v2_enabled = false;
343 dispatch_once(&onceToken, ^{
344 CFTypeRef v2Pref = (CFNumberRef)CFPreferencesCopyValue(CFSTR("engineV2"), CFSTR("com.apple.security"), kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
345
346 if (v2Pref && CFGetTypeID(v2Pref) == CFBooleanGetTypeID()) {
347 v2_enabled = CFBooleanGetValue((CFBooleanRef)v2Pref);
348 secinfo("server", "Engine v2 : %s", v2_enabled ? "enabled":"disabled");
349 }
350 CFReleaseSafe(v2Pref);
351 });
352
353 return v2_enabled;
354 #else
355 return false;
356 #endif
357 }
358
359
360 static CFDictionaryRef CreateDeviceGestaltDictionary(SCDynamicStoreRef store, CFArrayRef keys, void *context)
361 {
362 CFStringRef modelName = CopyModelName();
363 CFStringRef computerName = CopyComputerName(store);
364 CFStringRef osVersion = SOSCCCopyOSVersion();
365
366 SInt32 version = _EngineMessageProtocolV2Enabled() ? kEngineMessageProtocolVersion : 0;
367 CFNumberRef protocolVersion = CFNumberCreate(0, kCFNumberSInt32Type, &version);
368
369 CFDictionaryRef gestalt = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
370 kPIUserDefinedDeviceNameKey, computerName,
371 kPIDeviceModelNameKey, modelName,
372 kPIMessageProtocolVersionKey, protocolVersion,
373 kPIOSVersionKey, osVersion,
374 NULL);
375 CFReleaseSafe(osVersion);
376 CFReleaseSafe(modelName);
377 CFReleaseSafe(computerName);
378 CFReleaseSafe(protocolVersion);
379
380 return gestalt;
381 }
382
383 static void SOSCCProcessGestaltUpdate(SCDynamicStoreRef store, CFArrayRef keys, void *context)
384 {
385 do_with_account(^(SOSAccountTransaction* txn) {
386 if(txn.account){
387 CFDictionaryRef gestalt = CreateDeviceGestaltDictionary(store, keys, context);
388 if ([txn.account.trust updateGestalt:txn.account newGestalt:gestalt]) {
389 secnotice("circleOps", "Changed our peer's gestalt information. This is not a circle change.");
390 }
391 CFReleaseSafe(gestalt);
392 }
393 });
394 }
395
396
397 static CFDictionaryRef CreateDeviceGestaltDictionaryAndRegisterForUpdate(dispatch_queue_t queue, void *info)
398 {
399 SCDynamicStoreContext context = { .info = info };
400 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("com.apple.securityd.cloudcircleserver"), SOSCCProcessGestaltUpdate, &context);
401 CFStringRef computerKey = SCDynamicStoreKeyCreateComputerName(NULL);
402 CFArrayRef keys = NULL;
403 CFDictionaryRef gestalt = NULL;
404
405 if (store == NULL || computerKey == NULL) {
406 goto done;
407 }
408 keys = CFArrayCreate(NULL, (const void **)&computerKey, 1, &kCFTypeArrayCallBacks);
409 if (keys == NULL) {
410 goto done;
411 }
412 gestalt = CreateDeviceGestaltDictionary(store, keys, info);
413 SCDynamicStoreSetNotificationKeys(store, keys, NULL);
414 SCDynamicStoreSetDispatchQueue(store, queue);
415
416 done:
417 if (store) CFRelease(store);
418 if (computerKey) CFRelease(computerKey);
419 if (keys) CFRelease(keys);
420 return gestalt;
421 }
422
423 os_state_block_t accountStateBlock = ^os_state_data_t(os_state_hints_t hints) {
424 os_state_data_t retval = NULL;
425 CFDataRef savedAccount = NULL;
426 if(hints->osh_api != OS_STATE_API_REQUEST) return NULL;
427
428 /* Get account DER */
429 savedAccount = SOSKeychainCopySavedAccountData();
430 require_quiet(savedAccount, errOut);
431
432 /* make a os_state_data_t object to return. */
433 size_t statelen = CFDataGetLength(savedAccount);
434 retval = (os_state_data_t)calloc(1, OS_STATE_DATA_SIZE_NEEDED(statelen));
435 require_quiet(retval, errOut);
436
437 retval->osd_type = OS_STATE_DATA_PROTOCOL_BUFFER;
438 memcpy(retval->osd_data, CFDataGetBytePtr(savedAccount), statelen);
439 retval->osd_size = statelen;
440 strlcpy(retval->osd_title, "CloudCircle Account Object", sizeof(retval->osd_title));
441
442 errOut:
443 CFReleaseNull(savedAccount);
444 return retval;
445 };
446
447 #define FOR_EXISTING_ACCOUNT 1
448 #define CREATE_ACCOUNT_IF_NONE 0
449
450 static SOSAccount* GetSharedAccount(bool onlyIfItExists) {
451 static SOSAccount* sSharedAccount = NULL;
452 static dispatch_once_t onceToken;
453
454 #if !TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
455 if(geteuid() == 0){
456 secerror("Cannot inflate account object as root");
457 return NULL;
458 }
459 #endif
460
461 if(onlyIfItExists) {
462 return sSharedAccount;
463 }
464
465 dispatch_once(&onceToken, ^{
466 secdebug("account", "Account Creation start");
467
468 CFDictionaryRef gestalt = CreateDeviceGestaltDictionaryAndRegisterForUpdate(dispatch_get_global_queue(SOS_ACCOUNT_PRIORITY, 0), NULL);
469
470 if (!gestalt) {
471 #if TARGET_OS_IPHONE && TARGET_OS_SIMULATOR
472 gestalt = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, NULL);
473 #else
474 secerror("Didn't get machine gestalt! This is going to be ugly.");
475 #endif
476 }
477
478 sSharedAccount = SOSKeychainAccountCreateSharedAccount(gestalt);
479
480 SOSAccountAddChangeBlock(sSharedAccount, ^(SOSAccount *account, SOSCircleRef circle,
481 CFSetRef peer_additions, CFSetRef peer_removals,
482 CFSetRef applicant_additions, CFSetRef applicant_removals) {
483 CFErrorRef pi_error = NULL;
484 SOSPeerInfoRef me = account.peerInfo;
485 if(!me) {
486 secinfo("circleOps", "Change block called with no peerInfo");
487 return;
488 }
489
490 if(!SOSCircleHasPeer(circle, me, NULL)) {
491 secinfo("circleOps", "Change block called while not in circle");
492 return;
493 }
494
495 // TODO: Figure out why peer_additions isn't right in some cases (like when joining a v2 circle with a v0 peer.
496 if (CFSetGetCount(peer_additions) != 0) {
497 secnotice("updates", "Requesting Ensure Peer Registration.");
498 SOSCloudKeychainRequestEnsurePeerRegistration(dispatch_get_global_queue(SOS_ACCOUNT_PRIORITY, 0), NULL);
499 } else {
500 secinfo("updates", "Not requesting Ensure Peer Registration, since it's not needed");
501 }
502
503 if (CFSetContainsValue(peer_additions, me)) {
504 // TODO: Potentially remove from here and move this to the engine
505 // TODO: We also need to do this when our views change.
506 CFMutableSetRef peers = SOSCircleCopyPeers(circle, kCFAllocatorDefault);
507 CFSetRemoveValue(peers, me);
508 if (!CFSetIsEmpty(peers)) {
509 SOSCCRequestSyncWithPeers(peers);
510 }
511 CFReleaseNull(peers);
512 }
513
514 CFReleaseNull(pi_error);
515
516 if (CFSetGetCount(peer_additions) != 0 ||
517 CFSetGetCount(peer_removals) != 0 ||
518 CFSetGetCount(applicant_additions) != 0 ||
519 CFSetGetCount(applicant_removals) != 0) {
520
521 if(CFSetGetCount(peer_removals) != 0)
522 {
523 CFErrorRef localError = NULL;
524 CFMutableArrayRef removed = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
525 CFSetForEach(peer_removals, ^(const void *value) {
526 CFArrayAppendValue(removed, value);
527 });
528 SOSAccountRemoveBackupPeers(account, removed, &localError);
529 if(localError)
530 secerror("Had trouble removing: %@, error: %@", removed, localError);
531 CFReleaseNull(localError);
532 CFReleaseNull(removed);
533 }
534 secnotice("circleOps", "peer counts changed, posting kSOSCCCircleChangedNotification");
535 account.notifyCircleChangeOnExit = true;
536 }
537 });
538
539 SOSCloudKeychainSetItemsChangedBlock(^CFArrayRef(CFDictionaryRef changes) {
540 CFRetainSafe(changes);
541 __block CFMutableArrayRef handledKeys = NULL;
542 do_with_account(^(SOSAccountTransaction* txn) {
543 CFStringRef changeDescription = SOSItemsChangedCopyDescription(changes, false);
544 secdebug(SOSCKCSCOPE, "Received: %@", changeDescription);
545 CFReleaseSafe(changeDescription);
546
547 CFErrorRef error = NULL;
548 handledKeys = SOSTransportDispatchMessages(txn, changes, &error);
549 if (!handledKeys || error) {
550 secerror("Error handling updates: %@", error);
551 }
552 CFReleaseNull(error);
553 });
554 CFReleaseSafe(changes);
555 return handledKeys;
556 });
557 CFReleaseSafe(gestalt);
558
559 sSharedAccount.saveBlock = ^(CFDataRef flattenedAccount, CFErrorRef flattenFailError) {
560 if (flattenedAccount) {
561 SOSKeychainAccountEnsureSaved(flattenedAccount);
562 } else {
563 secerror("Failed to transform account into data, error: %@", flattenFailError);
564 }
565 };
566 // TODO: We should not be doing extra work whenever securityd is launched, let's see if we can eliminate this call
567 SOSCloudKeychainRequestEnsurePeerRegistration(dispatch_get_global_queue(SOS_ACCOUNT_PRIORITY, 0), NULL);
568
569 // provide state handler to sysdiagnose and logging
570 os_state_add_handler(dispatch_get_global_queue(0, 0), accountStateBlock);
571
572 [sSharedAccount ghostBustSchedule];
573
574 });
575
576 return sSharedAccount;
577 }
578
579 CFTypeRef GetSharedAccountRef(void)
580 {
581 return (__bridge CFTypeRef)GetSharedAccount(FOR_EXISTING_ACCOUNT);
582 }
583
584 static void do_with_account(void (^action)(SOSAccountTransaction* txn)) {
585 @autoreleasepool {
586 SOSAccount* account = GetSharedAccount(CREATE_ACCOUNT_IF_NONE);
587
588 if(account){
589 [account performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
590 action(txn);
591 }];
592 }
593 }
594 }
595
596 static bool isValidUser(CFErrorRef* error) {
597 #if !TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
598 if(geteuid() == 0){
599 secerror("Cannot inflate account object as root");
600 SOSErrorCreate(kSOSErrorUnsupported, error, NULL, CFSTR("Cannot inflate account object as root"));
601 return false;
602 }
603 #endif
604
605 return true;
606 }
607
608 static bool do_if_after_first_unlock(CFErrorRef *error, dispatch_block_t action)
609 {
610 #if TARGET_OS_SIMULATOR
611 action();
612 return true;
613 #else
614 bool beenUnlocked = false;
615 require_quiet(SecAKSGetHasBeenUnlocked(&beenUnlocked, error), fail);
616
617 require_action_quiet(beenUnlocked, fail,
618 SOSCreateErrorWithFormat(kSOSErrorNotReady, NULL, error, NULL,
619 CFSTR("Keybag never unlocked, ask after first unlock")));
620
621 action();
622
623 return true;
624
625 fail:
626 return false;
627 #endif
628 }
629
630 static bool do_with_account_if_after_first_unlock(CFErrorRef *error, bool (^action)(SOSAccountTransaction* txn, CFErrorRef* error))
631 {
632 __block bool action_result = false;
633
634 return isValidUser(error) && do_if_after_first_unlock(error, ^{
635 do_with_account(^(SOSAccountTransaction* txn) {
636 action_result = action(txn, error);
637 });
638
639 }) && action_result;
640 }
641
642 static bool isAssertionLockAcquireError(CFErrorRef error) {
643 return (CFErrorGetCode(error) == kIOReturnNotPermitted) && (CFEqualSafe(CFErrorGetDomain(error), kSecKernDomain));
644 }
645
646 static bool do_with_account_while_unlocked(CFErrorRef *error, bool (^action)(SOSAccountTransaction* txn, CFErrorRef* error))
647 {
648 bool result = false;
649
650 CFErrorRef statusError = NULL;
651
652 __block bool action_result = false;
653 __block bool attempted_action = false;
654 __block CFErrorRef localError = NULL;
655
656
657 if(!isValidUser(error)){
658 if (error && !*error && localError) {
659 CFTransferRetained(*error, localError);
660 }
661 CFReleaseNull(localError);
662 CFReleaseNull(statusError);
663
664 return result;
665 }
666
667 result = SecAKSDoWithUserBagLockAssertion(&localError, ^{
668 // SOSAccountGhostBustingOptions need to be retrieved from RAMP while not holding the account queue
669 // yet we only want to request RAMP info if it's "time" to ghostbust.
670
671 #if GHOSTBUST_PERIODIC && (TARGET_OS_IOS || TARGET_OS_OSX)
672 __block bool ghostbustnow = false;
673 __block SOSAccountGhostBustingOptions gbOptions = 0;
674
675 // Avoid mutual deadlock for just checking date.
676 // Check to see if we're InCircle using the client API - will read cached value if available; otherwise it'll do the round trip and lock appropriately
677 SOSCCStatus circleStatus = SOSCCThisDeviceIsInCircle(NULL);
678 if(circleStatus == kSOSCCInCircle) {
679 // Only need the account object to check settings
680 SOSAccount *tmpAccount = GetSharedAccount(FOR_EXISTING_ACCOUNT);
681 if(tmpAccount.settings) {
682 ghostbustnow = [tmpAccount ghostBustCheckDate];
683 }
684
685 // Get ramp settings from the Cloud
686 if(ghostbustnow) {
687 gbOptions = [SOSAccount ghostBustGetRampSettings];
688 gbOptions += SOSGhostBustiCloudIdentities;
689 }
690 }
691 #endif
692
693 do_with_account(^(SOSAccountTransaction* txn) {
694 SOSAccount *account = txn.account;
695 if ([account isInCircle:(NULL)] && [SOSAuthKitHelpers accountIsHSA2]) {
696 if(![SOSAuthKitHelpers peerinfoHasMID: account]) {
697 // This is the first good opportunity to update our FullPeerInfo and
698 // push the resulting circle.
699 [SOSAuthKitHelpers updateMIDInPeerInfo: account];
700 }
701 }
702 #if GHOSTBUST_PERIODIC && (TARGET_OS_IOS || TARGET_OS_OSX)
703 if(ghostbustnow) {
704 [account ghostBustPeriodic:gbOptions complete:^(bool ghostBusted, NSError *error) {
705 secnotice("ghostbust", "GhostBusting: %@", ghostBusted ? CFSTR("true"): CFSTR("false"));
706 }];
707 }
708 #endif
709 attempted_action = true;
710 action_result = action(txn, error);
711 });
712 });
713
714
715
716 // For <rdar://problem/24355048> 13E196: Circle join fails after successful recovery with a mach error if performed while device is locked
717 // 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.
718 // we assume our caller will hold the lock assertion for us to finsh our job.
719 // to be extra paranoid we track if we tried the caller's block. If we did we don't do it again.
720
721 if(result || !isAssertionLockAcquireError(localError)){
722 if (error && !*error && localError) {
723 CFTransferRetained(*error, localError);
724 }
725 CFReleaseNull(localError);
726 CFReleaseNull(statusError);
727
728 return (result && action_result);
729 }
730 if(attempted_action){
731 if (error && !*error && localError) {
732 CFTransferRetained(*error, localError);
733 }
734 CFReleaseNull(localError);
735 CFReleaseNull(statusError);
736
737 return (result && action_result);
738 }
739
740 bool isUnlocked = false;
741 (void) SecAKSGetIsUnlocked(&isUnlocked, &statusError);
742 if(!isUnlocked){
743 secnotice("while-unlocked-hack", "Not trying action, aks bag locked (%@)", statusError);
744 if (error && !*error && localError) {
745 CFTransferRetained(*error, localError);
746 }
747 CFReleaseNull(localError);
748 CFReleaseNull(statusError);
749
750 return result && action_result;
751 }
752
753 CFReleaseNull(localError);
754
755 secnotice("while-unlocked-hack", "Trying action while unlocked without assertion");
756
757 result = true;
758 do_with_account(^(SOSAccountTransaction* txn) {
759 action_result = action(txn, &localError);
760 });
761
762 secnotice("while-unlocked-hack", "Action %s (%@)", action_result ? "succeeded" : "failed", localError);
763
764 if (error && !*error && localError) {
765 CFTransferRetained(*error, localError);
766 }
767 CFReleaseNull(localError);
768 CFReleaseNull(statusError);
769
770 return result && action_result;
771 }
772
773
774
775 CFTypeRef SOSKeychainAccountGetSharedAccount()
776 {
777 __block SOSAccount* result = NULL;
778 result = GetSharedAccount(FOR_EXISTING_ACCOUNT);
779
780 if(!result) {
781 secnotice("secAccount", "Failed request for account object");
782 }
783 return (__bridge CFTypeRef)result;
784 }
785
786 //
787 // Mark: Credential processing
788 //
789
790
791 SOSViewResultCode SOSCCView_Server(CFStringRef viewname, SOSViewActionCode action, CFErrorRef *error) {
792 __block SOSViewResultCode status = kSOSCCGeneralViewError;
793
794 do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
795 bool retval = false;
796
797 switch(action) {
798 case kSOSCCViewQuery:
799 status = [txn.account.trust viewStatus:txn.account name:viewname err:error];
800 retval = true;
801 break;
802 case kSOSCCViewEnable:
803 status = [txn.account.trust updateView:txn.account name:viewname code:action err:error];
804 retval = true;
805 break;
806
807 case kSOSCCViewDisable:
808 status = [txn.account.trust updateView:txn.account name:viewname code:action err:error];
809 retval = true;
810 break;
811 default:
812 secnotice("views", "Bad SOSViewActionCode - %d", (int) action);
813 retval = false;
814 break;
815 }
816 return retval;
817 });
818 return status;
819 }
820
821 bool SOSCCViewSet_Server(CFSetRef enabledViews, CFSetRef disabledViews) {
822 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCViewSet);
823 __block bool status = false;
824
825 do_with_account_if_after_first_unlock(NULL, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
826 status = [txn.account.trust updateViewSets:txn.account enabled:enabledViews disabled:disabledViews];
827 return true;
828 });
829 OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCViewSet, OctagonSignpostNumber1(SOSSignpostNameSOSCCViewSet), (int)status);
830 return status;}
831
832
833 void sync_the_last_data_to_kvs(CFTypeRef account, bool waitForeverForSynchronization){
834 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSyncTheLastDataToKVS);
835 __block CFErrorRef localError = NULL;
836
837 dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
838
839 secnoticeq("force-push", "calling SOSCloudKeychainSynchronizeAndWait");
840
841 SOSCloudKeychainSynchronizeAndWait(dispatch_get_global_queue(SOS_TRANSPORT_PRIORITY, 0), ^(CFDictionaryRef returnedValues, CFErrorRef sync_error) {
842 if (sync_error) {
843 secerrorq("SOSCloudKeychainSynchronizeAndWait: %@", sync_error);
844 localError = sync_error;
845 } else {
846 secnoticeq("force-push", "returned from call; in callback to SOSCloudKeychainSynchronizeAndWait: results: %@", returnedValues);
847 }
848
849 dispatch_semaphore_signal(wait_for);
850 });
851
852 if(waitForeverForSynchronization)
853 dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER);
854 else
855 dispatch_semaphore_wait(wait_for, dispatch_time(DISPATCH_TIME_NOW, 60ull * NSEC_PER_SEC));
856
857 wait_for = nil;
858 bool subTaskSuccess = (localError == NULL) ? true : false;
859 OctagonSignpostEnd(signPost, SOSSignpostNameSyncTheLastDataToKVS, OctagonSignpostNumber1(SOSSignpostNameSyncTheLastDataToKVS), (int)subTaskSuccess);
860 }
861
862 #define kWAIT2MINID "EFRESH"
863
864 static bool SyncKVSAndWait(CFErrorRef *error) {
865 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSyncKVSAndWait);
866
867 dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
868
869 __block bool success = false;
870
871 secnoticeq("fresh", "EFP calling SOSCloudKeychainSynchronizeAndWait");
872
873 os_activity_initiate("CloudCircle EFRESH", OS_ACTIVITY_FLAG_DEFAULT, ^(void) {
874 SOSCloudKeychainSynchronizeAndWait(dispatch_get_global_queue(SOS_TRANSPORT_PRIORITY, 0), ^(__unused CFDictionaryRef returnedValues, CFErrorRef sync_error) {
875 secnotice("fresh", "EFP returned, callback error: %@", sync_error);
876
877 success = (sync_error == NULL);
878 if (error) {
879 CFRetainAssign(*error, sync_error);
880 }
881
882 dispatch_semaphore_signal(wait_for);
883 });
884
885
886 dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER);
887 secnotice("fresh", "EFP complete: %s %@", success ? "success" : "failure", error ? *error : NULL);
888 });
889 OctagonSignpostEnd(signPost, SOSSignpostNameSyncKVSAndWait, OctagonSignpostNumber1(SOSSignpostNameSyncKVSAndWait), (int)success);
890
891 return success;
892 }
893
894 static bool Flush(CFErrorRef *error) {
895 __block bool success = false;
896 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameFlush);
897
898 dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
899 secnotice("flush", "Starting");
900
901 SOSCloudKeychainFlush(dispatch_get_global_queue(SOS_TRANSPORT_PRIORITY, 0), ^(CFDictionaryRef returnedValues, CFErrorRef sync_error) {
902 success = (sync_error == NULL);
903 if (error) {
904 CFRetainAssign(*error, sync_error);
905 }
906
907 dispatch_semaphore_signal(wait_for);
908 });
909
910 dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER);
911
912 secnotice("flush", "Returned %s", success? "Success": "Failure");
913
914 OctagonSignpostEnd(signPost, SOSSignpostNameFlush, OctagonSignpostNumber1(SOSSignpostNameFlush), (int)success);
915
916 return success;
917 }
918
919 bool SOSCCTryUserCredentials_Server(CFStringRef user_label, CFDataRef user_password, CFStringRef dsid, CFErrorRef *error) {
920 __block bool result = false;
921 secnotice("updates", "Trying credentials and dsid (%@) for %@", dsid, user_label);
922
923 dispatch_sync(SOSCCCredentialQueue(), ^{
924 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCTryUserCredentials);
925
926 // Try the password with no EFRESH - attempting to get through this faster for rdar://problem/57242044
927 result = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
928 bool retval = false;
929 if (dsid != NULL && CFStringCompare(dsid, CFSTR(""), 0) != 0) {
930 SOSAccountAssertDSID(txn.account, dsid);
931 }
932 if(txn.account.accountKeyDerivationParameters) {
933 retval = SOSAccountTryUserCredentials(txn.account, user_label, user_password, block_error);
934 }
935 return retval;
936 });
937
938 // If that fails - either lacking parameters to begin with or failed to construct the correct key try with EFRESH
939 if(result == false) {
940 if(SyncKVSAndWait(error) && Flush(error)) {
941 result = do_with_account_while_unlocked(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *block_error) {
942 return SOSAccountTryUserCredentials(txn.account, user_label, user_password, block_error);
943 });
944 }
945 }
946
947 // if either key constructions passed do a flush to bring through anything we weren't "interested" in before.
948 if(result) {
949 Flush(error);
950 }
951 OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCTryUserCredentials, OctagonSignpostNumber1(SOSSignpostNameSOSCCTryUserCredentials), (int)result);
952 });
953 return result;
954 }
955
956 static bool SOSCCAssertUserCredentialsAndOptionalDSID(CFStringRef user_label, CFDataRef user_password, CFStringRef dsid, CFErrorRef *error) {
957 __block bool result = false;
958 secnotice("updates", "Setting credentials and dsid (%@) for %@", dsid, user_label);
959
960 dispatch_sync(SOSCCCredentialQueue(), ^{
961 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameAssertUserCredentialsAndOptionalDSID);
962
963 // Shortcut if we're talking to the same account and can construct the same trusted key
964 result = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
965 bool retval = false;
966 CFStringRef accountDSID = SOSAccountGetValue(txn.account, kSOSDSIDKey, NULL);
967 if(CFEqualSafe(accountDSID, dsid) && txn.account.accountKeyDerivationParameters && txn.account.accountKeyIsTrusted) {
968 retval = SOSAccountTryUserCredentials(txn.account, user_label, user_password, block_error);
969 }
970 return retval;
971 });
972 if(result) {
973 return; // shortcut to not do the following work if we're duping a setcreds operation.
974 }
975
976 result = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
977 if (dsid != NULL && CFStringCompare(dsid, CFSTR(""), 0) != 0) {
978 SOSAccountAssertDSID(txn.account, dsid);
979 }
980 return true;
981 });
982
983 if(result && SyncKVSAndWait(error) && Flush(error)) {
984 result = do_with_account_while_unlocked(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *block_error) {
985 return SOSAccountAssertUserCredentials(txn.account, user_label, user_password, block_error);
986 });
987 }
988
989 // if either key constructions passed do a flush to bring through anything we weren't "interested" in before.
990 if(result) {
991 Flush(error);
992 }
993
994 result = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
995 return SOSAccountGenerationSignatureUpdate(txn.account, error);
996 });
997
998 secnotice("updates", "Complete credentials and dsid (%@) for %@: %d %@",
999 dsid, user_label, result, error ? *error : NULL);
1000
1001 OctagonSignpostEnd(signPost, SOSSignpostNameAssertUserCredentialsAndOptionalDSID, OctagonSignpostNumber1(SOSSignpostNameAssertUserCredentialsAndOptionalDSID), (int)result);
1002 });
1003
1004 return result;
1005 }
1006
1007 bool SOSCCSetUserCredentialsAndDSID_Server(CFStringRef user_label, CFDataRef user_password, CFStringRef dsid, CFErrorRef *error)
1008 {
1009 // TODO: Return error if DSID is NULL to insist our callers provide one?
1010 return SOSCCAssertUserCredentialsAndOptionalDSID(user_label, user_password, dsid, error);
1011 }
1012
1013 bool SOSCCSetUserCredentials_Server(CFStringRef user_label, CFDataRef user_password, CFErrorRef *error)
1014 {
1015 return SOSCCAssertUserCredentialsAndOptionalDSID(user_label, user_password, NULL, error);
1016 }
1017
1018 bool SOSCCCanAuthenticate_Server(CFErrorRef *error)
1019 {
1020 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCCanAuthenticate);
1021
1022 bool result = do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1023 // If we reply that yes we can authenticate, then let's make sure we can authenticate for a while yet.
1024 // <rdar://problem/32732066>
1025 SOSAccountRestartPrivateCredentialTimer(txn.account);
1026 return SOSAccountGetPrivateCredential(txn.account, block_error) != NULL;
1027 });
1028
1029 if (!result && error && *error && CFErrorGetDomain(*error) == kSOSErrorDomain) {
1030 CFIndex code = CFErrorGetCode(*error);
1031 if (code == kSOSErrorPrivateKeyAbsent || code == kSOSErrorPublicKeyAbsent) {
1032 CFReleaseNull(*error);
1033 }
1034 }
1035
1036 OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCCanAuthenticate, OctagonSignpostNumber1(SOSSignpostNameSOSCCCanAuthenticate), (int)result);
1037
1038 return result;
1039 }
1040
1041 bool SOSCCPurgeUserCredentials_Server(CFErrorRef *error)
1042 {
1043 return do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1044 SOSAccountPurgePrivateCredential(txn.account);
1045 return true;
1046 });
1047 }
1048
1049 SOSCCStatus SOSCCThisDeviceIsInCircle_Server(CFErrorRef *error)
1050 {
1051 __block SOSCCStatus status;
1052
1053 return do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1054 status = [txn.account getCircleStatus:block_error];
1055
1056 return true;
1057 }) ? status : kSOSCCError;
1058 }
1059
1060 bool SOSCCRequestToJoinCircle_Server(CFErrorRef* error)
1061 {
1062 __block bool result = true;
1063 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCRequestToJoinCircle);
1064
1065 bool requested = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1066 result = SOSAccountJoinCircles(txn, block_error);
1067 return result;
1068 });
1069 OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCRequestToJoinCircle, OctagonSignpostNumber1(SOSSignpostNameSOSCCRequestToJoinCircle), (int)requested);
1070 return requested;
1071 }
1072
1073 bool SOSCCAccountHasPublicKey_Server(CFErrorRef *error)
1074 {
1075 __block bool result = true;
1076 __block CFErrorRef localError = NULL;
1077
1078 bool hasPublicKey = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1079 result = SOSAccountHasPublicKey(txn.account, &localError);
1080 return result;
1081 });
1082
1083 if(error != NULL && localError != NULL)
1084 *error = localError;
1085
1086 return hasPublicKey;
1087 }
1088
1089 bool SOSCCRequestToJoinCircleAfterRestore_Server(CFErrorRef* error)
1090 {
1091 __block bool result = true;
1092 bool returned = false;
1093 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCRequestToJoinCircleAfterRestore);
1094 returned = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1095 SOSAccountEnsurePeerRegistration(txn.account, block_error);
1096 if(block_error && *block_error){
1097 NSError* blockError = (__bridge NSError*)*block_error;
1098 if (blockError) {
1099 secerror("ensure peer registration error: %@", blockError);
1100 }
1101 }
1102 result = SOSAccountJoinCirclesAfterRestore(txn, block_error);
1103 return result;
1104 });
1105 OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCRequestToJoinCircleAfterRestore, OctagonSignpostNumber1(SOSSignpostNameSOSCCRequestToJoinCircleAfterRestore), (int)result);
1106 return returned;
1107 }
1108
1109 bool SOSCCAccountSetToNew_Server(CFErrorRef *error)
1110 {
1111 return do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1112 SOSAccountSetToNew(txn.account);
1113 return true;
1114 });
1115 }
1116
1117 bool SOSCCResetToOffering_Server(CFErrorRef* error)
1118 {
1119 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCResetToOffering);
1120
1121 bool resetResult = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1122 bool result = false;
1123
1124 SecKeyRef user_key = SOSAccountGetPrivateCredential(txn.account, error);
1125 if (!user_key) {
1126 return result;
1127 }
1128 result = [txn.account.trust resetToOffering:txn key:user_key err:block_error];
1129 return result;
1130 });
1131 OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCResetToOffering, OctagonSignpostNumber1(SOSSignpostNameSOSCCResetToOffering), (int)resetResult);
1132 return resetResult;
1133 }
1134
1135 bool SOSCCResetToEmpty_Server(CFErrorRef* error)
1136 {
1137 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCResetToEmpty);
1138
1139 bool resetResult = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1140 bool result = false;
1141
1142 if (!SOSAccountHasPublicKey(txn.account, error)) {
1143 return result;
1144 }
1145 result = [txn.account.trust resetAccountToEmpty:txn.account transport:txn.account.circle_transport err:block_error];
1146 return result;
1147 });
1148 OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCResetToEmpty, OctagonSignpostNumber1(SOSSignpostNameSOSCCResetToEmpty), (int)resetResult);
1149 return resetResult;
1150 }
1151
1152 bool SOSCCRemoveThisDeviceFromCircle_Server(CFErrorRef* error)
1153 {
1154 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCRemoveThisDeviceFromCircle);
1155
1156 bool removeResult = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1157 bool result = [txn.account.trust leaveCircle:txn.account err:block_error];
1158 return result;
1159 });
1160 OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCRemoveThisDeviceFromCircle, OctagonSignpostNumber1(SOSSignpostNameSOSCCRemoveThisDeviceFromCircle), (int)removeResult);
1161 return removeResult;
1162 }
1163
1164 bool SOSCCRemovePeersFromCircle_Server(CFArrayRef peers, CFErrorRef* error)
1165 {
1166 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCRemovePeersFromCircle);
1167
1168 bool removeResult = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1169 bool result = SOSAccountRemovePeersFromCircle(txn.account, peers, block_error);
1170 return result;
1171 });
1172 OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCRemovePeersFromCircle, OctagonSignpostNumber1(SOSSignpostNameSOSCCRemovePeersFromCircle), (int)removeResult);
1173 return removeResult;
1174 }
1175
1176 void SOSCCNotifyLoggedIntoAccount_Server() {
1177 // This call is mixed in with SOSCCSetUserCredentialsAndDSID calls from our accountsd plugin
1178 dispatch_async(SOSCCCredentialQueue(), ^{
1179 CFErrorRef error = NULL;
1180 bool loggedInResult = do_with_account_while_unlocked(&error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1181 secinfo("circleOps", "Signed into account!");
1182 txn.account.accountIsChanging = false; // we've changed
1183 return true;
1184 });
1185
1186 if(!loggedInResult || error != NULL) {
1187 secerror("circleOps: error delivering account-sign-in notification: %@", error);
1188 }
1189
1190 CFReleaseNull(error);
1191 });
1192 }
1193
1194 bool SOSCCLoggedOutOfAccount_Server(CFErrorRef *error)
1195 {
1196 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCLoggedOutOfAccount);
1197
1198 bool loggedOutResult = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1199 secnotice("circleOps", "Signed out of account!");
1200
1201
1202 bool waitForeverForSynchronization = true;
1203
1204 bool result = [txn.account.trust leaveCircle:txn.account err:block_error];
1205
1206 [txn restart]; // Make sure this gets finished before we set to new.
1207
1208 sync_the_last_data_to_kvs((__bridge CFTypeRef)(txn.account), waitForeverForSynchronization);
1209
1210 SOSAccountSetToNew(txn.account);
1211 txn.account.accountIsChanging = true;
1212
1213 return result;
1214 });
1215 OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCLoggedOutOfAccount, OctagonSignpostNumber1(SOSSignpostNameSOSCCLoggedOutOfAccount), (int)loggedOutResult);
1216 return loggedOutResult;
1217 }
1218
1219 bool SOSCCBailFromCircle_Server(uint64_t limit_in_seconds, CFErrorRef* error)
1220 {
1221 return do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1222 bool waitForeverForSynchronization = false;
1223
1224 bool result = SOSAccountBail(txn.account, limit_in_seconds, block_error);
1225
1226 [txn restart]; // Make sure this gets finished before we set to new.
1227
1228 sync_the_last_data_to_kvs((__bridge CFTypeRef)(txn.account), waitForeverForSynchronization);
1229
1230 return result;
1231 });
1232
1233 }
1234
1235 CFArrayRef SOSCCCopyApplicantPeerInfo_Server(CFErrorRef* error)
1236 {
1237 __block CFArrayRef result = NULL;
1238
1239 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCCopyApplicantPeerInfo);
1240
1241 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1242 result = SOSAccountCopyApplicants(txn.account, block_error);
1243 return result != NULL;
1244 });
1245
1246 OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCCopyApplicantPeerInfo, OctagonSignpostNumber1(SOSSignpostNameSOSCCCopyApplicantPeerInfo), (int)(result != NULL));
1247
1248 return result;
1249 }
1250
1251 CFArrayRef SOSCCCopyGenerationPeerInfo_Server(CFErrorRef* error)
1252 {
1253 __block CFArrayRef result = NULL;
1254
1255 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1256 result = SOSAccountCopyGeneration(txn.account, block_error);
1257 return result != NULL;
1258 });
1259
1260 return result;
1261 }
1262
1263 CFArrayRef SOSCCCopyValidPeerPeerInfo_Server(CFErrorRef* error)
1264 {
1265 __block CFArrayRef result = NULL;
1266 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCCopyValidPeerPeerInfo);
1267
1268 @autoreleasepool {
1269
1270 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1271 @autoreleasepool {
1272 result = SOSAccountCopyValidPeers(txn.account, block_error);
1273 }
1274 return result != NULL;
1275 });
1276 }
1277 OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCCopyValidPeerPeerInfo, OctagonSignpostNumber1(SOSSignpostNameSOSCCCopyValidPeerPeerInfo), (int)(result != NULL));
1278
1279 return result;
1280 }
1281
1282 bool SOSCCValidateUserPublic_Server(CFErrorRef* error)
1283 {
1284 __block bool result = NULL;
1285 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCValidateUserPublic);
1286
1287 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1288 result = SOSValidateUserPublic(txn.account, block_error);
1289 return result;
1290 });
1291 OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCValidateUserPublic, OctagonSignpostNumber1(SOSSignpostNameSOSCCValidateUserPublic), (int)result);
1292
1293 return result;
1294 }
1295
1296 CFArrayRef SOSCCCopyNotValidPeerPeerInfo_Server(CFErrorRef* error)
1297 {
1298 __block CFArrayRef result = NULL;
1299
1300 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1301 result = SOSAccountCopyNotValidPeers(txn.account, block_error);
1302 return result != NULL;
1303 });
1304
1305 return result;
1306 }
1307
1308 CFArrayRef SOSCCCopyRetirementPeerInfo_Server(CFErrorRef* error)
1309 {
1310 __block CFArrayRef result = NULL;
1311
1312 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1313 result = SOSAccountCopyRetired(txn.account, block_error);
1314 return result != NULL;
1315 });
1316
1317 return result;
1318 }
1319
1320 CFArrayRef SOSCCCopyViewUnawarePeerInfo_Server(CFErrorRef* error)
1321 {
1322 __block CFArrayRef result = NULL;
1323 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCCopyViewUnawarePeerInfo);
1324
1325 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1326 result = SOSAccountCopyViewUnaware(txn.account, block_error);
1327 return result != NULL;
1328 });
1329 OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCCopyViewUnawarePeerInfo, OctagonSignpostNumber1(SOSSignpostNameSOSCCCopyViewUnawarePeerInfo), (int)(result != NULL));
1330
1331 return result;
1332 }
1333
1334 CFArrayRef SOSCCCopyEngineState_Server(CFErrorRef* error)
1335 {
1336 CFArrayRef result = NULL;
1337 SOSDataSourceFactoryRef dsf = SecItemDataSourceFactoryGetDefault();
1338 SOSDataSourceRef ds = SOSDataSourceFactoryCreateDataSource(dsf, kSecAttrAccessibleWhenUnlocked, error);
1339 if (ds) {
1340 SOSEngineRef engine = SOSDataSourceGetSharedEngine(ds, error);
1341 result = SOSEngineCopyPeerConfirmedDigests(engine, error);
1342 SOSDataSourceRelease(ds, error);
1343 }
1344
1345 return result;
1346 }
1347
1348 static int64_t getTimeDifference(time_t start)
1349 {
1350 time_t stop;
1351 int64_t duration;
1352
1353 stop = time(NULL);
1354
1355 duration = stop - start;
1356
1357 return SecBucket1Significant(duration);
1358 }
1359
1360 static uint64_t initialSyncTimeoutFromDefaultsWrite(void)
1361 {
1362 uint64_t timeout = 10;
1363
1364 //sudo defaults write /Library/Preferences/com.apple.authd enforceEntitlement -bool true
1365 CFTypeRef initialSyncTimeout = (CFNumberRef)CFPreferencesCopyValue(CFSTR("InitialSync.WaitPeriod"), CFSTR("com.apple.security"), kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
1366
1367 if (isNumber(initialSyncTimeout)) {
1368 CFNumberGetValue((CFNumberRef)initialSyncTimeout, kCFNumberSInt64Type, &timeout);
1369 }
1370 CFReleaseNull(initialSyncTimeout);
1371 return timeout;
1372 }
1373
1374 bool SOSCCWaitForInitialSync_Server(CFErrorRef* error) {
1375
1376 __block dispatch_semaphore_t inSyncSema = NULL;
1377 __block bool result = false;
1378 __block bool synced = false;
1379 bool timed_out = false;
1380 __block CFStringRef inSyncCallID = NULL;
1381 __block time_t start;
1382 __block CFBooleanRef shouldUseInitialSyncV0 = false;
1383 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCWaitForInitialSync);
1384
1385 secnotice("initial sync", "Wait for initial sync start!");
1386
1387 result = do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1388 shouldUseInitialSyncV0 = (CFBooleanRef)SOSAccountGetValue(txn.account, kSOSInitialSyncTimeoutV0, error);
1389 bool alreadyInSync = (SOSAccountHasCompletedInitialSync(txn.account));
1390
1391 if (!alreadyInSync) {
1392 start = time(NULL);
1393 inSyncSema = dispatch_semaphore_create(0);
1394
1395 inSyncCallID = SOSAccountCallWhenInSync(txn.account, ^bool(SOSAccount* mightBeSynced) {
1396 synced = true;
1397
1398 if(inSyncSema){
1399 dispatch_semaphore_signal(inSyncSema);
1400
1401 }
1402 return true;
1403 });
1404 }
1405 else{
1406 synced = true;
1407 }
1408 return true;
1409 });
1410
1411 require_quiet(result, fail);
1412
1413 if(inSyncSema){
1414 if(shouldUseInitialSyncV0){
1415 secnotice("piggy","setting initial sync timeout to 5 minutes");
1416 timed_out = dispatch_semaphore_wait(inSyncSema, dispatch_time(DISPATCH_TIME_NOW, 300ull * NSEC_PER_SEC));
1417 }
1418 else{
1419 uint64_t timeoutFromDefaultsWrite = initialSyncTimeoutFromDefaultsWrite();
1420 secnotice("piggy","setting initial sync timeout to %llu seconds", timeoutFromDefaultsWrite);
1421 timed_out = dispatch_semaphore_wait(inSyncSema, dispatch_time(DISPATCH_TIME_NOW, timeoutFromDefaultsWrite * NSEC_PER_SEC));
1422 }
1423 }
1424 if (timed_out && shouldUseInitialSyncV0) {
1425 do_with_account(^(SOSAccountTransaction* txn) {
1426 if (SOSAccountUnregisterCallWhenInSync(txn.account, inSyncCallID)) {
1427 if(inSyncSema){
1428 inSyncSema = NULL; // We've canceled the timeout so we must be the last.
1429 }
1430 }
1431 });
1432 }
1433
1434 if(result) {
1435 [SecCoreAnalytics sendEvent:(__bridge id)SOSAggdSyncCompletionKey
1436 event:@{SecCoreAnalyticsValue: [NSNumber numberWithLong:getTimeDifference(start)]}];
1437 } else {
1438 [SecCoreAnalytics sendEvent:(__bridge id)SOSAggdSyncTimeoutKey
1439 event:@{SecCoreAnalyticsValue: @1}];
1440 }
1441 secnotice("initial sync", "Finished!: %d", result);
1442
1443 fail:
1444 CFReleaseNull(inSyncCallID);
1445 OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCWaitForInitialSync, OctagonSignpostNumber1(SOSSignpostNameSOSCCWaitForInitialSync), (int)result);
1446
1447 return result;
1448 }
1449
1450 bool SOSCCAcceptApplicants_Server(CFArrayRef applicants, CFErrorRef* error)
1451 {
1452 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCAcceptApplicants);
1453
1454 bool acceptResult = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1455 bool result = SOSAccountAcceptApplicants(txn.account, applicants, block_error);
1456 return result;
1457 });
1458 OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCAcceptApplicants, OctagonSignpostNumber1(SOSSignpostNameSOSCCAcceptApplicants), (int)acceptResult);
1459 return acceptResult;
1460 }
1461
1462 bool SOSCCRejectApplicants_Server(CFArrayRef applicants, CFErrorRef* error)
1463 {
1464 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCAcceptApplicants);
1465
1466 bool rejectResult = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1467 bool result = SOSAccountRejectApplicants(txn.account, applicants, block_error);
1468 return result;
1469 });
1470 OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCAcceptApplicants, OctagonSignpostNumber1(SOSSignpostNameSOSCCAcceptApplicants), (int)rejectResult);
1471 return rejectResult;
1472 }
1473
1474 CFArrayRef SOSCCCopyPeerPeerInfo_Server(CFErrorRef* error)
1475 {
1476 __block CFArrayRef result = NULL;
1477
1478 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1479 result = SOSAccountCopyPeers(txn.account, block_error);
1480 return result != NULL;
1481 });
1482
1483 return result;
1484 }
1485
1486 CFArrayRef SOSCCCopyConcurringPeerPeerInfo_Server(CFErrorRef* error)
1487 {
1488 __block CFArrayRef result = NULL;
1489 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCCopyConcurringPeerPeerInfo);
1490
1491 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1492 result = SOSAccountCopyConcurringPeers(txn.account, block_error);
1493 return result != NULL;
1494 });
1495 OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCCopyConcurringPeerPeerInfo, OctagonSignpostNumber1(SOSSignpostNameSOSCCCopyConcurringPeerPeerInfo), (int)(result != NULL));
1496
1497 return result;
1498 }
1499
1500 SOSPeerInfoRef SOSCCCopyMyPeerInfo_Server(CFErrorRef* error)
1501 {
1502 __block SOSPeerInfoRef result = NULL;
1503 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCCopyMyPeerInfo);
1504
1505 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1506 // Create a copy to be DERed/sent back to client
1507 result = SOSPeerInfoCreateCopy(kCFAllocatorDefault, txn.account.peerInfo, block_error);
1508 return result != NULL;
1509 });
1510 OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCCopyMyPeerInfo, OctagonSignpostNumber1(SOSSignpostNameSOSCCCopyMyPeerInfo), (int)(result != NULL));
1511
1512 return result;
1513 }
1514
1515 SOSPeerInfoRef SOSCCSetNewPublicBackupKey_Server(CFDataRef newPublicBackup, CFErrorRef *error){
1516 __block SOSPeerInfoRef result = NULL;
1517 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCSetNewPublicBackupKey);
1518
1519 secnotice("devRecovery", "SOSCCSetNewPublicBackupKey_Server acquiring account lock");
1520 (void) do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1521 secnotice("devRecovery", "SOSCCSetNewPublicBackupKey_Server acquired account lock");
1522 if(SOSAccountSetBackupPublicKey(txn,newPublicBackup, error)){
1523 secnotice("devRecovery", "SOSCCSetNewPublicBackupKey_Server, new public backup is set in account");
1524 [txn restart]; // Finish the transaction to update any changes to the peer info.
1525
1526 // Create a copy to be DERed/sent back to client
1527 result = SOSPeerInfoCreateCopy(kCFAllocatorDefault, txn.account.peerInfo, block_error);
1528 secnotice("devRecovery", "SOSCCSetNewPublicBackupKey_Server, new public backup is set and pushed");
1529 }
1530 else
1531 {
1532 secnotice("devRecovery", "SOSCCSetNewPublicBackupKey_Server, could not set new public backup");
1533 }
1534 return result != NULL;
1535 });
1536
1537 OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCSetNewPublicBackupKey, OctagonSignpostNumber1(SOSSignpostNameSOSCCSetNewPublicBackupKey), (int)(result != NULL));
1538 return result;
1539 }
1540
1541 bool SOSCCRegisterSingleRecoverySecret_Server(CFDataRef aks_bag, bool setupV0Only, CFErrorRef *error){
1542 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCRegisterSingleRecoverySecret);
1543
1544 bool registerResult = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1545 bool result = SOSAccountSetBSKBagForAllSlices(txn.account, aks_bag, setupV0Only, error);
1546 return result;
1547 });
1548 OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCRegisterSingleRecoverySecret, OctagonSignpostNumber1(SOSSignpostNameSOSCCRegisterSingleRecoverySecret), (int)registerResult);
1549 return registerResult;
1550 }
1551
1552 enum DepartureReason SOSCCGetLastDepartureReason_Server(CFErrorRef* error)
1553 {
1554 __block enum DepartureReason result = kSOSDepartureReasonError;
1555
1556 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1557 result = SOSAccountGetLastDepartureReason(txn.account, block_error);
1558 return result != kSOSDepartureReasonError;
1559 });
1560
1561 return result;
1562 }
1563
1564 bool SOSCCSetLastDepartureReason_Server(enum DepartureReason reason, CFErrorRef *error){
1565 return do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1566 SOSAccountSetLastDepartureReason(txn.account, reason);
1567 return true;
1568 });
1569 }
1570
1571 bool SOSCCProcessEnsurePeerRegistration_Server(CFErrorRef* error)
1572 {
1573 secnotice("updates", "Request for registering peers");
1574 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCProcessEnsurePeerRegistration);
1575
1576 bool processResult = do_with_account_while_unlocked(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
1577 bool result = SOSAccountEnsurePeerRegistration(txn.account, error);
1578 return result;
1579 });
1580 OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCProcessEnsurePeerRegistration, OctagonSignpostNumber1(SOSSignpostNameSOSCCProcessEnsurePeerRegistration), (int)processResult);
1581 return processResult;
1582 }
1583
1584 static bool internalSyncWithPeers(CFSetRef peers, CFSetRef backupPeers, CFMutableSetRef handled, CFErrorRef *error) {
1585 return do_with_account_while_unlocked(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
1586 CFSetRef addedResult = SOSAccountProcessSyncWithPeers(txn, peers, backupPeers, error);
1587 CFSetUnion(handled, addedResult);
1588 bool retval = (addedResult != NULL);
1589 CFReleaseNull(addedResult);
1590 return retval;
1591 });
1592 }
1593
1594 #define MAXPEERS 7
1595
1596 static bool SOSCFSubsetOfN(CFSetRef peers, size_t n, CFErrorRef* error, bool (^action)(CFSetRef subset, CFErrorRef* error)) {
1597 __block bool retval = true;
1598 __block size_t ready = 0;
1599 __block CFMutableSetRef subset = CFSetCreateMutable(kCFAllocatorDefault, 0, &kCFTypeSetCallBacks);
1600
1601 CFSetForEach(peers, ^(const void *value) {
1602 CFSetAddValue(subset, value);
1603 ready++;
1604 if(ready >= n) {
1605 retval &= action(subset, error);
1606 ready = 0;
1607 CFSetRemoveAllValues(subset);
1608 }
1609 });
1610 if(CFSetGetCount(subset)) {
1611 retval &= action(subset, error);
1612 }
1613 CFReleaseNull(subset);
1614 return retval;
1615 }
1616
1617 CF_RETURNS_RETAINED CFSetRef SOSCCProcessSyncWithPeers_Server(CFSetRef peers, CFSetRef backupPeers, CFErrorRef *error) {
1618 static dispatch_queue_t swpQueue = nil;
1619 static dispatch_once_t onceToken;
1620 dispatch_once(&onceToken, ^{
1621 swpQueue = dispatch_queue_create("syncWithPeers", DISPATCH_QUEUE_SERIAL);
1622 });
1623
1624 __block CFMutableSetRef handled = CFSetCreateMutable(kCFAllocatorDefault, 0, &kCFTypeSetCallBacks);
1625 __block CFSetRef noPeers = CFSetCreateMutable(kCFAllocatorDefault, 0, &kCFTypeSetCallBacks);
1626
1627 if(!peers) {
1628 peers = noPeers;
1629 }
1630
1631 if(!backupPeers) {
1632 backupPeers = noPeers;
1633 }
1634
1635 dispatch_sync(swpQueue, ^{
1636 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCProcessSyncWithPeers);
1637
1638 if((CFSetGetCount(peers) + CFSetGetCount(backupPeers)) < MAXPEERS) {
1639 internalSyncWithPeers(peers, backupPeers, handled, error);
1640 } else {
1641 // sync any backupPeers
1642 if(backupPeers && CFSetGetCount(backupPeers)) {
1643 SOSCFSubsetOfN(backupPeers, MAXPEERS, error, ^bool(CFSetRef subset, CFErrorRef *error) {
1644 return internalSyncWithPeers(noPeers, subset, handled, error);
1645 });
1646 }
1647
1648 // sync any device peers
1649 if(peers && CFSetGetCount(peers)) {
1650 SOSCFSubsetOfN(peers, MAXPEERS, error, ^bool(CFSetRef subset, CFErrorRef *error) {
1651 return internalSyncWithPeers(subset, noPeers, handled, error);
1652 });
1653 }
1654 }
1655
1656 OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCProcessSyncWithPeers, OctagonSignpostNumber1(SOSSignpostNameSOSCCProcessSyncWithPeers), (int)(CFSetGetCount(handled) != 0));
1657
1658 if(CFSetGetCount(handled) == 0) {
1659 CFReleaseNull(handled);
1660 }
1661 CFReleaseNull(noPeers);
1662 });
1663
1664 return handled;
1665 }
1666
1667 SyncWithAllPeersReason SOSCCProcessSyncWithAllPeers_Server(CFErrorRef* error)
1668 {
1669 /*
1670 #define kIOReturnLockedRead iokit_common_err(0x2c3) // device read locked
1671 #define kIOReturnLockedWrite iokit_common_err(0x2c4) // device write locked
1672 */
1673 __block SyncWithAllPeersReason result = kSyncWithAllPeersSuccess;
1674 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCProcessSyncWithAllPeers);
1675
1676 CFErrorRef action_error = NULL;
1677
1678 if (!do_with_account_while_unlocked(&action_error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
1679 return SOSAccountRequestSyncWithAllPeers(txn, block_error);
1680 })) {
1681 if (action_error) {
1682 if (SecErrorGetOSStatus(action_error) == errSecInteractionNotAllowed) {
1683 secnotice("updates", "SOSAccountSyncWithAllKVSPeers failed because device is locked; letting CloudKeychainProxy know");
1684 result = kSyncWithAllPeersLocked; // tell CloudKeychainProxy to call us back when device unlocks
1685 CFReleaseNull(action_error);
1686 } else {
1687 secerror("Unexpected error: %@", action_error);
1688 }
1689 }
1690
1691 SecErrorPropagate(action_error, error);
1692 }
1693
1694 OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCProcessSyncWithAllPeers, OctagonSignpostNumber1(SOSSignpostNameSOSCCProcessSyncWithAllPeers), (int)result);
1695
1696 return result;
1697 }
1698
1699 //
1700 // Sync requesting
1701 //
1702
1703 void SOSCCRequestSyncWithPeer(CFStringRef peerID) {
1704 CFArrayRef peers = CFArrayCreateForCFTypes(kCFAllocatorDefault, peerID, NULL);
1705
1706 SOSCCRequestSyncWithPeersList(peers);
1707
1708 CFReleaseNull(peers);
1709 }
1710
1711 void SOSCCRequestSyncWithPeers(CFSetRef /*SOSPeerInfoRef/CFStringRef*/ peerIDs) {
1712 CFMutableArrayRef peerIDArray = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1713
1714 CFSetForEach(peerIDs, ^(const void *value) {
1715 if (isString(value)) {
1716 CFArrayAppendValue(peerIDArray, value);
1717 } else if (isSOSPeerInfo(value)) {
1718 SOSPeerInfoRef peer = asSOSPeerInfo(value);
1719 CFArrayAppendValue(peerIDArray, SOSPeerInfoGetPeerID(peer));
1720 } else {
1721 secerror("Bad element, skipping: %@", value);
1722 }
1723 });
1724
1725 SOSCCRequestSyncWithPeersList(peerIDArray);
1726
1727 CFReleaseNull(peerIDArray);
1728 }
1729
1730 void SOSCCRequestSyncWithPeersList(CFArrayRef /*CFStringRef*/ peerIDs) {
1731 os_activity_initiate("CloudCircle RequestSyncWithPeersList", OS_ACTIVITY_FLAG_DEFAULT, ^(void) {
1732 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCRequestSyncWithPeersList);
1733
1734 CFArrayRef empty = CFArrayCreateForCFTypes(kCFAllocatorDefault, NULL);
1735
1736 CFStringArrayPerformWithDescription(peerIDs, ^(CFStringRef description) {
1737 secnotice("syncwith", "Request Sync With: %@", description);
1738 });
1739
1740 SOSCloudKeychainRequestSyncWithPeers(peerIDs, empty,
1741 dispatch_get_global_queue(SOS_ENGINE_PRIORITY, 0), NULL);
1742 CFReleaseNull(empty);
1743 OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCRequestSyncWithPeersList, OctagonSignpostNumber1(SOSSignpostNameSOSCCRequestSyncWithPeersList), (int)true);
1744 });
1745 }
1746
1747 void SOSCCRequestSyncWithBackupPeerList(CFArrayRef /* CFStringRef */ backupPeerIDs) {
1748 os_activity_initiate("CloudCircle SOSCCRequestSyncWithBackupPeerList", OS_ACTIVITY_FLAG_DEFAULT, ^(void) {
1749 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCRequestSyncWithBackupPeerList);
1750
1751 CFArrayRef empty = CFArrayCreateForCFTypes(kCFAllocatorDefault, NULL);
1752
1753 CFStringArrayPerformWithDescription(backupPeerIDs, ^(CFStringRef description) {
1754 secnotice("syncwith", "Request backup sync With: %@", description);
1755 });
1756
1757 SOSCloudKeychainRequestSyncWithPeers(empty, backupPeerIDs,
1758 dispatch_get_global_queue(SOS_ENGINE_PRIORITY, 0), NULL);
1759
1760 CFReleaseNull(empty);
1761 OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCRequestSyncWithBackupPeerList, OctagonSignpostNumber1(SOSSignpostNameSOSCCRequestSyncWithBackupPeerList), (int)true);
1762
1763 });
1764 }
1765
1766 bool SOSCCIsSyncPendingFor(CFStringRef peerID, CFErrorRef *error) {
1767 return false;
1768 }
1769
1770 void SOSCCEnsurePeerRegistration(void)
1771 {
1772 os_activity_initiate("CloudCircle EnsurePeerRegistration", OS_ACTIVITY_FLAG_DEFAULT, ^(void) {
1773 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCEnsurePeerRegistration);
1774 SOSCloudKeychainRequestEnsurePeerRegistration(dispatch_get_global_queue(SOS_ENGINE_PRIORITY, 0), NULL);
1775 OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCEnsurePeerRegistration, OctagonSignpostNumber1(SOSSignpostNameSOSCCEnsurePeerRegistration), (int)true);
1776
1777 });
1778 }
1779
1780 CF_RETURNS_RETAINED CFArrayRef SOSCCHandleUpdateMessage(CFDictionaryRef updates)
1781 {
1782 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCHandleUpdateMessage);
1783
1784 CFArrayRef result = NULL;
1785 SOSAccount* account = (__bridge SOSAccount *)(SOSKeychainAccountGetSharedAccount()); //HACK to make sure itemsChangedBlock is set
1786
1787 result = account ? SOSCloudKeychainHandleUpdateMessage(updates) : CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1788
1789 OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCHandleUpdateMessage, OctagonSignpostNumber1(SOSSignpostNameSOSCCHandleUpdateMessage), (int)(result != NULL));
1790
1791 return result;
1792 }
1793
1794 SOSPeerInfoRef SOSCCCopyApplication_Server(CFErrorRef *error) {
1795 __block SOSPeerInfoRef application = NULL;
1796 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCCopyApplication);
1797
1798 do_with_account_while_unlocked(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
1799 application = SOSAccountCopyApplication(txn.account, error);
1800 return application != NULL;
1801 });
1802 OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCCopyApplication, OctagonSignpostNumber1(SOSSignpostNameSOSCCCopyApplication), (int)(application != NULL));
1803
1804 return application;
1805 }
1806
1807 bool SOSCCCleanupKVSKeys_Server(CFErrorRef *error) {
1808 bool result = do_with_account_while_unlocked(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
1809 return SOSAccountCleanupAllKVSKeys(txn.account, error);
1810 });
1811 if(result && error && *error) {
1812 CFReleaseNull(*error);
1813 }
1814 return result;
1815 }
1816
1817 CFDataRef SOSCCCopyCircleJoiningBlob_Server(SOSPeerInfoRef applicant, CFErrorRef *error) {
1818 __block CFDataRef pbblob = NULL;
1819 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCCopyCircleJoiningBlob);
1820
1821 do_with_account_while_unlocked(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
1822 pbblob = SOSAccountCopyCircleJoiningBlob(txn.account, applicant, error);
1823 return pbblob != NULL;
1824 });
1825 OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCCopyCircleJoiningBlob, OctagonSignpostNumber1(SOSSignpostNameSOSCCCopyCircleJoiningBlob), (int)(pbblob != NULL));
1826
1827 return pbblob;
1828 }
1829
1830 CFDataRef SOSCCCopyInitialSyncData_Server(SOSInitialSyncFlags flags, CFErrorRef *error) {
1831 __block CFDataRef pbblob = NULL;
1832 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCCopyInitialSyncData);
1833
1834 do_with_account_while_unlocked(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
1835 pbblob = SOSAccountCopyInitialSyncData(txn.account, flags, error);
1836 return pbblob != NULL;
1837 });
1838 OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCCopyInitialSyncData, OctagonSignpostNumber1(SOSSignpostNameSOSCCCopyInitialSyncData), (int)(pbblob != NULL));
1839
1840 return pbblob;
1841 }
1842
1843 bool SOSCCJoinWithCircleJoiningBlob_Server(CFDataRef joiningBlob, PiggyBackProtocolVersion version, CFErrorRef *error) {
1844 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCJoinWithCircleJoiningBlob);
1845
1846 bool joinResult = do_with_account_while_unlocked(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
1847 bool result = SOSAccountJoinWithCircleJoiningBlob(txn.account, joiningBlob, version, error);
1848 return result;
1849 });
1850
1851 OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCJoinWithCircleJoiningBlob, OctagonSignpostNumber1(SOSSignpostNameSOSCCJoinWithCircleJoiningBlob), (int)joinResult);
1852 return joinResult;
1853 }
1854
1855 CFBooleanRef SOSCCPeersHaveViewsEnabled_Server(CFArrayRef viewNames, CFErrorRef *error) {
1856 __block CFBooleanRef result = NULL;
1857 do_with_account_if_after_first_unlock(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
1858 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCPeersHaveViewsEnabled);
1859 result = SOSAccountPeersHaveViewsEnabled(txn.account, viewNames, error);
1860 OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCPeersHaveViewsEnabled, OctagonSignpostNumber1(SOSSignpostNameSOSCCPeersHaveViewsEnabled), (int)(result != NULL));
1861 return result != NULL;
1862 });
1863
1864 return result;
1865 }
1866
1867 bool SOSCCRegisterRecoveryPublicKey_Server(CFDataRef recovery_key, CFErrorRef *error){
1868 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCRegisterRecoveryPublicKey);
1869
1870 bool registerResult = do_with_account_if_after_first_unlock(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
1871 bool result = false;
1872 if(recovery_key != NULL && CFDataGetLength(recovery_key) != 0) {
1873 result = SOSAccountRegisterRecoveryPublicKey(txn, recovery_key, error);
1874 }
1875 else {
1876 result = SOSAccountClearRecoveryPublicKey(txn, recovery_key, error);
1877 }
1878 return result;
1879 });
1880 OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCRegisterRecoveryPublicKey, OctagonSignpostNumber1(SOSSignpostNameSOSCCRegisterRecoveryPublicKey), (int)registerResult);
1881 return registerResult;
1882 }
1883
1884 CFDataRef SOSCCCopyRecoveryPublicKey_Server(CFErrorRef *error){
1885
1886 __block CFDataRef result = NULL;
1887 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCCopyRecoveryPublicKey);
1888 do_with_account_if_after_first_unlock(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
1889 result = SOSAccountCopyRecoveryPublicKey(txn, error);
1890 return result != NULL;
1891 });
1892 OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCCopyRecoveryPublicKey, OctagonSignpostNumber1(SOSSignpostNameSOSCCCopyRecoveryPublicKey), (int)(result != NULL));
1893 return result;
1894 }
1895
1896 bool SOSCCMessageFromPeerIsPending_Server(SOSPeerInfoRef peer, CFErrorRef *error) {
1897 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCMessageFromPeerIsPending);
1898
1899 bool pendingResult = do_with_account_if_after_first_unlock(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
1900 bool result = SOSAccountMessageFromPeerIsPending(txn, peer, error);
1901 return result;
1902 });
1903 OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCMessageFromPeerIsPending, OctagonSignpostNumber1(SOSSignpostNameSOSCCMessageFromPeerIsPending), (int)pendingResult);
1904 return pendingResult;
1905 }
1906
1907 bool SOSCCSendToPeerIsPending_Server(SOSPeerInfoRef peer, CFErrorRef *error) {
1908 OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCSendToPeerIsPending);
1909
1910 bool sendResult = do_with_account_if_after_first_unlock(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
1911 bool result = SOSAccountSendToPeerIsPending(txn, peer, error);
1912 return result;
1913 });
1914 OctagonSignpostEnd(signPost, SOSSignpostNameSOSCCSendToPeerIsPending, OctagonSignpostNumber1(SOSSignpostNameSOSCCSendToPeerIsPending), (int)sendResult);
1915 return sendResult;
1916 }
1917
1918 void SOSCCResetOTRNegotiation_Server(CFStringRef peerid)
1919 {
1920 CFErrorRef localError = NULL;
1921 do_with_account_while_unlocked(&localError, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
1922 SOSAccountResetOTRNegotiationCoder(txn.account, peerid);
1923 return true;
1924 });
1925 if(localError)
1926 {
1927 secerror("error resetting otr negotation: %@", localError);
1928 }
1929 }
1930
1931 void SOSCCPeerRateLimiterSendNextMessage_Server(CFStringRef peerid, CFStringRef accessGroup)
1932 {
1933 CFErrorRef localError = NULL;
1934 do_with_account_while_unlocked(&localError, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
1935 SOSAccountTimerFiredSendNextMessage(txn, (__bridge NSString*)peerid, (__bridge NSString*)accessGroup);
1936 return true;
1937 });
1938 if(localError)
1939 {
1940 secerror("error sending next message: %@", localError);
1941 }
1942 }
1943
1944 void SOSCCPerformWithOctagonSigningKey(void (^action)(SecKeyRef octagonPrivSigningKey, CFErrorRef error))
1945 {
1946 CFErrorRef error = NULL;
1947 do_with_account_if_after_first_unlock(&error, ^bool(SOSAccountTransaction *txn, CFErrorRef *err) {
1948 SOSFullPeerInfoRef fpi = txn.account.trust.fullPeerInfo;
1949 SecKeyRef signingKey = SOSFullPeerInfoCopyOctagonSigningKey(fpi, err);
1950 CFErrorRef errorArg = err ? *err : NULL;
1951 action(signingKey, errorArg);
1952 CFReleaseNull(signingKey);
1953 return true;
1954 });
1955 CFReleaseNull(error);
1956 }
1957
1958 void SOSCCPerformWithOctagonSigningPublicKey(void (^action)(SecKeyRef octagonPublicKey, CFErrorRef error))
1959 {
1960 CFErrorRef error = NULL;
1961 do_with_account_if_after_first_unlock(&error, ^bool(SOSAccountTransaction *txn, CFErrorRef *err) {
1962 SOSFullPeerInfoRef fpi = txn.account.trust.fullPeerInfo;
1963 SecKeyRef signingKey = SOSFullPeerInfoCopyOctagonPublicSigningKey(fpi, err);
1964 CFErrorRef errorArg = err ? *err : NULL;
1965 action(signingKey, errorArg);
1966 CFReleaseNull(signingKey);
1967 return true;
1968 });
1969 CFReleaseNull(error);
1970 }
1971
1972 void SOSCCPerformWithOctagonEncryptionKey(void (^action)(SecKeyRef octagonPrivEncryptionKey, CFErrorRef error))
1973 {
1974 CFErrorRef error = NULL;
1975 do_with_account_if_after_first_unlock(&error, ^bool(SOSAccountTransaction *txn, CFErrorRef *err) {
1976 SOSFullPeerInfoRef fpi = txn.account.trust.fullPeerInfo;
1977 SecKeyRef signingKey = SOSFullPeerInfoCopyOctagonEncryptionKey(fpi, err);
1978 CFErrorRef errorArg = err ? *err : NULL;
1979 action(signingKey, errorArg);
1980 CFReleaseNull(signingKey);
1981 return true;
1982 });
1983 CFReleaseNull(error);
1984 }
1985
1986 void SOSCCPerformWithOctagonEncryptionPublicKey(void (^action)(SecKeyRef octagonPublicEncryptionKey, CFErrorRef error))
1987 {
1988 CFErrorRef error = NULL;
1989 do_with_account_if_after_first_unlock(&error, ^bool(SOSAccountTransaction *txn, CFErrorRef *err) {
1990 SOSFullPeerInfoRef fpi = txn.account.trust.fullPeerInfo;
1991 SecKeyRef signingKey = SOSFullPeerInfoCopyOctagonPublicEncryptionKey(fpi, err);
1992 CFErrorRef errorArg = err ? *err : NULL;
1993 action(signingKey, errorArg);
1994 CFReleaseNull(signingKey);
1995 return true;
1996 });
1997 CFReleaseNull(error);
1998 }
1999
2000 void SOSCCPerformWithAllOctagonKeys(void (^action)(SecKeyRef octagonEncryptionKey, SecKeyRef octagonSigningKey, CFErrorRef error))
2001 {
2002 CFErrorRef localError = NULL;
2003 do_with_account_if_after_first_unlock(&localError, ^bool(SOSAccountTransaction *txn, CFErrorRef *err) {
2004 SecKeyRef encryptionKey = NULL;
2005 SecKeyRef signingKey = NULL;
2006 CFErrorRef errorArg = err ? *err : NULL;
2007
2008 SOSFullPeerInfoRef fpi = txn.account.trust.fullPeerInfo;
2009 require_action_quiet(fpi, fail, secerror("device does not have a peer"); SOSCreateError(kSOSErrorPeerNotFound, CFSTR("No Peer for Account"), NULL, &errorArg));
2010
2011 signingKey = SOSFullPeerInfoCopyOctagonSigningKey(fpi, &errorArg);
2012 require_action_quiet(signingKey && !errorArg, fail, secerror("SOSCCPerformWithAllOctagonKeys signing key error: %@", errorArg));
2013 CFReleaseNull(errorArg);
2014
2015 encryptionKey = SOSFullPeerInfoCopyOctagonEncryptionKey(fpi, &errorArg);
2016 require_action_quiet(encryptionKey && !errorArg, fail, secerror("SOSCCPerformWithAllOctagonKeys encryption key error: %@", errorArg));
2017
2018 action(encryptionKey, signingKey, errorArg);
2019 CFReleaseNull(signingKey);
2020 CFReleaseNull(encryptionKey);
2021 CFReleaseNull(errorArg);
2022 return true;
2023 fail:
2024 action(NULL, NULL, errorArg);
2025 CFReleaseNull(errorArg);
2026 CFReleaseNull(signingKey);
2027 CFReleaseNull(encryptionKey);
2028 return true;
2029 });
2030 CFReleaseNull(localError);
2031 }
2032
2033 bool SOSCCSaveOctagonKeysToKeychain(NSString* keyLabel, NSData* keyDataToSave, __unused int keySize, SecKeyRef octagonPublicKey, NSError** error) {
2034 NSError* localerror = nil;
2035
2036
2037 NSMutableDictionary* query = [((NSDictionary*)CFBridgingRelease(SecKeyGeneratePrivateAttributeDictionary(octagonPublicKey,
2038 kSecAttrKeyTypeEC,
2039 (__bridge CFDataRef)keyDataToSave))) mutableCopy];
2040
2041 query[(id)kSecAttrLabel] = keyLabel;
2042 query[(id)kSecUseDataProtectionKeychain] = @YES;
2043 query[(id)kSecAttrSynchronizable] = (id)kCFBooleanFalse;
2044 query[(id)kSecAttrAccessGroup] = (id)kSOSInternalAccessGroup;
2045
2046 CFTypeRef result = NULL;
2047 OSStatus status = SecItemAdd((__bridge CFDictionaryRef)query, &result);
2048
2049 if(status == errSecSuccess) {
2050 return true;
2051 }
2052 if(status == errSecDuplicateItem) {
2053 // Add every primary key attribute to this find dictionary
2054 NSMutableDictionary* findQuery = [[NSMutableDictionary alloc] init];
2055 findQuery[(id)kSecClass] = query[(id)kSecClass];
2056 findQuery[(id)kSecAttrKeyType] = query[(id)kSecAttrKeyTypeEC];
2057 findQuery[(id)kSecAttrKeyClass] = query[(id)kSecAttrKeyClassPrivate];
2058 findQuery[(id)kSecAttrAccessGroup] = query[(id)kSecAttrAccessGroup];
2059 findQuery[(id)kSecAttrLabel] = query[(id)kSecAttrLabel];
2060 findQuery[(id)kSecAttrApplicationLabel] = query[(id)kSecAttrApplicationLabel];
2061 findQuery[(id)kSecUseDataProtectionKeychain] = query[(id)kSecUseDataProtectionKeychain];
2062
2063 NSMutableDictionary* updateQuery = [query mutableCopy];
2064 updateQuery[(id)kSecClass] = nil;
2065
2066 status = SecItemUpdate((__bridge CFDictionaryRef)findQuery, (__bridge CFDictionaryRef)updateQuery);
2067
2068 if(status) {
2069 localerror = [NSError
2070 errorWithDomain:NSOSStatusErrorDomain
2071 code:status
2072 description:[NSString stringWithFormat:@"SecItemUpdate: %d", (int)status]];
2073 }
2074 } else {
2075 localerror = [NSError
2076 errorWithDomain:NSOSStatusErrorDomain
2077 code:status
2078 description:[NSString stringWithFormat:@"SecItemAdd: %d", (int)status]];
2079 }
2080 if(localerror && error) {
2081 *error = localerror;
2082 }
2083
2084 return (status == errSecSuccess);
2085 }
2086
2087 void SOSCCEnsureAccessGroupOfKey(SecKeyRef publicKey, NSString* oldAgrp, NSString* newAgrp)
2088 {
2089 NSData* publicKeyHash = CFBridgingRelease(SecKeyCopyPublicKeyHash(publicKey));
2090
2091 NSDictionary* query = @{
2092 (id)kSecClass : (id)kSecClassKey,
2093 (id)kSecAttrSynchronizable: (id)kSecAttrSynchronizableAny,
2094 (id)kSecAttrApplicationLabel: publicKeyHash,
2095 (id)kSecAttrAccessGroup: oldAgrp,
2096 };
2097
2098 OSStatus status = SecItemUpdate((__bridge CFDictionaryRef)query,
2099 (__bridge CFDictionaryRef)@{
2100 (id)kSecAttrAccessGroup: newAgrp,
2101 });
2102
2103 secnotice("octagon", "Ensuring key agrp ('%@' from '%@') status: %d", newAgrp, oldAgrp, (int)status);
2104 };
2105
2106 static NSString* createKeyLabel(NSDictionary *gestalt, NSString* circleName, NSString* prefix)
2107 {
2108 NSString *keyName = [NSString stringWithFormat:@"ID for %@-%@",SOSPeerGestaltGetName((__bridge CFDictionaryRef)(gestalt)), circleName];
2109
2110 NSString* octagonSigningKeyName = [prefix stringByAppendingString: keyName];
2111
2112 return octagonSigningKeyName;
2113 }
2114
2115 static NSError* saveKeysToKeychain(SOSAccount* account, NSData* octagonSigningFullKey, NSData* octagonEncryptionFullKey, SecKeyRef octagonSigningPublicKeyRef, SecKeyRef octagonEncryptionPublicKeyRef)
2116 {
2117 NSError* saveToKeychainError = nil;
2118
2119 NSString* circleName = (__bridge NSString*)(SOSCircleGetName(account.trust.trustedCircle));
2120
2121 NSString* signingPrefix = @"Octagon Peer Signing ";
2122 NSString* encryptionPrefix = @"Octagon Peer Encryption ";
2123 NSString* octagonSigningKeyName = createKeyLabel(account.gestalt, circleName, signingPrefix);
2124 NSString* octagonEncryptionKeyName = createKeyLabel(account.gestalt, circleName, encryptionPrefix);
2125
2126 /* behavior mimics GeneratePermanentFullECKey_internal */
2127 SOSCCSaveOctagonKeysToKeychain(octagonSigningKeyName, octagonSigningFullKey, 384, octagonSigningPublicKeyRef, &saveToKeychainError);
2128 if(saveToKeychainError) {
2129 secerror("octagon: could not save signing key: %@", saveToKeychainError);
2130 return saveToKeychainError;
2131 }
2132 SOSCCSaveOctagonKeysToKeychain(octagonEncryptionKeyName, octagonEncryptionFullKey, 384, octagonEncryptionPublicKeyRef, &saveToKeychainError);
2133 if(saveToKeychainError) {
2134 secerror("octagon: could not save encryption key: %@", saveToKeychainError);
2135 return saveToKeychainError;
2136 }
2137
2138 return nil;
2139 }
2140
2141 void SOSCCPerformUpdateOfAllOctagonKeys(CFDataRef octagonSigningFullKey, CFDataRef octagonEncryptionFullKey,
2142 CFDataRef signingPublicKey, CFDataRef encryptionPublicKey,
2143 SecKeyRef octagonSigningPublicKeyRef, SecKeyRef octagonEncryptionPublicKeyRef,
2144 void (^action)(CFErrorRef error))
2145 {
2146 CFErrorRef localError = NULL;
2147 do_with_account_if_after_first_unlock(&localError, ^bool(SOSAccountTransaction *txn, CFErrorRef *err) {
2148 CFErrorRef updateOctagonKeysError = NULL;
2149 bool updatedPeerInfo = SOSAccountUpdatePeerInfoAndPush(txn.account, CFSTR("Updating Octagon Keys in SOS"), &updateOctagonKeysError, ^bool(SOSPeerInfoRef pi, CFErrorRef *error) {
2150
2151 //save octagon key set to the keychain
2152 NSError* saveError = nil;
2153 saveError = saveKeysToKeychain(txn.account, (__bridge NSData*)octagonSigningFullKey, (__bridge NSData*)octagonEncryptionFullKey,
2154 octagonSigningPublicKeyRef, octagonEncryptionPublicKeyRef);
2155
2156 if(saveError) {
2157 secerror("octagon: failed to save Octagon keys to the keychain: %@", saveError);
2158 action((__bridge CFErrorRef)saveError);
2159 return false;
2160 }
2161
2162 //now update the peer info to contain octagon keys
2163 if(pi){
2164 CFErrorRef setError = NULL;
2165 SOSPeerInfoSetOctagonKeysInDescription(pi, octagonSigningPublicKeyRef, octagonEncryptionPublicKeyRef, &setError);
2166 if(setError) {
2167 secerror("octagon: Failed to set Octagon Keys in peerInfo: %@", setError);
2168 action(setError);
2169 return false;
2170 }
2171 } else {
2172 secnotice("octagon", "No peer info to update?");
2173 NSError *noPIError = [NSError errorWithDomain:(__bridge NSString*)kSOSErrorDomain code:kSOSErrorPeerNotFound userInfo:@{NSLocalizedDescriptionKey : @"Device has no full peer info"}];
2174 action((__bridge CFErrorRef)noPIError);
2175 return false;
2176 }
2177
2178 secnotice("octagon", "Success! Upated Octagon keys in SOS!");
2179
2180 action(nil);
2181 return true;
2182 });
2183 return updatedPeerInfo;
2184 });
2185 CFReleaseNull(localError);
2186 }
2187
2188 void SOSCCPerformPreloadOfAllOctagonKeys(CFDataRef octagonSigningFullKey, CFDataRef octagonEncryptionFullKey,
2189 SecKeyRef octagonSigningFullKeyRef, SecKeyRef octagonEncryptionFullKeyRef,
2190 SecKeyRef octagonSigningPublicKeyRef, SecKeyRef octagonEncryptionPublicKeyRef,
2191 void (^action)(CFErrorRef error))
2192 {
2193 CFErrorRef localError = NULL;
2194 do_with_account_if_after_first_unlock(&localError, ^bool(SOSAccountTransaction *txn, CFErrorRef *err) {
2195
2196 //save octagon key set to the keychain
2197 NSError* saveError = nil;
2198 saveError = saveKeysToKeychain(txn.account, (__bridge NSData*)octagonSigningFullKey, (__bridge NSData*)octagonEncryptionFullKey,
2199 octagonSigningPublicKeyRef, octagonEncryptionPublicKeyRef);
2200
2201 if(saveError) {
2202 secerror("octagon-preload-keys: failed to save Octagon keys to the keychain: %@", saveError);
2203 action((__bridge CFErrorRef)saveError);
2204 return false;
2205 }
2206
2207 //now update the sos account to contain octagon keys
2208 if(txn.account){
2209 txn.account.octagonSigningFullKeyRef = CFRetainSafe(octagonSigningFullKeyRef);
2210 txn.account.octagonEncryptionFullKeyRef = CFRetainSafe(octagonEncryptionFullKeyRef);
2211 } else {
2212 secnotice("octagon-preload-keys", "No SOSAccount to update?");
2213 NSError *noAccountError = [NSError errorWithDomain:(__bridge NSString*)kSOSErrorDomain code:kSOSErrorNoAccount userInfo:@{NSLocalizedDescriptionKey : @"Device has no SOSAccount"}];
2214 action((__bridge CFErrorRef)noAccountError);
2215 return false;
2216 }
2217
2218 secnotice("octagon-preload-keys", "Success! Octagon Keys Preloaded!");
2219
2220 action(nil);
2221 return true;
2222 });
2223 CFReleaseNull(localError);
2224 }
2225
2226 bool SOSCCSetCKKS4AllStatus(bool supports, CFErrorRef* error)
2227 {
2228 CFErrorRef cfAccountError = NULL;
2229 bool ret = do_with_account_if_after_first_unlock(&cfAccountError, ^bool(SOSAccountTransaction *txn, CFErrorRef *cferror) {
2230 SOSAccountUpdatePeerInfo(txn.account, CFSTR("CKKS4All update"), cferror, ^bool(SOSFullPeerInfoRef fpi, CFErrorRef *blockError) {
2231 return SOSFullPeerInfoSetCKKS4AllSupport(fpi, supports, blockError);
2232 });
2233 return true;
2234 });
2235 CFErrorPropagate(cfAccountError, error);
2236 return ret;
2237 }
2238
2239 void SOSCCPerformWithTrustedPeers(void (^action)(CFSetRef sosPeerInfoRefs, CFErrorRef error))
2240 {
2241 CFErrorRef cfAccountError = NULL;
2242 do_with_account_if_after_first_unlock(&cfAccountError, ^bool(SOSAccountTransaction *txn, CFErrorRef *cferror) {
2243 CFSetRef sosPeerSet = [txn.account.trust copyPeerSetMatching:^bool(SOSPeerInfoRef peer) {
2244 return true;
2245 }];
2246
2247 CFErrorRef errorArg = cferror ? *cferror : NULL;
2248 action(sosPeerSet, errorArg);
2249 CFReleaseNull(sosPeerSet);
2250 return true;
2251 });
2252 CFReleaseNull(cfAccountError);
2253 }
2254
2255 void SOSCCPerformWithPeerID(void (^action)(CFStringRef peerID, CFErrorRef error))
2256 {
2257 CFErrorRef cfAccountError = NULL;
2258 do_with_account_if_after_first_unlock(&cfAccountError, ^bool(SOSAccountTransaction *txn, CFErrorRef *cferror) {
2259 SOSAccount* account = txn.account;
2260 NSString* peerID = nil;
2261 CFErrorRef localError = nil;
2262
2263 if([account getCircleStatus:nil] == kSOSCCInCircle){
2264 peerID = [txn.account peerID];
2265 }
2266 else{
2267 SOSErrorCreate(kSOSErrorNoCircle, &localError, NULL, CFSTR("Not in circle"));
2268 }
2269 action((__bridge CFStringRef)peerID, localError);
2270 CFReleaseNull(localError);
2271 return true;
2272 });
2273 CFReleaseNull(cfAccountError);
2274 }
2275
2276 void
2277 SOSCCAccountTriggerSyncWithBackupPeer_server(CFStringRef peer)
2278 {
2279 #if OCTAGON
2280 secnotice("syncwith", "SOSCCAccountTriggerSyncWithBackupPeer_server: %@", peer);
2281 if (peer == NULL) {
2282 return;
2283 }
2284 SOSAccount* account = (__bridge SOSAccount*)GetSharedAccountRef();
2285 [account triggerBackupForPeers:@[(__bridge NSString *)peer]];
2286 #endif
2287 }
2288