]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SOSCircle/CKBridge/SOSCloudKeychainClient.c
Security-57740.60.18.tar.gz
[apple/security.git] / OSX / sec / SOSCircle / CKBridge / SOSCloudKeychainClient.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 SOSCloudTransport.c - Implementation of the transport layer from CKBridge to SOSAccount/SOSCircle
26 These are the exported functions from CloudKeychainProxy
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 #include <AssertMacros.h>
40
41 #include <xpc/xpc.h>
42 #include <CoreFoundation/CoreFoundation.h>
43 #include <CoreFoundation/CFXPCBridge.h>
44 #include <sysexits.h>
45 #include <syslog.h>
46 #include <os/activity.h>
47 #include <CoreFoundation/CFUserNotification.h>
48 #include <Security/SecureObjectSync/SOSInternal.h>
49
50 #include <utilities/debugging.h>
51 #include <utilities/SecCFWrappers.h>
52 #include <utilities/SecXPCError.h>
53
54 #include "SOSCloudKeychainConstants.h"
55 #include "SOSCloudKeychainClient.h"
56 #include "SOSKVSKeys.h"
57 #include "SOSUserKeygen.h"
58 #include "SecOTRSession.h"
59
60 #include <os/activity.h>
61 #include <os/state_private.h>
62
63
64 static CFStringRef sErrorDomain = CFSTR("com.apple.security.sos.transport.error");
65
66 #define SOSCKCSCOPE "sync"
67
68 // MARK: ---------- SOSCloudTransport ----------
69
70 /* SOSCloudTransport, a statically initialized transport singleton. */
71 static SOSCloudTransportRef sTransport = NULL;
72
73 static SOSCloudTransportRef SOSCloudTransportCreateXPCTransport(void);
74
75 void SOSCloudKeychainSetTransport(SOSCloudTransportRef transport) {
76 sTransport = transport;
77 }
78
79 void SOSCloudTransportGet(SOSCloudTransportRef transport, CFArrayRef keysToGet, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock);
80
81
82
83 /* Return the singleton cloud transport instance. */
84 CFDictionaryRef SOSCloudCopyKVSState(void) {
85 __block CFDictionaryRef retval = NULL;
86
87 static dispatch_queue_t processQueue = NULL;
88 static dispatch_once_t onceToken;
89 dispatch_once(&onceToken, ^{
90 processQueue = dispatch_queue_create("KVSStateCapture", DISPATCH_QUEUE_SERIAL);
91 });
92
93 if (processQueue == NULL)
94 return NULL;
95
96 dispatch_semaphore_t waitSemaphore = NULL;
97
98 waitSemaphore = dispatch_semaphore_create(0);
99
100 CloudKeychainReplyBlock replyBlock = ^ (CFDictionaryRef returnedValues, CFErrorRef error) {
101 retval = returnedValues;
102 if (retval) CFRetain(retval);
103 dispatch_semaphore_signal(waitSemaphore);
104 };
105
106 SOSCloudKeychainGetAllObjectsFromCloud(processQueue, replyBlock);
107
108 dispatch_semaphore_wait(waitSemaphore, DISPATCH_TIME_FOREVER);
109 dispatch_release(waitSemaphore);
110
111 return retval;
112 }
113
114
115 os_state_block_t kvsStateBlock = ^os_state_data_t(os_state_hints_t hints) {
116 os_state_data_t retval = NULL;
117 __block CFDictionaryRef kvsdict = NULL;
118 CFDataRef serializedKVS = NULL;
119
120 require_quiet(hints->osh_api == 3, errOut); // only grab on sysdiagnose or command lin
121
122 kvsdict = SOSCloudCopyKVSState();
123
124 require_quiet(kvsdict, errOut);
125 serializedKVS = CFPropertyListCreateData(kCFAllocatorDefault, kvsdict, kCFPropertyListBinaryFormat_v1_0, 0, NULL);
126 size_t statelen = CFDataGetLength(serializedKVS);
127 retval = (os_state_data_t)calloc(1, OS_STATE_DATA_SIZE_NEEDED(statelen));
128 require_quiet(retval, errOut);
129
130 retval->osd_type = OS_STATE_DATA_SERIALIZED_NSCF_OBJECT;
131 memcpy(retval->osd_data, CFDataGetBytePtr(serializedKVS), statelen);
132 retval->osd_size = statelen;
133 strcpy(retval->osd_title, "CloudCircle KVS Object");
134 errOut:
135 CFReleaseNull(kvsdict);
136 CFReleaseNull(serializedKVS);
137 return retval;
138 };
139
140
141 static SOSCloudTransportRef SOSCloudTransportDefaultTransport(void)
142 {
143 static dispatch_once_t sTransportOnce;
144 dispatch_once(&sTransportOnce, ^{
145 if (!sTransport)
146 SOSCloudKeychainSetTransport(SOSCloudTransportCreateXPCTransport());
147 // provide state handler to sysdiagnose and logging
148 os_state_add_handler(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), kvsStateBlock);
149 });
150 return sTransport;
151 }
152
153
154 // MARK: ----- utilities -----
155
156 static CFErrorRef makeError(CFIndex which)
157 {
158 CFDictionaryRef userInfo = NULL;
159 return CFErrorCreate(kCFAllocatorDefault, sErrorDomain, which, userInfo);
160 }
161
162 // MARK: ----- DEBUG Utilities -----
163
164 //------------------------------------------------------------------------------------------------
165 // DEBUG only
166 //------------------------------------------------------------------------------------------------
167
168 static void describeXPCObject(char *prefix, xpc_object_t object)
169 {
170 //#ifndef NDEBUG
171 // This is useful for debugging.
172 if (object)
173 {
174 char *desc = xpc_copy_description(object);
175 secdebug(SOSCKCSCOPE, "%s%s\n", prefix, desc);
176 free(desc);
177 }
178 else
179 secdebug(SOSCKCSCOPE, "%s<NULL>\n", prefix);
180 //#endif
181 }
182
183 static void describeXPCType(char *prefix, xpc_type_t xtype)
184 {
185 // Add others as necessary, e.g. XPC_TYPE_DOUBLE
186 #ifndef NDEBUG
187 // This is useful for debugging.
188 char msg[256]={0,};
189 if (XPC_TYPE_CONNECTION == xtype)
190 strcpy(msg, "XPC_TYPE_CONNECTION");
191 else if (XPC_TYPE_ERROR == xtype)
192 strcpy(msg, "XPC_TYPE_ERROR");
193 else if (XPC_TYPE_DICTIONARY == xtype)
194 strcpy(msg, "XPC_TYPE_DICTIONARY");
195 else
196 strcpy(msg, "<unknown>");
197
198 secdebug(SOSCKCSCOPE, "%s type:%s\n", prefix, msg);
199 #endif
200 }
201
202 // MARK: ---------- SOSXPCCloudTransport ----------
203
204 typedef struct SOSXPCCloudTransport *SOSXPCCloudTransportRef;
205 struct SOSXPCCloudTransport
206 {
207 struct SOSCloudTransport transport;
208 xpc_connection_t serviceConnection;
209 xpc_connection_t idsProxyServiceConnection;
210 dispatch_queue_t xpc_queue;
211 };
212
213 static bool xpc_event_filter(const xpc_connection_t peer, xpc_object_t event, CFErrorRef *error)
214 {
215 // return true if the type is XPC_TYPE_DICTIONARY (and therefore something more to process)
216 secdebug(SOSCKCSCOPE, "handle_connection_event\n");
217 xpc_type_t xtype = xpc_get_type(event);
218 describeXPCType("handle_xpc_event", xtype);
219 if (XPC_TYPE_CONNECTION == xtype)
220 {
221 secdebug(SOSCKCSCOPE, "handle_xpc_event: XPC_TYPE_CONNECTION (unexpected)");
222 // The client of an XPC service does not get connection events
223 // For now, we log this and keep going
224 describeXPCObject("handle_xpc_event: XPC_TYPE_CONNECTION, obj : ", event);
225 #if 0
226 if (error)
227 *error = makeError(kSOSOUnexpectedConnectionEvent); // FIX
228 assert(true);
229 #endif
230 }
231 else
232 if (XPC_TYPE_ERROR == xtype)
233 {
234 #ifndef NDEBUG
235 const char *estr = xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION);
236 #endif
237 secdebug(SOSCKCSCOPE, "default: xpc error: %s\n", estr);
238 #if 0 // just log for now
239 CFStringRef errStr = CFStringCreateWithCString(kCFAllocatorDefault, estr, kCFStringEncodingUTF8);
240 CFMutableDictionaryRef userInfo = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
241 if (errStr)
242 CFDictionaryAddValue(userInfo, kCFErrorLocalizedDescriptionKey, errStr);
243 if (error)
244 *error = CFErrorCreate(kCFAllocatorDefault, sErrorDomain, kSOSOXPCErrorEvent, userInfo);
245 CFReleaseSafe(errStr);
246 CFReleaseSafe(userInfo);
247 #endif
248 }
249 else
250 if (XPC_TYPE_DICTIONARY == xtype)
251 {
252 secdebug(SOSCKCSCOPE, "received dictionary event %p\n", event);
253 return true;
254 }
255 else
256 {
257 secdebug(SOSCKCSCOPE, "default: unexpected connection event %p\n", event);
258 describeXPCObject("handle_xpc_event: obj : ", event);
259 if (error)
260 *error = makeError(kSOSOUnexpectedXPCEvent);
261 }
262 return false;
263 }
264
265 static void SOSXPCCloudTransportInit(SOSXPCCloudTransportRef transport)
266 {
267 secdebug(SOSCKCSCOPE, "initXPCConnection\n");
268
269 transport->xpc_queue = dispatch_queue_create(xpcServiceName, DISPATCH_QUEUE_SERIAL);
270
271 transport->serviceConnection = xpc_connection_create_mach_service(xpcServiceName, transport->xpc_queue, 0);
272
273
274 secdebug(SOSCKCSCOPE, "serviceConnection: %p\n", transport->serviceConnection);
275
276
277 xpc_connection_set_event_handler(transport->serviceConnection, ^(xpc_object_t event)
278 {
279 secdebug(SOSCKCSCOPE, "xpc_connection_set_event_handler\n");
280 });
281
282 xpc_connection_resume(transport->serviceConnection);
283 xpc_retain(transport->serviceConnection);
284
285 transport->idsProxyServiceConnection = xpc_connection_create_mach_service(xpcIDSServiceName, transport->xpc_queue, 0);
286
287 secdebug(SOSCKCSCOPE, "ids service connection: %p\n", transport->idsProxyServiceConnection);
288
289 xpc_connection_set_event_handler(transport->idsProxyServiceConnection, ^(xpc_object_t object) {
290 secdebug(SOSCKCSCOPE, "IDS Transport, xpc_connection_set_event_handler\n");
291 });
292 xpc_connection_resume(transport->idsProxyServiceConnection);
293 xpc_retain(transport->idsProxyServiceConnection);
294
295 }
296
297 static void talkWithIDS(SOSXPCCloudTransportRef transport, xpc_object_t message, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
298 {
299 CFErrorRef connectionError = NULL;
300
301 os_activity_t trace_activity = os_activity_start("talkWithIDS", OS_ACTIVITY_FLAG_DEFAULT);
302 require_action(transport->idsProxyServiceConnection, xit, connectionError = makeError(kSOSConnectionNotOpen));
303 require_action(message, xit, connectionError = makeError(kSOSObjectNotFoundError));
304 dispatch_retain(processQueue);
305
306 xpc_connection_send_message_with_reply(transport->idsProxyServiceConnection, message, transport->xpc_queue, ^(xpc_object_t reply)
307 {
308 CFErrorRef serverError = NULL;
309 CFTypeRef object = NULL;
310 if (xpc_event_filter(transport->idsProxyServiceConnection, reply, &serverError) && reply)
311 {
312 describeXPCObject("IDS Proxy: reply : ", reply);
313 if (serverError)
314 secerror("Error from xpc_event_filter: %@", serverError);
315 xpc_object_t xrv = xpc_dictionary_get_value(reply, kMessageKeyValue);
316 if (xrv)
317 {
318 describeXPCObject("talkwithIDS: xrv: ", xrv);
319 /*
320 * The given XPC object must be one that was previously returned by
321 * _CFXPCCreateXPCMessageWithCFObject().
322 */
323 object = _CFXPCCreateCFObjectFromXPCObject(xrv); // CF object is retained; release in callback
324 secnotice("talkwithIDS", "converted CF object: %@", object);
325 }
326 else
327 secerror("missing value reply");
328
329 xpc_object_t xerror = xpc_dictionary_get_value(reply, kMessageKeyError);
330 if (xerror)
331 serverError = SecCreateCFErrorWithXPCObject(xerror); // use SecCFCreateErrorWithFormat?
332 }
333 dispatch_async(processQueue, ^{
334 if (replyBlock)
335 replyBlock(object, serverError);
336 CFReleaseSafe(object);
337 if (serverError)
338 {
339 secerror("talkwithIDS callback error: %@", serverError);
340 CFReleaseSafe(serverError);
341 }
342 dispatch_release(processQueue);
343 });
344 });
345 return;
346
347 xit:
348 secerror("talkWithIDS error: %@", connectionError);
349 dispatch_async(processQueue, ^{
350 if (replyBlock)
351 replyBlock(NULL, connectionError);
352 CFReleaseSafe(connectionError);
353 dispatch_release(processQueue);
354 });
355
356 os_activity_end(trace_activity);
357 }
358
359 typedef void (^ProxyReplyBlock)(xpc_object_t reply);
360
361 static bool messageToProxy(SOSXPCCloudTransportRef transport, xpc_object_t message, CFErrorRef *error, dispatch_queue_t processQueue, ProxyReplyBlock replyBlock) {
362 CFErrorRef connectionError = NULL;
363
364 require_action(transport->serviceConnection, xit, connectionError = makeError(kSOSConnectionNotOpen));
365 require_action(message, xit, connectionError = makeError(kSOSObjectNotFoundError));
366
367 xpc_connection_send_message_with_reply(transport->serviceConnection, message, processQueue, replyBlock);
368 xit:
369 return CFErrorPropagate(connectionError, error);
370 }
371
372 static void talkWithKVS(SOSXPCCloudTransportRef transport, xpc_object_t message, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
373 {
374 CFErrorRef messagingError = NULL;
375 dispatch_retain(processQueue);
376 bool messaged = messageToProxy(transport, message, &messagingError, transport->xpc_queue, ^(xpc_object_t reply)
377 {
378 CFErrorRef serverError = NULL;
379 CFTypeRef object = NULL;
380 if (xpc_event_filter(transport->serviceConnection, reply, &serverError) && reply)
381 {
382 describeXPCObject("getValuesFromKVS: reply : ", reply);
383 if (serverError)
384 secerror("Error from xpc_event_filter: %@", serverError);
385 xpc_object_t xrv = xpc_dictionary_get_value(reply, kMessageKeyValue);
386 if (xrv)
387 {
388 describeXPCObject("talkWithKVS: xrv: ", xrv);
389 /*
390 * The given XPC object must be one that was previously returned by
391 * _CFXPCCreateXPCMessageWithCFObject().
392 */
393 object = _CFXPCCreateCFObjectFromXPCObject(xrv); // CF object is retained; release in callback
394 secnotice("talkwithkvs", "converted CF object: %@", object);
395 }
396 else
397 secerror("missing value reply");
398
399 xpc_object_t xerror = xpc_dictionary_get_value(reply, kMessageKeyError);
400 if (xerror)
401 serverError = SecCreateCFErrorWithXPCObject(xerror); // use SecCFCreateErrorWithFormat?
402 }
403 dispatch_async(processQueue, ^{
404 if (replyBlock)
405 replyBlock(object, serverError);
406 CFReleaseSafe(object);
407 if (serverError)
408 {
409 secerror("callback error: %@", serverError);
410 CFReleaseSafe(serverError);
411 }
412 dispatch_release(processQueue);
413 });
414 });
415
416 if (!messaged) {
417 secerror("talkWithKVS error: %@", messagingError);
418 dispatch_async(processQueue, ^{
419 if (replyBlock)
420 replyBlock(NULL, messagingError);
421 CFReleaseSafe(messagingError);
422 dispatch_release(processQueue);
423 });
424 }
425 }
426
427 // MARK: ---------- SOSXPCCloudTransport Client Calls ----------
428
429 /* Concrete function backend implementations. */
430 static void SOSCloudTransportSetItemsChangedBlock(SOSCloudTransportRef transport,
431 CloudItemsChangedBlock itemsChangedBlock) {
432 if (transport->itemsChangedBlock != itemsChangedBlock)
433 {
434 secnotice(SOSCKCSCOPE, "Changing itemsChangedBlock");
435 if (transport->itemsChangedBlock)
436 Block_release(transport->itemsChangedBlock);
437 transport->itemsChangedBlock = Block_copy(itemsChangedBlock);
438 }
439 }
440
441 /* Virtual function backend implementations. */
442 static void SOSCloudTransportPut(SOSCloudTransportRef transport, CFDictionaryRef values, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
443 {
444 SOSXPCCloudTransportRef xpcTransport = (SOSXPCCloudTransportRef)transport;
445 secdebug(SOSCKCSCOPE, "%@", values);
446 CFErrorRef error = NULL;
447 xpc_object_t message = NULL;
448 xpc_object_t xobject = NULL;
449 require_action(values, xit, error = makeError(kSOSObjectNotFoundError));
450
451 message = xpc_dictionary_create(NULL, NULL, 0);
452 xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion);
453 xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationPUTDictionary);
454
455 xobject = _CFXPCCreateXPCObjectFromCFObject(values);
456 require_action(xobject, xit, error = makeError(kSOSObjectCantBeConvertedToXPCObject));
457 xpc_dictionary_set_value(message, kMessageKeyValue, xobject);
458 xpc_release(xobject);
459
460 talkWithKVS(xpcTransport, message, processQueue, replyBlock);
461 xpc_release(message);
462 return;
463
464 xit:
465 if (replyBlock)
466 replyBlock(NULL, error);
467 CFReleaseSafe(error);
468 }
469
470 /* Get from KVS */
471 void SOSCloudTransportGet(SOSCloudTransportRef transport, CFArrayRef keysToGet, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
472 {
473 SOSXPCCloudTransportRef xpcTransport = (SOSXPCCloudTransportRef)transport;
474 secdebug(SOSCKCSCOPE, "%@", keysToGet);
475 CFErrorRef error = NULL;
476 xpc_object_t xkeysOfInterest = xpc_dictionary_create(NULL, NULL, 0);
477 xpc_object_t xkeysToGet = keysToGet ? _CFXPCCreateXPCObjectFromCFObject(keysToGet) : xpc_null_create();
478
479 require_action(xkeysToGet, xit, error = makeError(kSOSObjectNotFoundError));
480
481 if (keysToGet) // don't add if nulll; will call getall
482 xpc_dictionary_set_value(xkeysOfInterest, kMessageKeyKeysToGet, xkeysToGet);
483
484 xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
485 xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion);
486 xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationGETv2);
487 xpc_dictionary_set_value(message, kMessageKeyValue, xkeysOfInterest);
488
489 talkWithKVS(xpcTransport, message, processQueue, replyBlock);
490 xpc_release(xkeysToGet);
491 xpc_release(xkeysOfInterest);
492 xpc_release(message);
493 return;
494
495 xit:
496 if(xkeysOfInterest)
497 xpc_release(xkeysOfInterest);
498 if(xkeysToGet)
499 xpc_release(xkeysToGet);
500 if (replyBlock)
501 replyBlock(NULL, error);
502 CFReleaseSafe(error);
503 }
504
505 //
506 // Handles NULL by seting xpc_null.
507 static void SecXPCDictionarySetCFObject(xpc_object_t xdict, const char *key, CFTypeRef object)
508 {
509 xpc_object_t xpc_obj = object ? _CFXPCCreateXPCObjectFromCFObject(object) : xpc_null_create();
510 xpc_dictionary_set_value(xdict, key, xpc_obj);
511 xpc_release(xpc_obj);
512 }
513
514 static void SOSCloudTransportGetIDSDeviceID(SOSCloudTransportRef transport, CloudKeychainReplyBlock replyBlock)
515 {
516 dispatch_queue_t processQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
517
518 SOSXPCCloudTransportRef xpcTransport = (SOSXPCCloudTransportRef)transport;
519
520 xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
521 xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion);
522 xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationGetDeviceID);
523
524 talkWithIDS(xpcTransport, message, processQueue, replyBlock);
525 xpc_release(message);
526 }
527
528 static void SOSCloudTransportSendFragmentedIDSMessage(SOSCloudTransportRef transport, CFDictionaryRef messageData, CFStringRef deviceName, CFStringRef peerID, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock){
529
530 SOSXPCCloudTransportRef xpcTransport = (SOSXPCCloudTransportRef)transport;
531 xpc_object_t xmessageData = _CFXPCCreateXPCObjectFromCFObject(messageData);
532
533 xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
534 xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion);
535 xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationSendFragmentedIDSMessage);
536
537 xpc_dictionary_set_value(message, kMessageKeyValue, xmessageData);
538 SecXPCDictionarySetCFObject(message, kMessageKeyDeviceName, deviceName);
539 SecXPCDictionarySetCFObject(message, kMessageKeyPeerID, peerID);
540 talkWithIDS(xpcTransport, message, processQueue, replyBlock);
541
542 xpc_release(xmessageData);
543 xpc_release(message);
544 }
545
546 static void SOSCloudTransportRetrievePendingMessagesInFlight(SOSCloudTransportRef transport, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock){
547
548 SOSXPCCloudTransportRef xpcTransport = (SOSXPCCloudTransportRef)transport;
549
550 xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
551 xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion);
552 xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationGetPendingMesages);
553
554 talkWithIDS(xpcTransport, message, processQueue, replyBlock);
555
556 xpc_release(message);
557 }
558
559 static void SOSCloudTransportCheckIDSDeviceIDAvailability(SOSCloudTransportRef transport, CFArrayRef ids, CFStringRef peerID, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
560 {
561 secdebug(SOSCKCSCOPE, "start");
562 SOSXPCCloudTransportRef xpcTransport = (SOSXPCCloudTransportRef)transport;
563
564 xpc_object_t xIDSArray = _CFXPCCreateXPCObjectFromCFObject(ids);
565
566 xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
567 xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion);
568 xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationSendDeviceList);
569
570 SecXPCDictionarySetCFObject(message, kMessageKeyPeerID, peerID);
571 xpc_dictionary_set_value(message, kMessageKeyValue, xIDSArray);
572
573 talkWithIDS(xpcTransport, message, processQueue, replyBlock);
574
575 xpc_release(xIDSArray);
576 xpc_release(message);
577
578 }
579
580 static void SOSCloudTransportSendIDSMessage(SOSCloudTransportRef transport, CFDictionaryRef messageData, CFStringRef deviceName, CFStringRef peerID, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock){
581
582 SOSXPCCloudTransportRef xpcTransport = (SOSXPCCloudTransportRef)transport;
583 xpc_object_t xmessageData = _CFXPCCreateXPCObjectFromCFObject(messageData);
584
585 xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
586 xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion);
587 xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationSendIDSMessage);
588
589 xpc_dictionary_set_value(message, kMessageKeyValue, xmessageData);
590 SecXPCDictionarySetCFObject(message, kMessageKeyDeviceName, deviceName);
591 SecXPCDictionarySetCFObject(message, kMessageKeyPeerID, peerID);
592 talkWithIDS(xpcTransport, message, processQueue, replyBlock);
593
594 xpc_release(xmessageData);
595 xpc_release(message);
596 }
597
598 static void SOSCloudTransportUpdateKeys(SOSCloudTransportRef transport,
599 CFDictionaryRef keys,
600 CFStringRef accountUUID,
601 dispatch_queue_t processQueue,
602 CloudKeychainReplyBlock replyBlock)
603 {
604 SOSXPCCloudTransportRef xpcTransport = (SOSXPCCloudTransportRef)transport;
605
606 xpc_object_t xkeysOfInterest = xpc_dictionary_create(NULL, NULL, 0);
607 SecXPCDictionarySetCFObject(xkeysOfInterest, kMessageAllKeys, keys);
608
609 xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
610 xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion);
611 xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationRegisterKeys);
612 xpc_dictionary_set_value(message, kMessageKeyValue, xkeysOfInterest);
613 SecXPCDictionarySetCFObject(message, kMessageKeyAccountUUID, accountUUID);
614
615 talkWithKVS(xpcTransport, message, processQueue, replyBlock);
616 xpc_release(message);
617 xpc_release(xkeysOfInterest);
618 }
619
620 static void SOSCloudTransportGetAll(SOSCloudTransportRef transport, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
621 {
622 secdebug(SOSCKCSCOPE, "start");
623 SOSCloudTransportGet(transport, NULL, processQueue, replyBlock);
624 }
625
626 static void SOSCloudTransportSync(SOSCloudTransportRef transport, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
627 {
628 SOSXPCCloudTransportRef xpcTransport = (SOSXPCCloudTransportRef)transport;
629 secdebug(SOSCKCSCOPE, "start");
630 xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
631 xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion);
632 xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationSynchronize);
633 talkWithKVS(xpcTransport, message, processQueue, replyBlock);
634 xpc_release(message);
635 }
636
637 static void SOSCloudTransportSyncAndWait(SOSCloudTransportRef transport, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
638 {
639 SOSXPCCloudTransportRef xpcTransport = (SOSXPCCloudTransportRef)transport;
640 secnotice(SOSCKCSCOPE, "%s XPC request to CKD: %s", kWAIT2MINID, kOperationSynchronizeAndWait);
641
642 xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
643 xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion);
644 xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationSynchronizeAndWait);
645
646 talkWithKVS(xpcTransport, message, processQueue, replyBlock);
647 xpc_release(message);
648 }
649
650 static void SOSCloudTransportClearAll(SOSCloudTransportRef transport, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
651 {
652 SOSXPCCloudTransportRef xpcTransport = (SOSXPCCloudTransportRef)transport;
653 secdebug(SOSCKCSCOPE, "start");
654 xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
655 xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion);
656 xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationClearStore);
657 talkWithKVS(xpcTransport, message, processQueue, replyBlock);
658 xpc_release(message);
659 }
660
661 static void SOSCloudTransportRequestSyncWithPeers(SOSCloudTransportRef transport, CFArrayRef /* CFStringRef */ peers, CFArrayRef /* CFStringRef */ backupPeers, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
662 {
663 secdebug(SOSCKCSCOPE, "start");
664 SOSXPCCloudTransportRef xpcTransport = (SOSXPCCloudTransportRef)transport;
665
666 xpc_object_t xpcmessage = xpc_dictionary_create(NULL, NULL, 0);
667
668 xpc_dictionary_set_uint64(xpcmessage, kMessageKeyVersion, kCKDXPCVersion);
669 xpc_dictionary_set_string(xpcmessage, kMessageKeyOperation, kOperationRequestSyncWithPeers);
670
671 SecXPCDictionarySetCFObject(xpcmessage, kMessageKeyPeerIDList, peers);
672 SecXPCDictionarySetCFObject(xpcmessage, kMesssgeKeyBackupPeerIDList, backupPeers);
673
674 talkWithKVS(xpcTransport, xpcmessage, processQueue, replyBlock);
675
676 xpc_release(xpcmessage);
677 }
678
679
680 static bool SOSCloudTransportHasPeerSyncPending(SOSCloudTransportRef transport, CFStringRef peerID, CFErrorRef* error)
681 {
682 secdebug(SOSCKCSCOPE, "start");
683 SOSXPCCloudTransportRef xpcTransport = (SOSXPCCloudTransportRef)transport;
684
685 __block bool isSyncing = false;
686
687 xpc_object_t xpcmessage = xpc_dictionary_create(NULL, NULL, 0);
688
689 xpc_dictionary_set_uint64(xpcmessage, kMessageKeyVersion, kCKDXPCVersion);
690 xpc_dictionary_set_string(xpcmessage, kMessageKeyOperation, kOperationHasPendingSyncWithPeer);
691
692 SecXPCDictionarySetCFObject(xpcmessage, kMessageKeyPeerID, peerID);
693
694 dispatch_semaphore_t wait = dispatch_semaphore_create(0);
695 bool sent = messageToProxy(xpcTransport, xpcmessage, error, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(xpc_object_t reply) {
696 isSyncing = xpc_dictionary_get_bool(reply, kMessageKeyValue);
697 dispatch_semaphore_signal(wait);
698 });
699
700 if (sent) {
701 dispatch_semaphore_wait(wait, DISPATCH_TIME_FOREVER);
702 }
703
704 dispatch_release(wait);
705
706 return sent && isSyncing;
707 }
708
709
710 static bool SOSCloudTransportHasPendingKey(SOSCloudTransportRef transport, CFStringRef keyName, CFErrorRef* error)
711 {
712 secdebug(SOSCKCSCOPE, "start");
713 SOSXPCCloudTransportRef xpcTransport = (SOSXPCCloudTransportRef)transport;
714
715 __block bool kvsHasMessage = false;
716
717 xpc_object_t xpcmessage = xpc_dictionary_create(NULL, NULL, 0);
718
719 xpc_dictionary_set_uint64(xpcmessage, kMessageKeyVersion, kCKDXPCVersion);
720 xpc_dictionary_set_string(xpcmessage, kMessageKeyOperation, kOperationHasPendingKey);
721
722 SecXPCDictionarySetCFObject(xpcmessage, kMessageKeyKey, keyName);
723
724 dispatch_semaphore_t kvsWait = dispatch_semaphore_create(0);
725 bool kvsSent = messageToProxy(xpcTransport, xpcmessage, error, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(xpc_object_t reply) {
726 kvsHasMessage = xpc_dictionary_get_bool(reply, kMessageKeyValue);
727 dispatch_semaphore_signal(kvsWait);
728 });
729
730 if (kvsSent) {
731 dispatch_semaphore_wait(kvsWait, DISPATCH_TIME_FOREVER);
732 }
733
734 dispatch_release(kvsWait);
735
736 return kvsSent && kvsHasMessage;
737 }
738
739
740 static void SOSCloudTransportRequestEnsurePeerRegistration(SOSCloudTransportRef transport, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
741 {
742 secdebug(SOSCKCSCOPE, "start");
743 SOSXPCCloudTransportRef xpcTransport = (SOSXPCCloudTransportRef)transport;
744
745 xpc_object_t xpcmessage = xpc_dictionary_create(NULL, NULL, 0);
746 xpc_dictionary_set_uint64(xpcmessage, kMessageKeyVersion, kCKDXPCVersion);
747 xpc_dictionary_set_string(xpcmessage, kMessageKeyOperation, kOperationRequestEnsurePeerRegistration);
748
749 talkWithKVS(xpcTransport, xpcmessage, processQueue, replyBlock);
750
751 xpc_release(xpcmessage);
752 }
753
754 static void SOSCloudTransportFlush(SOSCloudTransportRef transport, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
755 {
756 secdebug(SOSCKCSCOPE, "start");
757 SOSXPCCloudTransportRef xpcTransport = (SOSXPCCloudTransportRef)transport;
758
759 xpc_object_t xpcmessage = xpc_dictionary_create(NULL, NULL, 0);
760 xpc_dictionary_set_uint64(xpcmessage, kMessageKeyVersion, kCKDXPCVersion);
761 xpc_dictionary_set_string(xpcmessage, kMessageKeyOperation, kOperationFlush);
762
763 talkWithKVS(xpcTransport, xpcmessage, processQueue, replyBlock);
764
765 xpc_release(xpcmessage);
766 }
767
768 static SOSCloudTransportRef SOSCloudTransportCreateXPCTransport(void)
769 {
770 SOSXPCCloudTransportRef st;
771 st = calloc(1, sizeof(*st));
772 st->transport.put = SOSCloudTransportPut;
773 st->transport.updateKeys = SOSCloudTransportUpdateKeys;
774 st->transport.sendIDSMessage = SOSCloudTransportSendIDSMessage;
775 st->transport.sendFragmentedIDSMessage = SOSCloudTransportSendFragmentedIDSMessage;
776 st->transport.retrieveMessages = SOSCloudTransportRetrievePendingMessagesInFlight;
777
778 st->transport.getDeviceID = SOSCloudTransportGetIDSDeviceID;
779 st->transport.get = SOSCloudTransportGet;
780 st->transport.getAll = SOSCloudTransportGetAll;
781 st->transport.synchronize = SOSCloudTransportSync;
782 st->transport.synchronizeAndWait = SOSCloudTransportSyncAndWait;
783 st->transport.clearAll = SOSCloudTransportClearAll;
784 st->transport.requestSyncWithPeers = SOSCloudTransportRequestSyncWithPeers;
785 st->transport.hasPeerSyncPending = SOSCloudTransportHasPeerSyncPending;
786 st->transport.hasPendingKey = SOSCloudTransportHasPendingKey;
787 st->transport.requestEnsurePeerRegistration = SOSCloudTransportRequestEnsurePeerRegistration;
788 st->transport.getIDSDeviceAvailability = SOSCloudTransportCheckIDSDeviceIDAvailability;
789 st->transport.flush = SOSCloudTransportFlush;
790 st->transport.itemsChangedBlock = Block_copy(^CFArrayRef(CFDictionaryRef changes) {
791 secerror("Calling default itemsChangedBlock - fatal: %@", changes);
792 assert(false);
793 return NULL;
794 });
795 SOSXPCCloudTransportInit(st);
796 return &st->transport;
797 }
798
799 // MARK: ---------- SOSCloudKeychain concrete client APIs ----------
800 void SOSCloudKeychainSetItemsChangedBlock(CloudItemsChangedBlock itemsChangedBlock)
801 {
802 secdebug(SOSCKCSCOPE, "start");
803 SOSCloudTransportSetItemsChangedBlock(SOSCloudTransportDefaultTransport(),
804 itemsChangedBlock);
805 }
806
807 // MARK: ---------- SOSCloudKeychain virtual client APIs ----------
808
809 void SOSCloudKeychainPutObjectsInCloud(CFDictionaryRef objects, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
810 {
811 SOSCloudTransportRef cTransportRef = SOSCloudTransportDefaultTransport();
812 if (cTransportRef)
813 cTransportRef->put(cTransportRef, objects, processQueue, replyBlock);
814 }
815
816 void SOSCloudKeychainUpdateKeys(CFDictionaryRef keys, CFStringRef accountUUID, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
817 {
818 SOSCloudTransportRef cTransportRef = SOSCloudTransportDefaultTransport();
819 if (cTransportRef)
820 cTransportRef->updateKeys(cTransportRef, keys, accountUUID, processQueue, replyBlock);
821 }
822
823 void SOSCloudKeychainSendIDSMessage(CFDictionaryRef message, CFStringRef deviceName, CFStringRef peerID, dispatch_queue_t processQueue, CFBooleanRef fragmentation, CloudKeychainReplyBlock replyBlock)
824 {
825 SOSCloudTransportRef cTransportRef = SOSCloudTransportDefaultTransport();
826
827 if(cTransportRef && fragmentation == kCFBooleanTrue)
828 cTransportRef->sendFragmentedIDSMessage(cTransportRef, message, deviceName, peerID, processQueue, replyBlock);
829 else if(cTransportRef)
830 cTransportRef->sendIDSMessage(cTransportRef, message, deviceName, peerID, processQueue, replyBlock);
831
832 }
833
834 void SOSCloudKeychainRetrievePendingMessageFromProxy(dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
835 {
836 SOSCloudTransportRef cTransportRef = SOSCloudTransportDefaultTransport();
837
838 if(cTransportRef)
839 cTransportRef->retrieveMessages(cTransportRef, processQueue, replyBlock);
840
841 }
842 void SOSCloudKeychainGetIDSDeviceID(CloudKeychainReplyBlock replyBlock)
843 {
844 SOSCloudTransportRef cTransportRef = SOSCloudTransportDefaultTransport();
845 if (cTransportRef)
846 cTransportRef->getDeviceID(cTransportRef, replyBlock);
847
848 }
849 void SOSCloudKeychainGetIDSDeviceAvailability(CFArrayRef ids, CFStringRef peerID, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock){
850
851 SOSCloudTransportRef cTransportRef = SOSCloudTransportDefaultTransport();
852
853 if (cTransportRef)
854 cTransportRef->getIDSDeviceAvailability(cTransportRef, ids, peerID, processQueue, replyBlock);
855 }
856 CF_RETURNS_RETAINED CFArrayRef SOSCloudKeychainHandleUpdateMessage(CFDictionaryRef updates)
857 {
858 CFArrayRef result = NULL;
859 SOSCloudTransportRef cTransportRef = SOSCloudTransportDefaultTransport();
860 if (cTransportRef->itemsChangedBlock)
861 result = ((CloudItemsChangedBlock)cTransportRef->itemsChangedBlock)(updates);
862 return result;
863 }
864
865 void SOSCloudKeychainGetObjectsFromCloud(CFArrayRef keysToGet, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
866 {
867 SOSCloudTransportRef cTransportRef = SOSCloudTransportDefaultTransport();
868 if (cTransportRef)
869 cTransportRef->get(cTransportRef, keysToGet, processQueue, replyBlock);
870 }
871
872 void SOSCloudKeychainGetAllObjectsFromCloud(dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
873 {
874 SOSCloudTransportRef cTransportRef = SOSCloudTransportDefaultTransport();
875 if (cTransportRef)
876 cTransportRef->getAll(cTransportRef, processQueue, replyBlock);
877 }
878
879 void SOSCloudKeychainSynchronizeAndWait(dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
880 {
881 SOSCloudTransportRef cTransportRef = SOSCloudTransportDefaultTransport();
882 if (cTransportRef)
883 cTransportRef->synchronizeAndWait(cTransportRef, processQueue, replyBlock);
884 }
885
886 //DEBUG ONLY
887 void SOSCloudKeychainSynchronize(dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
888 {
889 SOSCloudTransportRef cTransportRef = SOSCloudTransportDefaultTransport();
890 if (cTransportRef)
891 cTransportRef->synchronize(cTransportRef, processQueue, replyBlock);
892 }
893
894 //DEBUG ONLY
895 void SOSCloudKeychainClearAll(dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
896 {
897 SOSCloudTransportRef cTransportRef = SOSCloudTransportDefaultTransport();
898 if (cTransportRef)
899 cTransportRef->clearAll(cTransportRef, processQueue, replyBlock);
900 }
901
902 void SOSCloudKeychainRequestSyncWithPeers(CFArrayRef /* CFStringRef */ peers, CFArrayRef /* CFStringRef */ backupPeers, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
903 {
904 SOSCloudTransportRef cTransportRef = SOSCloudTransportDefaultTransport();
905 if (cTransportRef)
906 cTransportRef->requestSyncWithPeers(cTransportRef, peers, backupPeers, processQueue, replyBlock);
907 }
908
909 bool SOSCloudKeychainHasPendingKey(CFStringRef keyName, CFErrorRef* error) {
910 SOSCloudTransportRef cTransportRef = SOSCloudTransportDefaultTransport();
911
912 return cTransportRef && cTransportRef->hasPendingKey(cTransportRef, keyName, error);
913 }
914
915 bool SOSCloudKeychainHasPendingSyncWithPeer(CFStringRef peerID, CFErrorRef* error) {
916 SOSCloudTransportRef cTransportRef = SOSCloudTransportDefaultTransport();
917
918 return cTransportRef && cTransportRef->hasPeerSyncPending(cTransportRef, peerID, error);
919 }
920
921 void SOSCloudKeychainRequestEnsurePeerRegistration(dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
922 {
923 SOSCloudTransportRef cTransportRef = SOSCloudTransportDefaultTransport();
924 if (cTransportRef)
925 cTransportRef->requestEnsurePeerRegistration(cTransportRef, processQueue, replyBlock);
926 }
927
928 void SOSCloudKeychainFlush(dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
929 {
930 SOSCloudTransportRef cTransportRef = SOSCloudTransportDefaultTransport();
931 if (cTransportRef)
932 cTransportRef->flush(cTransportRef, processQueue, replyBlock);
933 }