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