]> git.saurik.com Git - apple/security.git/blob - Security/sec/SOSCircle/CKBridge/CKClient.c
Security-57031.1.35.tar.gz
[apple/security.git] / Security / sec / SOSCircle / CKBridge / CKClient.c
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 // ckdxpcclient.c
26 // ckd-xpc
27 //
28
29 /*
30 This XPC service is essentially just a proxy to iCloud KVS, which exists since
31 the main security code cannot link against Foundation.
32
33 See sendTSARequestWithXPC in tsaSupport.c for how to call the service
34
35 The client of an XPC service does not get connection events, nor does it
36 need to deal with transactions.
37 */
38
39 //------------------------------------------------------------------------------------------------
40
41 #include <AssertMacros.h>
42
43 #include <xpc/xpc.h>
44 #include <CoreFoundation/CoreFoundation.h>
45 #include <CoreFoundation/CFXPCBridge.h>
46 #include <sysexits.h>
47 #include <syslog.h>
48
49 #include <utilities/debugging.h>
50 #include <utilities/SecCFWrappers.h>
51
52 #include "CKConstants.h"
53
54 #define __CKDXPC_CLIENT_PRIVATE_INDIRECT__ 1
55 #include "CKClient.h"
56
57
58 #define pdebug(format...) secerror(format)
59
60 #define verboseCKDDebugging 1
61
62 #ifndef NDEBUG
63 #define xpdebug(format...) \
64 do { \
65 if (verboseCKDDebugging) \
66 printf(format); \
67 } while (0)
68 #else
69 //empty
70 #define xpdebug(format...)
71 #endif
72
73
74 static xpc_connection_t serviceConnection = NULL;
75 static dispatch_queue_t xpc_queue = NULL;
76 static CloudKeychainReplyBlock itemsChangedBlock;
77
78 static bool handle_xpc_event(const xpc_connection_t peer, xpc_object_t event);
79 static bool xpc_event_filter(const xpc_connection_t peer, xpc_object_t event, CFErrorRef *error);
80
81 // extern CFTypeRef _CFXPCCreateCFObjectFromXPCObject(xpc_object_t xo);
82 // extern xpc_object_t _CFXPCCreateXPCObjectFromCFObject(CFTypeRef cf);
83
84 // Debug
85 static void describeXPCObject(char *prefix, xpc_object_t object);
86 void describeXPCType(char *prefix, xpc_type_t xtype);
87
88 static CFStringRef sErrorDomain = CFSTR("com.apple.security.cloudkeychain");
89
90 enum {
91 kSOSObjectMallocFailed = 1,
92 kAddDuplicateEntry,
93 kSOSObjectNotFoundError = 1,
94 kSOSObjectCantBeConvertedToXPCObject,
95 kSOSOUnexpectedConnectionEvent,
96 kSOSOUnexpectedXPCEvent,
97 kSOSConnectionNotOpen
98 };
99
100 #define WANTXPCREPLY 0
101
102 #pragma mark ----- utilities -----
103
104 static CFErrorRef makeError(CFIndex which)
105 {
106 CFDictionaryRef userInfo = NULL;
107 return CFErrorCreate(kCFAllocatorDefault, sErrorDomain, which, userInfo);
108 }
109
110 #pragma mark ----- SPI -----
111
112 void initXPCConnection()
113 {
114 pdebug("initXPCConnection\n");
115
116 xpc_queue = dispatch_queue_create(xpcServiceName, DISPATCH_QUEUE_SERIAL);
117
118 serviceConnection = xpc_connection_create_mach_service(xpcServiceName, xpc_queue, 0);
119
120 // serviceConnection = xpc_connection_create(xpcServiceName, xpc_queue);
121 pdebug("serviceConnection: %p\n", serviceConnection);
122
123 xpc_connection_set_event_handler(serviceConnection, ^(xpc_object_t event)
124 {
125 pdebug("xpc_connection_set_event_handler\n");
126 handle_xpc_event(serviceConnection, event);
127 });
128
129 xpc_connection_resume(serviceConnection);
130 xpc_retain(serviceConnection);
131 }
132
133 void closeXPCConnection()
134 {
135 pdebug("closeXPCConnection\n");
136 xpc_release(serviceConnection);
137 }
138
139 void setItemsChangedBlock(CloudKeychainReplyBlock icb)
140 {
141 if (icb != itemsChangedBlock)
142 {
143 if (itemsChangedBlock)
144 Block_release(itemsChangedBlock);
145 itemsChangedBlock = icb;
146 Block_copy(itemsChangedBlock);
147 }
148 }
149
150 // typedef void (^CloudKeychainReplyBlock)(CFDictionaryRef returnedValues, CFErrorRef error);
151
152 static bool handle_xpc_event(const xpc_connection_t peer, xpc_object_t event)
153 {
154 CFErrorRef localError = NULL;
155 pdebug(">>>>> handle_connection_event via event_handler <<<<<\n");
156 bool result = false;
157 if ((result = xpc_event_filter(peer, event, &localError)))
158 {
159 const char *operation = xpc_dictionary_get_string(event, kMessageKeyOperation);
160 if (!operation || strcmp(operation, kMessageOperationItemChanged)) // some op we don't care about
161 {
162 pdebug("operation: %s", operation);
163 return result;
164 }
165
166 xpc_object_t xrv = xpc_dictionary_get_value(event, kMessageKeyValue);
167 if (!xrv)
168 {
169 pdebug("xrv null for kMessageKeyValue");
170 return result;
171 }
172 describeXPCObject("xrv", xrv);
173
174 CFDictionaryRef returnedValues = _CFXPCCreateCFObjectFromXPCObject(xrv);
175 pdebug("returnedValues: %@", returnedValues);
176
177 if (itemsChangedBlock)
178 itemsChangedBlock(returnedValues, localError);
179 }
180 CFReleaseSafe(localError);
181
182 return result;
183 }
184
185 static bool xpc_event_filter(const xpc_connection_t peer, xpc_object_t event, CFErrorRef *error)
186 {
187 // return true if the type is XPC_TYPE_DICTIONARY (and therefore something more to process)
188 pdebug("handle_connection_event\n");
189 xpc_type_t xtype = xpc_get_type(event);
190 describeXPCType("handle_xpc_event", xtype);
191 if (XPC_TYPE_CONNECTION == xtype)
192 {
193 pdebug("handle_xpc_event: XPC_TYPE_CONNECTION (unexpected)");
194 // The client of an XPC service does not get connection events
195 // For nwo, we log this and keep going
196 describeXPCObject("handle_xpc_event: XPC_TYPE_CONNECTION, obj : ", event);
197 #if 0
198 if (error)
199 *error = makeError(kSOSOUnexpectedConnectionEvent); // FIX
200 assert(true);
201 #endif
202 }
203 else
204 if (XPC_TYPE_ERROR == xtype)
205 {
206 pdebug("default: xpc error: %s\n", xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION));
207 if (error)
208 *error = makeError(kSOSOUnexpectedConnectionEvent); // FIX
209 }
210 else
211 if (XPC_TYPE_DICTIONARY == xtype)
212 {
213 pdebug("received dictionary event %p\n", event);
214 return true;
215 }
216 else
217 {
218 pdebug("default: unexpected connection event %p\n", event);
219 describeXPCObject("handle_xpc_event: obj : ", event);
220 if (error)
221 *error = makeError(kSOSOUnexpectedXPCEvent);
222 }
223 return false;
224 }
225
226 static void talkWithKVS(xpc_object_t message, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
227 {
228 secerror("start");
229 __block CFErrorRef error = NULL;
230 __block CFTypeRef object = NULL;
231
232 dispatch_block_t callback = ^{
233 secerror("callback");
234 if (replyBlock)
235 replyBlock(object, error);
236 // if (object)
237 // CFRelease(object);
238 if (error)
239 {
240 secerror("callback error: %@", error);
241 // CFRelease(error);
242 }
243 dispatch_release(processQueue);
244 };
245
246 require_action(serviceConnection, xit, error = makeError(kSOSConnectionNotOpen));
247 require_action(message, xit, error = makeError(kSOSObjectNotFoundError));
248 dispatch_retain(processQueue);
249 secerror("xpc_connection_send_message_with_reply called");
250
251 Block_copy(callback);
252
253 //#if !WANTXPCREPLY
254 // xpc_connection_send_message(serviceConnection, message); // Send message; don't want a reply
255 //#else
256 xpc_connection_send_message_with_reply(serviceConnection, message, xpc_queue, ^(xpc_object_t reply)
257 {
258 secerror("xpc_connection_send_message_with_reply handler called back");
259 if (xpc_event_filter(serviceConnection, reply, &error) && reply)
260 {
261 describeXPCObject("getValuesFromKVS: reply : ", reply);
262 xpc_object_t xrv = xpc_dictionary_get_value(reply, kMessageKeyValue);
263 if (xrv)
264 {
265 describeXPCObject("talkWithKVS: xrv: ", xrv);
266 /*
267 * The given XPC object must be one that was previously returned by
268 * _CFXPCCreateXPCMessageWithCFObject().
269 */
270 object = _CFXPCCreateCFObjectFromXPCObject(xrv); // CF object is retained; release in callback
271 secerror("converted CF object: %@", object);
272 }
273 else
274 secerror("missing value reply");
275 }
276 dispatch_async(processQueue, callback);
277 });
278 //#endif
279
280 //sleep(5); // DEBUG DEBUG FIX
281 // xpc_release(message);
282 return;
283
284 xit:
285 secerror("talkWithKVS error: %@", error);
286 if (replyBlock)
287 dispatch_async(processQueue, callback);
288 }
289
290 void putValuesWithXPC(CFDictionaryRef values, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
291 {
292 CFErrorRef error = NULL;
293
294 require_action(values, xit, error = makeError(kSOSObjectNotFoundError));
295
296 xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
297 xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion);
298 xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationPUTDictionary);
299
300 xpc_object_t xobject = _CFXPCCreateXPCObjectFromCFObject(values);
301 require_action(xobject, xit, error = makeError(kSOSObjectCantBeConvertedToXPCObject));
302 xpc_dictionary_set_value(message, kMessageKeyValue, xobject);
303
304 talkWithKVS(message, processQueue, replyBlock);
305 return;
306
307 xit:
308 if (replyBlock)
309 replyBlock(NULL, error);
310 }
311
312 void synchronizeKVS(dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
313 {
314 xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
315 xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion);
316 xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationSynchronize);
317 talkWithKVS(message, processQueue, replyBlock);
318 }
319
320 void clearAll(dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
321 {
322 xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
323 xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion);
324 xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationClearStore);
325 talkWithKVS(message, processQueue, replyBlock);
326 }
327
328 /*
329 extern xpc_object_t xpc_create_reply_with_format(xpc_object_t original, const char * format, ...);
330 xpc_object_t reply = xpc_create_reply_with_format(event,
331 "{keychain-paths: %value, all-paths: %value, extensions: %value, keychain-home: %value}",
332 keychain_paths, all_paths, sandbox_extensions, home);
333 */
334
335 void getValuesFromKVS(CFArrayRef keysToGet, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
336 {
337 secerror("start");
338 CFErrorRef error = NULL;
339 xpc_object_t xkeysOfInterest = xpc_dictionary_create(NULL, NULL, 0);
340 xpc_object_t xkeysToGet = keysToGet ? _CFXPCCreateXPCObjectFromCFObject(keysToGet) : xpc_null_create();
341
342 require_action(xkeysToGet, xit, error = makeError(kSOSObjectNotFoundError));
343
344 xpc_dictionary_set_value(xkeysOfInterest, kMessageKeyKeysToGet, xkeysToGet);
345
346 xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
347 xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion);
348 xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationGETv2);
349 xpc_dictionary_set_value(message, kMessageKeyValue, xkeysOfInterest);
350
351 talkWithKVS(message, processQueue, replyBlock);
352
353 xpc_release(message);
354 return;
355
356 xit:
357 if (replyBlock)
358 replyBlock(NULL, error);
359 }
360
361 void registerKeysForKVS(CFArrayRef keysToGet, CFStringRef clientIdentifier, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
362 {
363 secerror("start");
364
365 xpc_object_t xkeysOfInterest = xpc_dictionary_create(NULL, NULL, 0);
366 xpc_object_t xkeysToRegister = keysToGet ? _CFXPCCreateXPCObjectFromCFObject(keysToGet) : xpc_null_create();
367 xpc_dictionary_set_value(xkeysOfInterest, kMessageKeyKeysToGet, xkeysToRegister);
368
369 xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
370 xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion);
371 xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationRegisterKeysAndGet);
372 xpc_dictionary_set_value(message, kMessageKeyValue, xkeysOfInterest);
373
374 if (clientIdentifier)
375 {
376 char *clientid = CFStringToCString(clientIdentifier);
377 if (clientid)
378 {
379 xpc_dictionary_set_string(message, kMessageKeyClientIdentifier, clientid);
380 free(clientid);
381 }
382 }
383
384 setItemsChangedBlock(replyBlock);
385 talkWithKVS(message, processQueue, replyBlock);
386
387 xpc_release(message);
388 }
389
390 void unregisterKeysForKVS(CFArrayRef keysToUnregister, CFStringRef clientIdentifier, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
391 {
392 #if NO_SERVERz
393 if (gCKD->unregisterKeys) {
394 return gCKD->unregisterKeys(...);
395 }
396 #endif
397
398 secerror("start");
399
400 xpc_object_t xkeysOfInterest = xpc_dictionary_create(NULL, NULL, 0);
401 xpc_object_t xkeysToUnregister = keysToUnregister ? _CFXPCCreateXPCObjectFromCFObject(keysToUnregister) : xpc_null_create();
402 xpc_dictionary_set_value(xkeysOfInterest, kMessageKeyKeysToGet, xkeysToUnregister);
403
404 xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
405 xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion);
406 xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationUnregisterKeys);
407 xpc_dictionary_set_value(message, kMessageKeyValue, xkeysOfInterest);
408
409 if (clientIdentifier)
410 {
411 char *clientid = CFStringToCString(clientIdentifier);
412 if (clientid)
413 {
414 xpc_dictionary_set_string(message, kMessageKeyClientIdentifier, clientid);
415 free(clientid);
416 }
417 }
418
419 talkWithKVS(message, processQueue, replyBlock);
420
421 xpc_release(message);
422 }
423
424 #pragma mark ----- CF-XPC Utilities -----
425
426
427 #pragma mark ----- DEBUG Utilities -----
428
429 //------------------------------------------------------------------------------------------------
430 // DEBUG only
431 //------------------------------------------------------------------------------------------------
432
433 void clearStore()
434 {
435 xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
436 xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion);
437 xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationClearStore);
438 xpc_connection_send_message(serviceConnection, message); // Send message; don't wait for a reply
439 xpc_release(message);
440 }
441
442 void describeXPCObject(char *prefix, xpc_object_t object)
443 {
444 //#ifndef NDEBUG
445 // This is useful for debugging.
446 if (object)
447 {
448 char *desc = xpc_copy_description(object);
449 pdebug("%s%s\n", prefix, desc);
450 free(desc);
451 }
452 else
453 pdebug("%s<NULL>\n", prefix);
454 //#endif
455 }
456
457 void describeXPCType(char *prefix, xpc_type_t xtype)
458 {
459 /*
460 Add these as necessary:
461 XPC_TYPE_ENDPOINT
462 XPC_TYPE_NULL
463 XPC_TYPE_BOOL
464 XPC_TYPE_INT64
465 XPC_TYPE_UINT64
466 XPC_TYPE_DOUBLE
467 XPC_TYPE_DATE
468 XPC_TYPE_DATA
469 XPC_TYPE_STRING
470 XPC_TYPE_UUID
471 XPC_TYPE_FD
472 XPC_TYPE_SHMEM
473 XPC_TYPE_ARRAY
474 */
475
476 #ifndef NDEBUG
477 // This is useful for debugging.
478 char msg[256]={0,};
479 if (XPC_TYPE_CONNECTION == xtype)
480 strcpy(msg, "XPC_TYPE_CONNECTION");
481 else if (XPC_TYPE_ERROR == xtype)
482 strcpy(msg, "XPC_TYPE_ERROR");
483 else if (XPC_TYPE_DICTIONARY == xtype)
484 strcpy(msg, "XPC_TYPE_DICTIONARY");
485 else
486 strcpy(msg, "<unknown>");
487
488 pdebug("%s type:%s\n", prefix, msg);
489 #endif
490 }
491
492
493