]> git.saurik.com Git - apple/security.git/blob - KVSKeychainSyncingProxy/cloudkeychainproxy.m
Security-57740.1.18.tar.gz
[apple/security.git] / KVSKeychainSyncingProxy / cloudkeychainproxy.m
1 /*
2 * Copyright (c) 2012-2014 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 //
25 // main.m
26 // ckd-xpc
27 //
28 //
29
30 /*
31 This XPC service is essentially just a proxy to iCloud KVS, which exists since
32 the main security code cannot link against Foundation.
33
34 See sendTSARequestWithXPC in tsaSupport.c for how to call the service
35
36 send message to app with xpc_connection_send_message
37
38 For now, build this with:
39
40 ~rc/bin/buildit . --rootsDirectory=/var/tmp -noverify -offline -target CloudKeychainProxy
41
42 and install or upgrade with:
43
44 darwinup install /var/tmp/sec.roots/sec~dst
45 darwinup upgrade /var/tmp/sec.roots/sec~dst
46
47 You must use darwinup during development to update system caches
48 */
49
50 //------------------------------------------------------------------------------------------------
51
52 #include <AssertMacros.h>
53
54 #import <Foundation/Foundation.h>
55 #import <Security/Security.h>
56 #import <utilities/SecCFRelease.h>
57 #import <xpc/xpc.h>
58 #import <xpc/private.h>
59 #import <CoreFoundation/CFXPCBridge.h>
60 #import <sysexits.h>
61 #import <syslog.h>
62 #import <CommonCrypto/CommonDigest.h>
63 #include <utilities/SecXPCError.h>
64 #include <utilities/SecCFError.h>
65 #include <TargetConditionals.h>
66
67 #import "CKDKVSProxy.h"
68
69 void finalize_connection(void *not_used);
70 void handle_connection_event(const xpc_connection_t peer);
71 static void cloudkeychainproxy_peer_dictionary_handler(const xpc_connection_t peer, xpc_object_t event);
72
73 static bool operation_put_dictionary(xpc_object_t event);
74 static bool operation_get_v2(xpc_connection_t peer, xpc_object_t event);
75
76 int ckdproxymain(int argc, const char *argv[]);
77
78 #define PROXYXPCSCOPE "xpcproxy"
79
80 static void describeXPCObject(char *prefix, xpc_object_t object)
81 {
82 //#ifndef NDEBUG
83 // This is useful for debugging.
84 if (object)
85 {
86 char *desc = xpc_copy_description(object);
87 secdebug(PROXYXPCSCOPE, "%s%s\n", prefix, desc);
88 free(desc);
89 }
90 else
91 secdebug(PROXYXPCSCOPE, "%s<NULL>\n", prefix);
92
93 //#endif
94 }
95
96 static void cloudkeychainproxy_peer_dictionary_handler(const xpc_connection_t peer, xpc_object_t event)
97 {
98 bool result = false;
99 int err = 0;
100
101 require_action_string(xpc_get_type(event) == XPC_TYPE_DICTIONARY, xit, err = -51, "expected XPC_TYPE_DICTIONARY");
102
103 const char *operation = xpc_dictionary_get_string(event, kMessageKeyOperation);
104 require_action(operation, xit, result = false);
105
106 // Check protocol version
107 uint64_t version = xpc_dictionary_get_uint64(event, kMessageKeyVersion);
108 secdebug(PROXYXPCSCOPE, "Reply version: %lld\n", version);
109 require_action(version == kCKDXPCVersion, xit, result = false);
110
111 // Operations
112 secdebug(PROXYXPCSCOPE, "Handling %s operation", operation);
113
114
115 if (operation && !strcmp(operation, kOperationPUTDictionary))
116 {
117 operation_put_dictionary(event);
118 }
119 else if (operation && !strcmp(operation, kOperationGETv2))
120 {
121 operation_get_v2(peer, event);
122 }
123 else if (operation && !strcmp(operation, kOperationClearStore))
124 {
125 [[UbiqitousKVSProxy sharedKVSProxy] clearStore];
126 xpc_object_t replyMessage = xpc_dictionary_create_reply(event);
127 if (replyMessage) // Caller wanted an ACK, so give one
128 {
129 xpc_dictionary_set_string(replyMessage, kMessageKeyValue, "ACK");
130 xpc_connection_send_message(peer, replyMessage);
131 }
132 }
133 else if (operation && !strcmp(operation, kOperationSynchronize))
134 {
135 [[UbiqitousKVSProxy sharedKVSProxy] synchronizeStore];
136 xpc_object_t replyMessage = xpc_dictionary_create_reply(event);
137 if (replyMessage) // Caller wanted an ACK, so give one
138 {
139 xpc_dictionary_set_string(replyMessage, kMessageKeyValue, "ACK");
140 xpc_connection_send_message(peer, replyMessage);
141 }
142 }
143 else if (operation && !strcmp(operation, kOperationSynchronizeAndWait))
144 {
145 xpc_object_t replyMessage = xpc_dictionary_create_reply(event);
146 secnotice(XPROXYSCOPE, "%s XPC request: %s", kWAIT2MINID, kOperationSynchronizeAndWait);
147
148 [[UbiqitousKVSProxy sharedKVSProxy] waitForSynchronization:^(__unused NSDictionary *values, NSError *error)
149 {
150 secnotice(PROXYXPCSCOPE, "%s Result from [[UbiqitousKVSProxy sharedKVSProxy] waitForSynchronization:]: %@", kWAIT2MINID, error);
151
152 if (replyMessage) // Caller wanted an ACK, so give one
153 {
154 if (error)
155 {
156 xpc_object_t xerrobj = SecCreateXPCObjectWithCFError((__bridge CFErrorRef)(error));
157 xpc_dictionary_set_value(replyMessage, kMessageKeyError, xerrobj);
158 } else {
159 xpc_dictionary_set_string(replyMessage, kMessageKeyValue, "ACK");
160 }
161 xpc_connection_send_message(peer, replyMessage);
162 }
163 }];
164 }
165 else if (operation && !strcmp(operation, kOperationRegisterKeys))
166 {
167 xpc_object_t xkeysToRegisterDict = xpc_dictionary_get_value(event, kMessageKeyValue);
168
169 xpc_object_t xKTRallkeys = xpc_dictionary_get_value(xkeysToRegisterDict, kMessageAllKeys);
170
171 NSDictionary *KTRallkeys = (__bridge_transfer NSDictionary *)(_CFXPCCreateCFObjectFromXPCObject(xKTRallkeys));
172
173 [[UbiqitousKVSProxy sharedKVSProxy] registerKeys: KTRallkeys];
174
175 xpc_object_t replyMessage = xpc_dictionary_create_reply(event);
176 xpc_dictionary_set_string(replyMessage, kMessageKeyValue, "ACK");
177 xpc_connection_send_message(peer, replyMessage);
178
179 secdebug(PROXYXPCSCOPE, "RegisterKeys message sent");
180 }
181 else if (operation && !strcmp(operation, kOperationRequestSyncWithAllPeers))
182 {
183 [[UbiqitousKVSProxy sharedKVSProxy] requestSyncWithAllPeers];
184 xpc_object_t replyMessage = xpc_dictionary_create_reply(event);
185 if (replyMessage) // Caller wanted an ACK, so give one
186 {
187 xpc_dictionary_set_string(replyMessage, kMessageKeyValue, "ACK");
188 xpc_connection_send_message(peer, replyMessage);
189 }
190 secdebug(PROXYXPCSCOPE, "RequestSyncWithAllPeers reply sent");
191 }
192 else if (operation && !strcmp(operation, kOperationRequestEnsurePeerRegistration))
193 {
194 [[UbiqitousKVSProxy sharedKVSProxy] requestEnsurePeerRegistration];
195 xpc_object_t replyMessage = xpc_dictionary_create_reply(event);
196 if (replyMessage) // Caller wanted an ACK, so give one
197 {
198 xpc_dictionary_set_string(replyMessage, kMessageKeyValue, "ACK");
199 xpc_connection_send_message(peer, replyMessage);
200 }
201 secdebug(PROXYXPCSCOPE, "RequestEnsurePeerRegistration reply sent");
202 }
203 else if (operation && !strcmp(operation, kOperationFlush))
204 {
205 [[UbiqitousKVSProxy sharedKVSProxy] doAfterFlush:^{
206 xpc_object_t replyMessage = xpc_dictionary_create_reply(event);
207 if (replyMessage) // Caller wanted an ACK, so give one
208 {
209 xpc_dictionary_set_string(replyMessage, kMessageKeyValue, "ACK");
210 xpc_connection_send_message(peer, replyMessage);
211 }
212 secdebug(PROXYXPCSCOPE, "flush reply sent");
213 }];
214 }
215 else
216 {
217 char *description = xpc_copy_description(event);
218 secdebug(PROXYXPCSCOPE, "Unknown op=%s request from pid %d: %s", operation, xpc_connection_get_pid(peer), description);
219 free(description);
220 }
221 result = true;
222 xit:
223 if (!result)
224 describeXPCObject("handle_operation fail: ", event);
225 }
226
227 void finalize_connection(void *not_used)
228 {
229 secdebug(PROXYXPCSCOPE, "finalize_connection");
230 [[UbiqitousKVSProxy sharedKVSProxy] synchronizeStore];
231 xpc_transaction_end();
232 }
233
234 static bool operation_put_dictionary(xpc_object_t event)
235 {
236 // PUT a set of objects into the KVS store. Return false if error
237 describeXPCObject("operation_put_dictionary event: ", event);
238 xpc_object_t xvalue = xpc_dictionary_get_value(event, kMessageKeyValue);
239 if (!xvalue) {
240 return false;
241 }
242
243 NSObject* object = (__bridge_transfer NSObject*) _CFXPCCreateCFObjectFromXPCObject(xvalue);
244 if (![object isKindOfClass:[NSDictionary<NSString*, NSObject*> class]]) {
245 describeXPCObject("operation_put_dictionary unable to convert to CF: ", xvalue);
246 return false;
247 }
248
249 [[UbiqitousKVSProxy sharedKVSProxy] setObjectsFromDictionary: (NSDictionary<NSString*, NSObject*> *)object];
250
251 return true;
252 }
253
254 static bool operation_get_v2(xpc_connection_t peer, xpc_object_t event)
255 {
256 // GET a set of objects from the KVS store. Return false if error
257 describeXPCObject("operation_get_v2 event: ", event);
258
259 xpc_object_t replyMessage = xpc_dictionary_create_reply(event);
260 if (!replyMessage)
261 {
262 secdebug(PROXYXPCSCOPE, "can't create replyMessage");
263 assert(true); //must have a reply handler
264 return false;
265 }
266 xpc_object_t returnedValues = xpc_dictionary_create(NULL, NULL, 0);
267 if (!returnedValues)
268 {
269 secdebug(PROXYXPCSCOPE, "can't create returnedValues");
270 assert(true); // must have a spot for the returned values
271 return false;
272 }
273
274 xpc_object_t xvalue = xpc_dictionary_get_value(event, kMessageKeyValue);
275 if (!xvalue)
276 {
277 secdebug(PROXYXPCSCOPE, "missing \"value\" key");
278 return false;
279 }
280
281 xpc_object_t xkeystoget = xpc_dictionary_get_value(xvalue, kMessageKeyKeysToGet);
282 if (xkeystoget)
283 {
284 secdebug(PROXYXPCSCOPE, "got xkeystoget");
285 CFTypeRef keystoget = _CFXPCCreateCFObjectFromXPCObject(xkeystoget);
286 if (!keystoget || (CFGetTypeID(keystoget)!=CFArrayGetTypeID())) // not "getAll", this is an error of some kind
287 {
288 secdebug(PROXYXPCSCOPE, "can't convert keystoget or is not an array");
289 CFReleaseSafe(keystoget);
290 return false;
291 }
292
293 [(__bridge NSArray *)keystoget enumerateObjectsUsingBlock: ^ (id obj, NSUInteger idx, BOOL *stop)
294 {
295 NSString *key = (NSString *)obj;
296 id object = [[UbiqitousKVSProxy sharedKVSProxy] objectForKey:key];
297 secdebug(PROXYXPCSCOPE, "[UbiqitousKVSProxy sharedKVSProxy] get: key: %@, object: %@", key, object);
298 xpc_object_t xobject = object ? _CFXPCCreateXPCObjectFromCFObject((__bridge CFTypeRef)object) : xpc_null_create();
299 xpc_dictionary_set_value(returnedValues, [key UTF8String], xobject);
300 describeXPCObject("operation_get_v2: value from kvs: ", xobject);
301 }];
302 }
303 else // get all values from kvs
304 {
305 secdebug(PROXYXPCSCOPE, "get all values from kvs");
306 NSDictionary *all = [[UbiqitousKVSProxy sharedKVSProxy] copyAsDictionary];
307 [all enumerateKeysAndObjectsUsingBlock: ^ (id key, id obj, BOOL *stop)
308 {
309 xpc_object_t xobject = obj ? _CFXPCCreateXPCObjectFromCFObject((__bridge CFTypeRef)obj) : xpc_null_create();
310 xpc_dictionary_set_value(returnedValues, [(NSString *)key UTF8String], xobject);
311 }];
312 }
313
314 xpc_dictionary_set_uint64(replyMessage, kMessageKeyVersion, kCKDXPCVersion);
315 xpc_dictionary_set_value(replyMessage, kMessageKeyValue, returnedValues);
316 xpc_connection_send_message(peer, replyMessage);
317
318 return true;
319 }
320
321 static void cloudkeychainproxy_peer_event_handler(xpc_connection_t peer, xpc_object_t event)
322 {
323 describeXPCObject("peer: ", peer);
324 xpc_type_t type = xpc_get_type(event);
325 if (type == XPC_TYPE_ERROR) {
326 if (event == XPC_ERROR_CONNECTION_INVALID) {
327 // The client process on the other end of the connection has either
328 // crashed or cancelled the connection. After receiving this error,
329 // the connection is in an invalid state, and you do not need to
330 // call xpc_connection_cancel(). Just tear down any associated state
331 // here.
332 } else if (event == XPC_ERROR_TERMINATION_IMMINENT) {
333 // Handle per-connection termination cleanup.
334 }
335 } else {
336 assert(type == XPC_TYPE_DICTIONARY);
337 // Handle the message.
338 // describeXPCObject("dictionary:", event);
339 dispatch_async(dispatch_get_main_queue(), ^{
340 cloudkeychainproxy_peer_dictionary_handler(peer, event);
341 });
342 }
343 }
344
345 static void cloudkeychainproxy_event_handler(xpc_connection_t peer)
346 {
347 // By defaults, new connections will target the default dispatch
348 // concurrent queue.
349
350 if (xpc_get_type(peer) != XPC_TYPE_CONNECTION)
351 {
352 secdebug(PROXYXPCSCOPE, "expected XPC_TYPE_CONNECTION");
353 return;
354 }
355
356 xpc_connection_set_event_handler(peer, ^(xpc_object_t event)
357 {
358 cloudkeychainproxy_peer_event_handler(peer, event);
359 });
360
361 // This will tell the connection to begin listening for events. If you
362 // have some other initialization that must be done asynchronously, then
363 // you can defer this call until after that initialization is done.
364 xpc_connection_resume(peer);
365 }
366
367 static void diagnostics(int argc, const char *argv[])
368 {
369 @autoreleasepool
370 {
371 NSDictionary *all = [[UbiqitousKVSProxy sharedKVSProxy] copyAsDictionary];
372 NSLog(@"All: %@",all);
373 }
374 }
375
376 int ckdproxymain(int argc, const char *argv[])
377 {
378 secdebug(PROXYXPCSCOPE, "Starting CloudKeychainProxy");
379 char *wait4debugger = getenv("WAIT4DEBUGGER");
380
381 if (wait4debugger && !strcasecmp("YES", wait4debugger))
382 {
383 syslog(LOG_ERR, "Waiting for debugger");
384 kill(getpid(), SIGTSTP);
385 }
386
387 if (argc > 1) {
388 diagnostics(argc, argv);
389 return 0;
390 }
391
392 id proxyID = [UbiqitousKVSProxy sharedKVSProxy];
393
394 if (proxyID) { // nothing bad happened when initializing
395
396
397 xpc_connection_t listener = xpc_connection_create_mach_service(xpcServiceName, NULL, XPC_CONNECTION_MACH_SERVICE_LISTENER);
398 xpc_connection_set_event_handler(listener, ^(xpc_object_t object){ cloudkeychainproxy_event_handler(object); });
399
400 // It looks to me like there is insufficient locking to allow a request to come in on the XPC connection while doing the initial all items.
401 // Therefore I'm leaving the XPC connection suspended until that has time to process.
402 xpc_connection_resume(listener);
403
404 @autoreleasepool
405 {
406 secdebug(PROXYXPCSCOPE, "Starting mainRunLoop");
407 NSRunLoop *runLoop = [NSRunLoop mainRunLoop];
408 [runLoop run];
409 }
410 }
411
412 secdebug(PROXYXPCSCOPE, "Exiting CloudKeychainProxy");
413
414 return EXIT_FAILURE;
415 }
416
417 int main(int argc, const char *argv[])
418 {
419 return ckdproxymain(argc, argv);
420 }