2 * Copyright (c) 2016 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
24 #include <dispatch/dispatch.h>
25 #import <Foundation/Foundation.h>
27 #import <CloudKit/CloudKit.h>
30 #include "keychain/securityd/SecItemServer.h"
31 #include <Security/SecItemPriv.h>
33 #import <Foundation/Foundation.h>
34 #import "keychain/ckks/CKKS.h"
35 #import "keychain/ckks/CKKSKeychainView.h"
36 #import "keychain/ckks/CKKSViewManager.h"
37 #import "keychain/ckks/CKKSKey.h"
39 #import "keychain/ot/OTManager.h"
40 #import "keychain/ot/OctagonStateMachineHelpers.h"
42 NSDictionary<CKKSZoneKeyState*, NSNumber*>* CKKSZoneKeyStateMap(void) {
43 static NSDictionary<CKKSZoneKeyState*, NSNumber*>* map = nil;
44 static dispatch_once_t onceToken;
45 dispatch_once(&onceToken, ^{
47 SecCKKSZoneKeyStateReady: @0U,
48 SecCKKSZoneKeyStateError: @1U,
49 //SecCKKSZoneKeyStateCancelled: @2U,
51 SecCKKSZoneKeyStateInitializing: @3U,
52 SecCKKSZoneKeyStateInitialized: @4U,
53 SecCKKSZoneKeyStateFetchComplete: @5U,
54 SecCKKSZoneKeyStateWaitForTLK: @6U,
55 SecCKKSZoneKeyStateWaitForUnlock: @7U,
56 SecCKKSZoneKeyStateUnhealthy: @8U,
57 SecCKKSZoneKeyStateBadCurrentPointers: @9U,
58 SecCKKSZoneKeyStateNewTLKsFailed: @10U,
59 SecCKKSZoneKeyStateNeedFullRefetch: @11U,
60 SecCKKSZoneKeyStateHealTLKShares: @12U,
61 SecCKKSZoneKeyStateHealTLKSharesFailed:@13U,
62 SecCKKSZoneKeyStateWaitForFixupOperation:@14U,
63 SecCKKSZoneKeyStateReadyPendingUnlock: @15U,
64 SecCKKSZoneKeyStateFetch: @16U,
65 SecCKKSZoneKeyStateResettingZone: @17U,
66 SecCKKSZoneKeyStateResettingLocalData: @18U,
67 SecCKKSZoneKeyStateLoggedOut: @19U,
68 SecCKKSZoneKeyStateZoneCreationFailed: @20U,
69 SecCKKSZoneKeyStateWaitForTrust: @21U,
70 SecCKKSZoneKeyStateWaitForTLKUpload: @22U,
71 SecCKKSZoneKeyStateWaitForTLKCreation: @23U,
72 SecCKKSZoneKeyStateProcess: @24U,
73 SecCKKSZoneKeyStateBecomeReady: @25U,
74 SecCKKSZoneKeyStateLoseTrust: @26U,
75 SecCKKSZoneKeyStateTLKMissing: @27U,
76 SecCKKSZoneKeyStateWaitForCloudKitAccountStatus:@28U,
77 SecCKKSZoneKeyStateBeginFetch: @29U,
83 NSDictionary<NSNumber*, CKKSZoneKeyState*>* CKKSZoneKeyStateInverseMap(void) {
84 static NSDictionary<NSNumber*, CKKSZoneKeyState*>* backwardMap = nil;
85 static dispatch_once_t onceToken;
86 dispatch_once(&onceToken, ^{
87 NSDictionary<CKKSZoneKeyState*, NSNumber*>* forwardMap = CKKSZoneKeyStateMap();
88 backwardMap = [NSDictionary dictionaryWithObjects:[forwardMap allKeys] forKeys:[forwardMap allValues]];
93 NSNumber* CKKSZoneKeyToNumber(CKKSZoneKeyState* state) {
95 return CKKSZoneKeyStateMap()[SecCKKSZoneKeyStateError];
97 NSNumber* result = CKKSZoneKeyStateMap()[state];
101 return CKKSZoneKeyStateMap()[SecCKKSZoneKeyStateError];
103 CKKSZoneKeyState* CKKSZoneKeyRecover(NSNumber* stateNumber) {
105 return SecCKKSZoneKeyStateError;
107 CKKSZoneKeyState* result = CKKSZoneKeyStateInverseMap()[stateNumber];
111 return SecCKKSZoneKeyStateError;
114 NSSet<CKKSZoneKeyState*>* CKKSKeyStateNonTransientStates()
116 static NSSet<CKKSZoneKeyState*>* states = nil;
117 static dispatch_once_t onceToken;
118 dispatch_once(&onceToken, ^{
119 states = [NSSet setWithArray:@[
120 SecCKKSZoneKeyStateReady,
121 SecCKKSZoneKeyStateReadyPendingUnlock,
122 SecCKKSZoneKeyStateWaitForTrust,
123 SecCKKSZoneKeyStateWaitForTLK,
124 SecCKKSZoneKeyStateWaitForTLKCreation,
125 SecCKKSZoneKeyStateWaitForTLKUpload,
126 SecCKKSZoneKeyStateWaitForUnlock,
127 SecCKKSZoneKeyStateError,
128 SecCKKSZoneKeyStateLoggedOut,
130 OctagonStateMachineHalted,
138 // If you want CKKS to run in your daemon/tests, you must call SecCKKSEnable before bringing up the keychain db
139 static bool enableCKKS = false;
140 static bool testCKKS = false;
142 bool SecCKKSIsEnabled(void) {
143 if([CKDatabase class] == nil) {
144 // CloudKit is not linked. We cannot bring CKKS up; disable it with prejudice.
145 ckkserror_global("ckks", "CloudKit.framework appears to not be linked. Cannot enable CKKS (on pain of crash).");
152 bool SecCKKSEnable() {
157 bool SecCKKSDisable() {
162 bool SecCKKSResetSyncing(void) {
163 // The function name is a bit of a lie, but it does the thing.
164 [OTManager resetManager:true to:nil];
165 return SecCKKSIsEnabled();
168 bool SecCKKSTestsEnabled(void) {
172 bool SecCKKSTestsEnable(void) {
173 if([CKDatabase class] == nil) {
174 // CloudKit is not linked. We cannot bring CKKS up; disable it with prejudice.
175 ckkserror_global("ckks", "CloudKit.framework appears to not be linked. Cannot enable CKKS testing.");
184 bool SecCKKSTestsDisable(void) {
189 // Feature flags to twiddle behavior
190 static bool CKKSSyncManifests = false;
191 bool SecCKKSSyncManifests(void) {
192 return CKKSSyncManifests;
194 bool SecCKKSEnableSyncManifests() {
195 CKKSSyncManifests = true;
196 return CKKSSyncManifests;
198 bool SecCKKSSetSyncManifests(bool value) {
199 CKKSSyncManifests = value;
200 return CKKSSyncManifests;
203 static bool CKKSEnforceManifests = false;
204 bool SecCKKSEnforceManifests(void) {
205 return CKKSEnforceManifests;
207 bool SecCKKSEnableEnforceManifests() {
208 CKKSEnforceManifests = true;
209 return CKKSEnforceManifests;
211 bool SecCKKSSetEnforceManifests(bool value) {
212 CKKSEnforceManifests = value;
213 return CKKSEnforceManifests;
216 // defaults write com.apple.security.ckks reduce-rate-limiting YES
217 static bool CKKSReduceRateLimiting = false;
218 bool SecCKKSReduceRateLimiting(void) {
219 static dispatch_once_t onceToken;
220 dispatch_once(&onceToken, ^{
221 // Use the default value as above, or apply the preferences value if it exists
222 NSUserDefaults* defaults = [[NSUserDefaults alloc] initWithSuiteName:SecCKKSUserDefaultsSuite];
223 NSString* key = @"reduce-rate-limiting";
224 [defaults registerDefaults: @{key: CKKSReduceRateLimiting ? @YES : @NO}];
226 CKKSReduceRateLimiting = !![defaults boolForKey:@"reduce-rate-limiting"];
227 ckksnotice_global("ratelimit", "reduce-rate-limiting is %@", CKKSReduceRateLimiting ? @"on" : @"off");
230 return CKKSReduceRateLimiting;
233 bool SecCKKSSetReduceRateLimiting(bool value) {
234 (void) SecCKKSReduceRateLimiting(); // Call this once to read the defaults write
235 CKKSReduceRateLimiting = value;
236 ckksnotice_global("ratelimit", "reduce-rate-limiting is now %@", CKKSReduceRateLimiting ? @"on" : @"off");
237 return CKKSReduceRateLimiting;
240 // Here's a mechanism for CKKS feature flags with default values from NSUserDefaults:
241 /*static bool CKKSShareTLKs = true;
242 bool SecCKKSShareTLKs(void) {
244 static dispatch_once_t onceToken;
245 dispatch_once(&onceToken, ^{
246 // Use the default value as above, or apply the preferences value if it exists
247 NSUserDefaults* defaults = [[NSUserDefaults alloc] initWithSuiteName:SecCKKSUserDefaultsSuite];
248 [defaults registerDefaults: @{@"tlksharing": CKKSShareTLKs ? @YES : @NO}];
250 CKKSShareTLKs = !![defaults boolForKey:@"tlksharing"];
251 ckksnotice_global("ckksshare", "TLK sharing is %@", CKKSShareTLKs ? @"on" : @"off");
254 return CKKSShareTLKs;
257 // Feature flags to twiddle behavior for tests
258 static bool CKKSDisableAutomaticUUID = false;
259 bool SecCKKSTestDisableAutomaticUUID(void) {
261 return CKKSDisableAutomaticUUID;
266 void SecCKKSTestSetDisableAutomaticUUID(bool set) {
267 CKKSDisableAutomaticUUID = set;
270 static bool CKKSDisableSOS = false;
271 bool SecCKKSTestDisableSOS(void) {
273 return CKKSDisableSOS;
278 void SecCKKSTestSetDisableSOS(bool set) {
279 CKKSDisableSOS = set;
283 static bool CKKSDisableKeyNotifications = false;
284 bool SecCKKSTestDisableKeyNotifications(void) {
286 return CKKSDisableKeyNotifications;
291 void SecCKKSTestSetDisableKeyNotifications(bool set) {
292 CKKSDisableKeyNotifications = set;
295 static bool CKKSSkipScan = false;
296 bool SecCKKSTestSkipScan(void) {
299 bool SecCKKSSetTestSkipScan(bool value) {
300 CKKSSkipScan = value;
304 void SecCKKSTestResetFlags(void) {
305 SecCKKSTestSetDisableAutomaticUUID(false);
306 SecCKKSTestSetDisableSOS(false);
307 SecCKKSTestSetDisableKeyNotifications(false);
308 SecCKKSSetTestSkipScan(false);
311 #else /* NO OCTAGON */
313 bool SecCKKSIsEnabled(void) {
314 ckkserror_global("ckks", "CKKS was disabled at compile time.");
318 bool SecCKKSEnable() {
322 bool SecCKKSDisable() {
326 bool SecCKKSResetSyncing(void) {
327 return SecCKKSIsEnabled();
334 void SecCKKSInitialize(SecDbRef db) {
337 CKKSViewManager* manager = [CKKSViewManager manager];
338 [manager createViews];
339 [manager setupAnalytics];
341 SecDbAddNotifyPhaseBlock(db, ^(SecDbConnectionRef dbconn, SecDbTransactionPhase phase, SecDbTransactionSource source, CFArrayRef changes) {
342 SecCKKSNotifyBlock(dbconn, phase, source, changes);
345 [manager.completedSecCKKSInitialize fulfill];
347 if(!SecCKKSTestsEnabled()) {
348 static dispatch_once_t onceToken;
349 dispatch_once(&onceToken, ^{
350 [[OctagonAPSReceiver receiverForNamedDelegatePort:SecCKKSAPSNamedPort apsConnectionClass:[APSConnection class]] registerForEnvironment:APSEnvironmentProduction];
357 void SecCKKSNotifyBlock(SecDbConnectionRef dbconn, SecDbTransactionPhase phase, SecDbTransactionSource source, CFArrayRef changes) {
359 if(phase == kSecDbTransactionDidRollback) {
363 // Ignore our own changes, otherwise we'd infinite-loop.
364 if(source == kSecDbCKKSTransaction) {
365 ckksinfo_global("ckks", "Ignoring kSecDbCKKSTransaction notification");
369 CFArrayForEach(changes, ^(CFTypeRef r) {
370 SecDbItemRef deleted = NULL;
371 SecDbItemRef added = NULL;
373 SecDbEventTranslateComponents(r, (CFTypeRef*) &deleted, (CFTypeRef*) &added);
375 if(!added && !deleted) {
376 ckkserror_global("ckks", "SecDbEvent gave us garbage: %@", r);
380 [[CKKSViewManager manager] handleKeychainEventDbConnection: dbconn source:source added: added deleted: deleted];
385 void SecCKKS24hrNotification() {
388 [[CKKSViewManager manager] xpc24HrNotification];
393 void CKKSRegisterSyncStatusCallback(CFStringRef cfuuid, SecBoolCFErrorCallback cfcallback) {
395 // Keep plumbing, but transition to NS.
396 SecBoolNSErrorCallback nscallback = ^(bool result, NSError* err) {
397 cfcallback(result, (__bridge CFErrorRef) err);
400 [[CKKSViewManager manager] registerSyncStatusCallback: (__bridge NSString*) cfuuid callback:nscallback];
404 void SecCKKSPerformLocalResync() {
406 if(SecCKKSIsEnabled()) {
407 ckksnotice_global("reset", "Local keychain was reset; performing local resync");
408 [[CKKSViewManager manager] rpcResyncLocal:nil reply:^(NSError *result) {
410 ckksnotice_global("reset", "Local keychain reset resync finished with an error: %@", result);
412 ckksnotice_global("reset", "Local keychain reset resync finished successfully");