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 #ifndef CKKSKeychainView_h
26 #define CKKSKeychainView_h
28 #import <Foundation/Foundation.h>
29 #include <dispatch/dispatch.h>
32 #import "keychain/ckks/CloudKitDependencies.h"
33 #import "keychain/ckks/CKKSAPSReceiver.h"
34 #import "keychain/ckks/CKKSLockStateTracker.h"
37 #include <utilities/SecDb.h>
38 #include <securityd/SecDbItem.h>
40 #import "keychain/ckks/CKKS.h"
41 #import "keychain/ckks/CKKSIncomingQueueOperation.h"
42 #import "keychain/ckks/CKKSOutgoingQueueOperation.h"
43 #import "keychain/ckks/CKKSNearFutureScheduler.h"
44 #import "keychain/ckks/CKKSNewTLKOperation.h"
45 #import "keychain/ckks/CKKSProcessReceivedKeysOperation.h"
46 #import "keychain/ckks/CKKSReencryptOutgoingItemsOperation.h"
47 #import "keychain/ckks/CKKSFetchAllRecordZoneChangesOperation.h"
48 #import "keychain/ckks/CKKSScanLocalItemsOperation.h"
49 #import "keychain/ckks/CKKSUpdateDeviceStateOperation.h"
50 #import "keychain/ckks/CKKSGroupOperation.h"
51 #import "keychain/ckks/CKKSZone.h"
52 #import "keychain/ckks/CKKSZoneChangeFetcher.h"
53 #import "keychain/ckks/CKKSNotifier.h"
54 #import "keychain/ckks/CKKSPeer.h"
55 #import "keychain/ckks/CKKSTLKShare.h"
60 @interface CKKSKeychainView
: NSObject
{
61 NSString
* _containerName
;
68 @
class CKKSSynchronizeOperation
;
69 @
class CKKSRateLimiter
;
71 @
class CKKSEgoManifest
;
72 @
class CKKSOutgoingQueueEntry
;
73 @
class CKKSZoneChangeFetcher
;
75 @interface CKKSKeychainView
: CKKSZone
<CKKSZoneUpdateReceiver
,
76 CKKSChangeFetcherErrorOracle
,
77 CKKSPeerUpdateListener
> {
78 CKKSZoneKeyState
* _keyHierarchyState
;
81 @property CKKSLockStateTracker
* lockStateTracker
;
83 @property CKKSZoneKeyState
* keyHierarchyState
;
84 @property NSError
* keyHierarchyError
;
85 @property CKOperationGroup
* keyHierarchyOperationGroup
;
86 @property NSOperation
* keyStateMachineOperation
;
88 // If the key hierarchy isn't coming together, it might be because we're out of sync with cloudkit.
89 // Use this to track if we've completed a full refetch, so fix-up operations can be done.
90 @property
bool keyStateMachineRefetched
;
91 @property CKKSEgoManifest
* egoManifest
;
92 @property CKKSManifest
* latestManifest
;
93 @property CKKSResultOperation
* keyStateReadyDependency
;
95 @
property (readonly
) NSString
*lastActiveTLKUUID
;
97 // Full of condition variables, if you'd like to try to wait until the key hierarchy is in some state
98 @property NSMutableDictionary
<CKKSZoneKeyState
*, CKKSCondition
*>* keyHierarchyConditions
;
100 @property CKKSZoneChangeFetcher
* zoneChangeFetcher
;
102 @
property (weak
) CKKSNearFutureScheduler
* savedTLKNotifier
;
104 // Differs from the zonesetupoperation: zoneSetup is only for CK modifications, viewSetup handles local db changes too
105 @property CKKSGroupOperation
* viewSetupOperation
;
107 /* Used for debugging: just what happened last time we ran this? */
108 @property CKKSIncomingQueueOperation
* lastIncomingQueueOperation
;
109 @property CKKSNewTLKOperation
* lastNewTLKOperation
;
110 @property CKKSOutgoingQueueOperation
* lastOutgoingQueueOperation
;
111 @property CKKSProcessReceivedKeysOperation
* lastProcessReceivedKeysOperation
;
112 @property CKKSFetchAllRecordZoneChangesOperation
* lastRecordZoneChangesOperation
;
113 @property CKKSReencryptOutgoingItemsOperation
* lastReencryptOutgoingItemsOperation
;
114 @property CKKSScanLocalItemsOperation
* lastScanLocalItemsOperation
;
115 @property CKKSSynchronizeOperation
* lastSynchronizeOperation
;
116 @property CKKSResultOperation
* lastFixupOperation
;
118 /* Used for testing: pause operation types by adding operations here */
119 @property NSOperation
* holdReencryptOutgoingItemsOperation
;
120 @property NSOperation
* holdOutgoingQueueOperation
;
122 /* Trigger this to tell the whole machine that this view has changed */
123 @property CKKSNearFutureScheduler
* notifyViewChangedScheduler
;
125 // These are available when you're in a dispatchSyncWithAccountKeys call, but at no other time
126 // These must be pre-fetched before you get on the CKKS queue, otherwise we end up with CKKS<->SQLite<->SOSAccountQueue deadlocks
127 @
property (nonatomic
, readonly
) CKKSSelves
* currentSelfPeers
;
128 @
property (nonatomic
, readonly
) NSError
* currentSelfPeersError
;
129 @
property (nonatomic
, readonly
) NSSet
<id
<CKKSPeer
>>* currentTrustedPeers
;
130 @
property (nonatomic
, readonly
) NSError
* currentTrustedPeersError
;
132 - (instancetype
)initWithContainer
: (CKContainer
*) container
133 zoneName
: (NSString
*) zoneName
134 accountTracker
:(CKKSCKAccountStateTracker
*) accountTracker
135 lockStateTracker
:(CKKSLockStateTracker
*) lockStateTracker
136 savedTLKNotifier
:(CKKSNearFutureScheduler
*) savedTLKNotifier
137 peerProvider
:(id
<CKKSPeerProvider
>)peerProvider
138 fetchRecordZoneChangesOperationClass
: (Class
<CKKSFetchRecordZoneChangesOperation
>) fetchRecordZoneChangesOperationClass
139 fetchRecordsOperationClass
: (Class
<CKKSFetchRecordsOperation
>)fetchRecordsOperationClass
140 queryOperationClass
:(Class
<CKKSQueryOperation
>)queryOperationClass
141 modifySubscriptionsOperationClass
: (Class
<CKKSModifySubscriptionsOperation
>) modifySubscriptionsOperationClass
142 modifyRecordZonesOperationClass
: (Class
<CKKSModifyRecordZonesOperation
>) modifyRecordZonesOperationClass
143 apsConnectionClass
: (Class
<CKKSAPSConnection
>) apsConnectionClass
144 notifierClass
: (Class
<CKKSNotifier
>) notifierClass
;
146 /* Synchronous operations */
148 - (void) handleKeychainEventDbConnection
:(SecDbConnectionRef
) dbconn
149 added
:(SecDbItemRef
) added
150 deleted
:(SecDbItemRef
) deleted
151 rateLimiter
:(CKKSRateLimiter
*) rateLimiter
152 syncCallback
:(SecBoolNSErrorCallback
) syncCallback
;
154 -(void)setCurrentItemForAccessGroup
:(SecDbItemRef
)newItem
155 hash
:(NSData
*)newItemSHA1
156 accessGroup
:(NSString
*)accessGroup
157 identifier
:(NSString
*)identifier
158 replacing
:(SecDbItemRef
)oldItem
159 hash
:(NSData
*)oldItemSHA1
160 complete
:(void (^) (NSError
* operror
)) complete
;
162 -(void)getCurrentItemForAccessGroup
:(NSString
*)accessGroup
163 identifier
:(NSString
*)identifier
164 fetchCloudValue
:(bool)fetchCloudValue
165 complete
:(void (^) (NSString
* uuid
, NSError
* operror
)) complete
;
167 - (bool) outgoingQueueEmpty
: (NSError
* __autoreleasing
*) error
;
169 - (CKKSResultOperation
*)waitForFetchAndIncomingQueueProcessing
;
170 - (void) waitForKeyHierarchyReadiness
;
171 - (void) cancelAllOperations
;
173 - (CKKSKey
*) keyForItem
: (SecDbItemRef
) item error
: (NSError
* __autoreleasing
*) error
;
175 - (bool)_onqueueWithAccountKeysCheckTLK
: (CKKSKey
*) proposedTLK error
: (NSError
* __autoreleasing
*) error
;
177 /* Asynchronous kickoffs */
179 - (void) initializeZone
;
181 - (CKKSOutgoingQueueOperation
*)processOutgoingQueue
:(CKOperationGroup
*)ckoperationGroup
;
182 - (CKKSOutgoingQueueOperation
*)processOutgoingQueueAfter
:(CKKSResultOperation
*)after ckoperationGroup
:(CKOperationGroup
*)ckoperationGroup
;
184 - (CKKSIncomingQueueOperation
*) processIncomingQueue
:(bool)failOnClassA
;
185 - (CKKSIncomingQueueOperation
*) processIncomingQueue
:(bool)failOnClassA after
: (CKKSResultOperation
*) after
;
187 // Schedules a process queueoperation to happen after the next device unlock. This may be Immediately, if the device is unlocked.
188 - (void)processIncomingQueueAfterNextUnlock
;
190 // Schedules an operation to update this device's state record in CloudKit
191 // If rateLimit is true, the operation will abort if it's updated the record in the past 3 days
192 - (CKKSUpdateDeviceStateOperation
*)updateDeviceState
:(bool)rateLimit
193 waitForKeyHierarchyInitialization
:(uint64_t)timeout
194 ckoperationGroup
:(CKOperationGroup
*)ckoperationGroup
;
196 - (CKKSSynchronizeOperation
*) resyncWithCloud
;
198 - (CKKSResultOperation
*)fetchAndProcessCKChanges
:(CKKSFetchBecause
*)because
;
200 - (CKKSResultOperation
*)resetLocalData
;
201 - (CKKSResultOperation
*)resetCloudKitZone
;
203 // Call this to pick and start the next key hierarchy operation for the zone
204 - (void)advanceKeyStateMachine
;
206 // Call this to tell the key state machine that you think some new data has arrived that it is interested in
207 - (void)keyStateMachineRequestProcess
;
209 // For our serial queue to work with how handleKeychainEventDbConnection is called from the main thread,
210 // every block on our queue must have a SecDBConnectionRef available to it before it begins on the queue.
211 // Use these helper methods to make sure those exist.
212 - (void) dispatchAsync
: (bool (^)(void)) block
;
213 - (void) dispatchSync
: (bool (^)(void)) block
;
214 - (void)dispatchSyncWithAccountKeys
:(bool (^)(void))block
;
216 /* Synchronous operations which must be called from inside a dispatchAsync or dispatchSync block */
218 // Call this to request the key hierarchy state machine to fetch new updates
219 - (void)_onqueueKeyStateMachineRequestFetch
;
221 // Call this to request the key hierarchy state machine to refetch everything in Cloudkit
222 - (void)_onqueueKeyStateMachineRequestFullRefetch
;
224 // Call this to request the key hierarchy state machine to reprocess
225 - (void)_onqueueKeyStateMachineRequestProcess
;
227 // Call this from a key hierarchy operation to move the state machine, and record the results of the last move.
228 - (void)_onqueueAdvanceKeyStateMachineToState
: (CKKSZoneKeyState
*) state withError
: (NSError
*) error
;
230 // Since we might have people interested in the state transitions of objects, please do those transitions via these methods
231 - (bool)_onqueueChangeOutgoingQueueEntry
: (CKKSOutgoingQueueEntry
*) oqe toState
: (NSString
*) state error
: (NSError
* __autoreleasing
*) error
;
232 - (bool)_onqueueErrorOutgoingQueueEntry
: (CKKSOutgoingQueueEntry
*) oqe itemError
: (NSError
*) itemError error
: (NSError
* __autoreleasing
*) error
;
234 // 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
236 // Note that you need to tell this function the records you wanted to save, so it can determine which record failed from its CKRecordID.
237 // I don't know why CKRecordIDs don't have record types, either.
238 - (bool)_onqueueCKWriteFailed
:(NSError
*)ckerror attemptedRecordsChanged
:(NSDictionary
<CKRecordID
*,CKRecord
*>*)savedRecords
;
240 - (bool) _onqueueCKRecordChanged
:(CKRecord
*)record resync
:(bool)resync
;
241 - (bool) _onqueueCKRecordDeleted
:(CKRecordID
*)recordID recordType
:(NSString
*)recordType resync
:(bool)resync
;
243 // For this key, who doesn't yet have a CKKSTLKShare for it?
244 // Note that we really want a record sharing the TLK to ourselves, so this function might return
245 // a non-empty set even if all peers have the TLK: it wants us to make a record for ourself.
246 - (NSSet
<id
<CKKSPeer
>>*)_onqueueFindPeersMissingShare
:(CKKSKey
*)key error
:(NSError
* __autoreleasing
*)error
;
248 // For this key, share it to all trusted peers who don't have it yet
249 - (NSSet
<CKKSTLKShare
*>*)_onqueueCreateMissingKeyShares
:(CKKSKey
*)key error
:(NSError
* __autoreleasing
*)error
;
251 - (bool)_onQueueUpdateLatestManifestWithError
:(NSError
**)error
;
253 - (CKKSDeviceStateEntry
*)_onqueueCurrentDeviceStateEntry
: (NSError
* __autoreleasing
*)error
;
255 // Called by the CKKSZoneChangeFetcher
256 - (bool) isFatalCKFetchError
: (NSError
*) error
;
258 // Please don't use these unless you're an Operation in this package
259 @property NSHashTable
<CKKSIncomingQueueOperation
*>* incomingQueueOperations
;
260 @property NSHashTable
<CKKSOutgoingQueueOperation
*>* outgoingQueueOperations
;
261 @property CKKSScanLocalItemsOperation
* initialScanOperation
;
263 // Returns the current state of this view
264 -(NSDictionary
<NSString
*, NSString
*>*)status
;
269 #define SecTranslateError(nserrorptr, cferror) \
271 *nserrorptr = (__bridge_transfer NSError*) cferror; \
273 CFReleaseNull(cferror); \
276 #endif /* CKKSKeychainView_h */