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.
74typedef 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
84static SOSCCAccountDataSourceFactoryBlock accountDataSourceOverride = NULL;
85
86bool SOSKeychainAccountSetFactoryForAccount(SOSCCAccountDataSourceFactoryBlock block)
87{
88    accountDataSourceOverride = Block_copy(block);
89
90    return true;
91}
92
93//
94// Forward declared
95//
96
97static void do_with_account(void (^action)(SOSAccountRef account));
98static void do_with_account_async(void (^action)(SOSAccountRef account));
99
100//
101// Constants
102//
103CFStringRef kSOSInternalAccessGroup = CFSTR("com.apple.security.sos");
104
105CFStringRef kSOSAccountLabel = CFSTR("iCloud Keychain Account Meta-data");
106
107static CFStringRef accountFileName = CFSTR("PersistedAccount.plist");
108
109static 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
119CFDataRef 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
144static 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
155bool 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
180static 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
185static 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
204bool 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
222xit:
223    CFReleaseSafe(accountStatusFileURL);
224    CFReleaseSafe(accountStatus);
225
226    return circle_on;
227}
228
229#if 0
230static size_t ccder_sizeof_bool(bool value __unused, CFErrorRef *error)
231{
232    return ccder_sizeof(CCDER_BOOLEAN, 1);
233}
234
235
236static 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
245static 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
267exit:
268    CFReleaseNull(saveError);
269    CFReleaseNull(accountStatusAsData);
270}
271
272static 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
290static 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
311exit:
312    CFReleaseNull(saveError);
313    CFReleaseNull(accountAsData);
314}
315
316
317/*
318 Stolen from keychain_sync.c
319 */
320
321static 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
342static 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
379static 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
395static 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
404static 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
427static 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
447static 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
461static 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
480done:
481    if (store) CFRelease(store);
482    if (computerKey) CFRelease(computerKey);
483    if (keys) CFRelease(keys);
484    return gestalt;
485}
486
487static void do_with_account(void (^action)(SOSAccountRef account));
488static void do_with_account_async(void (^action)(SOSAccountRef account));
489
490static 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
575static 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
596static 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
604static 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
616fail:
617    return false;
618}
619
620static 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
638static 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
657SOSAccountRef 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
673bool 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
682static 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
731static 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
757bool 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
774bool 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
790bool 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
800static bool sAccountInCircleCache = false;
801
802static 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
839bool 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
852void 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
863SOSCCStatus 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
873bool 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
883bool 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
896bool 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
907CFStringRef 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
918bool 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}
927bool 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
939bool 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
950bool 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
960bool 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
971CFArrayRef 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
983CFArrayRef 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
995CFArrayRef 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
1007bool 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
1019CFArrayRef 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
1031CFArrayRef 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
1043bool 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
1053bool 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
1062CFArrayRef 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
1074CFArrayRef 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
1086CFStringRef 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
1098enum 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
1111bool 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
1119SyncWithAllPeersReason 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
1161void SOSCCSyncWithAllPeers(void)
1162{
1163    SOSCloudKeychainRequestSyncWithAllPeers(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), NULL);
1164}
1165
1166CF_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
1174CF_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
1182CF_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
1191