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