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@
26 * SOSCloudTransport.c - Implementation of the transport layer from CKBridge to SOSAccount/SOSCircle
29 This XPC service is essentially just a proxy to iCloud KVS, which exists since
30 the main security code cannot link against Foundation.
32 See sendTSARequestWithXPC in tsaSupport.c for how to call the service
34 The client of an XPC service does not get connection events, nor does it
35 need to deal with transactions.
38 #include <SOSCloudTransport.h>
39 #include <utilities/SecCFError.h>
40 #include <dispatch/dispatch.h>
43 static CFStringRef sErrorDomain
= CFSTR("com.apple.security.sos.transport.error");
47 kSOSObjectMallocFailed
= 1,
49 kSOSObjectNotFoundError
= 1,
50 kSOSObjectCantBeConvertedToXPCObject
,
51 kSOSOUnexpectedConnectionEvent
,
52 kSOSOUnexpectedXPCEvent
,
56 /* SOSCloudTransport, a statically initialized transport singleton. */
57 struct SOSCloudTransport
59 struct CloudTransport t
;
65 #pragma mark ---------- Communication with XPC ----------
68 //------------------------------------------------------------------------------------------------
70 #include <AssertMacros.h>
73 #include <CoreFoundation/CoreFoundation.h>
74 #include <CoreFoundation/CFXPCBridge.h>
78 #include <utilities/debugging.h>
79 #include <utilities/SecCFWrappers.h>
81 #include "CKConstants.h"
83 #define zsecdebug(format...) secerror(format)
85 static xpc_connection_t serviceConnection
= NULL
;
86 static dispatch_queue_t xpc_queue
= NULL
;
87 static CloudKeychainReplyBlock itemsChangedBlock
;
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
);
92 // extern CFTypeRef _CFXPCCreateCFObjectFromXPCObject(xpc_object_t xo);
93 // extern xpc_object_t _CFXPCCreateXPCObjectFromCFObject(CFTypeRef cf);
96 static void describeXPCObject(char *prefix
, xpc_object_t object
);
97 static void describeXPCType(char *prefix
, xpc_type_t xtype
);
101 #define WANTXPCREPLY 0
103 #pragma mark ----- utilities -----
105 static CFErrorRef
makeError(CFIndex which
)
107 CFDictionaryRef userInfo
= NULL
;
108 return CFErrorCreate(kCFAllocatorDefault
, sErrorDomain
, which
, userInfo
);
111 #pragma mark ----- SPI -----
113 static void initXPCConnection()
115 zsecdebug("initXPCConnection\n");
117 xpc_queue
= dispatch_queue_create(xpcServiceName
, DISPATCH_QUEUE_SERIAL
);
119 serviceConnection
= xpc_connection_create_mach_service(xpcServiceName
, xpc_queue
, 0);
121 // serviceConnection = xpc_connection_create(xpcServiceName, xpc_queue);
122 zsecdebug("serviceConnection: %p\n", serviceConnection
);
124 xpc_connection_set_event_handler(serviceConnection
, ^(xpc_object_t event
)
126 zsecdebug("xpc_connection_set_event_handler\n");
127 handle_xpc_event(serviceConnection
, event
);
130 xpc_connection_resume(serviceConnection
);
131 xpc_retain(serviceConnection
);
135 static void closeXPCConnection()
137 zsecdebug("closeXPCConnection\n");
138 xpc_release(serviceConnection
);
142 static void setItemsChangedBlock(CloudKeychainReplyBlock icb
)
144 if (icb
!= itemsChangedBlock
)
146 if (itemsChangedBlock
)
147 Block_release(itemsChangedBlock
);
148 itemsChangedBlock
= icb
;
149 Block_copy(itemsChangedBlock
);
153 // typedef void (^CloudKeychainReplyBlock)(CFDictionaryRef returnedValues, CFErrorRef error);
155 static bool handle_xpc_event(const xpc_connection_t peer
, xpc_object_t event
)
157 CFErrorRef localError
= NULL
;
158 zsecdebug(">>>>> handle_connection_event via event_handler <<<<<\n");
160 if ((result
= xpc_event_filter(peer
, event
, &localError
)))
162 const char *operation
= xpc_dictionary_get_string(event
, kMessageKeyOperation
);
163 if (!operation
|| strcmp(operation
, kMessageOperationItemChanged
)) // some op we don't care about
165 zsecdebug("operation: %s", operation
);
169 xpc_object_t xrv
= xpc_dictionary_get_value(event
, kMessageKeyValue
);
172 zsecdebug("xrv null for kMessageKeyValue");
175 describeXPCObject("xrv", xrv
);
177 CFDictionaryRef returnedValues
= _CFXPCCreateCFObjectFromXPCObject(xrv
);
178 zsecdebug("returnedValues: %@", returnedValues
);
180 if (itemsChangedBlock
)
181 itemsChangedBlock(returnedValues
, localError
);
183 CFReleaseSafe(localError
);
188 static bool xpc_event_filter(const xpc_connection_t peer
, xpc_object_t event
, CFErrorRef
*error
)
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
)
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
);
202 *error
= makeError(kSOSOUnexpectedConnectionEvent
); // FIX
207 if (XPC_TYPE_ERROR
== xtype
)
209 zsecdebug("default: xpc error: %s\n", xpc_dictionary_get_string(event
, XPC_ERROR_KEY_DESCRIPTION
));
211 *error
= makeError(kSOSOUnexpectedConnectionEvent
); // FIX
214 if (XPC_TYPE_DICTIONARY
== xtype
)
216 zsecdebug("received dictionary event %p\n", event
);
221 zsecdebug("default: unexpected connection event %p\n", event
);
222 describeXPCObject("handle_xpc_event: obj : ", event
);
224 *error
= makeError(kSOSOUnexpectedXPCEvent
);
229 static void talkWithKVS(xpc_object_t message
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
232 __block CFErrorRef error
= NULL
;
233 __block CFTypeRef object
= NULL
;
235 dispatch_block_t callback
= ^{
236 secerror("callback");
238 replyBlock(object
, error
);
240 // CFRelease(object);
243 secerror("callback error: %@", error
);
246 dispatch_release(processQueue
);
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");
254 Block_copy(callback
); // TODO -- this should not be needed, I think
257 // xpc_connection_send_message(serviceConnection, message); // Send message; don't want a reply
259 xpc_connection_send_message_with_reply(serviceConnection
, message
, xpc_queue
, ^(xpc_object_t reply
)
261 secerror("xpc_connection_send_message_with_reply handler called back");
262 if (xpc_event_filter(serviceConnection
, reply
, &error
) && reply
)
264 describeXPCObject("getValuesFromKVS: reply : ", reply
);
265 xpc_object_t xrv
= xpc_dictionary_get_value(reply
, kMessageKeyValue
);
268 describeXPCObject("talkWithKVS: xrv: ", xrv
);
270 * The given XPC object must be one that was previously returned by
271 * _CFXPCCreateXPCMessageWithCFObject().
273 object
= _CFXPCCreateCFObjectFromXPCObject(xrv
); // CF object is retained; release in callback
274 secerror("converted CF object: %@", object
);
277 secerror("missing value reply");
279 dispatch_async(processQueue
, callback
);
283 //sleep(5); // DEBUG DEBUG FIX
284 // xpc_release(message);
288 secerror("talkWithKVS error: %@", error
);
290 dispatch_async(processQueue
, callback
);
293 static void putValuesWithXPC(CFDictionaryRef values
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
295 CFErrorRef error
= NULL
;
297 require_action(values
, xit
, error
= makeError(kSOSObjectNotFoundError
));
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
);
303 xpc_object_t xobject
= _CFXPCCreateXPCObjectFromCFObject(values
);
304 require_action(xobject
, xit
, error
= makeError(kSOSObjectCantBeConvertedToXPCObject
));
305 xpc_dictionary_set_value(message
, kMessageKeyValue
, xobject
);
307 talkWithKVS(message
, processQueue
, replyBlock
);
312 replyBlock(NULL
, error
);
315 static void synchronizeKVS(dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
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
);
323 static void clearAll(dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
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
);
331 static void getValuesFromKVS(CFArrayRef keysToGet
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
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();
338 require_action(xkeysToGet
, xit
, error
= makeError(kSOSObjectNotFoundError
));
340 xpc_dictionary_set_value(xkeysOfInterest
, kMessageKeyKeysToGet
, xkeysToGet
);
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
);
347 talkWithKVS(message
, processQueue
, replyBlock
);
349 xpc_release(message
);
354 replyBlock(NULL
, error
);
357 static void registerKeysForKVS(CFArrayRef keysToGet
, CFStringRef clientIdentifier
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
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
);
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
);
370 if (clientIdentifier
)
372 char *clientid
= CFStringToCString(clientIdentifier
);
375 xpc_dictionary_set_string(message
, kMessageKeyClientIdentifier
, clientid
);
380 setItemsChangedBlock(replyBlock
);
381 talkWithKVS(message
, processQueue
, replyBlock
);
383 xpc_release(message
);
386 static void unregisterKeysForKVS(CFArrayRef keysToUnregister
, CFStringRef clientIdentifier
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
389 if (gCKD
->unregisterKeys
) {
390 return gCKD
->unregisterKeys(...);
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
);
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
);
405 if (clientIdentifier
)
407 char *clientid
= CFStringToCString(clientIdentifier
);
410 xpc_dictionary_set_string(message
, kMessageKeyClientIdentifier
, clientid
);
415 talkWithKVS(message
, processQueue
, replyBlock
);
417 xpc_release(message
);
420 #pragma mark ----- DEBUG Utilities -----
422 //------------------------------------------------------------------------------------------------
424 //------------------------------------------------------------------------------------------------
426 static void describeXPCObject(char *prefix
, xpc_object_t object
)
429 // This is useful for debugging.
432 char *desc
= xpc_copy_description(object
);
433 zsecdebug("%s%s\n", prefix
, desc
);
437 zsecdebug("%s<NULL>\n", prefix
);
441 static void describeXPCType(char *prefix
, xpc_type_t xtype
)
444 Add these as necessary:
461 // This is useful for debugging.
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");
470 strcpy(msg
, "<unknown>");
472 zsecdebug("%s type:%s\n", prefix
, msg
);
476 #pragma mark ---------- SOSCloudTransport ----------
478 static void SOSCloudTransportPut(SOSCloudTransportRef transport
, CFDictionaryRef valuesToPut
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
480 secerror("%@", valuesToPut
);
481 putValuesWithXPC(valuesToPut
, processQueue
, replyBlock
);
484 static void SOSCloudTransportGet(SOSCloudTransportRef transport
, CFArrayRef keysToGet
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
486 // struct SOSCloudTransport *t = (struct SOSCloudTransport *)transport;
488 secerror("%@", keysToGet
);
489 getValuesFromKVS(keysToGet
, processQueue
, replyBlock
);
492 static void SOSCloudTransportRegisterKeys(SOSCloudTransportRef transport
, CFArrayRef keysToGet
, CFStringRef clientIdentifier
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
494 /* RegisterKeys with KVS */
495 registerKeysForKVS(keysToGet
, clientIdentifier
, processQueue
, replyBlock
);
498 static void SOSCloudTransportUnregisterKeys(SOSCloudTransportRef transport
, CFArrayRef keysToUnregister
, CFStringRef clientIdentifier
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
500 /* unregister from KVS */
501 unregisterKeysForKVS(keysToUnregister
, clientIdentifier
, processQueue
, replyBlock
);
504 static void SOSCloudTransportGetAll(SOSCloudTransportRef transport
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
507 getValuesFromKVS(NULL
, processQueue
, replyBlock
);
510 static void SOSCloudTransportSync(SOSCloudTransportRef transport
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
512 // struct SOSCloudTransport *t = (struct SOSCloudTransport *)transport;
514 synchronizeKVS(processQueue
, replyBlock
);
517 static void SOSCloudTransportClearAll(SOSCloudTransportRef transport
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
519 // struct SOSCloudTransport *t = (struct SOSCloudTransport *)transport;
521 clearAll(processQueue
, replyBlock
);
524 static void SOSCloudTransportSetItemsChangedBlock(SOSCloudTransportRef transport
, CloudKeychainReplyBlock itemsChangedBlock
)
526 // struct SOSCloudTransport *t = (struct SOSCloudTransport *)transport;
528 setItemsChangedBlock(itemsChangedBlock
);
531 static void SOSCloudTransportInit(void *ctx
)
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
;
550 SOSCloudTransportRef
SOSCloudTransportDefaultTransport(CFStringRef kvsID
, uint32_t options
)
552 static struct SOSCloudTransport cloudTransport
= {};
553 cloudTransport
.kvsID
= kvsID
;
554 dispatch_once_f(&cloudTransport
.once
, &cloudTransport
, SOSCloudTransportInit
);
555 return &cloudTransport
.t
;