]> git.saurik.com Git - apple/security.git/blame - sec/SOSCircle/CloudKeychainProxy/cloudkeychainproxy.m
Security-55471.14.18.tar.gz
[apple/security.git] / sec / SOSCircle / CloudKeychainProxy / cloudkeychainproxy.m
CommitLineData
427c49bc
A
1/*
2 * Copyright (c) 2012 Apple Computer, 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// Created by John Hurley on 7/19/12.
29// Copyright (c) 2012 John Hurley. All rights reserved.
30//
31
32/*
33 This XPC service is essentially just a proxy to iCloud KVS, which exists since
34 the main security code cannot link against Foundation.
35
36 See sendTSARequestWithXPC in tsaSupport.c for how to call the service
37
38 send message to app with xpc_connection_send_message
39
40 For now, build this with:
41
42 ~rc/bin/buildit . --rootsDirectory=/var/tmp -noverify -offline -target CloudKeychainProxy
43
44 and install or upgrade with:
45
46 darwinup install /var/tmp/sec.roots/sec~dst
47 darwinup upgrade /var/tmp/sec.roots/sec~dst
48
49 You must use darwinup during development to update system caches
50*/
51
52//------------------------------------------------------------------------------------------------
53
54#include <AssertMacros.h>
55
56#import <Foundation/Foundation.h>
57#import <Security/Security.h>
58#import <utilities/SecCFRelease.h>
59#import <xpc/xpc.h>
60#import <xpc/private.h>
61#import <CoreFoundation/CFXPCBridge.h>
62#import <sysexits.h>
63#import <syslog.h>
64#import <CommonCrypto/CommonDigest.h>
65#include <utilities/SecXPCError.h>
66
67#import "SOSCloudKeychainConstants.h"
68#import "CKDKVSProxy.h"
69
70void finalize_connection(void *not_used);
71void handle_connection_event(const xpc_connection_t peer);
72static void cloudkeychainproxy_peer_dictionary_handler(const xpc_connection_t peer, xpc_object_t event);
73
74static bool operation_put_dictionary(xpc_object_t event);
75static bool operation_get_v2(xpc_object_t event);
76
77int ckdproxymain(int argc, const char *argv[]);
78
79static 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(XPROXYSCOPE, "%s%s\n", prefix, desc);
87 free(desc);
88 }
89 else
90 secdebug(XPROXYSCOPE, "%s<NULL>\n", prefix);
91
92//#endif
93}
94
95static 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(XPROXYSCOPE, "Reply version: %lld\n", version);
108 require_action(version == kCKDXPCVersion, xit, result = false);
109
110 // Operations
111 secdebug(XPROXYSCOPE, "Handling %s operation", operation);
112
113 if (operation && !strcmp(operation, kOperationPUTDictionary))
114 operation_put_dictionary(event);
115 else
116 if (operation && !strcmp(operation, kOperationGETv2))
117 operation_get_v2(event);
118 else
119 if (operation && !strcmp(operation, kOperationClearStore))
120 {
121 [[UbiqitousKVSProxy sharedKVSProxy] clearStore];
122 xpc_object_t replyMessage = xpc_dictionary_create_reply(event);
123 if (replyMessage) // Caller wanted an ACK, so give one
124 {
125 xpc_dictionary_set_string(replyMessage, kMessageKeyValue, "ACK");
126 xpc_connection_send_message(peer, replyMessage);
127 }
128 }
129 else if (operation && !strcmp(operation, kOperationRemoveObjectForKey))
130 {
131 const char *keyToRemove = xpc_dictionary_get_string(event, kMessageKeyKey);
132 [[UbiqitousKVSProxy sharedKVSProxy] removeObjectForKey:[NSString stringWithCString:keyToRemove encoding:NSUTF8StringEncoding]];
133 xpc_object_t replyMessage = xpc_dictionary_create_reply(event);
134 if (replyMessage) // Caller wanted an ACK, so give one
135 {
136 xpc_dictionary_set_string(replyMessage, kMessageKeyValue, "ACK");
137 xpc_connection_send_message(peer, replyMessage);
138 }
139 }
140 else if (operation && !strcmp(operation, kOperationSynchronize))
141 {
142 [[UbiqitousKVSProxy sharedKVSProxy] requestSynchronization:YES];
143 xpc_object_t replyMessage = xpc_dictionary_create_reply(event);
144 if (replyMessage) // Caller wanted an ACK, so give one
145 {
146 xpc_dictionary_set_string(replyMessage, kMessageKeyValue, "ACK");
147 xpc_connection_send_message(peer, replyMessage);
148 }
149 }
150 else if (operation && !strcmp(operation, kOperationSynchronizeAndWait))
151 {
152 xpc_object_t xkeysToGetDict = xpc_dictionary_get_value(event, kMessageKeyValue);
153 xpc_object_t replyMessage = xpc_dictionary_create_reply(event);
154 xpc_object_t xkeys = xpc_dictionary_get_value(xkeysToGetDict, kMessageKeyKeysToGet);
155 NSArray *keysToGet = (__bridge NSArray *)(_CFXPCCreateCFObjectFromXPCObject(xkeys));
156 [[UbiqitousKVSProxy sharedKVSProxy] waitForSynchronization:keysToGet
157 handler:^(NSDictionary *values, NSError *err) {
158 if (replyMessage) // Caller wanted an ACK, so give one
159 {
160 secdebug("keytrace", "Result from [[UbiqitousKVSProxy sharedKVSProxy] waitForSynchronization:]: %@", err);
161 xpc_object_t xobject = values ? _CFXPCCreateXPCObjectFromCFObject((__bridge CFTypeRef)(values)) : xpc_null_create();
162 xpc_dictionary_set_value(replyMessage, kMessageKeyValue, xobject);
163 if (err)
164 {
165 xpc_object_t xerrobj = SecCreateXPCObjectWithCFError((CFErrorRef)CFBridgingRetain(err));
166 xpc_dictionary_set_value(replyMessage, kMessageKeyError, xerrobj);
167 CFReleaseSafe((__bridge CFTypeRef)(err));
168 }
169 xpc_connection_send_message(peer, replyMessage);
170 }
171 }];
172 CFReleaseSafe((__bridge CFTypeRef)(keysToGet));
173 }
174 else if (operation && !strcmp(operation, kOperationRegisterKeysAndGet))
175 {
176 xpc_object_t xkeysToRegisterDict = xpc_dictionary_get_value(event, kMessageKeyValue);
177// describeXPCObject("xkeysToRegister: ", xkeysToRegisterDict);
178 // KTR = keysToRegister
179 bool getNewKeysOnly = xpc_dictionary_get_bool(xkeysToRegisterDict, kMessageKeyGetNewKeysOnly);
180 xpc_object_t xKTRallkeys = xpc_dictionary_get_value(xkeysToRegisterDict, kMessageKeyKeysToGet);
181 xpc_object_t xKTRrequiresFirstUnlock = xpc_dictionary_get_value(xkeysToRegisterDict, kMessageKeyKeysRequireFirstUnlock);
182 xpc_object_t xKTRrequiresUnlock = xpc_dictionary_get_value(xkeysToRegisterDict, kMessageKeyKeysRequiresUnlocked);
183
184 NSArray *KTRallkeys = (__bridge NSArray *)(_CFXPCCreateCFObjectFromXPCObject(xKTRallkeys));
185 NSArray *KTRrequiresFirstUnlock = (__bridge NSArray *)(_CFXPCCreateCFObjectFromXPCObject(xKTRrequiresFirstUnlock));
186 NSArray *KTRrequiresUnlock = (__bridge NSArray *)(_CFXPCCreateCFObjectFromXPCObject(xKTRrequiresUnlock));
187
188 id object = [[UbiqitousKVSProxy sharedKVSProxy] registerKeysAndGet:getNewKeysOnly always:KTRallkeys reqFirstUnlock:KTRrequiresFirstUnlock reqUnlocked:KTRrequiresUnlock];
189
190 CFReleaseSafe((__bridge CFTypeRef)(KTRallkeys));
191 CFReleaseSafe((__bridge CFTypeRef)(KTRrequiresFirstUnlock));
192 CFReleaseSafe((__bridge CFTypeRef)(KTRrequiresUnlock));
193
194 secdebug("keytrace", "Result from [[UbiqitousKVSProxy sharedKVSProxy] registerKeysAndGet:]: %@", object);
195
196 xpc_object_t xobject = object ? _CFXPCCreateXPCObjectFromCFObject((__bridge CFTypeRef)(object)) : xpc_null_create();
197
198 xpc_object_t replyMessage = xpc_dictionary_create_reply(event);
199 xpc_dictionary_set_value(replyMessage, kMessageKeyValue, xobject);
200 xpc_connection_send_message(peer, replyMessage);
201 secdebug(XPROXYSCOPE, "RegisterKeysAndGet message sent");
202 }
203 else if (operation && !strcmp(operation, kOperationUILocalNotification))
204 {
205 xpc_object_t xLocalNotificationDict = xpc_dictionary_get_value(event, kMessageKeyValue);
206// describeXPCObject("xLocalNotificationDict: ", xLocalNotificationDict);
207 NSDictionary *localNotificationDict = (__bridge NSDictionary *)(_CFXPCCreateCFObjectFromXPCObject(xLocalNotificationDict));
208 int64_t outFlags = 0;
209 id object = [[UbiqitousKVSProxy sharedKVSProxy] localNotification:localNotificationDict outFlags:&outFlags];
210 secdebug(XPROXYSCOPE, "Result from [[UbiqitousKVSProxy sharedKVSProxy] localNotification:]: %@", object);
211 xpc_object_t xobject = object ? _CFXPCCreateXPCObjectFromCFObject((__bridge CFTypeRef)(object)) : xpc_null_create();
212 xpc_object_t replyMessage = xpc_dictionary_create_reply(event);
213 xpc_dictionary_set_int64(xobject, kMessageKeyNotificationFlags, outFlags);
214 xpc_dictionary_set_value(replyMessage, kMessageKeyValue, xobject);
215 xpc_connection_send_message(peer, replyMessage);
216 secdebug(XPROXYSCOPE, "localNotification reply sent");
217 CFReleaseSafe((__bridge CFTypeRef)(localNotificationDict));
218 }
219 else if (operation && !strcmp(operation, kOperationSetParams))
220 {
221 xpc_object_t xParamsDict = xpc_dictionary_get_value(event, kMessageKeyValue);
222 NSDictionary *paramsDict = (__bridge NSDictionary *)(_CFXPCCreateCFObjectFromXPCObject(xParamsDict));
223 [[UbiqitousKVSProxy sharedKVSProxy] setParams:paramsDict];
224 xpc_object_t replyMessage = xpc_dictionary_create_reply(event);
225 if (replyMessage) // Caller wanted an ACK, so give one
226 {
227 xpc_dictionary_set_string(replyMessage, kMessageKeyValue, "ACK");
228 xpc_connection_send_message(peer, replyMessage);
229 }
230 secdebug(XPROXYSCOPE, "setParams reply sent");
231 CFReleaseSafe((__bridge CFTypeRef)(paramsDict));
232 }
233 else if (operation && !strcmp(operation, kOperationRequestSyncWithAllPeers))
234 {
235 [[UbiqitousKVSProxy sharedKVSProxy] requestSyncWithAllPeers];
236 xpc_object_t replyMessage = xpc_dictionary_create_reply(event);
237 if (replyMessage) // Caller wanted an ACK, so give one
238 {
239 xpc_dictionary_set_string(replyMessage, kMessageKeyValue, "ACK");
240 xpc_connection_send_message(peer, replyMessage);
241 }
242 secdebug(XPROXYSCOPE, "RequestSyncWithAllPeers reply sent");
243 }
244 else
245 {
246 char *description = xpc_copy_description(event);
247 secdebug(XPROXYSCOPE, "Unknown op=%s request from pid %d: %s", operation, xpc_connection_get_pid(peer), description);
248 free(description);
249 }
250 result = true;
251xit:
252 if (!result)
253 describeXPCObject("handle_operation fail: ", event);
254}
255
256void finalize_connection(void *not_used)
257{
258 secdebug(XPROXYSCOPE, "finalize_connection");
259 [[UbiqitousKVSProxy sharedKVSProxy] requestSynchronization:YES];
260 xpc_transaction_end();
261}
262
263static bool operation_put_dictionary(xpc_object_t event)
264{
265 // PUT a set of objects into the KVS store. Return false if error
266
267 describeXPCObject("operation_put_dictionary event: ", event);
268 xpc_object_t xvalue = xpc_dictionary_get_value(event, kMessageKeyValue);
269 if (!xvalue)
270 return false;
271
272 CFTypeRef cfvalue = _CFXPCCreateCFObjectFromXPCObject(xvalue);
273 if (cfvalue && (CFGetTypeID(cfvalue)==CFDictionaryGetTypeID()))
274 {
275 [[UbiqitousKVSProxy sharedKVSProxy] setObjectsFromDictionary:(__bridge NSDictionary *)cfvalue];
276 CFReleaseSafe(cfvalue);
277 return true;
278 }
279 else{
280 describeXPCObject("operation_put_dictionary unable to convert to CF: ", xvalue);
281 CFReleaseSafe(cfvalue);
282 }
283 return false;
284}
285
286static bool operation_get_v2(xpc_object_t event)
287{
288 // GET a set of objects from the KVS store. Return false if error
289 describeXPCObject("operation_get_v2 event: ", event);
290 xpc_connection_t peer = xpc_dictionary_get_remote_connection(event);
291 describeXPCObject("operation_get_v2: peer: ", peer);
292
293 xpc_object_t replyMessage = xpc_dictionary_create_reply(event);
294 if (!replyMessage)
295 {
296 secdebug(XPROXYSCOPE, "can't create replyMessage");
297 assert(true); //must have a reply handler
298 return false;
299 }
300 xpc_object_t returnedValues = xpc_dictionary_create(NULL, NULL, 0);
301 if (!returnedValues)
302 {
303 secdebug(XPROXYSCOPE, "can't create returnedValues");
304 assert(true); // must have a spot for the returned values
305 return false;
306 }
307
308 xpc_object_t xvalue = xpc_dictionary_get_value(event, kMessageKeyValue);
309 if (!xvalue)
310 {
311 secdebug(XPROXYSCOPE, "missing \"value\" key");
312 return false;
313 }
314
315 xpc_object_t xkeystoget = xpc_dictionary_get_value(xvalue, kMessageKeyKeysToGet);
316 if (xkeystoget)
317 {
318 secdebug(XPROXYSCOPE, "got xkeystoget");
319 CFTypeRef keystoget = _CFXPCCreateCFObjectFromXPCObject(xkeystoget);
320 if (!keystoget || (CFGetTypeID(keystoget)!=CFArrayGetTypeID())) // not "getAll", this is an error of some kind
321 {
322 secdebug(XPROXYSCOPE, "can't convert keystoget or is not an array");
323 CFReleaseSafe(keystoget);
324 return false;
325 }
326
327 [(__bridge NSArray *)keystoget enumerateObjectsUsingBlock: ^ (id obj, NSUInteger idx, BOOL *stop)
328 {
329 NSString *key = (NSString *)obj;
330 id object = [[UbiqitousKVSProxy sharedKVSProxy] get:key];
331 secdebug(XPROXYSCOPE, "[UbiqitousKVSProxy sharedKVSProxy] get: key: %@, object: %@", key, object);
332 xpc_object_t xobject = object ? _CFXPCCreateXPCObjectFromCFObject((__bridge CFTypeRef)object) : xpc_null_create();
333 xpc_dictionary_set_value(returnedValues, [key UTF8String], xobject);
334 describeXPCObject("operation_get_v2: value from kvs: ", xobject);
335 }];
336 }
337 else // get all values from kvs
338 {
339 secdebug(XPROXYSCOPE, "get all values from kvs");
340 NSDictionary *all = [[UbiqitousKVSProxy sharedKVSProxy] getAll];
341 [all enumerateKeysAndObjectsUsingBlock: ^ (id key, id obj, BOOL *stop)
342 {
343 xpc_object_t xobject = obj ? _CFXPCCreateXPCObjectFromCFObject((__bridge CFTypeRef)obj) : xpc_null_create();
344 xpc_dictionary_set_value(returnedValues, [(NSString *)key UTF8String], xobject);
345 }];
346 }
347
348 xpc_dictionary_set_uint64(replyMessage, kMessageKeyVersion, kCKDXPCVersion);
349 xpc_dictionary_set_value(replyMessage, kMessageKeyValue, returnedValues);
350 xpc_connection_send_message(peer, replyMessage);
351
352 return true;
353}
354
355static void initializeProxyObjectWithConnection(const xpc_connection_t connection)
356{
357 [[UbiqitousKVSProxy sharedKVSProxy] setItemsChangedBlock:^(CFDictionaryRef values)
358 {
359 secdebug(XPROXYSCOPE, "UbiqitousKVSProxy called back");
360 xpc_object_t xobj = _CFXPCCreateXPCObjectFromCFObject(values);
361 xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
362 xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion);
363 xpc_dictionary_set_string(message, kMessageKeyOperation, kMessageOperationItemChanged);
364 xpc_dictionary_set_value(message, kMessageKeyValue, xobj?xobj:xpc_null_create());
365 xpc_connection_send_message(connection, message); // Send message; don't wait for a reply
366 }];
367}
368
369static void cloudkeychainproxy_peer_event_handler(xpc_connection_t peer, xpc_object_t event)
370{
371 describeXPCObject("peer: ", peer);
372 xpc_type_t type = xpc_get_type(event);
373 if (type == XPC_TYPE_ERROR) {
374 if (event == XPC_ERROR_CONNECTION_INVALID) {
375 // The client process on the other end of the connection has either
376 // crashed or cancelled the connection. After receiving this error,
377 // the connection is in an invalid state, and you do not need to
378 // call xpc_connection_cancel(). Just tear down any associated state
379 // here.
380 } else if (event == XPC_ERROR_TERMINATION_IMMINENT) {
381 // Handle per-connection termination cleanup.
382 }
383 } else {
384 assert(type == XPC_TYPE_DICTIONARY);
385 // Handle the message.
386 // describeXPCObject("dictionary:", event);
387 cloudkeychainproxy_peer_dictionary_handler(peer, event);
388 }
389}
390
391static void cloudkeychainproxy_event_handler(xpc_connection_t peer)
392{
393 // By defaults, new connections will target the default dispatch
394 // concurrent queue.
395
396 if (xpc_get_type(peer) != XPC_TYPE_CONNECTION)
397 {
398 secdebug(XPROXYSCOPE, "expected XPC_TYPE_CONNECTION");
399 return;
400 }
401 initializeProxyObjectWithConnection(peer);
402 xpc_connection_set_event_handler(peer, ^(xpc_object_t event)
403 {
404 cloudkeychainproxy_peer_event_handler(peer, event);
405 });
406
407 // This will tell the connection to begin listening for events. If you
408 // have some other initialization that must be done asynchronously, then
409 // you can defer this call until after that initialization is done.
410 xpc_connection_resume(peer);
411}
412
413static void diagnostics(int argc, const char *argv[])
414{
415 @autoreleasepool
416 {
417 NSDictionary *all = [[UbiqitousKVSProxy sharedKVSProxy] getAll];
418 NSLog(@"All: %@",all);
419 }
420}
421
422int ckdproxymain(int argc, const char *argv[])
423{
424 secdebug(XPROXYSCOPE, "Starting CloudKeychainProxy");
425 char *wait4debugger = getenv("WAIT4DEBUGGER");
426 if (wait4debugger && !strcasecmp("YES", wait4debugger))
427 {
428 syslog(LOG_ERR, "Waiting for debugger");
429 kill(getpid(), SIGSTOP);
430 }
431
432 if (argc > 1) {
433 diagnostics(argc, argv);
434 return 0;
435 }
436
437#if !TARGET_IPHONE_SIMULATOR
438 xpc_track_activity(); // sets us up for idle timeout handling
439#endif
440
441 // DISPATCH_TARGET_QUEUE_DEFAULT
442 xpc_connection_t listener = xpc_connection_create_mach_service(xpcServiceName, NULL, XPC_CONNECTION_MACH_SERVICE_LISTENER);
443 xpc_connection_set_event_handler(listener, ^(xpc_object_t object){ cloudkeychainproxy_event_handler(object); });
444
445 [UbiqitousKVSProxy sharedKVSProxy];
446
447 // 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.
448 // Therefore I'm leaving the XPC connection suspended until that has time to process.
449 xpc_connection_resume(listener);
450
451 @autoreleasepool
452 {
453 secdebug(XPROXYSCOPE, "Starting mainRunLoop");
454 NSRunLoop *runLoop = [NSRunLoop mainRunLoop];
455 [runLoop run];
456 }
457
458 secdebug(XPROXYSCOPE, "Exiting CloudKeychainProxy");
459
460 return EXIT_FAILURE;
461}