]> git.saurik.com Git - apple/security.git/blame - sec/securityd/SOSCloudCircleServer.c
Security-55471.14.18.tar.gz
[apple/security.git] / sec / securityd / SOSCloudCircleServer.c
CommitLineData
427c49bc
A
1//
2// SOSCloudCircleServer.c
3// sec
4//
5// Created by Mitch Adler on 11/15/12.
6//
7//
8
9#include <AssertMacros.h>
10#include <CoreFoundation/CFURL.h>
11
12#include <securityd/SOSCloudCircleServer.h>
13#include <SecureObjectSync/SOSCloudCircle.h>
14#include <SecureObjectSync/SOSCloudCircleInternal.h>
15#include <SecureObjectSync/SOSCircle.h>
16#include <SecureObjectSync/SOSAccount.h>
17#include <SecureObjectSync/SOSFullPeerInfo.h>
18#include <SecureObjectSync/SOSPeerInfoInternal.h>
19#include <SecureObjectSync/SOSInternal.h>
20#include <SecureObjectSync/SOSUserKeygen.h>
21
22#include <utilities/SecCFWrappers.h>
23#include <utilities/SecCFRelease.h>
24#include <utilities/debugging.h>
25#include "SOSCloudKeychainClient.h"
26
27#include <corecrypto/ccrng.h>
28#include <corecrypto/ccrng_pbkdf2_prng.h>
29#include <corecrypto/ccec.h>
30#include <corecrypto/ccdigest.h>
31#include <corecrypto/ccsha2.h>
32#include <CommonCrypto/CommonRandomSPI.h>
33#include <Security/SecKeyPriv.h>
34#include <Security/SecFramework.h>
35
36#include <utilities/SecFileLocations.h>
37#include <utilities/SecAKSWrappers.h>
38#include <SecItemServer.h>
39#include <SecItemPriv.h>
40
41#include <TargetConditionals.h>
42
43#include <utilities/iCloudKeychainTrace.h>
44
45#if TARGET_OS_EMBEDDED || TARGET_IPHONE_SIMULATOR
46#include <MobileGestalt.h>
47#else
48#include <AppleSystemInfo/AppleSystemInfo.h>
49
50// We need authorization, but that doesn't exist
51// on sec built for desktop (iOS in a process)
52// Define AuthorizationRef here to make SystemConfiguration work
53// as if it's on iOS.
54typedef const struct AuthorizationOpaqueRef * AuthorizationRef;
55#endif
56
57#define SOSCKCSCOPE "sync"
58
59#define USE_SYSTEMCONFIGURATION_PRIVATE_HEADERS
60#import <SystemConfiguration/SystemConfiguration.h>
61
62#include <notify.h>
63
64CloudKeychainReplyBlock log_error = ^(CFDictionaryRef returnedValues __unused, CFErrorRef error) {
65 if (error) {
66 secerror("Error putting: %@", error);
67 CFReleaseSafe(error);
68 }
69};
70
71static SOSCCAccountDataSourceFactoryBlock accountDataSourceOverride = NULL;
72
73bool SOSKeychainAccountSetFactoryForAccount(SOSCCAccountDataSourceFactoryBlock block)
74{
75 accountDataSourceOverride = Block_copy(block);
4d3cab3d 76
427c49bc
A
77 return true;
78}
79
80static void do_with_account(void (^action)(SOSAccountRef account));
81
82
83//
84// Constants
85//
86CFStringRef kSOSInternalAccessGroup = CFSTR("com.apple.security.sos");
87
88CFStringRef kSOSAccountLabel = CFSTR("iCloud Keychain Account Meta-data");
89CFStringRef kSOSPeerDataLabel = CFSTR("iCloud Peer Data Meta-data");
90
91static CFStringRef accountFileName = CFSTR("PersistedAccount.plist");
92
93static CFDictionaryRef SOSItemCopyQueryForSyncItems(CFStringRef service, bool returnData)
94{
95 return CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
96 kSecClass, kSecClassGenericPassword,
97 kSecAttrService, service,
98 kSecAttrAccessGroup, kSOSInternalAccessGroup,
99 kSecReturnData, returnData ? kCFBooleanTrue : kCFBooleanFalse,
100 NULL);
101}
102
103CFDataRef SOSItemGet(CFStringRef service, CFErrorRef* error)
104{
105 CFDictionaryRef query = SOSItemCopyQueryForSyncItems(service, true);
106
107 CFDataRef result = NULL;
108
109 OSStatus copyResult = SecItemCopyMatching(query, (CFTypeRef*) &result);
110
111 CFReleaseNull(query);
112
113 if (copyResult != noErr) {
114 SecError(copyResult, error, CFSTR("Error %@ reading for service '%@'"), result, service);
115 CFReleaseNull(result);
116 return NULL;
117 }
118
119 if (!isData(result)) {
120 SOSCreateErrorWithFormat(kSOSErrorProcessingFailure, NULL, error, NULL, CFSTR("SecItemCopyMatching returned non-data in '%@'"), service);
121 return NULL;
122 }
123
124 return result;
125}
126
127static CFDataRef SOSKeychainCopySavedAccountData()
128{
129 CFErrorRef error = NULL;
130 CFDataRef accountData = SOSItemGet(kSOSAccountLabel, &error);
131 if (!accountData)
132 secnotice("account", "Failed to load account: %@", error);
133 CFReleaseNull(error);
134
135 return accountData;
136}
137
138
139bool SOSItemUpdateOrAdd(CFStringRef service, CFStringRef accessibility, CFDataRef data, CFErrorRef *error)
140{
141 CFDictionaryRef query = SOSItemCopyQueryForSyncItems(service, false);
142
143 CFDictionaryRef update = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
144 kSecValueData, data,
145 kSecAttrAccessible, accessibility,
146 NULL);
147 OSStatus saveStatus = SecItemUpdate(query, update);
148
149 if (errSecItemNotFound == saveStatus) {
150 CFMutableDictionaryRef add = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, query);
151 CFDictionaryForEach(update, ^(const void *key, const void *value) {
152 CFDictionaryAddValue(add, key, value);
153 });
154 saveStatus = SecItemAdd(add, NULL);
155 CFReleaseNull(add);
156 }
157
158 CFReleaseNull(query);
159 CFReleaseNull(update);
160
161 return SecError(saveStatus, error, CFSTR("Error saving %@ to service '%@'"), data, service);
162}
163
164static CFStringRef accountStatusFileName = CFSTR("accountStatus.plist");
165#include <utilities/der_plist.h>
166#include <utilities/der_plist_internal.h>
167#include <corecrypto/ccder.h>
168
169static const uint8_t* ccder_decode_bool(bool* boolean, const uint8_t* der, const uint8_t *der_end)
170{
171 if (NULL == der)
172 return NULL;
4d3cab3d 173
427c49bc
A
174 size_t payload_size = 0;
175 const uint8_t *payload = ccder_decode_tl(CCDER_BOOLEAN, &payload_size, der, der_end);
4d3cab3d 176
427c49bc
A
177 if (NULL == payload || (der_end - payload) < 1 || payload_size != 1) {
178 return NULL;
179 }
4d3cab3d 180
427c49bc
A
181 if (boolean)
182 *boolean = (*payload != 0);
4d3cab3d 183
427c49bc
A
184 return payload + payload_size;
185}
186
187bool SOSCCCircleIsOn_Artifact(void) {
188 CFURLRef accountStatusFileURL = SecCopyURLForFileInKeychainDirectory(accountStatusFileName);
189 CFDataRef accountStatus = (CFDataRef) CFPropertyListReadFromFile(accountStatusFileURL);
190 CFReleaseSafe(accountStatusFileURL);
191 bool circle_on = false;
4d3cab3d 192
427c49bc
A
193 if (accountStatus && !isData(accountStatus)) {
194 CFReleaseNull(accountStatus);
195 } else if(accountStatus) {
196 size_t size = CFDataGetLength(accountStatus);
197 const uint8_t *der = CFDataGetBytePtr(accountStatus);
198 const uint8_t *der_p = der;
4d3cab3d 199
427c49bc
A
200 const uint8_t *sequence_end;
201 der_p = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &sequence_end, der_p, der_p + size);
202 der_p = ccder_decode_bool(&circle_on, der_p, sequence_end);
203 } else {
4d3cab3d 204
427c49bc
A
205 }
206 return circle_on;
207}
208
209
210static size_t ccder_sizeof_bool(bool value __unused, CFErrorRef *error)
211{
212 return ccder_sizeof(CCDER_BOOLEAN, 1);
213}
214
215
216static uint8_t* ccder_encode_bool(bool value, const uint8_t *der, uint8_t *der_end)
217{
218 uint8_t value_byte = value;
4d3cab3d 219
427c49bc
A
220 return ccder_encode_tl(CCDER_BOOLEAN, 1, der,
221 ccder_encode_body(1, &value_byte, der, der_end));
222}
223
224static void SOSCCCircleIsOn_SetArtifact(bool account_on) {
225 static CFDataRef sLastSavedAccountStatus = NULL;
226 CFErrorRef saveError = NULL;
227 size_t der_size = ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, ccder_sizeof_bool(account_on, NULL));
228 uint8_t der[der_size];
229 uint8_t *der_end = der + der_size;
230 der_end = ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
231 ccder_encode_bool(account_on, der, der_end));
232
233 CFDataRef accountStatusAsData = CFDataCreate(kCFAllocatorDefault, der_end, der_size);
4d3cab3d 234
427c49bc
A
235 require_quiet(accountStatusAsData, exit);
236 if (sLastSavedAccountStatus && CFEqual(sLastSavedAccountStatus, accountStatusAsData)) goto exit;
4d3cab3d 237
427c49bc
A
238 CFURLRef accountStatusFileURL = SecCopyURLForFileInKeychainDirectory(accountStatusFileName);
239 CFPropertyListWriteToFile((CFPropertyListRef) accountStatusAsData, accountStatusFileURL);
240 CFReleaseSafe(accountStatusFileURL);
4d3cab3d 241
427c49bc
A
242 CFReleaseNull(sLastSavedAccountStatus);
243 sLastSavedAccountStatus = accountStatusAsData;
244 accountStatusAsData = NULL;
4d3cab3d 245
427c49bc
A
246exit:
247 CFReleaseNull(saveError);
248 CFReleaseNull(accountStatusAsData);
249}
250
4d3cab3d
A
251static void SOSCCCircleIsOn_UpdateArtifact(SOSCCStatus status)
252{
253 switch (status) {
254 case kSOSCCCircleAbsent:
255 case kSOSCCNotInCircle:
256 SOSCCCircleIsOn_SetArtifact(false);
257 break;
258 case kSOSCCInCircle:
259 case kSOSCCRequestPending:
260 SOSCCCircleIsOn_SetArtifact(true);
261 break;
262 case kSOSCCError:
263 default:
264 // do nothing
265 break;
266 }
267}
427c49bc
A
268
269static void SOSKeychainAccountEnsureSaved(SOSAccountRef account)
270{
271 static CFDataRef sLastSavedAccountData = NULL;
4d3cab3d 272
427c49bc 273 CFErrorRef saveError = NULL;
4d3cab3d
A
274 SOSCCCircleIsOn_UpdateArtifact(SOSAccountIsInCircles(account, NULL));
275
427c49bc 276 CFDataRef accountAsData = SOSAccountCopyEncodedData(account, kCFAllocatorDefault, &saveError);
4d3cab3d 277
427c49bc
A
278 require_action_quiet(accountAsData, exit, secerror("Failed to transform account into data, error: %@", saveError));
279 require_quiet(!CFEqualSafe(sLastSavedAccountData, accountAsData), exit);
4d3cab3d 280
427c49bc
A
281 if (!SOSItemUpdateOrAdd(kSOSAccountLabel, kSecAttrAccessibleAlwaysThisDeviceOnly, accountAsData, &saveError)) {
282 secerror("Can't save account: %@", saveError);
283 goto exit;
284 }
4d3cab3d 285
427c49bc
A
286 CFReleaseNull(sLastSavedAccountData);
287 sLastSavedAccountData = accountAsData;
288 accountAsData = NULL;
4d3cab3d 289
427c49bc
A
290exit:
291 CFReleaseNull(saveError);
292 CFReleaseNull(accountAsData);
293}
294
295
296/*
297 Stolen from keychain_sync.c
298 */
299
300static bool clearAllKVS(CFErrorRef *error)
301{
302 return true;
303 __block bool result = false;
304 const uint64_t maxTimeToWaitInSeconds = 30ull * NSEC_PER_SEC;
305 dispatch_queue_t processQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
306 dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
307 dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds);
308
309 SOSCloudKeychainClearAll(processQueue, ^(CFDictionaryRef returnedValues, CFErrorRef cerror)
310 {
311 result = (cerror != NULL);
312 dispatch_semaphore_signal(waitSemaphore);
313 });
314
315 dispatch_semaphore_wait(waitSemaphore, finishTime);
316 dispatch_release(waitSemaphore);
317
318 return result;
319}
320
321static SOSAccountRef SOSKeychainAccountCreateSharedAccount(CFDictionaryRef our_gestalt)
322{
323 SOSAccountKeyInterestBlock updateKVSKeys = ^(bool getNewKeysOnly, CFArrayRef alwaysKeys, CFArrayRef afterFirstUnlockKeys, CFArrayRef unlockedKeys) {
324 CFErrorRef error = NULL;
325
326 if (!SOSCloudKeychainUpdateKeys(getNewKeysOnly, alwaysKeys, afterFirstUnlockKeys, unlockedKeys, &error))
327 {
328 secerror("Error updating keys: %@", error);
329 // TODO: propagate error(s) to callers.
330 } else {
331 if (CFArrayGetCount(unlockedKeys) == 0) {
332 secnotice(SOSCKCSCOPE, "Unlocked keys were empty!");
333 }
334 // This leaks 3 CFStringRefs in DEBUG builds.
335 CFStringRef alwaysKeysDesc = SOSInterestListCopyDescription(alwaysKeys);
336 CFStringRef afterFirstUnlockKeysDesc = SOSInterestListCopyDescription(afterFirstUnlockKeys);
337 CFStringRef unlockedKeysDesc = SOSInterestListCopyDescription(unlockedKeys);
338 secdebug(SOSCKCSCOPE, "Updating interest: always: %@,\nfirstUnlock: %@,\nunlockedKeys: %@",
339 alwaysKeysDesc,
340 afterFirstUnlockKeysDesc,
341 unlockedKeysDesc);
342 CFReleaseNull(alwaysKeysDesc);
343 CFReleaseNull(afterFirstUnlockKeysDesc);
344 CFReleaseNull(unlockedKeysDesc);
345 }
346
347 CFReleaseNull(error);
348 };
4d3cab3d 349
427c49bc
A
350 SOSAccountDataUpdateBlock updateKVS = ^ bool (CFDictionaryRef changes, CFErrorRef *error) {
351 SOSCloudKeychainPutObjectsInCloud(changes, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), log_error);
352
353 CFStringRef changeDescription = SOSChangesCopyDescription(changes, true);
354 secnotice("account", "Keys Sent: %@", changeDescription);
355 CFReleaseSafe(changeDescription);
356
357 return true;
358 };
359
360 secdebug("account", "Created account");
4d3cab3d 361
427c49bc 362 CFDataRef savedAccount = SOSKeychainCopySavedAccountData();
4d3cab3d 363
427c49bc
A
364 // At this point we might have an account structure from keychain that may or may not match the account we're building this for
365 // murf ZZZ we should probably make sure this is a good thing before using it.
366
367 SOSAccountRef account = NULL;
4d3cab3d 368
427c49bc
A
369 if (savedAccount) {
370 CFErrorRef inflationError = NULL;
371 SOSDataSourceFactoryRef factory = accountDataSourceOverride ? accountDataSourceOverride() : SecItemDataSourceFactoryCreateDefault();
372
373 account = SOSAccountCreateFromData(kCFAllocatorDefault, savedAccount, factory, updateKVSKeys, updateKVS, &inflationError);
4d3cab3d 374
427c49bc
A
375 if (account)
376 SOSAccountUpdateGestalt(account, our_gestalt);
377 else
378 secerror("Got error inflating account: %@", inflationError);
379 CFReleaseNull(inflationError);
380 }
381
382 CFReleaseSafe(savedAccount);
4d3cab3d 383
427c49bc
A
384 if (!account) {
385 // If we get here then we are creating a new accout and so increment the peer count for ourselves.
386 SOSDataSourceFactoryRef factory = accountDataSourceOverride ? accountDataSourceOverride() : SecItemDataSourceFactoryCreateDefault();
387
388 account = SOSAccountCreate(kCFAllocatorDefault, our_gestalt, factory, updateKVSKeys, updateKVS);
389
390 if (!account)
391 secerror("Got NULL creating account");
392 }
393
394 return account;
395}
396
397//
398// Mark: Gestalt Handling
399//
400
401static CFStringRef CopyModelName(void)
402{
403 static dispatch_once_t once;
404 static CFStringRef modelName = NULL;
405 dispatch_once(&once, ^{
406#if TARGET_OS_EMBEDDED || TARGET_IPHONE_SIMULATOR
407 modelName = MGCopyAnswer(kMGQDeviceName, NULL);
408#else
409 modelName = ASI_CopyComputerModelName(FALSE);
410#endif
411 if (modelName == NULL)
412 modelName = CFSTR("Unknown model");
413 });
414 return CFStringCreateCopy(kCFAllocatorDefault, modelName);
415}
416
417static CFStringRef CopyComputerName(SCDynamicStoreRef store)
418{
419 CFStringRef deviceName = SCDynamicStoreCopyComputerName(store, NULL);
420 if (deviceName == NULL) {
421 deviceName = CFSTR("Unknown name");
422 }
423 return deviceName;
424}
425
426static CFDictionaryRef GatherDeviceGestalt(SCDynamicStoreRef store, CFArrayRef keys, void *context)
427{
428 CFStringRef modelName = CopyModelName();
429 CFStringRef computerName = CopyComputerName(store);
430
431 CFDictionaryRef gestalt = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
432 kPIUserDefinedDeviceName, computerName,
433 kPIDeviceModelName, modelName,
434 NULL);
435 CFRelease(modelName);
436 CFRelease(computerName);
437
438 return gestalt;
439}
440
441static void SOSCCProcessGestaltUpdate(SCDynamicStoreRef store, CFArrayRef keys, void *context)
442{
443 do_with_account(^(SOSAccountRef account) {
444 CFDictionaryRef gestalt = GatherDeviceGestalt(store, keys, context);
445 if (SOSAccountUpdateGestalt(account, gestalt)) {
446 notify_post(kSOSCCCircleChangedNotification);
447 }
448 CFReleaseSafe(gestalt);
449 });
450}
451
452
453static CFDictionaryRef RegisterForGestaltUpdate(dispatch_queue_t queue, void *info)
454{
455 SCDynamicStoreContext context = { .info = info };
456 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("com.apple.securityd.cloudcircleserver"), SOSCCProcessGestaltUpdate, &context);
457 CFStringRef computerKey = SCDynamicStoreKeyCreateComputerName(NULL);
458 CFArrayRef keys = NULL;
459 CFDictionaryRef gestalt = NULL;
460
461 if (store == NULL || computerKey == NULL) {
462 goto done;
463 }
464 keys = CFArrayCreate(NULL, (const void **)&computerKey, 1, &kCFTypeArrayCallBacks);
465 if (keys == NULL) {
466 goto done;
467 }
468 gestalt = GatherDeviceGestalt(store, keys, info);
469 SCDynamicStoreSetNotificationKeys(store, keys, NULL);
470 SCDynamicStoreSetDispatchQueue(store, queue);
471
472done:
473 if (store) CFRelease(store);
474 if (computerKey) CFRelease(computerKey);
475 if (keys) CFRelease(keys);
476 return gestalt;
477}
478
479static void do_with_account(void (^action)(SOSAccountRef account));
480static void do_with_account_async(void (^action)(SOSAccountRef account));
481
482static void do_with_account_dynamic(void (^action)(SOSAccountRef account), bool sync) {
483 static SOSAccountRef sSharedAccount;
484 static dispatch_once_t onceToken;
4d3cab3d 485
427c49bc
A
486 dispatch_once(&onceToken, ^{
487 secdebug(SOSCKCSCOPE, "Account Creation start");
4d3cab3d 488
427c49bc 489 CFDictionaryRef gestalt = RegisterForGestaltUpdate(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), NULL);
4d3cab3d 490
427c49bc
A
491 if (!gestalt) {
492#if TARGET_OS_IPHONE && TARGET_IPHONE_SIMULATOR
493 gestalt = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, NULL);
494#else
495 secerror("Didn't get machine gestalt! This is going to be ugly.");
496#endif
497 }
498
499 sSharedAccount = SOSKeychainAccountCreateSharedAccount(gestalt);
500
501 CFReleaseSafe(gestalt);
4d3cab3d 502
427c49bc 503 SOSCCSetThisDeviceDefinitelyNotActiveInCircle(SOSAccountIsInCircles(sSharedAccount, NULL));
4d3cab3d 504
427c49bc
A
505 SOSAccountAddChangeBlock(sSharedAccount, ^(SOSCircleRef circle,
506 CFArrayRef peer_additions, CFArrayRef peer_removals,
507 CFArrayRef applicant_additions, CFArrayRef applicant_removals) {
508 CFErrorRef pi_error = NULL;
509 SOSPeerInfoRef me = SOSAccountGetMyPeerInCircle(sSharedAccount, circle, &pi_error);
510 if (!me) {
511 secerror("Error finding me for change: %@", pi_error);
512 CFReleaseNull(pi_error);
513 } else {
514 CFReleaseSafe(pi_error);
4d3cab3d 515
427c49bc
A
516 if (CFArrayContainsValue(peer_additions, CFRangeMake(0, CFArrayGetCount(peer_additions)), me)) {
517 SOSCCSyncWithAllPeers();
518 }
519 }
520
521 if (CFArrayGetCount(peer_additions) != 0 ||
522 CFArrayGetCount(peer_removals) != 0 ||
523 CFArrayGetCount(applicant_additions) != 0 ||
524 CFArrayGetCount(applicant_removals) != 0) {
4d3cab3d 525
427c49bc
A
526 SOSCCSetThisDeviceDefinitelyNotActiveInCircle(SOSAccountIsInCircles(sSharedAccount, NULL));
527 notify_post(kSOSCCCircleChangedNotification);
528 }
529 });
4d3cab3d 530
427c49bc
A
531 SOSCloudKeychainSetItemsChangedBlock(^(CFDictionaryRef changes) {
532 CFRetainSafe(changes);
533 do_with_account_async(^(SOSAccountRef account) {
534 CFStringRef changeDescription = SOSChangesCopyDescription(changes, false);
535 secdebug(SOSCKCSCOPE, "Received: %@", changeDescription);
536 CFReleaseSafe(changeDescription);
4d3cab3d 537
427c49bc
A
538 CFErrorRef error = NULL;
539 if (!SOSAccountHandleUpdates(account, changes, &error)) {
540 secerror("Error handling updates: %@", error);
541 CFReleaseNull(error);
542 return;
543 }
544 CFReleaseNull(error);
545 CFReleaseSafe(changes);
546 });
547 });
548
549 });
4d3cab3d 550
427c49bc
A
551 dispatch_block_t do_action_and_save = ^{
552 action(sSharedAccount);
553 SOSKeychainAccountEnsureSaved(sSharedAccount);
554 };
555
556 if (sync) {
557 dispatch_sync(SOSAccountGetQueue(sSharedAccount), do_action_and_save);
558 } else {
559 dispatch_async(SOSAccountGetQueue(sSharedAccount), do_action_and_save);
560 }
561}
562
563static void do_with_account_async(void (^action)(SOSAccountRef account)) {
564 do_with_account_dynamic(action, false);
565}
566
567static void do_with_account(void (^action)(SOSAccountRef account)) {
568 do_with_account_dynamic(action, true);
569}
570
571#if TARGET_IPHONE_SIMULATOR
572#define MKBDeviceUnlockedSinceBoot() true
573#endif
574
575static bool do_if_after_first_unlock(CFErrorRef *error, dispatch_block_t action)
576{
577 bool beenUnlocked = false;
578 require_quiet(SecAKSGetHasBeenUnlocked(&beenUnlocked, error), fail);
579
580 require_action_quiet(beenUnlocked, fail,
581 SOSCreateErrorWithFormat(kSOSErrorNotReady, NULL, error, NULL,
582 CFSTR("Keybag never unlocked, ask after first unlock")));
583
584 action();
585 return true;
586
587fail:
588 return false;
589}
590
591static bool do_with_account_if_after_first_unlock(CFErrorRef *error, bool (^action)(SOSAccountRef account, CFErrorRef* error))
592{
593 __block bool action_result = false;
594
595 return do_if_after_first_unlock(error, ^{
596 do_with_account(^(SOSAccountRef account) {
597 action_result = action(account, error);
598 });
599
600 }) && action_result;
601}
602
603static bool do_with_account_while_unlocked(CFErrorRef *error, bool (^action)(SOSAccountRef account, CFErrorRef* error))
604{
605 __block bool action_result = false;
606
607 return SecAKSDoWhileUserBagLocked(error, ^{
608 do_with_account(^(SOSAccountRef account) {
609 action_result = action(account, error);
610 });
611
612 }) && action_result;
613}
614
615SOSAccountRef SOSKeychainAccountGetSharedAccount()
616{
617 __block SOSAccountRef result = NULL;
4d3cab3d 618
427c49bc
A
619 do_with_account(^(SOSAccountRef account) {
620 result = account;
621 });
4d3cab3d 622
427c49bc
A
623 return result;
624}
625
626//
627// Mark: Credential processing
628//
629
630
631bool SOSCCTryUserCredentials_Server(CFStringRef user_label, CFDataRef user_password, CFErrorRef *error)
632{
633 return do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
634 return SOSAccountTryUserCredentials(account, user_label, user_password, block_error);
635 });
636}
637
638
639static bool EnsureFreshParameters(SOSAccountRef account, CFErrorRef *error) {
640 dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
641 dispatch_retain(wait_for); // Both this scope and the block own it.
4d3cab3d 642
427c49bc
A
643 CFMutableArrayRef keysToGet = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
644 CFArrayAppendValue(keysToGet, kSOSKVSKeyParametersKey);
4d3cab3d 645
427c49bc
A
646 __block CFDictionaryRef valuesToUpdate = NULL;
647 __block bool success = false;
4d3cab3d 648
427c49bc
A
649 SOSAccountForEachCircle(account, ^(SOSCircleRef circle) {
650 CFStringRef circle_key = SOSCircleKeyCreateWithName(SOSCircleGetName(circle), NULL);
651 CFArrayAppendValue(keysToGet, circle_key);
652 CFReleaseNull(circle_key);
653 });
4d3cab3d 654
427c49bc
A
655 secnotice("updates", "***** EnsureFreshParameters *****");
656
657 SOSCloudKeychainSynchronizeAndWait(keysToGet, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFDictionaryRef returnedValues, CFErrorRef sync_error) {
658
659 if (sync_error) {
660 secerror("SOSCloudKeychainSynchronizeAndWait: %@", sync_error);
661 if (error) {
662 *error = sync_error;
663 CFRetainSafe(*error);
664 }
665 } else {
666 secnotice("updates", "SOSCloudKeychainSynchronizeAndWait: results: %@", returnedValues);
667 valuesToUpdate = returnedValues;
668 CFRetainSafe(valuesToUpdate);
669 success = true;
670 }
4d3cab3d 671
427c49bc
A
672 dispatch_semaphore_signal(wait_for);
673 dispatch_release(wait_for);
674 });
675
676 dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER);
677 // TODO: Maybe we timeout here... used to dispatch_time(DISPATCH_TIME_NOW, 30ull * NSEC_PER_SEC));
678 dispatch_release(wait_for);
4d3cab3d 679
427c49bc
A
680 if ((valuesToUpdate) && (account)) {
681 if (!SOSAccountHandleUpdates(account, valuesToUpdate, error)) {
682 secerror("Freshness update failed: %@", *error);
4d3cab3d 683
427c49bc
A
684 success = false;
685 }
686 }
687
688 CFReleaseNull(valuesToUpdate);
689 CFReleaseNull(keysToGet);
690
691 return success;
692}
693
694static bool EnsureFreshParameters_once(SOSAccountRef account, CFErrorRef *error) {
695 static dispatch_once_t once;
696 __block bool retval = false;
697 dispatch_once(&once, ^{
698 retval = EnsureFreshParameters(account, error);
699 });
700 return retval;
701}
702
703bool SOSCCSetUserCredentials_Server(CFStringRef user_label, CFDataRef user_password, CFErrorRef *error)
704{
4d3cab3d 705 return do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
427c49bc
A
706 if (!EnsureFreshParameters(account, block_error)) {
707 secnotice("updates", "EnsureFreshParameters error: %@", *block_error);
708 return false;
709 }
710 if (!SOSAccountAssertUserCredentials(account, user_label, user_password, block_error)) {
711 secnotice("updates", "EnsureFreshParameters/SOSAccountAssertUserCredentials error: %@", *block_error);
712 return false;
713 }
714
715 return true;
716 });
717
718}
719
720bool SOSCCCanAuthenticate_Server(CFErrorRef *error)
721{
722 bool result = do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
723 return SOSAccountGetPrivateCredential(account, block_error) != NULL;
724 });
4d3cab3d 725
427c49bc
A
726 if (!result && error && *error && CFErrorGetDomain(*error) == kSOSErrorDomain) {
727 CFIndex code = CFErrorGetCode(*error);
728 if (code == kSOSErrorPrivateKeyAbsent || code == kSOSErrorPublicKeyAbsent) {
729 CFReleaseNull(*error);
730 }
731 }
732
733 return result;
734}
735
736bool SOSCCPurgeUserCredentials_Server(CFErrorRef *error)
737{
738 return do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
739 SOSAccountPurgePrivateCredential(account);
740 return true;
741 });
742}
743
744
745#if USE_BETTER
746static bool sAccountInCircleCache = false;
747
748static void do_with_not_in_circle_bool_queue(bool start_account, dispatch_block_t action)
749{
750 static dispatch_queue_t account_start_queue;
751 static dispatch_queue_t not_in_circle_queue;
752 static bool account_started = false;
753
754 static dispatch_once_t onceToken;
755 dispatch_once(&onceToken, ^{
756 not_in_circle_queue = dispatch_queue_create("nis queue", DISPATCH_QUEUE_SERIAL);
757 account_start_queue = dispatch_queue_create("init nis queue", DISPATCH_QUEUE_SERIAL);;
758 account_started = false;
759 });
4d3cab3d 760
427c49bc
A
761 __block bool done = false;
762 dispatch_sync(not_in_circle_queue, ^{
763 if (account_started) {
764 done = true;
765 action();
766 }
767 });
4d3cab3d 768
427c49bc
A
769 if (!done && start_account) {
770 dispatch_sync(account_start_queue, ^{
771 __block bool do_start = false;
772 dispatch_sync(not_in_circle_queue, ^{
773 do_start = !account_started;
774 account_started = true;
775 });
776 if (do_start)
777 SOSCCThisDeviceIsInCircle(NULL); // Inflate account.
778 });
779
780 dispatch_sync(not_in_circle_queue, action);
781 }
782}
783#endif
784
785bool SOSCCThisDeviceDefinitelyNotActiveInCircle()
786{
787 return !SOSCCCircleIsOn_Artifact();
788#if USE_BETTER
789 __block bool result = false;
790 do_with_not_in_circle_bool_queue(true, ^{
791 result = sAccountInCircleCache;
792 });
4d3cab3d 793
427c49bc
A
794 return result;
795#endif
796}
797
798void SOSCCSetThisDeviceDefinitelyNotActiveInCircle(SOSCCStatus currentStatus)
799{
4d3cab3d 800 SOSCCCircleIsOn_UpdateArtifact(currentStatus);
427c49bc
A
801#if USE_BETTER
802 do_with_not_in_circle_bool_queue(false, ^{
803 sAccountInCircleCache = notActive;
804 });
805#endif
806}
807
808
809SOSCCStatus SOSCCThisDeviceIsInCircle_Server(CFErrorRef *error)
810{
811 __block SOSCCStatus status;
4d3cab3d 812
427c49bc
A
813 return do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
814 EnsureFreshParameters_once(account, NULL);
815 status = SOSAccountIsInCircles(account, block_error);
816 return true;
817 }) ? status : kSOSCCError;
818}
819
820bool SOSCCRequestToJoinCircle_Server(CFErrorRef* error)
821{
822 return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
823 EnsureFreshParameters_once(account, NULL);
824 return SOSAccountJoinCircles(account, block_error);
825 });
826}
827
828bool SOSCCRequestToJoinCircleAfterRestore_Server(CFErrorRef* error)
829{
830 return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
831 EnsureFreshParameters_once(account, NULL);
832 return SOSAccountJoinCirclesAfterRestore(account, block_error);
833 });
834}
835
836bool SOSCCResetToOffering_Server(CFErrorRef* error)
837{
838 return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
839 EnsureFreshParameters_once(account, NULL);
840 clearAllKVS(NULL);
841 return SOSAccountResetToOffering(account, block_error);
842 });
843}
844
845bool SOSCCResetToEmpty_Server(CFErrorRef* error)
846{
847 return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
848 return SOSAccountResetToEmpty(account, block_error);
849 });
850}
851
852bool SOSCCRemoveThisDeviceFromCircle_Server(CFErrorRef* error)
853{
854 return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
855 bool result = SOSAccountLeaveCircles(account, block_error);
856 return result;
857 });
858}
859
860bool SOSCCBailFromCircle_Server(uint64_t limit_in_seconds, CFErrorRef* error)
861{
862 return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
863 return SOSAccountBail(account, limit_in_seconds, block_error);
864 });
865}
866
867CFArrayRef SOSCCCopyApplicantPeerInfo_Server(CFErrorRef* error)
868{
869 __block CFArrayRef result = NULL;
870
871 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
872 EnsureFreshParameters_once(account, NULL);
873 result = SOSAccountCopyApplicants(account, block_error);
874 return result != NULL;
875 });
4d3cab3d 876
427c49bc
A
877 return result;
878}
879
880
881bool SOSCCAcceptApplicants_Server(CFArrayRef applicants, CFErrorRef* error)
882{
883 return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
884 EnsureFreshParameters_once(account, NULL);
885 return SOSAccountAcceptApplicants(account, applicants, block_error);
886 });
887}
888
889bool SOSCCRejectApplicants_Server(CFArrayRef applicants, CFErrorRef* error)
890{
891 return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
892 EnsureFreshParameters_once(account, NULL);
893 return SOSAccountRejectApplicants(account, applicants, block_error);
894 });
895}
896
897CFArrayRef SOSCCCopyPeerPeerInfo_Server(CFErrorRef* error)
898{
899 __block CFArrayRef result = NULL;
4d3cab3d 900
427c49bc
A
901 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
902 EnsureFreshParameters_once(account, NULL);
903 result = SOSAccountCopyPeers(account, block_error);
904 return result != NULL;
905 });
4d3cab3d 906
427c49bc
A
907 return result;
908}
909
910CFArrayRef SOSCCCopyConcurringPeerPeerInfo_Server(CFErrorRef* error)
911{
912 __block CFArrayRef result = NULL;
4d3cab3d 913
427c49bc
A
914 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
915 EnsureFreshParameters_once(account, NULL);
916 result = SOSAccountCopyConcurringPeers(account, block_error);
917 return result != NULL;
918 });
4d3cab3d 919
427c49bc
A
920 return result;
921}
922
923CFStringRef SOSCCCopyIncompatibilityInfo_Server(CFErrorRef* error)
924{
925 __block CFStringRef result = NULL;
4d3cab3d 926
427c49bc
A
927 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
928 EnsureFreshParameters_once(account, NULL);
929 result = SOSAccountCopyIncompatibilityInfo(account, block_error);
930 return result != NULL;
931 });
4d3cab3d 932
427c49bc
A
933 return result;
934}
935
936enum DepartureReason SOSCCGetLastDepartureReason_Server(CFErrorRef* error)
937{
938 __block enum DepartureReason result = kSOSDepartureReasonError;
4d3cab3d 939
427c49bc
A
940 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
941 EnsureFreshParameters_once(account, NULL);
942 result = SOSAccountGetLastDepartureReason(account, block_error);
943 return result != kSOSDepartureReasonError;
944 });
4d3cab3d 945
427c49bc
A
946 return result;
947}
948
949SyncWithAllPeersReason SOSCCProcessSyncWithAllPeers_Server(CFErrorRef* error)
950{
951 /*
952 #define kIOReturnLockedRead iokit_common_err(0x2c3) // device read locked
953 #define kIOReturnLockedWrite iokit_common_err(0x2c4) // device write locked
954 */
955 __block SyncWithAllPeersReason result = kSyncWithAllPeersSuccess;
956 CFErrorRef action_error = NULL;
957
958 if (!do_with_account_while_unlocked(&action_error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
959 CFErrorRef localError = NULL;
960 if (!SOSAccountSyncWithAllPeers(account, &localError)) {
961 secerror("sync with all peers failed: %@", localError);
962 CFReleaseSafe(localError);
963 // This isn't a device-locked error, but returning false will
964 // have CloudKeychainProxy ask us to try sync again after next unlock
965 result = kSyncWithAllPeersOtherFail;
966 return false;
967 }
968 return true;
969 })) {
970 if (action_error) {
971 if (SecErrorGetOSStatus(action_error) == errSecInteractionNotAllowed) {
972 secnotice("updates", "SOSAccountSyncWithAllPeers failed because device is locked; letting CloudKeychainProxy know");
973 result = kSyncWithAllPeersLocked; // tell CloudKeychainProxy to call us back when device unlocks
974 CFReleaseNull(action_error);
975 } else {
976 secerror("Unexpected error: %@", action_error);
977 }
978
979 if (error && *error == NULL) {
980 *error = action_error;
981 action_error = NULL;
982 }
983
984 CFReleaseNull(action_error);
985 }
986 }
987
988 return result;
989}
990
991void SOSCCSyncWithAllPeers(void)
992{
993 SOSCloudKeychainRequestSyncWithAllPeers(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), NULL);
994}
995
996void SOSCCHandleUpdate(CFDictionaryRef updates)
997{
998 SOSCloudKeychainHandleUpdate(updates);
999}
1000