]> git.saurik.com Git - apple/security.git/blob - Security/sec/securityd/SOSCloudCircleServer.c
Security-57031.40.6.tar.gz
[apple/security.git] / Security / sec / securityd / SOSCloudCircleServer.c
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/CFURL.h>
27
28 #include <securityd/SOSCloudCircleServer.h>
29 #include <SecureObjectSync/SOSCloudCircle.h>
30 #include <SecureObjectSync/SOSCloudCircleInternal.h>
31 #include <SecureObjectSync/SOSCircle.h>
32 #include <SecureObjectSync/SOSAccount.h>
33 #include <SecureObjectSync/SOSAccountPriv.h>
34 #include <SecureObjectSync/SOSFullPeerInfo.h>
35 #include <SecureObjectSync/SOSPeerInfoInternal.h>
36 #include <SecureObjectSync/SOSInternal.h>
37 #include <SecureObjectSync/SOSUserKeygen.h>
38 #include <SecureObjectSync/SOSMessage.h>
39 #include <SecureObjectSync/SOSTransport.h>
40 #include <SecureObjectSync/SOSKVSKeys.h>
41
42 #include <utilities/SecCFWrappers.h>
43 #include <utilities/SecCFRelease.h>
44 #include <utilities/debugging.h>
45 #include <CKBridge/SOSCloudKeychainClient.h>
46
47 #include <corecrypto/ccrng.h>
48 #include <corecrypto/ccrng_pbkdf2_prng.h>
49 #include <corecrypto/ccec.h>
50 #include <corecrypto/ccdigest.h>
51 #include <corecrypto/ccsha2.h>
52 #include <CommonCrypto/CommonRandomSPI.h>
53 #include <Security/SecKeyPriv.h>
54 #include <Security/SecFramework.h>
55
56 #include <utilities/SecFileLocations.h>
57 #include <utilities/SecAKSWrappers.h>
58 #include <securityd/SecItemServer.h>
59 #include <Security/SecItemPriv.h>
60
61 #include <TargetConditionals.h>
62
63 #include <utilities/iCloudKeychainTrace.h>
64
65 #if TARGET_OS_EMBEDDED || TARGET_IPHONE_SIMULATOR
66 #include <MobileGestalt.h>
67 #else
68 #include <AppleSystemInfo/AppleSystemInfo.h>
69
70 // We need authorization, but that doesn't exist
71 // on sec built for desktop (iOS in a process)
72 // Define AuthorizationRef here to make SystemConfiguration work
73 // as if it's on iOS.
74 typedef const struct AuthorizationOpaqueRef * AuthorizationRef;
75 #endif
76
77 #define SOSCKCSCOPE "sync"
78
79 #define USE_SYSTEMCONFIGURATION_PRIVATE_HEADERS
80 #import <SystemConfiguration/SystemConfiguration.h>
81
82 #include <notify.h>
83
84 static SOSCCAccountDataSourceFactoryBlock accountDataSourceOverride = NULL;
85
86 bool SOSKeychainAccountSetFactoryForAccount(SOSCCAccountDataSourceFactoryBlock block)
87 {
88 accountDataSourceOverride = Block_copy(block);
89
90 return true;
91 }
92
93 //
94 // Forward declared
95 //
96
97 static void do_with_account(void (^action)(SOSAccountRef account));
98 static void do_with_account_async(void (^action)(SOSAccountRef account));
99
100 //
101 // Constants
102 //
103 CFStringRef kSOSInternalAccessGroup = CFSTR("com.apple.security.sos");
104
105 CFStringRef kSOSAccountLabel = CFSTR("iCloud Keychain Account Meta-data");
106
107 static CFStringRef accountFileName = CFSTR("PersistedAccount.plist");
108
109 static CFDictionaryRef SOSItemCopyQueryForSyncItems(CFStringRef service, bool returnData)
110 {
111 return CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
112 kSecClass, kSecClassGenericPassword,
113 kSecAttrService, service,
114 kSecAttrAccessGroup, kSOSInternalAccessGroup,
115 kSecReturnData, returnData ? kCFBooleanTrue : kCFBooleanFalse,
116 NULL);
117 }
118
119 CFDataRef SOSItemCopy(CFStringRef service, CFErrorRef* error)
120 {
121 CFDictionaryRef query = SOSItemCopyQueryForSyncItems(service, true);
122
123 CFDataRef result = NULL;
124
125 OSStatus copyResult = SecItemCopyMatching(query, (CFTypeRef*) &result);
126
127 CFReleaseNull(query);
128
129 if (copyResult != noErr) {
130 SecError(copyResult, error, CFSTR("Error %@ reading for service '%@'"), result, service);
131 CFReleaseNull(result);
132 return NULL;
133 }
134
135 if (!isData(result)) {
136 SOSCreateErrorWithFormat(kSOSErrorProcessingFailure, NULL, error, NULL, CFSTR("SecItemCopyMatching returned non-data in '%@'"), service);
137 CFReleaseNull(result);
138 return NULL;
139 }
140
141 return result;
142 }
143
144 static CFDataRef SOSKeychainCopySavedAccountData()
145 {
146 CFErrorRef error = NULL;
147 CFDataRef accountData = SOSItemCopy(kSOSAccountLabel, &error);
148 if (!accountData)
149 secnotice("account", "Failed to load account: %@", error);
150 CFReleaseNull(error);
151
152 return accountData;
153 }
154
155 bool SOSItemUpdateOrAdd(CFStringRef service, CFStringRef accessibility, CFDataRef data, CFErrorRef *error)
156 {
157 CFDictionaryRef query = SOSItemCopyQueryForSyncItems(service, false);
158
159 CFDictionaryRef update = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
160 kSecValueData, data,
161 kSecAttrAccessible, accessibility,
162 NULL);
163 OSStatus saveStatus = SecItemUpdate(query, update);
164
165 if (errSecItemNotFound == saveStatus) {
166 CFMutableDictionaryRef add = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, query);
167 CFDictionaryForEach(update, ^(const void *key, const void *value) {
168 CFDictionaryAddValue(add, key, value);
169 });
170 saveStatus = SecItemAdd(add, NULL);
171 CFReleaseNull(add);
172 }
173
174 CFReleaseNull(query);
175 CFReleaseNull(update);
176
177 return SecError(saveStatus, error, CFSTR("Error saving %@ to service '%@'"), data, service);
178 }
179
180 static CFStringRef accountStatusFileName = CFSTR("accountStatus.plist");
181 #include <utilities/der_plist.h>
182 #include <utilities/der_plist_internal.h>
183 #include <corecrypto/ccder.h>
184 #if 0
185 static const uint8_t* ccder_decode_bool(bool* boolean, const uint8_t* der, const uint8_t *der_end)
186 {
187 if (NULL == der)
188 return NULL;
189
190 size_t payload_size = 0;
191 const uint8_t *payload = ccder_decode_tl(CCDER_BOOLEAN, &payload_size, der, der_end);
192
193 if (NULL == payload || (der_end - payload) < 1 || payload_size != 1) {
194 return NULL;
195 }
196
197 if (boolean)
198 *boolean = (*payload != 0);
199
200 return payload + payload_size;
201 }
202 #endif
203
204 bool SOSCCCircleIsOn_Artifact(void) {
205 bool circle_on = false;
206 CFDataRef accountStatus = NULL;
207 CFURLRef accountStatusFileURL = SecCopyURLForFileInKeychainDirectory(accountStatusFileName);
208 require_quiet(accountStatusFileURL && CFURLResourceIsReachable(accountStatusFileURL, NULL), xit);
209 accountStatus = (CFDataRef) CFPropertyListReadFromFile(accountStatusFileURL);
210
211 if(isData(accountStatus)) {
212 size_t size = CFDataGetLength(accountStatus);
213 const uint8_t *der = CFDataGetBytePtr(accountStatus);
214 const uint8_t *der_p = der;
215
216 const uint8_t *sequence_end;
217 der_p = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &sequence_end, der_p, der_p + size);
218 der_p = ccder_decode_bool(&circle_on, der_p, sequence_end);
219 (void) der_p;
220 }
221
222 xit:
223 CFReleaseSafe(accountStatusFileURL);
224 CFReleaseSafe(accountStatus);
225
226 return circle_on;
227 }
228
229 #if 0
230 static size_t ccder_sizeof_bool(bool value __unused, CFErrorRef *error)
231 {
232 return ccder_sizeof(CCDER_BOOLEAN, 1);
233 }
234
235
236 static uint8_t* ccder_encode_bool(bool value, const uint8_t *der, uint8_t *der_end)
237 {
238 uint8_t value_byte = value;
239
240 return ccder_encode_tl(CCDER_BOOLEAN, 1, der,
241 ccder_encode_body(1, &value_byte, der, der_end));
242 }
243 #endif
244
245 static void SOSCCCircleIsOn_SetArtifact(bool account_on) {
246 static CFDataRef sLastSavedAccountStatus = NULL;
247 CFErrorRef saveError = NULL;
248 size_t der_size = ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, ccder_sizeof_bool(account_on, NULL));
249 uint8_t der[der_size];
250 uint8_t *der_end = der + der_size;
251 der_end = ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
252 ccder_encode_bool(account_on, der, der_end));
253
254 CFDataRef accountStatusAsData = CFDataCreate(kCFAllocatorDefault, der_end, der_size);
255
256 require_quiet(accountStatusAsData, exit);
257 if (sLastSavedAccountStatus && CFEqual(sLastSavedAccountStatus, accountStatusAsData)) goto exit;
258
259 CFURLRef accountStatusFileURL = SecCopyURLForFileInKeychainDirectory(accountStatusFileName);
260 CFPropertyListWriteToFile((CFPropertyListRef) accountStatusAsData, accountStatusFileURL);
261 CFReleaseSafe(accountStatusFileURL);
262
263 CFReleaseNull(sLastSavedAccountStatus);
264 sLastSavedAccountStatus = accountStatusAsData;
265 accountStatusAsData = NULL;
266
267 exit:
268 CFReleaseNull(saveError);
269 CFReleaseNull(accountStatusAsData);
270 }
271
272 static void SOSCCCircleIsOn_UpdateArtifact(SOSCCStatus status)
273 {
274 switch (status) {
275 case kSOSCCCircleAbsent:
276 case kSOSCCNotInCircle:
277 SOSCCCircleIsOn_SetArtifact(false);
278 break;
279 case kSOSCCInCircle:
280 case kSOSCCRequestPending:
281 SOSCCCircleIsOn_SetArtifact(true);
282 break;
283 case kSOSCCError:
284 default:
285 // do nothing
286 break;
287 }
288 }
289
290 static void SOSKeychainAccountEnsureSaved(SOSAccountRef account)
291 {
292 static CFDataRef sLastSavedAccountData = NULL;
293
294 CFErrorRef saveError = NULL;
295
296 if(SOSAccountGetMyFullPeerInCircleNamedIfPresent(account, CFSTR("ak"), NULL) == NULL) {
297 return;
298 }
299
300 SOSCCCircleIsOn_UpdateArtifact(SOSAccountIsInCircles(account, NULL));
301
302 CFDataRef accountAsData = SOSAccountCopyEncodedData(account, kCFAllocatorDefault, &saveError);
303
304 require_action_quiet(accountAsData, exit, secerror("Failed to transform account into data, error: %@", saveError));
305 require_quiet(!CFEqualSafe(sLastSavedAccountData, accountAsData), exit);
306
307 if (!SOSItemUpdateOrAdd(kSOSAccountLabel, kSecAttrAccessibleAlwaysThisDeviceOnly, accountAsData, &saveError)) {
308 secerror("Can't save account: %@", saveError);
309 goto exit;
310 }
311
312 CFReleaseNull(sLastSavedAccountData);
313 sLastSavedAccountData = accountAsData;
314 accountAsData = NULL;
315
316 exit:
317 CFReleaseNull(saveError);
318 CFReleaseNull(accountAsData);
319 }
320
321
322 /*
323 Stolen from keychain_sync.c
324 */
325
326 static bool clearAllKVS(CFErrorRef *error)
327 {
328 return true;
329 __block bool result = false;
330 const uint64_t maxTimeToWaitInSeconds = 30ull * NSEC_PER_SEC;
331 dispatch_queue_t processQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
332 dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
333 dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds);
334
335 SOSCloudKeychainClearAll(processQueue, ^(CFDictionaryRef returnedValues, CFErrorRef cerror)
336 {
337 result = (cerror != NULL);
338 dispatch_semaphore_signal(waitSemaphore);
339 });
340
341 dispatch_semaphore_wait(waitSemaphore, finishTime);
342 dispatch_release(waitSemaphore);
343
344 return result;
345 }
346
347 static SOSAccountRef SOSKeychainAccountCreateSharedAccount(CFDictionaryRef our_gestalt)
348 {
349 secdebug("account", "Created account");
350
351 CFDataRef savedAccount = SOSKeychainCopySavedAccountData();
352 SOSAccountRef account = NULL;
353 SOSDataSourceFactoryRef factory = accountDataSourceOverride ? accountDataSourceOverride()
354 : SecItemDataSourceFactoryGetDefault();
355
356 if (savedAccount) {
357 CFErrorRef inflationError = NULL;
358
359 account = SOSAccountCreateFromData(kCFAllocatorDefault, savedAccount, factory, &inflationError);
360
361 if(account && SOSAccountGetMyFullPeerInCircleNamedIfPresent(account, CFSTR("ak"), NULL) == NULL) {
362 SOSAccountRef newAccount = SOSAccountCreate(kCFAllocatorDefault, our_gestalt, factory);
363
364 if (!newAccount) {
365 secnotice("repair_account", "Tried to repair bad account - got null account");
366 } else {
367 account = newAccount;
368 }
369 }
370
371 if (account){
372 SOSAccountUpdateGestalt(account, our_gestalt);
373 } else {
374 secerror("Got error inflating account: %@", inflationError);
375 }
376
377 CFReleaseNull(inflationError);
378 }
379 CFReleaseSafe(savedAccount);
380
381 if (!account) {
382 account = SOSAccountCreate(kCFAllocatorDefault, our_gestalt, factory);
383
384 if (!account)
385 secerror("Got NULL creating account");
386 }
387
388 return account;
389 }
390
391 //
392 // Mark: Gestalt Handling
393 //
394
395 static CFStringRef CopyModelName(void)
396 {
397 static dispatch_once_t once;
398 static CFStringRef modelName = NULL;
399 dispatch_once(&once, ^{
400 #if TARGET_OS_EMBEDDED || TARGET_IPHONE_SIMULATOR
401 modelName = MGCopyAnswer(kMGQDeviceName, NULL);
402 #else
403 modelName = ASI_CopyComputerModelName(FALSE);
404 #endif
405 if (modelName == NULL)
406 modelName = CFSTR("Unknown model");
407 });
408 return CFStringCreateCopy(kCFAllocatorDefault, modelName);
409 }
410
411 static CFStringRef CopyComputerName(SCDynamicStoreRef store)
412 {
413 CFStringRef deviceName = SCDynamicStoreCopyComputerName(store, NULL);
414 if (deviceName == NULL) {
415 deviceName = CFSTR("Unknown name");
416 }
417 return deviceName;
418 }
419
420 static bool _EngineMessageProtocolV2Enabled(void)
421 {
422 #if DEBUG
423 //sudo rhr
424 static dispatch_once_t onceToken;
425 static bool v2_enabled = false;
426 dispatch_once(&onceToken, ^{
427 CFTypeRef v2Pref = (CFNumberRef)CFPreferencesCopyValue(CFSTR("engineV2"), CFSTR("com.apple.security"), kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
428
429 if (v2Pref && CFGetTypeID(v2Pref) == CFBooleanGetTypeID()) {
430 v2_enabled = CFBooleanGetValue((CFBooleanRef)v2Pref);
431 secnotice("server", "Engine v2 : %s", v2_enabled ? "enabled":"disabled");
432 }
433 CFReleaseSafe(v2Pref);
434 });
435
436 return v2_enabled;
437 #else
438 return false;
439 #endif
440 }
441
442
443 static CFDictionaryRef CFDictionaryCreateDeviceGestalt(SCDynamicStoreRef store, CFArrayRef keys, void *context)
444 {
445 CFStringRef modelName = CopyModelName();
446 CFStringRef computerName = CopyComputerName(store);
447 SInt32 version = _EngineMessageProtocolV2Enabled() ? kEngineMessageProtocolVersion : 0;
448 CFNumberRef protocolVersion = CFNumberCreate(0, kCFNumberSInt32Type, &version);
449
450
451 CFDictionaryRef gestalt = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
452 kPIUserDefinedDeviceName, computerName,
453 kPIDeviceModelName, modelName,
454 kPIMessageProtocolVersion, protocolVersion,
455 NULL);
456 CFReleaseSafe(modelName);
457 CFReleaseSafe(computerName);
458 CFReleaseSafe(protocolVersion);
459
460 return gestalt;
461 }
462
463 static void SOSCCProcessGestaltUpdate(SCDynamicStoreRef store, CFArrayRef keys, void *context)
464 {
465 do_with_account(^(SOSAccountRef account) {
466 if(account){
467 CFDictionaryRef gestalt = CFDictionaryCreateDeviceGestalt(store, keys, context);
468 if (SOSAccountUpdateGestalt(account, gestalt)) {
469 notify_post(kSOSCCCircleChangedNotification);
470 }
471 CFReleaseSafe(gestalt);
472 }
473 });
474 }
475
476
477 static CFDictionaryRef CFDictionaryCreateGestaltAndRegisterForUpdate(dispatch_queue_t queue, void *info)
478 {
479 SCDynamicStoreContext context = { .info = info };
480 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("com.apple.securityd.cloudcircleserver"), SOSCCProcessGestaltUpdate, &context);
481 CFStringRef computerKey = SCDynamicStoreKeyCreateComputerName(NULL);
482 CFArrayRef keys = NULL;
483 CFDictionaryRef gestalt = NULL;
484
485 if (store == NULL || computerKey == NULL) {
486 goto done;
487 }
488 keys = CFArrayCreate(NULL, (const void **)&computerKey, 1, &kCFTypeArrayCallBacks);
489 if (keys == NULL) {
490 goto done;
491 }
492 gestalt = CFDictionaryCreateDeviceGestalt(store, keys, info);
493 SCDynamicStoreSetNotificationKeys(store, keys, NULL);
494 SCDynamicStoreSetDispatchQueue(store, queue);
495
496 done:
497 if (store) CFRelease(store);
498 if (computerKey) CFRelease(computerKey);
499 if (keys) CFRelease(keys);
500 return gestalt;
501 }
502
503 static void do_with_account(void (^action)(SOSAccountRef account));
504 static void do_with_account_async(void (^action)(SOSAccountRef account));
505
506 static SOSAccountRef GetSharedAccount(void) {
507 static SOSAccountRef sSharedAccount = NULL;
508 static dispatch_once_t onceToken;
509
510 #if !(TARGET_OS_EMBEDDED)
511 if(geteuid() == 0){
512 secerror("Cannot inflate account object as root");
513 return NULL;
514 }
515 #endif
516
517 dispatch_once(&onceToken, ^{
518 secdebug("account", "Account Creation start");
519
520 CFDictionaryRef gestalt = CFDictionaryCreateGestaltAndRegisterForUpdate(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), NULL);
521
522 if (!gestalt) {
523 #if TARGET_OS_IPHONE && TARGET_IPHONE_SIMULATOR
524 gestalt = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, NULL);
525 #else
526 secerror("Didn't get machine gestalt! This is going to be ugly.");
527 #endif
528 }
529
530 sSharedAccount = SOSKeychainAccountCreateSharedAccount(gestalt);
531
532 SOSCCSetThisDeviceDefinitelyNotActiveInCircle(SOSAccountIsInCircles(sSharedAccount, NULL));
533
534 SOSAccountAddChangeBlock(sSharedAccount, ^(SOSCircleRef circle,
535 CFSetRef peer_additions, CFSetRef peer_removals,
536 CFSetRef applicant_additions, CFSetRef applicant_removals) {
537 CFErrorRef pi_error = NULL;
538 SOSPeerInfoRef me = SOSAccountGetMyPeerInCircle(sSharedAccount, circle, &pi_error);
539 if (!me) {
540 secerror("Error finding me for change: %@", pi_error);
541 } else {
542 if (SOSCircleHasPeer(circle, me, NULL) && CFSetGetCount(peer_additions) != 0) {
543 secnotice("updates", "Requesting Ensure Peer Registration.");
544 SOSCloudKeychainRequestEnsurePeerRegistration(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), NULL);
545 }
546
547 if (CFSetContainsValue(peer_additions, me)) {
548 SOSCCSyncWithAllPeers();
549 }
550 }
551
552 CFReleaseNull(pi_error);
553
554 if (CFSetGetCount(peer_additions) != 0 ||
555 CFSetGetCount(peer_removals) != 0 ||
556 CFSetGetCount(applicant_additions) != 0 ||
557 CFSetGetCount(applicant_removals) != 0) {
558
559 SOSCCSetThisDeviceDefinitelyNotActiveInCircle(SOSAccountIsInCircles(sSharedAccount, NULL));
560 notify_post(kSOSCCCircleChangedNotification);
561 }
562 });
563
564 SOSCloudKeychainSetItemsChangedBlock(^CFArrayRef(CFDictionaryRef changes) {
565 CFRetainSafe(changes);
566 __block CFMutableArrayRef handledKeys = NULL;
567 do_with_account(^(SOSAccountRef account) {
568 CFStringRef changeDescription = SOSChangesCopyDescription(changes, false);
569 secdebug(SOSCKCSCOPE, "Received: %@", changeDescription);
570 CFReleaseSafe(changeDescription);
571
572 CFErrorRef error = NULL;
573 handledKeys = SOSTransportDispatchMessages(account, changes, &error);
574 if (!handledKeys) {
575 secerror("Error handling updates: %@", error);
576 CFReleaseNull(error);
577 }
578 });
579 CFReleaseSafe(changes);
580 return handledKeys;
581 });
582 CFReleaseSafe(gestalt);
583
584 SOSCloudKeychainRequestEnsurePeerRegistration(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), NULL);
585 });
586
587
588 return sSharedAccount;
589 }
590
591 static void do_with_account_dynamic(void (^action)(SOSAccountRef account), bool sync) {
592 SOSAccountRef account = GetSharedAccount();
593
594 if(account){
595 dispatch_block_t do_action_and_save = ^{
596 action(account);
597 SOSKeychainAccountEnsureSaved(account);
598 };
599
600 if (sync) {
601 dispatch_sync(SOSAccountGetQueue(account), do_action_and_save);
602 } else {
603 dispatch_async(SOSAccountGetQueue(account), do_action_and_save);
604 }
605 }
606 }
607
608 __unused static void do_with_account_async(void (^action)(SOSAccountRef account)) {
609 do_with_account_dynamic(action, false);
610 }
611
612 static void do_with_account(void (^action)(SOSAccountRef account)) {
613 do_with_account_dynamic(action, true);
614 }
615
616 #if TARGET_IPHONE_SIMULATOR
617 #define MKBDeviceUnlockedSinceBoot() true
618 #endif
619
620 static bool do_if_after_first_unlock(CFErrorRef *error, dispatch_block_t action)
621 {
622 bool beenUnlocked = false;
623 require_quiet(SecAKSGetHasBeenUnlocked(&beenUnlocked, error), fail);
624
625 require_action_quiet(beenUnlocked, fail,
626 SOSCreateErrorWithFormat(kSOSErrorNotReady, NULL, error, NULL,
627 CFSTR("Keybag never unlocked, ask after first unlock")));
628
629 action();
630 return true;
631
632 fail:
633 return false;
634 }
635
636 static bool do_with_account_if_after_first_unlock(CFErrorRef *error, bool (^action)(SOSAccountRef account, CFErrorRef* error))
637 {
638 __block bool action_result = false;
639
640 #if !(TARGET_OS_EMBEDDED)
641 if(geteuid() == 0){
642 secerror("Cannot inflate account object as root");
643 return false;
644 }
645 #endif
646 return do_if_after_first_unlock(error, ^{
647 do_with_account(^(SOSAccountRef account) {
648 action_result = action(account, error);
649 });
650
651 }) && action_result;
652 }
653
654 static bool do_with_account_while_unlocked(CFErrorRef *error, bool (^action)(SOSAccountRef account, CFErrorRef* error))
655 {
656 __block bool action_result = false;
657
658 #if !(TARGET_OS_EMBEDDED)
659 if(geteuid() == 0){
660 secerror("Cannot inflate account object as root");
661 return false;
662 }
663 #endif
664
665 return SecAKSDoWhileUserBagLocked(error, ^{
666 do_with_account(^(SOSAccountRef account) {
667 action_result = action(account, error);
668 });
669
670 }) && action_result;
671 }
672
673 SOSAccountRef SOSKeychainAccountGetSharedAccount()
674 {
675 __block SOSAccountRef result = NULL;
676
677 do_with_account(^(SOSAccountRef account) {
678 result = account;
679 });
680
681 return result;
682 }
683
684 //
685 // Mark: Credential processing
686 //
687
688
689 bool SOSCCTryUserCredentials_Server(CFStringRef user_label, CFDataRef user_password, CFErrorRef *error)
690 {
691 return do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
692 return SOSAccountTryUserCredentials(account, user_label, user_password, block_error);
693 });
694 }
695
696 #define kWAIT2MINID "EFRESH"
697
698 static bool EnsureFreshParameters(SOSAccountRef account, CFErrorRef *error) {
699 dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
700 dispatch_retain(wait_for); // Both this scope and the block own it.
701
702 CFMutableArrayRef keysToGet = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
703 CFArrayAppendValue(keysToGet, kSOSKVSKeyParametersKey);
704
705 __block CFDictionaryRef valuesToUpdate = NULL;
706 __block bool success = false;
707
708 secnoticeq("fresh", "%s calling SOSCloudKeychainSynchronizeAndWait", kWAIT2MINID);
709
710 SOSCloudKeychainSynchronizeAndWait(keysToGet, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFDictionaryRef returnedValues, CFErrorRef sync_error) {
711
712 if (sync_error) {
713 secerrorq("%s SOSCloudKeychainSynchronizeAndWait: %@", kWAIT2MINID, sync_error);
714 if (error) {
715 *error = sync_error;
716 CFRetainSafe(*error);
717 }
718 } else {
719 secnoticeq("fresh", "%s returned from call; in callback to SOSCloudKeychainSynchronizeAndWait: results: %@", kWAIT2MINID, returnedValues);
720 valuesToUpdate = returnedValues;
721 CFRetainSafe(valuesToUpdate);
722 success = true;
723 }
724
725 dispatch_semaphore_signal(wait_for);
726 dispatch_release(wait_for);
727 });
728
729 dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER);
730 // TODO: Maybe we timeout here... used to dispatch_time(DISPATCH_TIME_NOW, 30ull * NSEC_PER_SEC));
731 dispatch_release(wait_for);
732 CFMutableArrayRef handledKeys = NULL;
733 if ((valuesToUpdate) && (account)) {
734 handledKeys = SOSTransportDispatchMessages(account, valuesToUpdate, error);
735 if (!handledKeys) {
736 secerrorq("%s Freshness update failed: %@", kWAIT2MINID, error ? *error : NULL);
737 success = false;
738 }
739 }
740 CFReleaseNull(handledKeys);
741 CFReleaseNull(valuesToUpdate);
742 CFReleaseNull(keysToGet);
743
744 return success;
745 }
746
747 static bool Flush(CFErrorRef *error) {
748 __block bool success = false;
749
750 dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
751 dispatch_retain(wait_for); // Both this scope and the block own it.
752
753 secnotice("flush", "Starting");
754
755 SOSCloudKeychainFlush(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFDictionaryRef returnedValues, CFErrorRef sync_error) {
756 success = (sync_error == NULL);
757 if (error) {
758 CFRetainAssign(*error, sync_error);
759 }
760
761 dispatch_semaphore_signal(wait_for);
762 dispatch_release(wait_for);
763 });
764
765 dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER);
766 dispatch_release(wait_for);
767
768 secnotice("flush", "Returned %s", success? "Success": "Failure");
769
770 return success;
771 }
772
773 bool SOSCCSetUserCredentials_Server(CFStringRef user_label, CFDataRef user_password, CFErrorRef *error)
774 {
775 secnotice("updates", "Setting credentials for %@", user_label); // TODO: remove this notice
776 bool result = do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
777 if (!EnsureFreshParameters(account, block_error)) {
778 return false;
779 }
780 if (!SOSAccountAssertUserCredentials(account, user_label, user_password, block_error)) {
781 secnotice("updates", "EnsureFreshParameters/SOSAccountAssertUserCredentials error: %@", *block_error);
782 return false;
783 }
784 return true;
785 });
786
787 return result && Flush(error);
788 }
789
790 bool SOSCCCanAuthenticate_Server(CFErrorRef *error)
791 {
792 bool result = do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
793 return SOSAccountGetPrivateCredential(account, block_error) != NULL;
794 });
795
796 if (!result && error && *error && CFErrorGetDomain(*error) == kSOSErrorDomain) {
797 CFIndex code = CFErrorGetCode(*error);
798 if (code == kSOSErrorPrivateKeyAbsent || code == kSOSErrorPublicKeyAbsent) {
799 CFReleaseNull(*error);
800 }
801 }
802
803 return result;
804 }
805
806 bool SOSCCPurgeUserCredentials_Server(CFErrorRef *error)
807 {
808 return do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
809 SOSAccountPurgePrivateCredential(account);
810 return true;
811 });
812 }
813
814
815 #if USE_BETTER
816 static bool sAccountInCircleCache = false;
817
818 static void do_with_not_in_circle_bool_queue(bool start_account, dispatch_block_t action)
819 {
820 static dispatch_queue_t account_start_queue;
821 static dispatch_queue_t not_in_circle_queue;
822 static bool account_started = false;
823
824 static dispatch_once_t onceToken;
825 dispatch_once(&onceToken, ^{
826 not_in_circle_queue = dispatch_queue_create("nis queue", DISPATCH_QUEUE_SERIAL);
827 account_start_queue = dispatch_queue_create("init nis queue", DISPATCH_QUEUE_SERIAL);;
828 account_started = false;
829 });
830
831 __block bool done = false;
832 dispatch_sync(not_in_circle_queue, ^{
833 if (account_started) {
834 done = true;
835 action();
836 }
837 });
838
839 if (!done && start_account) {
840 dispatch_sync(account_start_queue, ^{
841 __block bool do_start = false;
842 dispatch_sync(not_in_circle_queue, ^{
843 do_start = !account_started;
844 account_started = true;
845 });
846 if (do_start)
847 SOSCCThisDeviceIsInCircle(NULL); // Inflate account.
848 });
849
850 dispatch_sync(not_in_circle_queue, action);
851 }
852 }
853 #endif
854
855 bool SOSCCThisDeviceDefinitelyNotActiveInCircle()
856 {
857 return !SOSCCCircleIsOn_Artifact();
858 #if USE_BETTER
859 __block bool result = false;
860 do_with_not_in_circle_bool_queue(true, ^{
861 result = sAccountInCircleCache;
862 });
863
864 return result;
865 #endif
866 }
867
868 void SOSCCSetThisDeviceDefinitelyNotActiveInCircle(SOSCCStatus currentStatus)
869 {
870 SOSCCCircleIsOn_UpdateArtifact(currentStatus);
871 #if USE_BETTER
872 do_with_not_in_circle_bool_queue(false, ^{
873 sAccountInCircleCache = notActive;
874 });
875 #endif
876 }
877
878
879 SOSCCStatus SOSCCThisDeviceIsInCircle_Server(CFErrorRef *error)
880 {
881 __block SOSCCStatus status;
882
883 return do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
884 status = SOSAccountIsInCircles(account, block_error);
885 return true;
886 }) ? status : kSOSCCError;
887 }
888
889 bool SOSCCRequestToJoinCircle_Server(CFErrorRef* error)
890 {
891 __block bool result = true;
892
893 return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
894 result = SOSAccountJoinCircles(account, block_error);
895 return result;
896 });
897 }
898
899 bool SOSCCRequestToJoinCircleAfterRestore_Server(CFErrorRef* error)
900 {
901 __block bool result = true;
902 bool returned = false;
903 returned = do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
904 SOSAccountEnsurePeerRegistration(account, block_error);
905 result = SOSAccountJoinCirclesAfterRestore(account, block_error);
906 return result;
907 });
908 return returned;
909
910 }
911
912 bool SOSCCRequestEnsureFreshParameters_Server(CFErrorRef* error)
913 {
914 __block bool result = true;
915 bool returned = false;
916 returned = do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
917 result = EnsureFreshParameters(account, NULL);
918 return result;
919 });
920 return returned;
921 }
922
923 CFStringRef SOSCCRequestDeviceID_Server(CFErrorRef *error)
924 {
925 __block CFStringRef result = NULL;
926
927 (void) do_with_account_while_unlocked(error, ^bool(SOSAccountRef account, CFErrorRef *error) {
928 result = SOSAccountGetDeviceID(account, error);
929 return (!isNull(result));
930 });
931 return result;
932 }
933
934 bool SOSCCSetDeviceID_Server(CFStringRef IDS, CFErrorRef *error){
935 __block bool result = true;
936
937 return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
938 result = SOSAccountSetMyDSID(account, IDS, block_error);
939 return result;
940 });
941
942 }
943 bool SOSCCResetToOffering_Server(CFErrorRef* error)
944 {
945 __block bool result = true;
946
947 return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
948 clearAllKVS(NULL);
949 result = SOSAccountResetToOffering(account, block_error);
950 return result;
951 });
952
953 }
954
955 bool SOSCCResetToEmpty_Server(CFErrorRef* error)
956 {
957 __block bool result = true;
958
959 return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
960 result = SOSAccountResetToEmpty(account, block_error);
961 return result;
962 });
963
964 }
965
966 bool SOSCCRemoveThisDeviceFromCircle_Server(CFErrorRef* error)
967 {
968 __block bool result = true;
969
970 return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
971 result = SOSAccountLeaveCircles(account, block_error);
972 return result;
973 });
974 }
975
976 bool SOSCCBailFromCircle_Server(uint64_t limit_in_seconds, CFErrorRef* error)
977 {
978 __block bool result = true;
979
980 return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
981 result = SOSAccountBail(account, limit_in_seconds, block_error);
982 return result;
983 });
984
985 }
986
987 CFArrayRef SOSCCCopyApplicantPeerInfo_Server(CFErrorRef* error)
988 {
989 __block CFArrayRef result = NULL;
990
991 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
992 result = SOSAccountCopyApplicants(account, block_error);
993 return result != NULL;
994 });
995
996 return result;
997 }
998
999 CFArrayRef SOSCCCopyGenerationPeerInfo_Server(CFErrorRef* error)
1000 {
1001 __block CFArrayRef result = NULL;
1002
1003 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
1004 result = SOSAccountCopyGeneration(account, block_error);
1005 return result != NULL;
1006 });
1007
1008 return result;
1009 }
1010
1011 CFArrayRef SOSCCCopyValidPeerPeerInfo_Server(CFErrorRef* error)
1012 {
1013 __block CFArrayRef result = NULL;
1014
1015 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
1016 result = SOSAccountCopyValidPeers(account, block_error);
1017 return result != NULL;
1018 });
1019
1020 return result;
1021 }
1022
1023 bool SOSCCValidateUserPublic_Server(CFErrorRef* error)
1024 {
1025 __block bool result = NULL;
1026
1027 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
1028 result = SOSValidateUserPublic(account, block_error);
1029 return result;
1030 });
1031
1032 return result;
1033 }
1034
1035 CFArrayRef SOSCCCopyNotValidPeerPeerInfo_Server(CFErrorRef* error)
1036 {
1037 __block CFArrayRef result = NULL;
1038
1039 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
1040 result = SOSAccountCopyNotValidPeers(account, block_error);
1041 return result != NULL;
1042 });
1043
1044 return result;
1045 }
1046
1047 CFArrayRef SOSCCCopyRetirementPeerInfo_Server(CFErrorRef* error)
1048 {
1049 __block CFArrayRef result = NULL;
1050
1051 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
1052 result = SOSAccountCopyRetired(account, block_error);
1053 return result != NULL;
1054 });
1055
1056 return result;
1057 }
1058
1059 bool SOSCCAcceptApplicants_Server(CFArrayRef applicants, CFErrorRef* error)
1060 {
1061 __block bool result = true;
1062 return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
1063 result = SOSAccountAcceptApplicants(account, applicants, block_error);
1064 return result;
1065 });
1066
1067 }
1068
1069 bool SOSCCRejectApplicants_Server(CFArrayRef applicants, CFErrorRef* error)
1070 {
1071 __block bool result = true;
1072 return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
1073 result = SOSAccountRejectApplicants(account, applicants, block_error);
1074 return result;
1075 });
1076 }
1077
1078 CFArrayRef SOSCCCopyPeerPeerInfo_Server(CFErrorRef* error)
1079 {
1080 __block CFArrayRef result = NULL;
1081
1082 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
1083 result = SOSAccountCopyPeers(account, block_error);
1084 return result != NULL;
1085 });
1086
1087 return result;
1088 }
1089
1090 CFArrayRef SOSCCCopyConcurringPeerPeerInfo_Server(CFErrorRef* error)
1091 {
1092 __block CFArrayRef result = NULL;
1093
1094 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
1095 result = SOSAccountCopyConcurringPeers(account, block_error);
1096 return result != NULL;
1097 });
1098
1099 return result;
1100 }
1101
1102 CFStringRef SOSCCCopyIncompatibilityInfo_Server(CFErrorRef* error)
1103 {
1104 __block CFStringRef result = NULL;
1105
1106 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
1107 result = SOSAccountCopyIncompatibilityInfo(account, block_error);
1108 return result != NULL;
1109 });
1110
1111 return result;
1112 }
1113
1114 enum DepartureReason SOSCCGetLastDepartureReason_Server(CFErrorRef* error)
1115 {
1116 __block enum DepartureReason result = kSOSDepartureReasonError;
1117
1118 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
1119 result = SOSAccountGetLastDepartureReason(account, block_error);
1120 return result != kSOSDepartureReasonError;
1121 });
1122
1123 return result;
1124 }
1125
1126
1127 bool SOSCCProcessEnsurePeerRegistration_Server(CFErrorRef* error)
1128 {
1129 secnotice("updates", "Request for registering peers");
1130 return do_with_account_while_unlocked(error, ^bool(SOSAccountRef account, CFErrorRef *error) {
1131 return SOSAccountEnsurePeerRegistration(account, error);
1132 });
1133 }
1134
1135 SyncWithAllPeersReason SOSCCProcessSyncWithAllPeers_Server(CFErrorRef* error)
1136 {
1137 /*
1138 #define kIOReturnLockedRead iokit_common_err(0x2c3) // device read locked
1139 #define kIOReturnLockedWrite iokit_common_err(0x2c4) // device write locked
1140 */
1141 __block SyncWithAllPeersReason result = kSyncWithAllPeersSuccess;
1142 CFErrorRef action_error = NULL;
1143
1144 if (!do_with_account_while_unlocked(&action_error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
1145 CFErrorRef localError = NULL;
1146 if (!SOSAccountSyncWithAllPeers(account, &localError)) {
1147 secerror("sync with all peers failed: %@", localError);
1148 CFReleaseSafe(localError);
1149 // This isn't a device-locked error, but returning false will
1150 // have CloudKeychainProxy ask us to try sync again after next unlock
1151 result = kSyncWithAllPeersOtherFail;
1152 return false;
1153 }
1154 return true;
1155 })) {
1156 if (action_error) {
1157 if (SecErrorGetOSStatus(action_error) == errSecInteractionNotAllowed) {
1158 secnotice("updates", "SOSAccountSyncWithAllPeers failed because device is locked; letting CloudKeychainProxy know");
1159 result = kSyncWithAllPeersLocked; // tell CloudKeychainProxy to call us back when device unlocks
1160 CFReleaseNull(action_error);
1161 } else {
1162 secerror("Unexpected error: %@", action_error);
1163 }
1164
1165 if (error && *error == NULL) {
1166 *error = action_error;
1167 action_error = NULL;
1168 }
1169
1170 CFReleaseNull(action_error);
1171 }
1172 }
1173
1174 return result;
1175 }
1176
1177 void SOSCCSyncWithAllPeers(void)
1178 {
1179 SOSCloudKeychainRequestSyncWithAllPeers(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), NULL);
1180 }
1181
1182 CF_RETURNS_RETAINED CFArrayRef SOSCCHandleUpdateKeyParameter(CFDictionaryRef updates)
1183 {
1184 CFArrayRef result = NULL;
1185 SOSAccountRef account = SOSKeychainAccountGetSharedAccount(); //HACK to make sure itemsChangedBlock is set
1186 (account) ? (result = SOSCloudKeychainHandleUpdateKeyParameter(updates)) : (result = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault));
1187 return result;
1188 }
1189
1190 CF_RETURNS_RETAINED CFArrayRef SOSCCHandleUpdateCircle(CFDictionaryRef updates)
1191 {
1192 CFArrayRef result = NULL;
1193 SOSAccountRef account = SOSKeychainAccountGetSharedAccount(); //HACK to make sure itemsChangedBlock is set
1194 (account) ? (result = SOSCloudKeychainHandleUpdateKeyParameter(updates)) : (result = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault));
1195 return result;
1196 }
1197
1198 CF_RETURNS_RETAINED CFArrayRef SOSCCHandleUpdateMessage(CFDictionaryRef updates)
1199 {
1200 CFArrayRef result = NULL;
1201 SOSAccountRef account = SOSKeychainAccountGetSharedAccount(); //HACK to make sure itemsChangedBlock is set
1202 (account) ? (result = SOSCloudKeychainHandleUpdateKeyParameter(updates)) : (result = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault));
1203 return result;
1204 }
1205
1206