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@
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 //------------------------------------------------------------------------------------------------
41 #include <AssertMacros.h>
44 #include <CoreFoundation/CoreFoundation.h>
45 #include <CoreFoundation/CFXPCBridge.h>
49 #include <utilities/debugging.h>
50 #include <utilities/SecCFWrappers.h>
52 #include "CKConstants.h"
54 #define __CKDXPC_CLIENT_PRIVATE_INDIRECT__ 1
58 #define pdebug(format...) secerror(format)
60 #define verboseCKDDebugging 1
63 #define xpdebug(format...) \
65 if (verboseCKDDebugging) \
70 #define xpdebug(format...)
74 static xpc_connection_t serviceConnection
= NULL
;
75 static dispatch_queue_t xpc_queue
= NULL
;
76 static CloudKeychainReplyBlock itemsChangedBlock
;
78 static bool handle_xpc_event(const xpc_connection_t peer
, xpc_object_t event
);
79 static bool xpc_event_filter(const xpc_connection_t peer
, xpc_object_t event
, CFErrorRef
*error
);
81 // extern CFTypeRef _CFXPCCreateCFObjectFromXPCObject(xpc_object_t xo);
82 // extern xpc_object_t _CFXPCCreateXPCObjectFromCFObject(CFTypeRef cf);
85 static void describeXPCObject(char *prefix
, xpc_object_t object
);
86 void describeXPCType(char *prefix
, xpc_type_t xtype
);
88 static CFStringRef sErrorDomain
= CFSTR("com.apple.security.cloudkeychain");
91 kSOSObjectMallocFailed
= 1,
93 kSOSObjectNotFoundError
= 1,
94 kSOSObjectCantBeConvertedToXPCObject
,
95 kSOSOUnexpectedConnectionEvent
,
96 kSOSOUnexpectedXPCEvent
,
100 #define WANTXPCREPLY 0
102 #pragma mark ----- utilities -----
104 static CFErrorRef
makeError(CFIndex which
)
106 CFDictionaryRef userInfo
= NULL
;
107 return CFErrorCreate(kCFAllocatorDefault
, sErrorDomain
, which
, userInfo
);
110 #pragma mark ----- SPI -----
112 void initXPCConnection()
114 pdebug("initXPCConnection\n");
116 xpc_queue
= dispatch_queue_create(xpcServiceName
, DISPATCH_QUEUE_SERIAL
);
118 serviceConnection
= xpc_connection_create_mach_service(xpcServiceName
, xpc_queue
, 0);
120 // serviceConnection = xpc_connection_create(xpcServiceName, xpc_queue);
121 pdebug("serviceConnection: %p\n", serviceConnection
);
123 xpc_connection_set_event_handler(serviceConnection
, ^(xpc_object_t event
)
125 pdebug("xpc_connection_set_event_handler\n");
126 handle_xpc_event(serviceConnection
, event
);
129 xpc_connection_resume(serviceConnection
);
130 xpc_retain(serviceConnection
);
133 void closeXPCConnection()
135 pdebug("closeXPCConnection\n");
136 xpc_release(serviceConnection
);
139 void setItemsChangedBlock(CloudKeychainReplyBlock icb
)
141 if (icb
!= itemsChangedBlock
)
143 if (itemsChangedBlock
)
144 Block_release(itemsChangedBlock
);
145 itemsChangedBlock
= icb
;
146 Block_copy(itemsChangedBlock
);
150 // typedef void (^CloudKeychainReplyBlock)(CFDictionaryRef returnedValues, CFErrorRef error);
152 static bool handle_xpc_event(const xpc_connection_t peer
, xpc_object_t event
)
154 CFErrorRef localError
= NULL
;
155 pdebug(">>>>> handle_connection_event via event_handler <<<<<\n");
157 if ((result
= xpc_event_filter(peer
, event
, &localError
)))
159 const char *operation
= xpc_dictionary_get_string(event
, kMessageKeyOperation
);
160 if (!operation
|| strcmp(operation
, kMessageOperationItemChanged
)) // some op we don't care about
162 pdebug("operation: %s", operation
);
166 xpc_object_t xrv
= xpc_dictionary_get_value(event
, kMessageKeyValue
);
169 pdebug("xrv null for kMessageKeyValue");
172 describeXPCObject("xrv", xrv
);
174 CFDictionaryRef returnedValues
= _CFXPCCreateCFObjectFromXPCObject(xrv
);
175 pdebug("returnedValues: %@", returnedValues
);
177 if (itemsChangedBlock
)
178 itemsChangedBlock(returnedValues
, localError
);
180 CFReleaseSafe(localError
);
185 static bool xpc_event_filter(const xpc_connection_t peer
, xpc_object_t event
, CFErrorRef
*error
)
187 // return true if the type is XPC_TYPE_DICTIONARY (and therefore something more to process)
188 pdebug("handle_connection_event\n");
189 xpc_type_t xtype
= xpc_get_type(event
);
190 describeXPCType("handle_xpc_event", xtype
);
191 if (XPC_TYPE_CONNECTION
== xtype
)
193 pdebug("handle_xpc_event: XPC_TYPE_CONNECTION (unexpected)");
194 // The client of an XPC service does not get connection events
195 // For nwo, we log this and keep going
196 describeXPCObject("handle_xpc_event: XPC_TYPE_CONNECTION, obj : ", event
);
199 *error
= makeError(kSOSOUnexpectedConnectionEvent
); // FIX
204 if (XPC_TYPE_ERROR
== xtype
)
206 pdebug("default: xpc error: %s\n", xpc_dictionary_get_string(event
, XPC_ERROR_KEY_DESCRIPTION
));
208 *error
= makeError(kSOSOUnexpectedConnectionEvent
); // FIX
211 if (XPC_TYPE_DICTIONARY
== xtype
)
213 pdebug("received dictionary event %p\n", event
);
218 pdebug("default: unexpected connection event %p\n", event
);
219 describeXPCObject("handle_xpc_event: obj : ", event
);
221 *error
= makeError(kSOSOUnexpectedXPCEvent
);
226 static void talkWithKVS(xpc_object_t message
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
229 __block CFErrorRef error
= NULL
;
230 __block CFTypeRef object
= NULL
;
232 dispatch_block_t callback
= ^{
233 secerror("callback");
235 replyBlock(object
, error
);
237 // CFRelease(object);
240 secerror("callback error: %@", error
);
243 dispatch_release(processQueue
);
246 require_action(serviceConnection
, xit
, error
= makeError(kSOSConnectionNotOpen
));
247 require_action(message
, xit
, error
= makeError(kSOSObjectNotFoundError
));
248 dispatch_retain(processQueue
);
249 secerror("xpc_connection_send_message_with_reply called");
251 Block_copy(callback
);
254 // xpc_connection_send_message(serviceConnection, message); // Send message; don't want a reply
256 xpc_connection_send_message_with_reply(serviceConnection
, message
, xpc_queue
, ^(xpc_object_t reply
)
258 secerror("xpc_connection_send_message_with_reply handler called back");
259 if (xpc_event_filter(serviceConnection
, reply
, &error
) && reply
)
261 describeXPCObject("getValuesFromKVS: reply : ", reply
);
262 xpc_object_t xrv
= xpc_dictionary_get_value(reply
, kMessageKeyValue
);
265 describeXPCObject("talkWithKVS: xrv: ", xrv
);
267 * The given XPC object must be one that was previously returned by
268 * _CFXPCCreateXPCMessageWithCFObject().
270 object
= _CFXPCCreateCFObjectFromXPCObject(xrv
); // CF object is retained; release in callback
271 secerror("converted CF object: %@", object
);
274 secerror("missing value reply");
276 dispatch_async(processQueue
, callback
);
280 //sleep(5); // DEBUG DEBUG FIX
281 // xpc_release(message);
285 secerror("talkWithKVS error: %@", error
);
287 dispatch_async(processQueue
, callback
);
290 void putValuesWithXPC(CFDictionaryRef values
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
292 CFErrorRef error
= NULL
;
294 require_action(values
, xit
, error
= makeError(kSOSObjectNotFoundError
));
296 xpc_object_t message
= xpc_dictionary_create(NULL
, NULL
, 0);
297 xpc_dictionary_set_uint64(message
, kMessageKeyVersion
, kCKDXPCVersion
);
298 xpc_dictionary_set_string(message
, kMessageKeyOperation
, kOperationPUTDictionary
);
300 xpc_object_t xobject
= _CFXPCCreateXPCObjectFromCFObject(values
);
301 require_action(xobject
, xit
, error
= makeError(kSOSObjectCantBeConvertedToXPCObject
));
302 xpc_dictionary_set_value(message
, kMessageKeyValue
, xobject
);
304 talkWithKVS(message
, processQueue
, replyBlock
);
309 replyBlock(NULL
, error
);
312 void synchronizeKVS(dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
314 xpc_object_t message
= xpc_dictionary_create(NULL
, NULL
, 0);
315 xpc_dictionary_set_uint64(message
, kMessageKeyVersion
, kCKDXPCVersion
);
316 xpc_dictionary_set_string(message
, kMessageKeyOperation
, kOperationSynchronize
);
317 talkWithKVS(message
, processQueue
, replyBlock
);
320 void clearAll(dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
322 xpc_object_t message
= xpc_dictionary_create(NULL
, NULL
, 0);
323 xpc_dictionary_set_uint64(message
, kMessageKeyVersion
, kCKDXPCVersion
);
324 xpc_dictionary_set_string(message
, kMessageKeyOperation
, kOperationClearStore
);
325 talkWithKVS(message
, processQueue
, replyBlock
);
329 extern xpc_object_t xpc_create_reply_with_format(xpc_object_t original, const char * format, ...);
330 xpc_object_t reply = xpc_create_reply_with_format(event,
331 "{keychain-paths: %value, all-paths: %value, extensions: %value, keychain-home: %value}",
332 keychain_paths, all_paths, sandbox_extensions, home);
335 void getValuesFromKVS(CFArrayRef keysToGet
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
338 CFErrorRef error
= NULL
;
339 xpc_object_t xkeysOfInterest
= xpc_dictionary_create(NULL
, NULL
, 0);
340 xpc_object_t xkeysToGet
= keysToGet
? _CFXPCCreateXPCObjectFromCFObject(keysToGet
) : xpc_null_create();
342 require_action(xkeysToGet
, xit
, error
= makeError(kSOSObjectNotFoundError
));
344 xpc_dictionary_set_value(xkeysOfInterest
, kMessageKeyKeysToGet
, xkeysToGet
);
346 xpc_object_t message
= xpc_dictionary_create(NULL
, NULL
, 0);
347 xpc_dictionary_set_uint64(message
, kMessageKeyVersion
, kCKDXPCVersion
);
348 xpc_dictionary_set_string(message
, kMessageKeyOperation
, kOperationGETv2
);
349 xpc_dictionary_set_value(message
, kMessageKeyValue
, xkeysOfInterest
);
351 talkWithKVS(message
, processQueue
, replyBlock
);
353 xpc_release(message
);
358 replyBlock(NULL
, error
);
361 void registerKeysForKVS(CFArrayRef keysToGet
, CFStringRef clientIdentifier
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
365 xpc_object_t xkeysOfInterest
= xpc_dictionary_create(NULL
, NULL
, 0);
366 xpc_object_t xkeysToRegister
= keysToGet
? _CFXPCCreateXPCObjectFromCFObject(keysToGet
) : xpc_null_create();
367 xpc_dictionary_set_value(xkeysOfInterest
, kMessageKeyKeysToGet
, xkeysToRegister
);
369 xpc_object_t message
= xpc_dictionary_create(NULL
, NULL
, 0);
370 xpc_dictionary_set_uint64(message
, kMessageKeyVersion
, kCKDXPCVersion
);
371 xpc_dictionary_set_string(message
, kMessageKeyOperation
, kOperationRegisterKeysAndGet
);
372 xpc_dictionary_set_value(message
, kMessageKeyValue
, xkeysOfInterest
);
374 if (clientIdentifier
)
376 char *clientid
= CFStringToCString(clientIdentifier
);
379 xpc_dictionary_set_string(message
, kMessageKeyClientIdentifier
, clientid
);
384 setItemsChangedBlock(replyBlock
);
385 talkWithKVS(message
, processQueue
, replyBlock
);
387 xpc_release(message
);
390 void unregisterKeysForKVS(CFArrayRef keysToUnregister
, CFStringRef clientIdentifier
, dispatch_queue_t processQueue
, CloudKeychainReplyBlock replyBlock
)
393 if (gCKD
->unregisterKeys
) {
394 return gCKD
->unregisterKeys(...);
400 xpc_object_t xkeysOfInterest
= xpc_dictionary_create(NULL
, NULL
, 0);
401 xpc_object_t xkeysToUnregister
= keysToUnregister
? _CFXPCCreateXPCObjectFromCFObject(keysToUnregister
) : xpc_null_create();
402 xpc_dictionary_set_value(xkeysOfInterest
, kMessageKeyKeysToGet
, xkeysToUnregister
);
404 xpc_object_t message
= xpc_dictionary_create(NULL
, NULL
, 0);
405 xpc_dictionary_set_uint64(message
, kMessageKeyVersion
, kCKDXPCVersion
);
406 xpc_dictionary_set_string(message
, kMessageKeyOperation
, kOperationUnregisterKeys
);
407 xpc_dictionary_set_value(message
, kMessageKeyValue
, xkeysOfInterest
);
409 if (clientIdentifier
)
411 char *clientid
= CFStringToCString(clientIdentifier
);
414 xpc_dictionary_set_string(message
, kMessageKeyClientIdentifier
, clientid
);
419 talkWithKVS(message
, processQueue
, replyBlock
);
421 xpc_release(message
);
424 #pragma mark ----- CF-XPC Utilities -----
427 #pragma mark ----- DEBUG Utilities -----
429 //------------------------------------------------------------------------------------------------
431 //------------------------------------------------------------------------------------------------
435 xpc_object_t message
= xpc_dictionary_create(NULL
, NULL
, 0);
436 xpc_dictionary_set_uint64(message
, kMessageKeyVersion
, kCKDXPCVersion
);
437 xpc_dictionary_set_string(message
, kMessageKeyOperation
, kOperationClearStore
);
438 xpc_connection_send_message(serviceConnection
, message
); // Send message; don't wait for a reply
439 xpc_release(message
);
442 void describeXPCObject(char *prefix
, xpc_object_t object
)
445 // This is useful for debugging.
448 char *desc
= xpc_copy_description(object
);
449 pdebug("%s%s\n", prefix
, desc
);
453 pdebug("%s<NULL>\n", prefix
);
457 void describeXPCType(char *prefix
, xpc_type_t xtype
)
460 Add these as necessary:
477 // This is useful for debugging.
479 if (XPC_TYPE_CONNECTION
== xtype
)
480 strcpy(msg
, "XPC_TYPE_CONNECTION");
481 else if (XPC_TYPE_ERROR
== xtype
)
482 strcpy(msg
, "XPC_TYPE_ERROR");
483 else if (XPC_TYPE_DICTIONARY
== xtype
)
484 strcpy(msg
, "XPC_TYPE_DICTIONARY");
486 strcpy(msg
, "<unknown>");
488 pdebug("%s type:%s\n", prefix
, msg
);