CKKSControl.m
1 /* 2 * Copyright (c) 2017 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 #if __OBJC2__ 25 26 #import <Foundation/NSXPCConnection_Private.h> 27 #import <xpc/xpc.h> 28 29 #import <Security/SecItemPriv.h> 30 31 #import "keychain/ckks/CKKSControl.h" 32 #import "keychain/ckks/CKKSControlProtocol.h" 33 #import "keychain/ckks/CKKSControlServer.h" 34 #import "utilities/debugging.h" 35 36 @interface CKKSControl () 37 @property (readwrite,assign) BOOL synchronous; 38 @property xpc_endpoint_t endpoint; 39 @property NSXPCConnection *connection; 40 @end 41 42 @implementation CKKSControl 43 44 - (instancetype)initWithConnection:(NSXPCConnection*)connection { 45 if(self = [super init]) { 46 _connection = connection; 47 } 48 return self; 49 } 50 51 - (void)dealloc { 52 [self.connection invalidate]; 53 } 54 55 - (id<CKKSControlProtocol>)objectProxyWithErrorHandler:(void(^)(NSError * _Nonnull error))failureHandler 56 { 57 if (self.synchronous) { 58 return [self.connection synchronousRemoteObjectProxyWithErrorHandler:failureHandler]; 59 } else { 60 return [self.connection remoteObjectProxyWithErrorHandler:failureHandler]; 61 } 62 } 63 64 - (void)rpcStatus:(NSString*)viewName reply:(void(^)(NSArray<NSDictionary*>* result, NSError* error)) reply { 65 [[self objectProxyWithErrorHandler: ^(NSError* error) { 66 reply(nil, error); 67 68 }] rpcStatus:viewName reply:^(NSArray<NSDictionary*>* result, NSError* error){ 69 reply(result, error); 70 }]; 71 } 72 73 - (void)rpcFastStatus:(NSString*)viewName reply:(void(^)(NSArray<NSDictionary*>* result, NSError* error)) reply { 74 [[self objectProxyWithErrorHandler: ^(NSError* error) { 75 reply(nil, error); 76 77 }] rpcFastStatus:viewName reply:^(NSArray<NSDictionary*>* result, NSError* error){ 78 reply(result, error); 79 }]; 80 } 81 82 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) { 86 reply(error); 87 }] rpcResetLocal:viewName reply:^(NSError* error){ 88 if(error) { 89 secnotice("ckkscontrol", "Local reset finished with error: %@", error); 90 } else { 91 secnotice("ckkscontrol", "Local reset finished successfully"); 92 } 93 reply(error); 94 }]; 95 } 96 97 - (void)rpcResetCloudKit:(NSString*)viewName reason:(NSString *)reason reply:(void(^)(NSError* error))reply { 98 secnotice("ckkscontrol", "Requesting a CloudKit reset for view %@", viewName); 99 [[self objectProxyWithErrorHandler:^(NSError* error) { 100 reply(error); 101 }] rpcResetCloudKit:viewName reason:reason reply:^(NSError* error){ 102 if(error) { 103 secnotice("ckkscontrol", "CloudKit reset finished with error: %@", error); 104 } else { 105 secnotice("ckkscontrol", "CloudKit reset finished successfully"); 106 } 107 reply(error); 108 }]; 109 } 110 111 - (void)rpcResyncLocal:(NSString* _Nullable)viewName reply:(void (^)(NSError* _Nullable error))reply 112 { 113 secnotice("ckkscontrol", "Requesting a local resync for view %@", viewName); 114 [[self objectProxyWithErrorHandler:^(NSError* error) { 115 reply(error); 116 }] rpcResyncLocal:viewName reply:^(NSError* error){ 117 if(error) { 118 secnotice("ckkscontrol", "Local resync finished with error: %@", error); 119 } else { 120 secnotice("ckkscontrol", "Local resync finished successfully"); 121 } 122 reply(error); 123 }]; 124 } 125 - (void)rpcResync:(NSString*)viewName reply:(void(^)(NSError* error))reply { 126 secnotice("ckkscontrol", "Requesting a resync for view %@", viewName); 127 [[self objectProxyWithErrorHandler:^(NSError* error) { 128 reply(error); 129 }] rpcResync:viewName reply:^(NSError* error){ 130 if(error) { 131 secnotice("ckkscontrol", "Resync finished with error: %@", error); 132 } else { 133 secnotice("ckkscontrol", "Resync finished successfully"); 134 } 135 reply(error); 136 }]; 137 } 138 - (void)rpcFetchAndProcessChanges:(NSString*)viewName reply:(void(^)(NSError* error))reply { 139 secnotice("ckkscontrol", "Requesting a fetch for view %@", viewName); 140 [[self objectProxyWithErrorHandler:^(NSError* error) { 141 reply(error); 142 }] rpcFetchAndProcessChanges:viewName reply:^(NSError* error){ 143 if(error) { 144 secnotice("ckkscontrol", "Fetch(classA) finished with error: %@", error); 145 } else { 146 secnotice("ckkscontrol", "Fetch(classA) finished successfully"); 147 } 148 reply(error); 149 }]; 150 } 151 - (void)rpcFetchAndProcessClassAChanges:(NSString*)viewName reply:(void(^)(NSError* error))reply { 152 secnotice("ckkscontrol", "Requesting a fetch(classA) for view %@", viewName); 153 [[self objectProxyWithErrorHandler:^(NSError* error) { 154 reply(error); 155 }] rpcFetchAndProcessClassAChanges:viewName reply:^(NSError* error){ 156 if(error) { 157 secnotice("ckkscontrol", "Fetch finished with error: %@", error); 158 } else { 159 secnotice("ckkscontrol", "Fetch finished successfully"); 160 } 161 reply(error); 162 }]; 163 } 164 - (void)rpcPushOutgoingChanges:(NSString*)viewName reply:(void(^)(NSError* error))reply { 165 secnotice("ckkscontrol", "Requesting a push for view %@", viewName); 166 [[self objectProxyWithErrorHandler:^(NSError* error) { 167 reply(error); 168 }] rpcPushOutgoingChanges:viewName reply:^(NSError* error){ 169 if(error) { 170 secnotice("ckkscontrol", "Push finished with error: %@", error); 171 } else { 172 secnotice("ckkscontrol", "Push finished successfully"); 173 } 174 reply(error); 175 }]; 176 } 177 178 - (void)rpcCKMetric:(NSString *)eventName attributes:(NSDictionary *)attributes reply:(void(^)(NSError* error))reply { 179 [[self objectProxyWithErrorHandler:^(NSError* error) { 180 reply(error); 181 }] rpcCKMetric:eventName attributes:attributes reply:^(NSError* error){ 182 reply(error); 183 }]; 184 } 185 186 - (void)rpcPerformanceCounters:(void(^)(NSDictionary <NSString *,NSNumber *> *,NSError*))reply { 187 [[self objectProxyWithErrorHandler: ^(NSError* error) { 188 reply(nil, error); 189 }] performanceCounters:^(NSDictionary <NSString *, NSNumber *> *counters){ 190 reply(counters, nil); 191 }]; 192 } 193 194 - (void)rpcGetCKDeviceIDWithReply:(void (^)(NSString *))reply { 195 [[self objectProxyWithErrorHandler:^(NSError * _Nonnull error) { 196 reply(nil); 197 }] rpcGetCKDeviceIDWithReply:^(NSString *ckdeviceID) { 198 reply(ckdeviceID); 199 }]; 200 } 201 202 - (void)rpcTLKMissing:(NSString*)viewName reply:(void(^)(bool missing))reply { 203 [self rpcFastStatus:viewName reply:^(NSArray<NSDictionary*>* results, NSError* blockError) { 204 bool missing = false; 205 206 for(NSDictionary* result in results) { 207 NSString* name = result[@"view"]; 208 NSString* keystate = result[@"keystate"]; 209 210 if([name isEqualToString:@"global"]) { 211 // this is global status; no view implicated 212 continue; 213 } 214 215 if ([keystate isEqualToString:@"waitfortlk"] || [keystate isEqualToString:@"error"]) { 216 missing = true; 217 } 218 } 219 220 reply(missing); 221 }]; 222 } 223 224 - (void)rpcKnownBadState:(NSString* _Nullable)viewName reply:(void (^)(CKKSKnownBadState))reply { 225 [self rpcFastStatus:viewName reply:^(NSArray<NSDictionary*>* results, NSError* blockError) { 226 bool tlkMissing = false; 227 bool waitForUnlock = false; 228 bool waitForOctagon = false; 229 bool noAccount = false; 230 231 CKKSKnownBadState response = CKKSKnownStatePossiblyGood; 232 233 for(NSDictionary* result in results) { 234 NSString* name = result[@"view"]; 235 NSString* keystate = result[@"keystate"]; 236 237 if([name isEqualToString:@"global"]) { 238 // this is global status; no view implicated 239 continue; 240 } 241 242 if ([keystate isEqualToString:@"waitfortlk"] || [keystate isEqualToString:@"error"]) { 243 tlkMissing = true; 244 } 245 if ([keystate isEqualToString:@"waitforunlock"]) { 246 waitForUnlock = true; 247 } 248 249 if([keystate isEqualToString:@"waitfortlkcreation"] || 250 [keystate isEqualToString:@"waitfortlkupload"] || 251 [keystate isEqualToString:@"waitfortrust"]) { 252 waitForOctagon = true; 253 } 254 255 if([keystate isEqualToString:@"loggedout"]) { 256 noAccount = true; 257 } 258 } 259 260 response = (noAccount ? CKKSKnownStateNoCloudKitAccount : 261 (tlkMissing ? CKKSKnownStateTLKsMissing : 262 (waitForUnlock ? CKKSKnownStateWaitForUnlock : 263 (waitForOctagon ? CKKSKnownStateWaitForOctagon : 264 CKKSKnownStatePossiblyGood)))); 265 266 reply(response); 267 }]; 268 } 269 270 + (CKKSControl*)controlObject:(NSError* __autoreleasing *)error { 271 return [CKKSControl CKKSControlObject:NO error:error]; 272 } 273 274 + (CKKSControl*)CKKSControlObject:(BOOL)synchronous error:(NSError* __autoreleasing *)error { 275 276 NSXPCConnection* connection = [[NSXPCConnection alloc] initWithMachServiceName:@(kSecuritydCKKSServiceName) options:0]; 277 278 if (connection == nil) { 279 if(error) { 280 *error = [NSError errorWithDomain:@"securityd" code:-1 userInfo:@{NSLocalizedDescriptionKey: @"Couldn't create connection (no reason given)"}]; 281 } 282 return nil; 283 } 284 285 NSXPCInterface *interface = CKKSSetupControlProtocol([NSXPCInterface interfaceWithProtocol:@protocol(CKKSControlProtocol)]); 286 connection.remoteObjectInterface = interface; 287 [connection resume]; 288 289 CKKSControl* c = [[CKKSControl alloc] initWithConnection:connection]; 290 c.synchronous = synchronous; 291 return c; 292 } 293 294 @end 295 296 #endif // __OBJC2__