2 * Copyright (c) 2017 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@
26 #import <Foundation/NSXPCConnection_Private.h>
29 #import <Security/SecItemPriv.h>
31 #import "keychain/ckks/CKKSControl.h"
32 #import "keychain/ckks/CKKSControlProtocol.h"
33 #import "keychain/ckks/CKKSControlServer.h"
34 #import "utilities/debugging.h"
36 @interface CKKSControl ()
37 @property (readwrite,assign) BOOL synchronous;
38 @property xpc_endpoint_t endpoint;
39 @property NSXPCConnection *connection;
42 @implementation CKKSControl
44 - (instancetype)initWithConnection:(NSXPCConnection*)connection {
45 if(self = [super init]) {
46 _connection = connection;
52 [self.connection invalidate];
55 - (id<CKKSControlProtocol>)objectProxyWithErrorHandler:(void(^)(NSError * _Nonnull error))failureHandler
57 if (self.synchronous) {
58 return [self.connection synchronousRemoteObjectProxyWithErrorHandler:failureHandler];
60 return [self.connection remoteObjectProxyWithErrorHandler:failureHandler];
64 - (void)rpcStatus:(NSString*)viewName reply:(void(^)(NSArray<NSDictionary*>* result, NSError* error)) reply {
65 [[self objectProxyWithErrorHandler: ^(NSError* error) {
68 }] rpcStatus:viewName reply:^(NSArray<NSDictionary*>* result, NSError* error){
73 - (void)rpcFastStatus:(NSString*)viewName reply:(void(^)(NSArray<NSDictionary*>* result, NSError* error)) reply {
74 [[self objectProxyWithErrorHandler: ^(NSError* error) {
77 }] rpcFastStatus:viewName reply:^(NSArray<NSDictionary*>* result, NSError* error){
83 - (void)rpcResetLocal:(NSString*)viewName reply:(void(^)(NSError* error))reply {
84 secnotice("ckkscontrol", "Requesting a local reset for view %@", viewName);
85 [[self objectProxyWithErrorHandler:^(NSError* error) {
87 }] rpcResetLocal:viewName reply:^(NSError* error){
92 - (void)rpcResetCloudKit:(NSString*)viewName reason:(NSString *)reason reply:(void(^)(NSError* error))reply {
93 secnotice("ckkscontrol", "Requesting a CloudKit reset for view %@", viewName);
94 [[self objectProxyWithErrorHandler:^(NSError* error) {
96 }] rpcResetCloudKit:viewName reason:reason reply:^(NSError* error){
101 - (void)rpcResyncLocal:(NSString* _Nullable)viewName reply:(void (^)(NSError* _Nullable error))reply
103 secnotice("ckkscontrol", "Requesting a local resync for view %@", viewName);
104 [[self objectProxyWithErrorHandler:^(NSError* error) {
106 }] rpcResyncLocal:viewName reply:^(NSError* error){
110 - (void)rpcResync:(NSString*)viewName reply:(void(^)(NSError* error))reply {
111 secnotice("ckkscontrol", "Requesting a resync for view %@", viewName);
112 [[self objectProxyWithErrorHandler:^(NSError* error) {
114 }] rpcResync:viewName reply:^(NSError* error){
118 - (void)rpcFetchAndProcessChanges:(NSString*)viewName reply:(void(^)(NSError* error))reply {
119 secnotice("ckkscontrol", "Requesting a fetch for view %@", viewName);
120 [[self objectProxyWithErrorHandler:^(NSError* error) {
122 }] rpcFetchAndProcessChanges:viewName reply:^(NSError* error){
126 - (void)rpcFetchAndProcessClassAChanges:(NSString*)viewName reply:(void(^)(NSError* error))reply {
127 secnotice("ckkscontrol", "Requesting a fetch(classA) for view %@", viewName);
128 [[self objectProxyWithErrorHandler:^(NSError* error) {
130 }] rpcFetchAndProcessClassAChanges:viewName reply:^(NSError* error){
134 - (void)rpcPushOutgoingChanges:(NSString*)viewName reply:(void(^)(NSError* error))reply {
135 secnotice("ckkscontrol", "Requesting a push for view %@", viewName);
136 [[self objectProxyWithErrorHandler:^(NSError* error) {
138 }] rpcPushOutgoingChanges:viewName reply:^(NSError* error){
143 - (void)rpcCKMetric:(NSString *)eventName attributes:(NSDictionary *)attributes reply:(void(^)(NSError* error))reply {
144 [[self objectProxyWithErrorHandler:^(NSError* error) {
146 }] rpcCKMetric:eventName attributes:attributes reply:^(NSError* error){
151 - (void)rpcPerformanceCounters:(void(^)(NSDictionary <NSString *,NSNumber *> *,NSError*))reply {
152 [[self objectProxyWithErrorHandler: ^(NSError* error) {
154 }] performanceCounters:^(NSDictionary <NSString *, NSNumber *> *counters){
155 reply(counters, nil);
159 - (void)rpcGetCKDeviceIDWithReply:(void (^)(NSString *))reply {
160 [[self objectProxyWithErrorHandler:^(NSError * _Nonnull error) {
162 }] rpcGetCKDeviceIDWithReply:^(NSString *ckdeviceID) {
167 - (void)rpcTLKMissing:(NSString*)viewName reply:(void(^)(bool missing))reply {
168 [self rpcFastStatus:viewName reply:^(NSArray<NSDictionary*>* results, NSError* blockError) {
169 bool missing = false;
171 for(NSDictionary* result in results) {
172 NSString* name = result[@"view"];
173 NSString* keystate = result[@"keystate"];
175 if([name isEqualToString:@"global"]) {
176 // this is global status; no view implicated
180 if ([keystate isEqualToString:@"waitfortlk"] || [keystate isEqualToString:@"error"]) {
189 - (void)rpcKnownBadState:(NSString* _Nullable)viewName reply:(void (^)(CKKSKnownBadState))reply {
190 [self rpcFastStatus:viewName reply:^(NSArray<NSDictionary*>* results, NSError* blockError) {
191 bool tlkMissing = false;
192 bool waitForUnlock = false;
193 bool waitForOctagon = false;
194 bool noAccount = false;
196 CKKSKnownBadState response = CKKSKnownStatePossiblyGood;
198 for(NSDictionary* result in results) {
199 NSString* name = result[@"view"];
200 NSString* keystate = result[@"keystate"];
202 if([name isEqualToString:@"global"]) {
203 // this is global status; no view implicated
207 if ([keystate isEqualToString:@"waitfortlk"] || [keystate isEqualToString:@"error"]) {
210 if ([keystate isEqualToString:@"waitforunlock"]) {
211 waitForUnlock = true;
214 if([keystate isEqualToString:@"waitfortlkcreation"] ||
215 [keystate isEqualToString:@"waitfortlkupload"] ||
216 [keystate isEqualToString:@"waitfortrust"]) {
217 waitForOctagon = true;
220 if([keystate isEqualToString:@"loggedout"]) {
225 response = (noAccount ? CKKSKnownStateNoCloudKitAccount :
226 (tlkMissing ? CKKSKnownStateTLKsMissing :
227 (waitForUnlock ? CKKSKnownStateWaitForUnlock :
228 (waitForOctagon ? CKKSKnownStateWaitForOctagon :
229 CKKSKnownStatePossiblyGood))));
235 + (CKKSControl*)controlObject:(NSError* __autoreleasing *)error {
236 return [CKKSControl CKKSControlObject:NO error:error];
239 + (CKKSControl*)CKKSControlObject:(BOOL)synchronous error:(NSError* __autoreleasing *)error {
241 NSXPCConnection* connection = [[NSXPCConnection alloc] initWithMachServiceName:@(kSecuritydCKKSServiceName) options:0];
243 if (connection == nil) {
245 *error = [NSError errorWithDomain:@"securityd" code:-1 userInfo:@{NSLocalizedDescriptionKey: @"Couldn't create connection (no reason given)"}];
250 NSXPCInterface *interface = CKKSSetupControlProtocol([NSXPCInterface interfaceWithProtocol:@protocol(CKKSControlProtocol)]);
251 connection.remoteObjectInterface = interface;
254 CKKSControl* c = [[CKKSControl alloc] initWithConnection:connection];
255 c.synchronous = synchronous;