]> git.saurik.com Git - apple/security.git/blob - Security/sec/securityd/SOSCloudCircleServer.c
Security-57031.1.35.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 SOSCCCircleIsOn_UpdateArtifact(SOSAccountIsInCircles(account, NULL));
296
297 CFDataRef accountAsData = SOSAccountCopyEncodedData(account, kCFAllocatorDefault, &saveError);
298
299 require_action_quiet(accountAsData, exit, secerror("Failed to transform account into data, error: %@", saveError));
300 require_quiet(!CFEqualSafe(sLastSavedAccountData, accountAsData), exit);
301
302 if (!SOSItemUpdateOrAdd(kSOSAccountLabel, kSecAttrAccessibleAlwaysThisDeviceOnly, accountAsData, &saveError)) {
303 secerror("Can't save account: %@", saveError);
304 goto exit;
305 }
306
307 CFReleaseNull(sLastSavedAccountData);
308 sLastSavedAccountData = accountAsData;
309 accountAsData = NULL;
310
311 exit:
312 CFReleaseNull(saveError);
313 CFReleaseNull(accountAsData);
314 }
315
316
317 /*
318 Stolen from keychain_sync.c
319 */
320
321 static bool clearAllKVS(CFErrorRef *error)
322 {
323 return true;
324 __block bool result = false;
325 const uint64_t maxTimeToWaitInSeconds = 30ull * NSEC_PER_SEC;
326 dispatch_queue_t processQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
327 dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
328 dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds);
329
330 SOSCloudKeychainClearAll(processQueue, ^(CFDictionaryRef returnedValues, CFErrorRef cerror)
331 {
332 result = (cerror != NULL);
333 dispatch_semaphore_signal(waitSemaphore);
334 });
335
336 dispatch_semaphore_wait(waitSemaphore, finishTime);
337 dispatch_release(waitSemaphore);
338
339 return result;
340 }
341
342 static SOSAccountRef SOSKeychainAccountCreateSharedAccount(CFDictionaryRef our_gestalt)
343 {
344 secdebug("account", "Created account");
345
346 CFDataRef savedAccount = SOSKeychainCopySavedAccountData();
347 SOSAccountRef account = NULL;
348 SOSDataSourceFactoryRef factory = accountDataSourceOverride ? accountDataSourceOverride()
349 : SecItemDataSourceFactoryGetDefault();
350
351 if (savedAccount) {
352 CFErrorRef inflationError = NULL;
353
354 account = SOSAccountCreateFromData(kCFAllocatorDefault, savedAccount, factory, &inflationError);
355
356 if (account){
357 SOSAccountUpdateGestalt(account, our_gestalt);
358 }
359 else
360 secerror("Got error inflating account: %@", inflationError);
361 CFReleaseNull(inflationError);
362 }
363 CFReleaseSafe(savedAccount);
364
365 if (!account) {
366 account = SOSAccountCreate(kCFAllocatorDefault, our_gestalt, factory);
367
368 if (!account)
369 secerror("Got NULL creating account");
370 }
371
372 return account;
373 }
374
375 //
376 // Mark: Gestalt Handling
377 //
378
379 static CFStringRef CopyModelName(void)
380 {
381 static dispatch_once_t once;
382 static CFStringRef modelName = NULL;
383 dispatch_once(&once, ^{
384 #if TARGET_OS_EMBEDDED || TARGET_IPHONE_SIMULATOR
385 modelName = MGCopyAnswer(kMGQDeviceName, NULL);
386 #else
387 modelName = ASI_CopyComputerModelName(FALSE);
388 #endif
389 if (modelName == NULL)
390 modelName = CFSTR("Unknown model");
391 });
392 return CFStringCreateCopy(kCFAllocatorDefault, modelName);
393 }
394
395 static CFStringRef CopyComputerName(SCDynamicStoreRef store)
396 {
397 CFStringRef deviceName = SCDynamicStoreCopyComputerName(store, NULL);
398 if (deviceName == NULL) {
399 deviceName = CFSTR("Unknown name");
400 }
401 return deviceName;
402 }
403
404 static bool _EngineMessageProtocolV2Enabled(void)
405 {
406 #if DEBUG
407 //sudo rhr
408 static dispatch_once_t onceToken;
409 static bool v2_enabled = false;
410 dispatch_once(&onceToken, ^{
411 CFTypeRef v2Pref = (CFNumberRef)CFPreferencesCopyValue(CFSTR("engineV2"), CFSTR("com.apple.security"), kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
412
413 if (v2Pref && CFGetTypeID(v2Pref) == CFBooleanGetTypeID()) {
414 v2_enabled = CFBooleanGetValue((CFBooleanRef)v2Pref);
415 secnotice("server", "Engine v2 : %s", v2_enabled ? "enabled":"disabled");
416 }
417 CFReleaseSafe(v2Pref);
418 });
419
420 return v2_enabled;
421 #else
422 return false;
423 #endif
424 }
425
426
427 static CFDictionaryRef CFDictionaryCreateDeviceGestalt(SCDynamicStoreRef store, CFArrayRef keys, void *context)
428 {
429 CFStringRef modelName = CopyModelName();
430 CFStringRef computerName = CopyComputerName(store);
431 SInt32 version = _EngineMessageProtocolV2Enabled() ? kEngineMessageProtocolVersion : 0;
432 CFNumberRef protocolVersion = CFNumberCreate(0, kCFNumberSInt32Type, &version);
433
434
435 CFDictionaryRef gestalt = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
436 kPIUserDefinedDeviceName, computerName,
437 kPIDeviceModelName, modelName,
438 kPIMessageProtocolVersion, protocolVersion,
439 NULL);
440 CFReleaseSafe(modelName);
441 CFReleaseSafe(computerName);
442 CFReleaseSafe(protocolVersion);
443
444 return gestalt;
445 }
446
447 static void SOSCCProcessGestaltUpdate(SCDynamicStoreRef store, CFArrayRef keys, void *context)
448 {
449 do_with_account(^(SOSAccountRef account) {
450 if(account){
451 CFDictionaryRef gestalt = CFDictionaryCreateDeviceGestalt(store, keys, context);
452 if (SOSAccountUpdateGestalt(account, gestalt)) {
453 notify_post(kSOSCCCircleChangedNotification);
454 }
455 CFReleaseSafe(gestalt);
456 }
457 });
458 }
459
460
461 static CFDictionaryRef CFDictionaryCreateGestaltAndRegisterForUpdate(dispatch_queue_t queue, void *info)
462 {
463 SCDynamicStoreContext context = { .info = info };
464 SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("com.apple.securityd.cloudcircleserver"), SOSCCProcessGestaltUpdate, &context);
465 CFStringRef computerKey = SCDynamicStoreKeyCreateComputerName(NULL);
466 CFArrayRef keys = NULL;
467 CFDictionaryRef gestalt = NULL;
468
469 if (store == NULL || computerKey == NULL) {
470 goto done;
471 }
472 keys = CFArrayCreate(NULL, (const void **)&computerKey, 1, &kCFTypeArrayCallBacks);
473 if (keys == NULL) {
474 goto done;
475 }
476 gestalt = CFDictionaryCreateDeviceGestalt(store, keys, info);
477 SCDynamicStoreSetNotificationKeys(store, keys, NULL);
478 SCDynamicStoreSetDispatchQueue(store, queue);
479
480 done:
481 if (store) CFRelease(store);
482 if (computerKey) CFRelease(computerKey);
483 if (keys) CFRelease(keys);
484 return gestalt;
485 }
486
487 static void do_with_account(void (^action)(SOSAccountRef account));
488 static void do_with_account_async(void (^action)(SOSAccountRef account));
489
490 static SOSAccountRef GetSharedAccount(void) {
491 static SOSAccountRef sSharedAccount = NULL;
492 static dispatch_once_t onceToken;
493
494 #if !(TARGET_OS_EMBEDDED)
495 if(geteuid() == 0){
496 secerror("Cannot inflate account object as root");
497 return NULL;
498 }
499 #endif
500
501 dispatch_once(&onceToken, ^{
502 secdebug("account", "Account Creation start");
503
504 CFDictionaryRef gestalt = CFDictionaryCreateGestaltAndRegisterForUpdate(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), NULL);
505
506 if (!gestalt) {
507 #if TARGET_OS_IPHONE && TARGET_IPHONE_SIMULATOR
508 gestalt = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, NULL);
509 #else
510 secerror("Didn't get machine gestalt! This is going to be ugly.");
511 #endif
512 }
513
514 sSharedAccount = SOSKeychainAccountCreateSharedAccount(gestalt);
515
516 SOSCCSetThisDeviceDefinitelyNotActiveInCircle(SOSAccountIsInCircles(sSharedAccount, NULL));
517
518 SOSAccountAddChangeBlock(sSharedAccount, ^(SOSCircleRef circle,
519 CFSetRef peer_additions, CFSetRef peer_removals,
520 CFSetRef applicant_additions, CFSetRef applicant_removals) {
521 CFErrorRef pi_error = NULL;
522 SOSPeerInfoRef me = SOSAccountGetMyPeerInCircle(sSharedAccount, circle, &pi_error);
523 if (!me) {
524 secerror("Error finding me for change: %@", pi_error);
525 } else {
526 if (SOSCircleHasPeer(circle, me, NULL) && CFSetGetCount(peer_additions) != 0) {
527 secnotice("updates", "Requesting Ensure Peer Registration.");
528 SOSCloudKeychainRequestEnsurePeerRegistration(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), NULL);
529 }
530
531 if (CFSetContainsValue(peer_additions, me)) {
532 SOSCCSyncWithAllPeers();
533 }
534 }
535
536 CFReleaseNull(pi_error);
537
538 if (CFSetGetCount(peer_additions) != 0 ||
539 CFSetGetCount(peer_removals) != 0 ||
540 CFSetGetCount(applicant_additions) != 0 ||
541 CFSetGetCount(applicant_removals) != 0) {
542
543 SOSCCSetThisDeviceDefinitelyNotActiveInCircle(SOSAccountIsInCircles(sSharedAccount, NULL));
544 notify_post(kSOSCCCircleChangedNotification);
545 }
546 });
547
548 SOSCloudKeychainSetItemsChangedBlock(^CFArrayRef(CFDictionaryRef changes) {
549 CFRetainSafe(changes);
550 __block CFMutableArrayRef handledKeys = NULL;
551 do_with_account(^(SOSAccountRef account) {
552 CFStringRef changeDescription = SOSChangesCopyDescription(changes, false);
553 secdebug(SOSCKCSCOPE, "Received: %@", changeDescription);
554 CFReleaseSafe(changeDescription);
555
556 CFErrorRef error = NULL;
557 handledKeys = SOSTransportDispatchMessages(account, changes, &error);
558 if (!handledKeys) {
559 secerror("Error handling updates: %@", error);
560 CFReleaseNull(error);
561 }
562 });
563 CFReleaseSafe(changes);
564 return handledKeys;
565 });
566 CFReleaseSafe(gestalt);
567
568 SOSCloudKeychainRequestEnsurePeerRegistration(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), NULL);
569 });
570
571
572 return sSharedAccount;
573 }
574
575 static void do_with_account_dynamic(void (^action)(SOSAccountRef account), bool sync) {
576 SOSAccountRef account = GetSharedAccount();
577
578 if(account){
579 dispatch_block_t do_action_and_save = ^{
580 action(account);
581 SOSKeychainAccountEnsureSaved(account);
582 };
583
584 if (sync) {
585 dispatch_sync(SOSAccountGetQueue(account), do_action_and_save);
586 } else {
587 dispatch_async(SOSAccountGetQueue(account), do_action_and_save);
588 }
589 }
590 }
591
592 __unused static void do_with_account_async(void (^action)(SOSAccountRef account)) {
593 do_with_account_dynamic(action, false);
594 }
595
596 static void do_with_account(void (^action)(SOSAccountRef account)) {
597 do_with_account_dynamic(action, true);
598 }
599
600 #if TARGET_IPHONE_SIMULATOR
601 #define MKBDeviceUnlockedSinceBoot() true
602 #endif
603
604 static bool do_if_after_first_unlock(CFErrorRef *error, dispatch_block_t action)
605 {
606 bool beenUnlocked = false;
607 require_quiet(SecAKSGetHasBeenUnlocked(&beenUnlocked, error), fail);
608
609 require_action_quiet(beenUnlocked, fail,
610 SOSCreateErrorWithFormat(kSOSErrorNotReady, NULL, error, NULL,
611 CFSTR("Keybag never unlocked, ask after first unlock")));
612
613 action();
614 return true;
615
616 fail:
617 return false;
618 }
619
620 static bool do_with_account_if_after_first_unlock(CFErrorRef *error, bool (^action)(SOSAccountRef account, CFErrorRef* error))
621 {
622 __block bool action_result = false;
623
624 #if !(TARGET_OS_EMBEDDED)
625 if(geteuid() == 0){
626 secerror("Cannot inflate account object as root");
627 return false;
628 }
629 #endif
630 return do_if_after_first_unlock(error, ^{
631 do_with_account(^(SOSAccountRef account) {
632 action_result = action(account, error);
633 });
634
635 }) && action_result;
636 }
637
638 static bool do_with_account_while_unlocked(CFErrorRef *error, bool (^action)(SOSAccountRef account, CFErrorRef* error))
639 {
640 __block bool action_result = false;
641
642 #if !(TARGET_OS_EMBEDDED)
643 if(geteuid() == 0){
644 secerror("Cannot inflate account object as root");
645 return false;
646 }
647 #endif
648
649 return SecAKSDoWhileUserBagLocked(error, ^{
650 do_with_account(^(SOSAccountRef account) {
651 action_result = action(account, error);
652 });
653
654 }) && action_result;
655 }
656
657 SOSAccountRef SOSKeychainAccountGetSharedAccount()
658 {
659 __block SOSAccountRef result = NULL;
660
661 do_with_account(^(SOSAccountRef account) {
662 result = account;
663 });
664
665 return result;
666 }
667
668 //
669 // Mark: Credential processing
670 //
671
672
673 bool SOSCCTryUserCredentials_Server(CFStringRef user_label, CFDataRef user_password, CFErrorRef *error)
674 {
675 return do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
676 return SOSAccountTryUserCredentials(account, user_label, user_password, block_error);
677 });
678 }
679
680 #define kWAIT2MINID "EFRESH"
681
682 static bool EnsureFreshParameters(SOSAccountRef account, CFErrorRef *error) {
683 dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
684 dispatch_retain(wait_for); // Both this scope and the block own it.
685
686 CFMutableArrayRef keysToGet = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
687 CFArrayAppendValue(keysToGet, kSOSKVSKeyParametersKey);
688
689 __block CFDictionaryRef valuesToUpdate = NULL;
690 __block bool success = false;
691
692 secnoticeq("fresh", "%s calling SOSCloudKeychainSynchronizeAndWait", kWAIT2MINID);
693
694 SOSCloudKeychainSynchronizeAndWait(keysToGet, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFDictionaryRef returnedValues, CFErrorRef sync_error) {
695
696 if (sync_error) {
697 secerrorq("%s SOSCloudKeychainSynchronizeAndWait: %@", kWAIT2MINID, sync_error);
698 if (error) {
699 *error = sync_error;
700 CFRetainSafe(*error);
701 }
702 } else {
703 secnoticeq("fresh", "%s returned from call; in callback to SOSCloudKeychainSynchronizeAndWait: results: %@", kWAIT2MINID, returnedValues);
704 valuesToUpdate = returnedValues;
705 CFRetainSafe(valuesToUpdate);
706 success = true;
707 }
708
709 dispatch_semaphore_signal(wait_for);
710 dispatch_release(wait_for);
711 });
712
713 dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER);
714 // TODO: Maybe we timeout here... used to dispatch_time(DISPATCH_TIME_NOW, 30ull * NSEC_PER_SEC));
715 dispatch_release(wait_for);
716 CFMutableArrayRef handledKeys = NULL;
717 if ((valuesToUpdate) && (account)) {
718 handledKeys = SOSTransportDispatchMessages(account, valuesToUpdate, error);
719 if (!handledKeys) {
720 secerrorq("%s Freshness update failed: %@", kWAIT2MINID, error ? *error : NULL);
721 success = false;
722 }
723 }
724 CFReleaseNull(handledKeys);
725 CFReleaseNull(valuesToUpdate);
726 CFReleaseNull(keysToGet);
727
728 return success;
729 }
730
731 static bool Flush(CFErrorRef *error) {
732 __block bool success = false;
733
734 dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
735 dispatch_retain(wait_for); // Both this scope and the block own it.
736
737 secnotice("flush", "Starting");
738
739 SOSCloudKeychainFlush(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFDictionaryRef returnedValues, CFErrorRef sync_error) {
740 success = (sync_error == NULL);
741 if (error) {
742 CFRetainAssign(*error, sync_error);
743 }
744
745 dispatch_semaphore_signal(wait_for);
746 dispatch_release(wait_for);
747 });
748
749 dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER);
750 dispatch_release(wait_for);
751
752 secnotice("flush", "Returned %s", success? "Success": "Failure");
753
754 return success;
755 }
756
757 bool SOSCCSetUserCredentials_Server(CFStringRef user_label, CFDataRef user_password, CFErrorRef *error)
758 {
759 secnotice("updates", "Setting credentials for %@", user_label); // TODO: remove this notice
760 bool result = do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
761 if (!EnsureFreshParameters(account, block_error)) {
762 return false;
763 }
764 if (!SOSAccountAssertUserCredentials(account, user_label, user_password, block_error)) {
765 secnotice("updates", "EnsureFreshParameters/SOSAccountAssertUserCredentials error: %@", *block_error);
766 return false;
767 }
768 return true;
769 });
770
771 return result && Flush(error);
772 }
773
774 bool SOSCCCanAuthenticate_Server(CFErrorRef *error)
775 {
776 bool result = do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
777 return SOSAccountGetPrivateCredential(account, block_error) != NULL;
778 });
779
780 if (!result && error && *error && CFErrorGetDomain(*error) == kSOSErrorDomain) {
781 CFIndex code = CFErrorGetCode(*error);
782 if (code == kSOSErrorPrivateKeyAbsent || code == kSOSErrorPublicKeyAbsent) {
783 CFReleaseNull(*error);
784 }
785 }
786
787 return result;
788 }
789
790 bool SOSCCPurgeUserCredentials_Server(CFErrorRef *error)
791 {
792 return do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
793 SOSAccountPurgePrivateCredential(account);
794 return true;
795 });
796 }
797
798
799 #if USE_BETTER
800 static bool sAccountInCircleCache = false;
801
802 static void do_with_not_in_circle_bool_queue(bool start_account, dispatch_block_t action)
803 {
804 static dispatch_queue_t account_start_queue;
805 static dispatch_queue_t not_in_circle_queue;
806 static bool account_started = false;
807
808 static dispatch_once_t onceToken;
809 dispatch_once(&onceToken, ^{
810 not_in_circle_queue = dispatch_queue_create("nis queue", DISPATCH_QUEUE_SERIAL);
811 account_start_queue = dispatch_queue_create("init nis queue", DISPATCH_QUEUE_SERIAL);;
812 account_started = false;
813 });
814
815 __block bool done = false;
816 dispatch_sync(not_in_circle_queue, ^{
817 if (account_started) {
818 done = true;
819 action();
820 }
821 });
822
823 if (!done && start_account) {
824 dispatch_sync(account_start_queue, ^{
825 __block bool do_start = false;
826 dispatch_sync(not_in_circle_queue, ^{
827 do_start = !account_started;
828 account_started = true;
829 });
830 if (do_start)
831 SOSCCThisDeviceIsInCircle(NULL); // Inflate account.
832 });
833
834 dispatch_sync(not_in_circle_queue, action);
835 }
836 }
837 #endif
838
839 bool SOSCCThisDeviceDefinitelyNotActiveInCircle()
840 {
841 return !SOSCCCircleIsOn_Artifact();
842 #if USE_BETTER
843 __block bool result = false;
844 do_with_not_in_circle_bool_queue(true, ^{
845 result = sAccountInCircleCache;
846 });
847
848 return result;
849 #endif
850 }
851
852 void SOSCCSetThisDeviceDefinitelyNotActiveInCircle(SOSCCStatus currentStatus)
853 {
854 SOSCCCircleIsOn_UpdateArtifact(currentStatus);
855 #if USE_BETTER
856 do_with_not_in_circle_bool_queue(false, ^{
857 sAccountInCircleCache = notActive;
858 });
859 #endif
860 }
861
862
863 SOSCCStatus SOSCCThisDeviceIsInCircle_Server(CFErrorRef *error)
864 {
865 __block SOSCCStatus status;
866
867 return do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
868 status = SOSAccountIsInCircles(account, block_error);
869 return true;
870 }) ? status : kSOSCCError;
871 }
872
873 bool SOSCCRequestToJoinCircle_Server(CFErrorRef* error)
874 {
875 __block bool result = true;
876
877 return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
878 result = SOSAccountJoinCircles(account, block_error);
879 return result;
880 });
881 }
882
883 bool SOSCCRequestToJoinCircleAfterRestore_Server(CFErrorRef* error)
884 {
885 __block bool result = true;
886 bool returned = false;
887 returned = do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
888 SOSAccountEnsurePeerRegistration(account, block_error);
889 result = SOSAccountJoinCirclesAfterRestore(account, block_error);
890 return result;
891 });
892 return returned;
893
894 }
895
896 bool SOSCCRequestEnsureFreshParameters_Server(CFErrorRef* error)
897 {
898 __block bool result = true;
899 bool returned = false;
900 returned = do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
901 result = EnsureFreshParameters(account, NULL);
902 return result;
903 });
904 return returned;
905 }
906
907 CFStringRef SOSCCRequestDeviceID_Server(CFErrorRef *error)
908 {
909 __block CFStringRef result = NULL;
910
911 (void) do_with_account_while_unlocked(error, ^bool(SOSAccountRef account, CFErrorRef *error) {
912 result = SOSAccountGetDeviceID(account, error);
913 return (!isNull(result));
914 });
915 return result;
916 }
917
918 bool SOSCCSetDeviceID_Server(CFStringRef IDS, CFErrorRef *error){
919 __block bool result = true;
920
921 return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
922 result = SOSAccountSetMyDSID(account, IDS, block_error);
923 return result;
924 });
925
926 }
927 bool SOSCCResetToOffering_Server(CFErrorRef* error)
928 {
929 __block bool result = true;
930
931 return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
932 clearAllKVS(NULL);
933 result = SOSAccountResetToOffering(account, block_error);
934 return result;
935 });
936
937 }
938
939 bool SOSCCResetToEmpty_Server(CFErrorRef* error)
940 {
941 __block bool result = true;
942
943 return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
944 result = SOSAccountResetToEmpty(account, block_error);
945 return result;
946 });
947
948 }
949
950 bool SOSCCRemoveThisDeviceFromCircle_Server(CFErrorRef* error)
951 {
952 __block bool result = true;
953
954 return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
955 result = SOSAccountLeaveCircles(account, block_error);
956 return result;
957 });
958 }
959
960 bool SOSCCBailFromCircle_Server(uint64_t limit_in_seconds, CFErrorRef* error)
961 {
962 __block bool result = true;
963
964 return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
965 result = SOSAccountBail(account, limit_in_seconds, block_error);
966 return result;
967 });
968
969 }
970
971 CFArrayRef SOSCCCopyApplicantPeerInfo_Server(CFErrorRef* error)
972 {
973 __block CFArrayRef result = NULL;
974
975 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
976 result = SOSAccountCopyApplicants(account, block_error);
977 return result != NULL;
978 });
979
980 return result;
981 }
982
983 CFArrayRef SOSCCCopyGenerationPeerInfo_Server(CFErrorRef* error)
984 {
985 __block CFArrayRef result = NULL;
986
987 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
988 result = SOSAccountCopyGeneration(account, block_error);
989 return result != NULL;
990 });
991
992 return result;
993 }
994
995 CFArrayRef SOSCCCopyValidPeerPeerInfo_Server(CFErrorRef* error)
996 {
997 __block CFArrayRef result = NULL;
998
999 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
1000 result = SOSAccountCopyValidPeers(account, block_error);
1001 return result != NULL;
1002 });
1003
1004 return result;
1005 }
1006
1007 bool SOSCCValidateUserPublic_Server(CFErrorRef* error)
1008 {
1009 __block bool result = NULL;
1010
1011 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
1012 result = SOSValidateUserPublic(account, block_error);
1013 return result;
1014 });
1015
1016 return result;
1017 }
1018
1019 CFArrayRef SOSCCCopyNotValidPeerPeerInfo_Server(CFErrorRef* error)
1020 {
1021 __block CFArrayRef result = NULL;
1022
1023 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
1024 result = SOSAccountCopyNotValidPeers(account, block_error);
1025 return result != NULL;
1026 });
1027
1028 return result;
1029 }
1030
1031 CFArrayRef SOSCCCopyRetirementPeerInfo_Server(CFErrorRef* error)
1032 {
1033 __block CFArrayRef result = NULL;
1034
1035 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
1036 result = SOSAccountCopyRetired(account, block_error);
1037 return result != NULL;
1038 });
1039
1040 return result;
1041 }
1042
1043 bool SOSCCAcceptApplicants_Server(CFArrayRef applicants, CFErrorRef* error)
1044 {
1045 __block bool result = true;
1046 return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
1047 result = SOSAccountAcceptApplicants(account, applicants, block_error);
1048 return result;
1049 });
1050
1051 }
1052
1053 bool SOSCCRejectApplicants_Server(CFArrayRef applicants, CFErrorRef* error)
1054 {
1055 __block bool result = true;
1056 return do_with_account_while_unlocked(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
1057 result = SOSAccountRejectApplicants(account, applicants, block_error);
1058 return result;
1059 });
1060 }
1061
1062 CFArrayRef SOSCCCopyPeerPeerInfo_Server(CFErrorRef* error)
1063 {
1064 __block CFArrayRef result = NULL;
1065
1066 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
1067 result = SOSAccountCopyPeers(account, block_error);
1068 return result != NULL;
1069 });
1070
1071 return result;
1072 }
1073
1074 CFArrayRef SOSCCCopyConcurringPeerPeerInfo_Server(CFErrorRef* error)
1075 {
1076 __block CFArrayRef result = NULL;
1077
1078 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
1079 result = SOSAccountCopyConcurringPeers(account, block_error);
1080 return result != NULL;
1081 });
1082
1083 return result;
1084 }
1085
1086 CFStringRef SOSCCCopyIncompatibilityInfo_Server(CFErrorRef* error)
1087 {
1088 __block CFStringRef result = NULL;
1089
1090 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
1091 result = SOSAccountCopyIncompatibilityInfo(account, block_error);
1092 return result != NULL;
1093 });
1094
1095 return result;
1096 }
1097
1098 enum DepartureReason SOSCCGetLastDepartureReason_Server(CFErrorRef* error)
1099 {
1100 __block enum DepartureReason result = kSOSDepartureReasonError;
1101
1102 (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
1103 result = SOSAccountGetLastDepartureReason(account, block_error);
1104 return result != kSOSDepartureReasonError;
1105 });
1106
1107 return result;
1108 }
1109
1110
1111 bool SOSCCProcessEnsurePeerRegistration_Server(CFErrorRef* error)
1112 {
1113 secnotice("updates", "Request for registering peers");
1114 return do_with_account_while_unlocked(error, ^bool(SOSAccountRef account, CFErrorRef *error) {
1115 return SOSAccountEnsurePeerRegistration(account, error);
1116 });
1117 }
1118
1119 SyncWithAllPeersReason SOSCCProcessSyncWithAllPeers_Server(CFErrorRef* error)
1120 {
1121 /*
1122 #define kIOReturnLockedRead iokit_common_err(0x2c3) // device read locked
1123 #define kIOReturnLockedWrite iokit_common_err(0x2c4) // device write locked
1124 */
1125 __block SyncWithAllPeersReason result = kSyncWithAllPeersSuccess;
1126 CFErrorRef action_error = NULL;
1127
1128 if (!do_with_account_while_unlocked(&action_error, ^bool (SOSAccountRef account, CFErrorRef* block_error) {
1129 CFErrorRef localError = NULL;
1130 if (!SOSAccountSyncWithAllPeers(account, &localError)) {
1131 secerror("sync with all peers failed: %@", localError);
1132 CFReleaseSafe(localError);
1133 // This isn't a device-locked error, but returning false will
1134 // have CloudKeychainProxy ask us to try sync again after next unlock
1135 result = kSyncWithAllPeersOtherFail;
1136 return false;
1137 }
1138 return true;
1139 })) {
1140 if (action_error) {
1141 if (SecErrorGetOSStatus(action_error) == errSecInteractionNotAllowed) {
1142 secnotice("updates", "SOSAccountSyncWithAllPeers failed because device is locked; letting CloudKeychainProxy know");
1143 result = kSyncWithAllPeersLocked; // tell CloudKeychainProxy to call us back when device unlocks
1144 CFReleaseNull(action_error);
1145 } else {
1146 secerror("Unexpected error: %@", action_error);
1147 }
1148
1149 if (error && *error == NULL) {
1150 *error = action_error;
1151 action_error = NULL;
1152 }
1153
1154 CFReleaseNull(action_error);
1155 }
1156 }
1157
1158 return result;
1159 }
1160
1161 void SOSCCSyncWithAllPeers(void)
1162 {
1163 SOSCloudKeychainRequestSyncWithAllPeers(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), NULL);
1164 }
1165
1166 CF_RETURNS_RETAINED CFArrayRef SOSCCHandleUpdateKeyParameter(CFDictionaryRef updates)
1167 {
1168 CFArrayRef result = NULL;
1169 SOSAccountRef account = SOSKeychainAccountGetSharedAccount(); //HACK to make sure itemsChangedBlock is set
1170 (account) ? (result = SOSCloudKeychainHandleUpdateKeyParameter(updates)) : (result = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault));
1171 return result;
1172 }
1173
1174 CF_RETURNS_RETAINED CFArrayRef SOSCCHandleUpdateCircle(CFDictionaryRef updates)
1175 {
1176 CFArrayRef result = NULL;
1177 SOSAccountRef account = SOSKeychainAccountGetSharedAccount(); //HACK to make sure itemsChangedBlock is set
1178 (account) ? (result = SOSCloudKeychainHandleUpdateKeyParameter(updates)) : (result = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault));
1179 return result;
1180 }
1181
1182 CF_RETURNS_RETAINED CFArrayRef SOSCCHandleUpdateMessage(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