]> git.saurik.com Git - apple/security.git/blob - Security/sec/SOSCircle/CloudKeychainProxy/cloudkeychainproxy.m
Security-57031.1.35.tar.gz
[apple/security.git] / Security / sec / SOSCircle / CloudKeychainProxy / 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 <TargetConditionals.h>
65
66 #import "SOSCloudKeychainConstants.h"
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_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(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, kOperationRemoveObjectForKey))
134 {
135 const char *keyToRemove = xpc_dictionary_get_string(event, kMessageKeyKey);
136 [[UbiqitousKVSProxy sharedKVSProxy] removeObjectForKey:[NSString stringWithCString:keyToRemove encoding:NSUTF8StringEncoding]];
137 xpc_object_t replyMessage = xpc_dictionary_create_reply(event);
138 if (replyMessage) // Caller wanted an ACK, so give one
139 {
140 xpc_dictionary_set_string(replyMessage, kMessageKeyValue, "ACK");
141 xpc_connection_send_message(peer, replyMessage);
142 }
143 }
144 else if (operation && !strcmp(operation, kOperationSynchronize))
145 {
146 [[UbiqitousKVSProxy sharedKVSProxy] requestSynchronization:YES];
147 xpc_object_t replyMessage = xpc_dictionary_create_reply(event);
148 if (replyMessage) // Caller wanted an ACK, so give one
149 {
150 xpc_dictionary_set_string(replyMessage, kMessageKeyValue, "ACK");
151 xpc_connection_send_message(peer, replyMessage);
152 }
153 }
154 else if (operation && !strcmp(operation, kOperationSynchronizeAndWait))
155 {
156 xpc_object_t xkeysToGetDict = xpc_dictionary_get_value(event, kMessageKeyValue);
157 xpc_object_t replyMessage = xpc_dictionary_create_reply(event);
158 xpc_object_t xkeys = xpc_dictionary_get_value(xkeysToGetDict, kMessageKeyKeysToGet);
159 NSArray *keysToGet = (__bridge_transfer NSArray *)(_CFXPCCreateCFObjectFromXPCObject(xkeys));
160 secnotice(XPROXYSCOPE, "%s XPC request: %s", kWAIT2MINID, kOperationSynchronizeAndWait);
161
162 [[UbiqitousKVSProxy sharedKVSProxy] waitForSynchronization:keysToGet
163 handler:^(NSDictionary *values, NSError *err) {
164 if (replyMessage) // Caller wanted an ACK, so give one
165 {
166 secnotice(PROXYXPCSCOPE, "%s Result from [[UbiqitousKVSProxy sharedKVSProxy] waitForSynchronization:]: %@", kWAIT2MINID, err);
167 xpc_object_t xobject = values ? _CFXPCCreateXPCObjectFromCFObject((__bridge CFTypeRef)(values)) : xpc_null_create();
168 xpc_dictionary_set_value(replyMessage, kMessageKeyValue, xobject);
169 if (err)
170 {
171 xpc_object_t xerrobj = SecCreateXPCObjectWithCFError((__bridge CFErrorRef)(err));
172 xpc_dictionary_set_value(replyMessage, kMessageKeyError, xerrobj);
173 }
174 xpc_connection_send_message(peer, replyMessage);
175 }
176 else {
177 secerror("%s XPC request: %s - No replyMessage: %@", kWAIT2MINID, kOperationSynchronizeAndWait, err);
178 }
179 }];
180 }
181 else if (operation && !strcmp(operation, kOperationRegisterKeys))
182 {
183 xpc_object_t xkeysToRegisterDict = xpc_dictionary_get_value(event, kMessageKeyValue);
184 // describeXPCObject("xkeysToRegister: ", xkeysToRegisterDict);
185
186 // KTR = keysToRegister
187 xpc_object_t xKTRallkeys = xpc_dictionary_get_value(xkeysToRegisterDict, kMessageAllKeys);
188
189 NSDictionary *KTRallkeys = (__bridge_transfer NSDictionary *)(_CFXPCCreateCFObjectFromXPCObject(xKTRallkeys));
190
191 [[UbiqitousKVSProxy sharedKVSProxy] registerKeys: KTRallkeys];
192
193 xpc_object_t replyMessage = xpc_dictionary_create_reply(event);
194 xpc_dictionary_set_string(replyMessage, kMessageKeyValue, "ACK");
195 xpc_connection_send_message(peer, replyMessage);
196
197 secdebug(PROXYXPCSCOPE, "RegisterKeys message sent");
198 }
199 else if (operation && !strcmp(operation, kOperationUILocalNotification))
200 {
201 xpc_object_t xLocalNotificationDict = xpc_dictionary_get_value(event, kMessageKeyValue);
202 // describeXPCObject("xLocalNotificationDict: ", xLocalNotificationDict);
203 NSDictionary *localNotificationDict = (__bridge_transfer NSDictionary *)(_CFXPCCreateCFObjectFromXPCObject(xLocalNotificationDict));
204 int64_t outFlags = 0;
205 id object = [[UbiqitousKVSProxy sharedKVSProxy] localNotification:localNotificationDict outFlags:&outFlags];
206 secdebug(PROXYXPCSCOPE, "Result from [[UbiqitousKVSProxy sharedKVSProxy] localNotification:]: %@", object);
207 xpc_object_t xobject = object ? _CFXPCCreateXPCObjectFromCFObject((__bridge CFTypeRef)(object)) : xpc_null_create();
208 xpc_object_t replyMessage = xpc_dictionary_create_reply(event);
209 xpc_dictionary_set_int64(xobject, kMessageKeyNotificationFlags, outFlags);
210 xpc_dictionary_set_value(replyMessage, kMessageKeyValue, xobject);
211 xpc_connection_send_message(peer, replyMessage);
212 secdebug(PROXYXPCSCOPE, "localNotification reply sent");
213 }
214 else if (operation && !strcmp(operation, kOperationRequestSyncWithAllPeers))
215 {
216 [[UbiqitousKVSProxy sharedKVSProxy] requestSyncWithAllPeers];
217 xpc_object_t replyMessage = xpc_dictionary_create_reply(event);
218 if (replyMessage) // Caller wanted an ACK, so give one
219 {
220 xpc_dictionary_set_string(replyMessage, kMessageKeyValue, "ACK");
221 xpc_connection_send_message(peer, replyMessage);
222 }
223 secdebug(PROXYXPCSCOPE, "RequestSyncWithAllPeers reply sent");
224 }
225 else if (operation && !strcmp(operation, kOperationRequestEnsurePeerRegistration))
226 {
227 [[UbiqitousKVSProxy sharedKVSProxy] requestEnsurePeerRegistration];
228 xpc_object_t replyMessage = xpc_dictionary_create_reply(event);
229 if (replyMessage) // Caller wanted an ACK, so give one
230 {
231 xpc_dictionary_set_string(replyMessage, kMessageKeyValue, "ACK");
232 xpc_connection_send_message(peer, replyMessage);
233 }
234 secdebug(PROXYXPCSCOPE, "RequestEnsurePeerRegistration reply sent");
235 }
236 else if (operation && !strcmp(operation, kOperationFlush))
237 {
238 [[UbiqitousKVSProxy sharedKVSProxy] doAfterFlush:^{
239 xpc_object_t replyMessage = xpc_dictionary_create_reply(event);
240 if (replyMessage) // Caller wanted an ACK, so give one
241 {
242 xpc_dictionary_set_string(replyMessage, kMessageKeyValue, "ACK");
243 xpc_connection_send_message(peer, replyMessage);
244 }
245 secdebug(PROXYXPCSCOPE, "flush reply sent");
246 }];
247 }
248 else
249 {
250 char *description = xpc_copy_description(event);
251 secdebug(PROXYXPCSCOPE, "Unknown op=%s request from pid %d: %s", operation, xpc_connection_get_pid(peer), description);
252 free(description);
253 }
254 result = true;
255 xit:
256 if (!result)
257 describeXPCObject("handle_operation fail: ", event);
258 }
259
260 void finalize_connection(void *not_used)
261 {
262 secdebug(PROXYXPCSCOPE, "finalize_connection");
263 [[UbiqitousKVSProxy sharedKVSProxy] requestSynchronization:YES];
264 xpc_transaction_end();
265 }
266
267 static bool operation_put_dictionary(xpc_object_t event)
268 {
269 // PUT a set of objects into the KVS store. Return false if error
270
271 describeXPCObject("operation_put_dictionary event: ", event);
272 xpc_object_t xvalue = xpc_dictionary_get_value(event, kMessageKeyValue);
273 if (!xvalue)
274 return false;
275
276 CFTypeRef cfvalue = _CFXPCCreateCFObjectFromXPCObject(xvalue);
277 if (cfvalue && (CFGetTypeID(cfvalue)==CFDictionaryGetTypeID()))
278 {
279 [[UbiqitousKVSProxy sharedKVSProxy] setObjectsFromDictionary:(__bridge NSDictionary *)cfvalue];
280 CFReleaseSafe(cfvalue);
281 return true;
282 }
283 else{
284 describeXPCObject("operation_put_dictionary unable to convert to CF: ", xvalue);
285 CFReleaseSafe(cfvalue);
286 }
287 return false;
288 }
289
290 static bool operation_get_v2(xpc_object_t event)
291 {
292 // GET a set of objects from the KVS store. Return false if error
293 describeXPCObject("operation_get_v2 event: ", event);
294 xpc_connection_t peer = xpc_dictionary_get_remote_connection(event);
295 describeXPCObject("operation_get_v2: peer: ", peer);
296
297 xpc_object_t replyMessage = xpc_dictionary_create_reply(event);
298 if (!replyMessage)
299 {
300 secdebug(PROXYXPCSCOPE, "can't create replyMessage");
301 assert(true); //must have a reply handler
302 return false;
303 }
304 xpc_object_t returnedValues = xpc_dictionary_create(NULL, NULL, 0);
305 if (!returnedValues)
306 {
307 secdebug(PROXYXPCSCOPE, "can't create returnedValues");
308 assert(true); // must have a spot for the returned values
309 return false;
310 }
311
312 xpc_object_t xvalue = xpc_dictionary_get_value(event, kMessageKeyValue);
313 if (!xvalue)
314 {
315 secdebug(PROXYXPCSCOPE, "missing \"value\" key");
316 return false;
317 }
318
319 xpc_object_t xkeystoget = xpc_dictionary_get_value(xvalue, kMessageKeyKeysToGet);
320 if (xkeystoget)
321 {
322 secdebug(PROXYXPCSCOPE, "got xkeystoget");
323 CFTypeRef keystoget = _CFXPCCreateCFObjectFromXPCObject(xkeystoget);
324 if (!keystoget || (CFGetTypeID(keystoget)!=CFArrayGetTypeID())) // not "getAll", this is an error of some kind
325 {
326 secdebug(PROXYXPCSCOPE, "can't convert keystoget or is not an array");
327 CFReleaseSafe(keystoget);
328 return false;
329 }
330
331 [(__bridge NSArray *)keystoget enumerateObjectsUsingBlock: ^ (id obj, NSUInteger idx, BOOL *stop)
332 {
333 NSString *key = (NSString *)obj;
334 id object = [[UbiqitousKVSProxy sharedKVSProxy] get:key];
335 secdebug(PROXYXPCSCOPE, "[UbiqitousKVSProxy sharedKVSProxy] get: key: %@, object: %@", key, object);
336 xpc_object_t xobject = object ? _CFXPCCreateXPCObjectFromCFObject((__bridge CFTypeRef)object) : xpc_null_create();
337 xpc_dictionary_set_value(returnedValues, [key UTF8String], xobject);
338 describeXPCObject("operation_get_v2: value from kvs: ", xobject);
339 }];
340 }
341 else // get all values from kvs
342 {
343 secdebug(PROXYXPCSCOPE, "get all values from kvs");
344 NSDictionary *all = [[UbiqitousKVSProxy sharedKVSProxy] getAll];
345 [all enumerateKeysAndObjectsUsingBlock: ^ (id key, id obj, BOOL *stop)
346 {
347 xpc_object_t xobject = obj ? _CFXPCCreateXPCObjectFromCFObject((__bridge CFTypeRef)obj) : xpc_null_create();
348 xpc_dictionary_set_value(returnedValues, [(NSString *)key UTF8String], xobject);
349 }];
350 }
351
352 xpc_dictionary_set_uint64(replyMessage, kMessageKeyVersion, kCKDXPCVersion);
353 xpc_dictionary_set_value(replyMessage, kMessageKeyValue, returnedValues);
354 xpc_connection_send_message(peer, replyMessage);
355
356 return true;
357 }
358
359 static void initializeProxyObjectWithConnection(const xpc_connection_t connection)
360 {
361 [[UbiqitousKVSProxy sharedKVSProxy] setItemsChangedBlock:^CFArrayRef(CFDictionaryRef values)
362 {
363 secdebug(PROXYXPCSCOPE, "UbiqitousKVSProxy called back");
364 xpc_object_t xobj = _CFXPCCreateXPCObjectFromCFObject(values);
365 xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
366 xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion);
367 xpc_dictionary_set_string(message, kMessageKeyOperation, kMessageOperationItemChanged);
368 xpc_dictionary_set_value(message, kMessageKeyValue, xobj?xobj:xpc_null_create());
369 xpc_connection_send_message(connection, message); // Send message; don't wait for a reply
370 return NULL;
371 }];
372 }
373
374 static void cloudkeychainproxy_peer_event_handler(xpc_connection_t peer, xpc_object_t event)
375 {
376 describeXPCObject("peer: ", peer);
377 xpc_type_t type = xpc_get_type(event);
378 if (type == XPC_TYPE_ERROR) {
379 if (event == XPC_ERROR_CONNECTION_INVALID) {
380 // The client process on the other end of the connection has either
381 // crashed or cancelled the connection. After receiving this error,
382 // the connection is in an invalid state, and you do not need to
383 // call xpc_connection_cancel(). Just tear down any associated state
384 // here.
385 } else if (event == XPC_ERROR_TERMINATION_IMMINENT) {
386 // Handle per-connection termination cleanup.
387 }
388 } else {
389 assert(type == XPC_TYPE_DICTIONARY);
390 // Handle the message.
391 // describeXPCObject("dictionary:", event);
392 dispatch_async(dispatch_get_main_queue(), ^{
393 cloudkeychainproxy_peer_dictionary_handler(peer, event);
394 });
395 }
396 }
397
398 static void cloudkeychainproxy_event_handler(xpc_connection_t peer)
399 {
400 // By defaults, new connections will target the default dispatch
401 // concurrent queue.
402
403 if (xpc_get_type(peer) != XPC_TYPE_CONNECTION)
404 {
405 secdebug(PROXYXPCSCOPE, "expected XPC_TYPE_CONNECTION");
406 return;
407 }
408 initializeProxyObjectWithConnection(peer);
409 xpc_connection_set_event_handler(peer, ^(xpc_object_t event)
410 {
411 cloudkeychainproxy_peer_event_handler(peer, event);
412 });
413
414 // This will tell the connection to begin listening for events. If you
415 // have some other initialization that must be done asynchronously, then
416 // you can defer this call until after that initialization is done.
417 xpc_connection_resume(peer);
418 }
419
420 static void diagnostics(int argc, const char *argv[])
421 {
422 @autoreleasepool
423 {
424 NSDictionary *all = [[UbiqitousKVSProxy sharedKVSProxy] getAll];
425 NSLog(@"All: %@",all);
426 }
427 }
428
429 int ckdproxymain(int argc, const char *argv[])
430 {
431 secdebug(PROXYXPCSCOPE, "Starting CloudKeychainProxy");
432 char *wait4debugger = getenv("WAIT4DEBUGGER");
433
434 if (wait4debugger && !strcasecmp("YES", wait4debugger))
435 {
436 syslog(LOG_ERR, "Waiting for debugger");
437 kill(getpid(), SIGTSTP);
438 }
439
440 if (argc > 1) {
441 diagnostics(argc, argv);
442 return 0;
443 }
444
445 // DISPATCH_TARGET_QUEUE_DEFAULT
446 xpc_connection_t listener = xpc_connection_create_mach_service(xpcServiceName, NULL, XPC_CONNECTION_MACH_SERVICE_LISTENER);
447 xpc_connection_set_event_handler(listener, ^(xpc_object_t object){ cloudkeychainproxy_event_handler(object); });
448
449 [UbiqitousKVSProxy sharedKVSProxy];
450
451 // 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.
452 // Therefore I'm leaving the XPC connection suspended until that has time to process.
453 xpc_connection_resume(listener);
454
455 @autoreleasepool
456 {
457 secdebug(PROXYXPCSCOPE, "Starting mainRunLoop");
458 NSRunLoop *runLoop = [NSRunLoop mainRunLoop];
459 [runLoop run];
460 }
461
462 secdebug(PROXYXPCSCOPE, "Exiting CloudKeychainProxy");
463
464 return EXIT_FAILURE;
465 }