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@
25 #import <Foundation/Foundation.h>
26 #include <dispatch/dispatch.h>
28 #import "keychain/analytics/CKKSLaunchSequence.h"
29 #import "keychain/ckks/OctagonAPSReceiver.h"
30 #import "keychain/ckks/CKKSLockStateTracker.h"
31 #import "keychain/ckks/CKKSReachabilityTracker.h"
32 #import "keychain/ckks/CloudKitDependencies.h"
34 #import "keychain/ot/OctagonFlags.h"
35 #import "keychain/ot/OctagonStateMachine.h"
37 #include "keychain/securityd/SecDbItem.h"
38 #include <utilities/SecDb.h>
40 #import "keychain/ckks/CKKS.h"
41 #import "keychain/ckks/CKKSFetchAllRecordZoneChangesOperation.h"
42 #import "keychain/ckks/CKKSGroupOperation.h"
43 #import "keychain/ckks/CKKSIncomingQueueOperation.h"
44 #import "keychain/ckks/CKKSNearFutureScheduler.h"
45 #import "keychain/ckks/CKKSNewTLKOperation.h"
46 #import "keychain/ckks/CKKSNotifier.h"
47 #import "keychain/ckks/CKKSOutgoingQueueOperation.h"
48 #import "keychain/ckks/CKKSPeer.h"
49 #import "keychain/ckks/CKKSPeerProvider.h"
50 #import "keychain/ckks/CKKSProcessReceivedKeysOperation.h"
51 #import "keychain/ckks/CKKSReencryptOutgoingItemsOperation.h"
52 #import "keychain/ckks/CKKSScanLocalItemsOperation.h"
53 #import "keychain/ckks/CKKSTLKShareRecord.h"
54 #import "keychain/ckks/CKKSUpdateDeviceStateOperation.h"
55 #import "keychain/ckks/CKKSZoneModifier.h"
56 #import "keychain/ckks/CKKSZoneChangeFetcher.h"
57 #import "keychain/ckks/CKKSSynchronizeOperation.h"
58 #import "keychain/ckks/CKKSLocalSynchronizeOperation.h"
59 #import "keychain/ckks/CKKSProvideKeySetOperation.h"
60 #import "keychain/ckks/CKKSOperationDependencies.h"
61 #import "keychain/trust/TrustedPeers/TPSyncingPolicy.h"
65 NS_ASSUME_NONNULL_BEGIN
69 @
class CKKSSynchronizeOperation
;
70 @
class CKKSRateLimiter
;
71 @
class CKKSOutgoingQueueEntry
;
72 @
class CKKSZoneChangeFetcher
;
73 @
class CKKSCurrentKeySet
;
75 @interface CKKSKeychainView
: NSObject
<CKKSCloudKitAccountStateListener
,
76 CKKSChangeFetcherClient
,
77 CKKSPeerUpdateListener
,
78 CKKSDatabaseProviderProtocol
,
79 OctagonStateMachineEngine
>
81 @
property (readonly
) NSString
* zoneName
;
82 @property CKKSAccountStatus accountStatus
;
83 @
property (readonly
) CKContainer
* container
;
84 @
property (readonly
) CKDatabase
* database
;
85 @
property (weak
) CKKSAccountStateTracker
* accountTracker
;
86 @
property (weak
) CKKSReachabilityTracker
* reachabilityTracker
;
87 @
property (readonly
) CKKSCloudKitClassDependencies
* cloudKitClassDependencies
;
88 @
property (readonly
) dispatch_queue_t queue
;
90 @
property (readonly
) CKRecordZoneID
* zoneID
;
92 @property CKKSCondition
* loggedIn
;
93 @property CKKSCondition
* loggedOut
;
94 @property CKKSCondition
* accountStateKnown
;
96 @property CKKSAccountStatus trustStatus
;
98 @
property (nullable
) CKKSLaunchSequence
*launch
;
100 @property CKKSLockStateTracker
* lockStateTracker
;
102 // Is this view currently syncing keychain modifications?
103 @
property (readonly
) BOOL itemSyncingEnabled
;
105 @
property (readonly
) OctagonStateMachine
* stateMachine
;
107 // If the key hierarchy isn't coming together, it might be because we're out of sync with cloudkit.
108 // Use this to track if we've completed a full refetch, so fix-up operations can be done.
109 @property
bool keyStateMachineRefetched
;
111 // Set this to request a key state refetch (tests only)
112 @property
bool keyStateFullRefetchRequested
;
114 @
property (nullable
) CKKSResultOperation
* keyStateReadyDependency
;
116 // Full of condition variables, if you'd like to try to wait until the key hierarchy is in some state
117 @
property (readonly
) NSDictionary
<CKKSZoneKeyState
*, CKKSCondition
*>* keyHierarchyConditions
;
119 @property CKKSZoneChangeFetcher
* zoneChangeFetcher
;
121 @
property (nullable
) CKKSNearFutureScheduler
* suggestTLKUpload
;
122 @
property (nullable
) CKKSNearFutureScheduler
* requestPolicyCheck
;
124 /* Used for debugging: just what happened last time we ran this? */
125 @property CKKSIncomingQueueOperation
* lastIncomingQueueOperation
;
126 @property CKKSNewTLKOperation
* lastNewTLKOperation
;
127 @property CKKSOutgoingQueueOperation
* lastOutgoingQueueOperation
;
128 @property CKKSProcessReceivedKeysOperation
* lastProcessReceivedKeysOperation
;
129 @property CKKSReencryptOutgoingItemsOperation
* lastReencryptOutgoingItemsOperation
;
130 @property CKKSSynchronizeOperation
* lastSynchronizeOperation
;
131 @property CKKSResultOperation
* lastFixupOperation
;
133 /* Used for testing: pause operation types by adding operations here */
134 @property NSOperation
* holdReencryptOutgoingItemsOperation
;
135 @property NSOperation
* holdOutgoingQueueOperation
;
136 @property NSOperation
* holdIncomingQueueOperation
;
137 @property NSOperation
* holdLocalSynchronizeOperation
;
138 @property CKKSResultOperation
* holdFixupOperation
;
140 /* Used for testing */
141 @property BOOL initiatedLocalScan
;
143 /* Trigger this to tell the whole machine that this view has changed */
144 @property CKKSNearFutureScheduler
* notifyViewChangedScheduler
;
146 /* Trigger this to tell the whole machine that this view is more ready then before */
147 @property CKKSNearFutureScheduler
* notifyViewReadyScheduler
;
149 @
property (readonly
) CKKSOperationDependencies
* operationDependencies
;
151 - (instancetype
)initWithContainer
:(CKContainer
*)container
152 zoneName
:(NSString
*)zoneName
153 accountTracker
:(CKKSAccountStateTracker
*)accountTracker
154 lockStateTracker
:(CKKSLockStateTracker
*)lockStateTracker
155 reachabilityTracker
:(CKKSReachabilityTracker
*)reachabilityTracker
156 changeFetcher
:(CKKSZoneChangeFetcher
*)fetcher
157 zoneModifier
:(CKKSZoneModifier
*)zoneModifier
158 savedTLKNotifier
:(CKKSNearFutureScheduler
*)savedTLKNotifier
159 cloudKitClassDependencies
:(CKKSCloudKitClassDependencies
*)cloudKitClassDependencies
;
161 /* Trust state management */
163 // suggestTLKUpload and requestPolicyCheck are essentially callbacks to request certain procedures from the view owner.
164 // When suggestTLKUpload is triggered, the CKKS view believes it has some new TLKs that need uploading, and Octagon should take care of them.
165 // When requestPolicyCheck is triggered, the CKKS view would like Octagon to perform a live check on which syncing policy is in effect,
166 // successfully retrieving all peer's opinions, and would like -setCurrentSyncingPolicy to be called with the updated policy (even if it is
168 - (void)beginTrustedOperation
:(NSArray
<id
<CKKSPeerProvider
>>*)peerProviders
169 suggestTLKUpload
:(CKKSNearFutureScheduler
*)suggestTLKUpload
170 requestPolicyCheck
:(CKKSNearFutureScheduler
*)requestPolicyCheck
;
172 - (void)endTrustedOperation
;
174 /* CloudKit account management */
176 - (void)beginCloudKitOperation
;
178 // If this policy indicates that this view should not sync, this view will no longer sync keychain items,
179 // but it will continue to particpate in TLK sharing.
180 // If policyIsFresh is set, any items discovered that do not match this policy will be moved.
181 - (void)setCurrentSyncingPolicy
:(TPSyncingPolicy
*)syncingPolicy policyIsFresh
:(BOOL
)policyIsFresh
;
183 - (void)receivedItemForWrongView
;
185 /* Synchronous operations */
187 - (void)handleKeychainEventDbConnection
:(SecDbConnectionRef
)dbconn
188 source
:(SecDbTransactionSource
)txionSource
189 added
:(SecDbItemRef _Nullable
)added
190 deleted
:(SecDbItemRef _Nullable
)deleted
191 rateLimiter
:(CKKSRateLimiter
*)rateLimiter
;
193 - (void)setCurrentItemForAccessGroup
:(NSData
*)newItemPersistentRef
194 hash
:(NSData
*)newItemSHA1
195 accessGroup
:(NSString
*)accessGroup
196 identifier
:(NSString
*)identifier
197 replacing
:(NSData
* _Nullable
)oldCurrentItemPersistentRef
198 hash
:(NSData
* _Nullable
)oldItemSHA1
199 complete
:(void (^)(NSError
* operror
))complete
;
201 - (void)getCurrentItemForAccessGroup
:(NSString
*)accessGroup
202 identifier
:(NSString
*)identifier
203 fetchCloudValue
:(bool)fetchCloudValue
204 complete
:(void (^)(NSString
* uuid
, NSError
* operror
))complete
;
206 - (bool)outgoingQueueEmpty
:(NSError
* __autoreleasing
*)error
;
208 - (CKKSResultOperation
<CKKSKeySetProviderOperationProtocol
>*)findKeySet
:(BOOL
)refetchBeforeReturningKeySet
;
209 - (void)receiveTLKUploadRecords
:(NSArray
<CKRecord
*>*)records
;
211 // Returns true if this zone would like a new TLK to be uploaded
212 - (BOOL
)requiresTLKUpload
;
214 - (void)waitForKeyHierarchyReadiness
;
215 - (void)cancelAllOperations
;
217 /* Asynchronous kickoffs */
219 - (CKKSOutgoingQueueOperation
*)processOutgoingQueue
:(CKOperationGroup
* _Nullable
)ckoperationGroup
;
220 - (CKKSOutgoingQueueOperation
*)processOutgoingQueueAfter
:(CKKSResultOperation
* _Nullable
)after
221 ckoperationGroup
:(CKOperationGroup
* _Nullable
)ckoperationGroup
;
222 - (CKKSOutgoingQueueOperation
*)processOutgoingQueueAfter
:(CKKSResultOperation
* _Nullable
)after
223 requiredDelay
:(uint64_t)requiredDelay
224 ckoperationGroup
:(CKOperationGroup
* _Nullable
)ckoperationGroup
;
226 - (CKKSIncomingQueueOperation
*)processIncomingQueue
:(bool)failOnClassA
;
227 - (CKKSIncomingQueueOperation
*)processIncomingQueue
:(bool)failOnClassA after
:(CKKSResultOperation
* _Nullable
)after
;
229 - (CKKSScanLocalItemsOperation
*)scanLocalItems
:(NSString
*)name
;
231 // Schedules a process queueoperation to happen after the next device unlock. This may be Immediately, if the device is unlocked.
232 - (void)processIncomingQueueAfterNextUnlock
;
234 // This operation will complete directly after the next ProcessIncomingQueue, and should supply that IQO's result. Used mainly for testing; otherwise you'd just kick off a IQO directly.
235 - (CKKSResultOperation
*)resultsOfNextProcessIncomingQueueOperation
;
237 // Schedules an operation to update this device's state record in CloudKit
238 // If rateLimit is true, the operation will abort if it's updated the record in the past 3 days
239 - (CKKSUpdateDeviceStateOperation
*)updateDeviceState
:(bool)rateLimit
240 waitForKeyHierarchyInitialization
:(uint64_t)timeout
241 ckoperationGroup
:(CKOperationGroup
* _Nullable
)ckoperationGroup
;
243 - (CKKSSynchronizeOperation
*)resyncWithCloud
;
244 - (CKKSLocalSynchronizeOperation
*)resyncLocal
;
246 - (CKKSResultOperation
*)resetLocalData
;
247 - (CKKSResultOperation
*)resetCloudKitZone
:(CKOperationGroup
*)operationGroup
;
249 // Call this to tell the key state machine that you think some new data has arrived that it is interested in
250 - (void)keyStateMachineRequestProcess
;
252 // For our serial queue to work with how handleKeychainEventDbConnection is called from the main thread,
253 // every block on our queue must have a SecDBConnectionRef available to it before it begins on the queue.
254 // Use these helper methods to make sure those exist.
255 - (void)dispatchSyncWithSQLTransaction
:(CKKSDatabaseTransactionResult (^)(void))block
;
256 - (void)dispatchSyncWithReadOnlySQLTransaction
:(void (^)(void))block
;
258 /* Synchronous operations which must be called from inside a dispatchAsyncWithAccountKeys or dispatchSync block */
260 // Since we might have people interested in the state transitions of objects, please do those transitions via these methods
261 - (bool)_onqueueChangeOutgoingQueueEntry
:(CKKSOutgoingQueueEntry
*)oqe
262 toState
:(NSString
*)state
263 error
:(NSError
* __autoreleasing
*)error
;
264 - (bool)_onqueueErrorOutgoingQueueEntry
:(CKKSOutgoingQueueEntry
*)oqe
265 itemError
:(NSError
*)itemError
266 error
:(NSError
* __autoreleasing
*)error
;
268 // Call this if you've done a write and received an error. It'll pull out any new records returned as CKErrorServerRecordChanged and pretend we received them in a fetch
270 // Note that you need to tell this function the records you wanted to save, so it can determine which record failed from its CKRecordID.
271 // I don't know why CKRecordIDs don't have record types, either.
272 - (bool)_onqueueCKWriteFailed
:(NSError
*)ckerror attemptedRecordsChanged
:(NSDictionary
<CKRecordID
*, CKRecord
*>*)savedRecords
;
274 - (bool)_onqueueCKRecordChanged
:(CKRecord
*)record resync
:(bool)resync
;
275 - (bool)_onqueueCKRecordDeleted
:(CKRecordID
*)recordID recordType
:(NSString
*)recordType resync
:(bool)resync
;
277 - (CKKSDeviceStateEntry
* _Nullable
)_onqueueCurrentDeviceStateEntry
:(NSError
* __autoreleasing
*)error
;
279 // Please don't use these unless you're an Operation in this package
280 @property NSHashTable
<CKKSIncomingQueueOperation
*>* incomingQueueOperations
;
281 @property NSHashTable
<CKKSOutgoingQueueOperation
*>* outgoingQueueOperations
;
283 @property NSHashTable
<CKKSScanLocalItemsOperation
*>* scanLocalItemsOperations
;
285 // Returns the current state of this view, fastStatus is the same, but as name promise, no expensive calculations
286 - (NSDictionary
<NSString
*, NSString
*>*)status
;
287 - (NSDictionary
<NSString
*, NSString
*>*)fastStatus
;
289 - (void)xpc24HrNotification
;
291 // NSOperation Helpers
292 - (void)scheduleOperation
:(NSOperation
*)op
;
295 @interface
CKKSKeychainView (Testing
)
297 // Call this to just nudge the state machine (without a request)
298 // This is used internally, but you should only call it if you're a test.
299 - (void)_onqueuePokeKeyStateMachine
;
301 /* NSOperation helpers */
302 - (void)cancelAllOperations
;
303 - (void)waitUntilAllOperationsAreFinished
;
304 - (void)waitForOperationsOfClass
:(Class
)operationClass
;
306 - (void)waitForFetchAndIncomingQueueProcessing
;
310 - (void)handleCKLogout
;
314 NS_ASSUME_NONNULL_END
316 #import <Foundation/Foundation.h>
317 @interface CKKSKeychainView
: NSObject
319 NSString
* _containerName
;