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