2 * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 SOSCloudTransport.c - Implementation of the transport layer from CKBridge to SOSAccount/SOSCircle
26 These are the exported functions from CloudKeychainProxy
30 This XPC service is essentially just a proxy to iCloud KVS, which exists since
31 the main security code cannot link against Foundation.
33 See sendTSARequestWithXPC in tsaSupport.c for how to call the service
35 The client of an XPC service does not get connection events, nor does it
36 need to deal with transactions.
39 #include <AssertMacros.h>
42 #include <CoreFoundation/CoreFoundation.h>
43 #include <CoreFoundation/CFXPCBridge.h>
46 #include <os/activity.h>
47 #include <CoreFoundation/CFUserNotification.h>
48 #include <Security/SecureObjectSync/SOSInternal.h>
50 #include <utilities/debugging.h>
51 #include <utilities/SecCFWrappers.h>
52 #include <utilities/SecXPCError.h>
54 #include "SOSCloudKeychainConstants.h"
55 #include "SOSCloudKeychainClient.h"
56 #include "SOSKVSKeys.h"
57 #include "SOSUserKeygen.h"
58 #include "SecOTRSession.h"
60 #include <os/activity.h>
61 #include <os/state_private.h>
64 static CFStringRef sErrorDomain
= CFSTR("com.apple.security.sos.transport.error");
66 #define SOSCKCSCOPE "sync"
68 // MARK: ---------- SOSCloudTransport ----------
70 /* SOSCloudTransport, a statically initialized transport singleton. */
71 static SOSCloudTransportRef sTransport
= NULL
;
73 static SOSCloudTransportRef
SOSCloudTransportCreateXPCTransport(void);
75 void SOSCloudKeychainSetTransport(SOSCloudTransportRef transport
) {
76 sTransport
= transport
;
79 void SOSCloudTransportGet(SOSCloudTransportRef transport
, CFArrayRef keysToGet
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
);
83 /* Return the singleton cloud transport instance. */
84 CFDictionaryRef
SOSCloudCopyKVSState(void) {
85 __block CFDictionaryRef retval
= NULL
;
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
);
93 if (processQueue
== NULL
)
96 dispatch_semaphore_t waitSemaphore
= NULL
;
98 waitSemaphore
= dispatch_semaphore_create(0);
100 CloudKeychainReplyBlock replyBlock
= ^ (CFDictionaryRef returnedValues
, CFErrorRef error
) {
101 retval
= returnedValues
;
102 if (retval
) CFRetain(retval
);
103 dispatch_semaphore_signal(waitSemaphore
);
106 SOSCloudKeychainGetAllObjectsFromCloud(processQueue
, replyBlock
);
108 dispatch_semaphore_wait(waitSemaphore
, DISPATCH_TIME_FOREVER
);
109 dispatch_release(waitSemaphore
);
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
;
120 require_quiet(hints
->osh_api
== 3, errOut
); // only grab on sysdiagnose or command lin
122 kvsdict
= SOSCloudCopyKVSState();
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
);
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");
135 CFReleaseNull(kvsdict
);
136 CFReleaseNull(serializedKVS
);
141 static SOSCloudTransportRef
SOSCloudTransportDefaultTransport(void)
143 static dispatch_once_t sTransportOnce
;
144 dispatch_once(&sTransportOnce
, ^{
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
);
154 // MARK: ----- utilities -----
156 static CFErrorRef
makeError(CFIndex which
)
158 CFDictionaryRef userInfo
= NULL
;
159 return CFErrorCreate(kCFAllocatorDefault
, sErrorDomain
, which
, userInfo
);
162 // MARK: ----- DEBUG Utilities -----
164 //------------------------------------------------------------------------------------------------
166 //------------------------------------------------------------------------------------------------
168 static void describeXPCObject(char *prefix
, xpc_object_t object
)
171 // This is useful for debugging.
174 char *desc
= xpc_copy_description(object
);
175 secdebug(SOSCKCSCOPE
, "%s%s\n", prefix
, desc
);
179 secdebug(SOSCKCSCOPE
, "%s<NULL>\n", prefix
);
183 static void describeXPCType(char *prefix
, xpc_type_t xtype
)
185 // Add others as necessary, e.g. XPC_TYPE_DOUBLE
187 // This is useful for debugging.
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");
196 strcpy(msg
, "<unknown>");
198 secdebug(SOSCKCSCOPE
, "%s type:%s\n", prefix
, msg
);
202 // MARK: ---------- SOSXPCCloudTransport ----------
204 typedef struct SOSXPCCloudTransport
*SOSXPCCloudTransportRef
;
205 struct SOSXPCCloudTransport
207 struct SOSCloudTransport transport
;
208 xpc_connection_t serviceConnection
;
209 xpc_connection_t idsProxyServiceConnection
;
210 dispatch_queue_t xpc_queue
;
213 static bool xpc_event_filter(const xpc_connection_t peer
, xpc_object_t event
, CFErrorRef
*error
)
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
)
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
);
227 *error
= makeError(kSOSOUnexpectedConnectionEvent
); // FIX
232 if (XPC_TYPE_ERROR
== xtype
)
235 const char *estr
= xpc_dictionary_get_string(event
, XPC_ERROR_KEY_DESCRIPTION
);
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
);
242 CFDictionaryAddValue(userInfo
, kCFErrorLocalizedDescriptionKey
, errStr
);
244 *error
= CFErrorCreate(kCFAllocatorDefault
, sErrorDomain
, kSOSOXPCErrorEvent
, userInfo
);
245 CFReleaseSafe(errStr
);
246 CFReleaseSafe(userInfo
);
250 if (XPC_TYPE_DICTIONARY
== xtype
)
252 secdebug(SOSCKCSCOPE
, "received dictionary event %p\n", event
);
257 secdebug(SOSCKCSCOPE
, "default: unexpected connection event %p\n", event
);
258 describeXPCObject("handle_xpc_event: obj : ", event
);
260 *error
= makeError(kSOSOUnexpectedXPCEvent
);
265 static void SOSXPCCloudTransportInit(SOSXPCCloudTransportRef transport
)
267 secdebug(SOSCKCSCOPE
, "initXPCConnection\n");
269 transport
->xpc_queue
= dispatch_queue_create(xpcServiceName
, DISPATCH_QUEUE_SERIAL
);
271 transport
->serviceConnection
= xpc_connection_create_mach_service(xpcServiceName
, transport
->xpc_queue
, 0);
274 secdebug(SOSCKCSCOPE
, "serviceConnection: %p\n", transport
->serviceConnection
);
277 xpc_connection_set_event_handler(transport
->serviceConnection
, ^(xpc_object_t event
)
279 secdebug(SOSCKCSCOPE
, "xpc_connection_set_event_handler\n");
282 xpc_connection_resume(transport
->serviceConnection
);
283 xpc_retain(transport
->serviceConnection
);
285 transport
->idsProxyServiceConnection
= xpc_connection_create_mach_service(xpcIDSServiceName
, transport
->xpc_queue
, 0);
287 secdebug(SOSCKCSCOPE
, "ids service connection: %p\n", transport
->idsProxyServiceConnection
);
289 xpc_connection_set_event_handler(transport
->idsProxyServiceConnection
, ^(xpc_object_t object
) {
290 secdebug(SOSCKCSCOPE
, "IDS Transport, xpc_connection_set_event_handler\n");
292 xpc_connection_resume(transport
->idsProxyServiceConnection
);
293 xpc_retain(transport
->idsProxyServiceConnection
);
297 static void talkWithIDS(SOSXPCCloudTransportRef transport
, xpc_object_t message
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
299 CFErrorRef connectionError
= NULL
;
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
);
306 xpc_connection_send_message_with_reply(transport
->idsProxyServiceConnection
, message
, transport
->xpc_queue
, ^(xpc_object_t reply
)
308 CFErrorRef serverError
= NULL
;
309 CFTypeRef object
= NULL
;
310 if (xpc_event_filter(transport
->idsProxyServiceConnection
, reply
, &serverError
) && reply
)
312 describeXPCObject("IDS Proxy: reply : ", reply
);
314 secerror("Error from xpc_event_filter: %@", serverError
);
315 xpc_object_t xrv
= xpc_dictionary_get_value(reply
, kMessageKeyValue
);
318 describeXPCObject("talkwithIDS: xrv: ", xrv
);
320 * The given XPC object must be one that was previously returned by
321 * _CFXPCCreateXPCMessageWithCFObject().
323 object
= _CFXPCCreateCFObjectFromXPCObject(xrv
); // CF object is retained; release in callback
324 secnotice("talkwithIDS", "converted CF object: %@", object
);
327 secerror("missing value reply");
329 xpc_object_t xerror
= xpc_dictionary_get_value(reply
, kMessageKeyError
);
331 serverError
= SecCreateCFErrorWithXPCObject(xerror
); // use SecCFCreateErrorWithFormat?
333 dispatch_async(processQueue
, ^{
335 replyBlock(object
, serverError
);
336 CFReleaseSafe(object
);
339 secerror("talkwithIDS callback error: %@", serverError
);
340 CFReleaseSafe(serverError
);
342 dispatch_release(processQueue
);
348 secerror("talkWithIDS error: %@", connectionError
);
349 dispatch_async(processQueue
, ^{
351 replyBlock(NULL
, connectionError
);
352 CFReleaseSafe(connectionError
);
353 dispatch_release(processQueue
);
356 os_activity_end(trace_activity
);
359 typedef void (^ProxyReplyBlock
)(xpc_object_t reply
);
361 static bool messageToProxy(SOSXPCCloudTransportRef transport
, xpc_object_t message
, CFErrorRef
*error
, dispatch_queue_t processQueue
, ProxyReplyBlock replyBlock
) {
362 CFErrorRef connectionError
= NULL
;
364 require_action(transport
->serviceConnection
, xit
, connectionError
= makeError(kSOSConnectionNotOpen
));
365 require_action(message
, xit
, connectionError
= makeError(kSOSObjectNotFoundError
));
367 xpc_connection_send_message_with_reply(transport
->serviceConnection
, message
, processQueue
, replyBlock
);
369 return CFErrorPropagate(connectionError
, error
);
372 static void talkWithKVS(SOSXPCCloudTransportRef transport
, xpc_object_t message
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
374 CFErrorRef messagingError
= NULL
;
375 dispatch_retain(processQueue
);
376 bool messaged
= messageToProxy(transport
, message
, &messagingError
, transport
->xpc_queue
, ^(xpc_object_t reply
)
378 CFErrorRef serverError
= NULL
;
379 CFTypeRef object
= NULL
;
380 if (xpc_event_filter(transport
->serviceConnection
, reply
, &serverError
) && reply
)
382 describeXPCObject("getValuesFromKVS: reply : ", reply
);
384 secerror("Error from xpc_event_filter: %@", serverError
);
385 xpc_object_t xrv
= xpc_dictionary_get_value(reply
, kMessageKeyValue
);
388 describeXPCObject("talkWithKVS: xrv: ", xrv
);
390 * The given XPC object must be one that was previously returned by
391 * _CFXPCCreateXPCMessageWithCFObject().
393 object
= _CFXPCCreateCFObjectFromXPCObject(xrv
); // CF object is retained; release in callback
394 secnotice("talkwithkvs", "converted CF object: %@", object
);
397 secerror("missing value reply");
399 xpc_object_t xerror
= xpc_dictionary_get_value(reply
, kMessageKeyError
);
401 serverError
= SecCreateCFErrorWithXPCObject(xerror
); // use SecCFCreateErrorWithFormat?
403 dispatch_async(processQueue
, ^{
405 replyBlock(object
, serverError
);
406 CFReleaseSafe(object
);
409 secerror("callback error: %@", serverError
);
410 CFReleaseSafe(serverError
);
412 dispatch_release(processQueue
);
417 secerror("talkWithKVS error: %@", messagingError
);
418 dispatch_async(processQueue
, ^{
420 replyBlock(NULL
, messagingError
);
421 CFReleaseSafe(messagingError
);
422 dispatch_release(processQueue
);
427 // MARK: ---------- SOSXPCCloudTransport Client Calls ----------
429 /* Concrete function backend implementations. */
430 static void SOSCloudTransportSetItemsChangedBlock(SOSCloudTransportRef transport
,
431 CloudItemsChangedBlock itemsChangedBlock
) {
432 if (transport
->itemsChangedBlock
!= itemsChangedBlock
)
434 secnotice(SOSCKCSCOPE
, "Changing itemsChangedBlock");
435 if (transport
->itemsChangedBlock
)
436 Block_release(transport
->itemsChangedBlock
);
437 transport
->itemsChangedBlock
= Block_copy(itemsChangedBlock
);
441 /* Virtual function backend implementations. */
442 static void SOSCloudTransportPut(SOSCloudTransportRef transport
, CFDictionaryRef values
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
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
));
451 message
= xpc_dictionary_create(NULL
, NULL
, 0);
452 xpc_dictionary_set_uint64(message
, kMessageKeyVersion
, kCKDXPCVersion
);
453 xpc_dictionary_set_string(message
, kMessageKeyOperation
, kOperationPUTDictionary
);
455 xobject
= _CFXPCCreateXPCObjectFromCFObject(values
);
456 require_action(xobject
, xit
, error
= makeError(kSOSObjectCantBeConvertedToXPCObject
));
457 xpc_dictionary_set_value(message
, kMessageKeyValue
, xobject
);
458 xpc_release(xobject
);
460 talkWithKVS(xpcTransport
, message
, processQueue
, replyBlock
);
461 xpc_release(message
);
466 replyBlock(NULL
, error
);
467 CFReleaseSafe(error
);
471 void SOSCloudTransportGet(SOSCloudTransportRef transport
, CFArrayRef keysToGet
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
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();
479 require_action(xkeysToGet
, xit
, error
= makeError(kSOSObjectNotFoundError
));
481 if (keysToGet
) // don't add if nulll; will call getall
482 xpc_dictionary_set_value(xkeysOfInterest
, kMessageKeyKeysToGet
, xkeysToGet
);
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
);
489 talkWithKVS(xpcTransport
, message
, processQueue
, replyBlock
);
490 xpc_release(xkeysToGet
);
491 xpc_release(xkeysOfInterest
);
492 xpc_release(message
);
497 xpc_release(xkeysOfInterest
);
499 xpc_release(xkeysToGet
);
501 replyBlock(NULL
, error
);
502 CFReleaseSafe(error
);
506 // Handles NULL by seting xpc_null.
507 static void SecXPCDictionarySetCFObject(xpc_object_t xdict
, const char *key
, CFTypeRef object
)
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
);
514 static void SOSCloudTransportGetIDSDeviceID(SOSCloudTransportRef transport
, CloudKeychainReplyBlock replyBlock
)
516 dispatch_queue_t processQueue
= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0);
518 SOSXPCCloudTransportRef xpcTransport
= (SOSXPCCloudTransportRef
)transport
;
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
);
524 talkWithIDS(xpcTransport
, message
, processQueue
, replyBlock
);
525 xpc_release(message
);
528 static void SOSCloudTransportSendFragmentedIDSMessage(SOSCloudTransportRef transport
, CFDictionaryRef messageData
, CFStringRef deviceName
, CFStringRef peerID
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
){
530 SOSXPCCloudTransportRef xpcTransport
= (SOSXPCCloudTransportRef
)transport
;
531 xpc_object_t xmessageData
= _CFXPCCreateXPCObjectFromCFObject(messageData
);
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
);
537 xpc_dictionary_set_value(message
, kMessageKeyValue
, xmessageData
);
538 SecXPCDictionarySetCFObject(message
, kMessageKeyDeviceName
, deviceName
);
539 SecXPCDictionarySetCFObject(message
, kMessageKeyPeerID
, peerID
);
540 talkWithIDS(xpcTransport
, message
, processQueue
, replyBlock
);
542 xpc_release(xmessageData
);
543 xpc_release(message
);
546 static void SOSCloudTransportRetrievePendingMessagesInFlight(SOSCloudTransportRef transport
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
){
548 SOSXPCCloudTransportRef xpcTransport
= (SOSXPCCloudTransportRef
)transport
;
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
);
554 talkWithIDS(xpcTransport
, message
, processQueue
, replyBlock
);
556 xpc_release(message
);
559 static void SOSCloudTransportCheckIDSDeviceIDAvailability(SOSCloudTransportRef transport
, CFArrayRef ids
, CFStringRef peerID
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
561 secdebug(SOSCKCSCOPE
, "start");
562 SOSXPCCloudTransportRef xpcTransport
= (SOSXPCCloudTransportRef
)transport
;
564 xpc_object_t xIDSArray
= _CFXPCCreateXPCObjectFromCFObject(ids
);
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
);
570 SecXPCDictionarySetCFObject(message
, kMessageKeyPeerID
, peerID
);
571 xpc_dictionary_set_value(message
, kMessageKeyValue
, xIDSArray
);
573 talkWithIDS(xpcTransport
, message
, processQueue
, replyBlock
);
575 xpc_release(xIDSArray
);
576 xpc_release(message
);
580 static void SOSCloudTransportSendIDSMessage(SOSCloudTransportRef transport
, CFDictionaryRef messageData
, CFStringRef deviceName
, CFStringRef peerID
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
){
582 SOSXPCCloudTransportRef xpcTransport
= (SOSXPCCloudTransportRef
)transport
;
583 xpc_object_t xmessageData
= _CFXPCCreateXPCObjectFromCFObject(messageData
);
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
);
589 xpc_dictionary_set_value(message
, kMessageKeyValue
, xmessageData
);
590 SecXPCDictionarySetCFObject(message
, kMessageKeyDeviceName
, deviceName
);
591 SecXPCDictionarySetCFObject(message
, kMessageKeyPeerID
, peerID
);
592 talkWithIDS(xpcTransport
, message
, processQueue
, replyBlock
);
594 xpc_release(xmessageData
);
595 xpc_release(message
);
598 static void SOSCloudTransportUpdateKeys(SOSCloudTransportRef transport
,
599 CFDictionaryRef keys
,
600 CFStringRef accountUUID
,
601 dispatch_queue_t processQueue
,
602 CloudKeychainReplyBlock replyBlock
)
604 SOSXPCCloudTransportRef xpcTransport
= (SOSXPCCloudTransportRef
)transport
;
606 xpc_object_t xkeysOfInterest
= xpc_dictionary_create(NULL
, NULL
, 0);
607 SecXPCDictionarySetCFObject(xkeysOfInterest
, kMessageAllKeys
, keys
);
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
);
615 talkWithKVS(xpcTransport
, message
, processQueue
, replyBlock
);
616 xpc_release(message
);
617 xpc_release(xkeysOfInterest
);
620 static void SOSCloudTransportGetAll(SOSCloudTransportRef transport
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
622 secdebug(SOSCKCSCOPE
, "start");
623 SOSCloudTransportGet(transport
, NULL
, processQueue
, replyBlock
);
626 static void SOSCloudTransportSync(SOSCloudTransportRef transport
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
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
);
637 static void SOSCloudTransportSyncAndWait(SOSCloudTransportRef transport
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
639 SOSXPCCloudTransportRef xpcTransport
= (SOSXPCCloudTransportRef
)transport
;
640 secnotice(SOSCKCSCOPE
, "%s XPC request to CKD: %s", kWAIT2MINID
, kOperationSynchronizeAndWait
);
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
);
646 talkWithKVS(xpcTransport
, message
, processQueue
, replyBlock
);
647 xpc_release(message
);
650 static void SOSCloudTransportClearAll(SOSCloudTransportRef transport
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
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
);
661 static void SOSCloudTransportRequestSyncWithPeers(SOSCloudTransportRef transport
, CFArrayRef
/* CFStringRef */ peers
, CFArrayRef
/* CFStringRef */ backupPeers
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
663 secdebug(SOSCKCSCOPE
, "start");
664 SOSXPCCloudTransportRef xpcTransport
= (SOSXPCCloudTransportRef
)transport
;
666 xpc_object_t xpcmessage
= xpc_dictionary_create(NULL
, NULL
, 0);
668 xpc_dictionary_set_uint64(xpcmessage
, kMessageKeyVersion
, kCKDXPCVersion
);
669 xpc_dictionary_set_string(xpcmessage
, kMessageKeyOperation
, kOperationRequestSyncWithPeers
);
671 SecXPCDictionarySetCFObject(xpcmessage
, kMessageKeyPeerIDList
, peers
);
672 SecXPCDictionarySetCFObject(xpcmessage
, kMesssgeKeyBackupPeerIDList
, backupPeers
);
674 talkWithKVS(xpcTransport
, xpcmessage
, processQueue
, replyBlock
);
676 xpc_release(xpcmessage
);
680 static bool SOSCloudTransportHasPeerSyncPending(SOSCloudTransportRef transport
, CFStringRef peerID
, CFErrorRef
* error
)
682 secdebug(SOSCKCSCOPE
, "start");
683 SOSXPCCloudTransportRef xpcTransport
= (SOSXPCCloudTransportRef
)transport
;
685 __block
bool isSyncing
= false;
687 xpc_object_t xpcmessage
= xpc_dictionary_create(NULL
, NULL
, 0);
689 xpc_dictionary_set_uint64(xpcmessage
, kMessageKeyVersion
, kCKDXPCVersion
);
690 xpc_dictionary_set_string(xpcmessage
, kMessageKeyOperation
, kOperationHasPendingSyncWithPeer
);
692 SecXPCDictionarySetCFObject(xpcmessage
, kMessageKeyPeerID
, peerID
);
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
);
701 dispatch_semaphore_wait(wait
, DISPATCH_TIME_FOREVER
);
704 dispatch_release(wait
);
706 return sent
&& isSyncing
;
710 static bool SOSCloudTransportHasPendingKey(SOSCloudTransportRef transport
, CFStringRef keyName
, CFErrorRef
* error
)
712 secdebug(SOSCKCSCOPE
, "start");
713 SOSXPCCloudTransportRef xpcTransport
= (SOSXPCCloudTransportRef
)transport
;
715 __block
bool kvsHasMessage
= false;
717 xpc_object_t xpcmessage
= xpc_dictionary_create(NULL
, NULL
, 0);
719 xpc_dictionary_set_uint64(xpcmessage
, kMessageKeyVersion
, kCKDXPCVersion
);
720 xpc_dictionary_set_string(xpcmessage
, kMessageKeyOperation
, kOperationHasPendingKey
);
722 SecXPCDictionarySetCFObject(xpcmessage
, kMessageKeyKey
, keyName
);
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
);
731 dispatch_semaphore_wait(kvsWait
, DISPATCH_TIME_FOREVER
);
734 dispatch_release(kvsWait
);
736 return kvsSent
&& kvsHasMessage
;
740 static void SOSCloudTransportRequestEnsurePeerRegistration(SOSCloudTransportRef transport
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
742 secdebug(SOSCKCSCOPE
, "start");
743 SOSXPCCloudTransportRef xpcTransport
= (SOSXPCCloudTransportRef
)transport
;
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
);
749 talkWithKVS(xpcTransport
, xpcmessage
, processQueue
, replyBlock
);
751 xpc_release(xpcmessage
);
754 static void SOSCloudTransportFlush(SOSCloudTransportRef transport
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
756 secdebug(SOSCKCSCOPE
, "start");
757 SOSXPCCloudTransportRef xpcTransport
= (SOSXPCCloudTransportRef
)transport
;
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
);
763 talkWithKVS(xpcTransport
, xpcmessage
, processQueue
, replyBlock
);
765 xpc_release(xpcmessage
);
768 static SOSCloudTransportRef
SOSCloudTransportCreateXPCTransport(void)
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
;
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
);
795 SOSXPCCloudTransportInit(st
);
796 return &st
->transport
;
799 // MARK: ---------- SOSCloudKeychain concrete client APIs ----------
800 void SOSCloudKeychainSetItemsChangedBlock(CloudItemsChangedBlock itemsChangedBlock
)
802 secdebug(SOSCKCSCOPE
, "start");
803 SOSCloudTransportSetItemsChangedBlock(SOSCloudTransportDefaultTransport(),
807 // MARK: ---------- SOSCloudKeychain virtual client APIs ----------
809 void SOSCloudKeychainPutObjectsInCloud(CFDictionaryRef objects
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
811 SOSCloudTransportRef cTransportRef
= SOSCloudTransportDefaultTransport();
813 cTransportRef
->put(cTransportRef
, objects
, processQueue
, replyBlock
);
816 void SOSCloudKeychainUpdateKeys(CFDictionaryRef keys
, CFStringRef accountUUID
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
818 SOSCloudTransportRef cTransportRef
= SOSCloudTransportDefaultTransport();
820 cTransportRef
->updateKeys(cTransportRef
, keys
, accountUUID
, processQueue
, replyBlock
);
823 void SOSCloudKeychainSendIDSMessage(CFDictionaryRef message
, CFStringRef deviceName
, CFStringRef peerID
, dispatch_queue_t processQueue
, CFBooleanRef fragmentation
, CloudKeychainReplyBlock replyBlock
)
825 SOSCloudTransportRef cTransportRef
= SOSCloudTransportDefaultTransport();
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
);
834 void SOSCloudKeychainRetrievePendingMessageFromProxy(dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
836 SOSCloudTransportRef cTransportRef
= SOSCloudTransportDefaultTransport();
839 cTransportRef
->retrieveMessages(cTransportRef
, processQueue
, replyBlock
);
842 void SOSCloudKeychainGetIDSDeviceID(CloudKeychainReplyBlock replyBlock
)
844 SOSCloudTransportRef cTransportRef
= SOSCloudTransportDefaultTransport();
846 cTransportRef
->getDeviceID(cTransportRef
, replyBlock
);
849 void SOSCloudKeychainGetIDSDeviceAvailability(CFArrayRef ids
, CFStringRef peerID
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
){
851 SOSCloudTransportRef cTransportRef
= SOSCloudTransportDefaultTransport();
854 cTransportRef
->getIDSDeviceAvailability(cTransportRef
, ids
, peerID
, processQueue
, replyBlock
);
856 CF_RETURNS_RETAINED CFArrayRef
SOSCloudKeychainHandleUpdateMessage(CFDictionaryRef updates
)
858 CFArrayRef result
= NULL
;
859 SOSCloudTransportRef cTransportRef
= SOSCloudTransportDefaultTransport();
860 if (cTransportRef
->itemsChangedBlock
)
861 result
= ((CloudItemsChangedBlock
)cTransportRef
->itemsChangedBlock
)(updates
);
865 void SOSCloudKeychainGetObjectsFromCloud(CFArrayRef keysToGet
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
867 SOSCloudTransportRef cTransportRef
= SOSCloudTransportDefaultTransport();
869 cTransportRef
->get(cTransportRef
, keysToGet
, processQueue
, replyBlock
);
872 void SOSCloudKeychainGetAllObjectsFromCloud(dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
874 SOSCloudTransportRef cTransportRef
= SOSCloudTransportDefaultTransport();
876 cTransportRef
->getAll(cTransportRef
, processQueue
, replyBlock
);
879 void SOSCloudKeychainSynchronizeAndWait(dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
881 SOSCloudTransportRef cTransportRef
= SOSCloudTransportDefaultTransport();
883 cTransportRef
->synchronizeAndWait(cTransportRef
, processQueue
, replyBlock
);
887 void SOSCloudKeychainSynchronize(dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
889 SOSCloudTransportRef cTransportRef
= SOSCloudTransportDefaultTransport();
891 cTransportRef
->synchronize(cTransportRef
, processQueue
, replyBlock
);
895 void SOSCloudKeychainClearAll(dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
897 SOSCloudTransportRef cTransportRef
= SOSCloudTransportDefaultTransport();
899 cTransportRef
->clearAll(cTransportRef
, processQueue
, replyBlock
);
902 void SOSCloudKeychainRequestSyncWithPeers(CFArrayRef
/* CFStringRef */ peers
, CFArrayRef
/* CFStringRef */ backupPeers
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
904 SOSCloudTransportRef cTransportRef
= SOSCloudTransportDefaultTransport();
906 cTransportRef
->requestSyncWithPeers(cTransportRef
, peers
, backupPeers
, processQueue
, replyBlock
);
909 bool SOSCloudKeychainHasPendingKey(CFStringRef keyName
, CFErrorRef
* error
) {
910 SOSCloudTransportRef cTransportRef
= SOSCloudTransportDefaultTransport();
912 return cTransportRef
&& cTransportRef
->hasPendingKey(cTransportRef
, keyName
, error
);
915 bool SOSCloudKeychainHasPendingSyncWithPeer(CFStringRef peerID
, CFErrorRef
* error
) {
916 SOSCloudTransportRef cTransportRef
= SOSCloudTransportDefaultTransport();
918 return cTransportRef
&& cTransportRef
->hasPeerSyncPending(cTransportRef
, peerID
, error
);
921 void SOSCloudKeychainRequestEnsurePeerRegistration(dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
923 SOSCloudTransportRef cTransportRef
= SOSCloudTransportDefaultTransport();
925 cTransportRef
->requestEnsurePeerRegistration(cTransportRef
, processQueue
, replyBlock
);
928 void SOSCloudKeychainFlush(dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
930 SOSCloudTransportRef cTransportRef
= SOSCloudTransportDefaultTransport();
932 cTransportRef
->flush(cTransportRef
, processQueue
, replyBlock
);