1/* 2 * Copyright (c) 2012 Apple Computer, 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// ckdxpcclient.c 26// ckd-xpc 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//------------------------------------------------------------------------------------------------ 40 41#include <AssertMacros.h> 42 43#include <xpc/xpc.h> 44#include <CoreFoundation/CoreFoundation.h> 45#include <CoreFoundation/CFXPCBridge.h> 46#include <sysexits.h> 47#include <syslog.h> 48 49#include <utilities/debugging.h> 50#include <utilities/SecCFWrappers.h> 51 52#include "CKConstants.h" 53 54#define __CKDXPC_CLIENT_PRIVATE_INDIRECT__ 1 55#include "CKClient.h" 56 57 58#define pdebug(format...) secerror(format) 59 60#define verboseCKDDebugging 1 61 62#ifndef NDEBUG 63 #define xpdebug(format...) \ 64 do { \ 65 if (verboseCKDDebugging) \ 66 printf(format); \ 67 } while (0) 68#else 69 //empty 70 #define xpdebug(format...) 71#endif 72 73 74static xpc_connection_t serviceConnection = NULL; 75static dispatch_queue_t xpc_queue = NULL; 76static CloudKeychainReplyBlock itemsChangedBlock; 77 78static bool handle_xpc_event(const xpc_connection_t peer, xpc_object_t event); 79static bool xpc_event_filter(const xpc_connection_t peer, xpc_object_t event, CFErrorRef *error); 80 81// extern CFTypeRef _CFXPCCreateCFObjectFromXPCObject(xpc_object_t xo); 82// extern xpc_object_t _CFXPCCreateXPCObjectFromCFObject(CFTypeRef cf); 83 84// Debug 85static void describeXPCObject(char *prefix, xpc_object_t object); 86void describeXPCType(char *prefix, xpc_type_t xtype); 87 88static CFStringRef sErrorDomain = CFSTR("com.apple.security.cloudkeychain"); 89 90enum { 91 kSOSObjectMallocFailed = 1, 92 kAddDuplicateEntry, 93 kSOSObjectNotFoundError = 1, 94 kSOSObjectCantBeConvertedToXPCObject, 95 kSOSOUnexpectedConnectionEvent, 96 kSOSOUnexpectedXPCEvent, 97 kSOSConnectionNotOpen 98}; 99 100#define WANTXPCREPLY 0 101 102#pragma mark ----- utilities ----- 103 104static CFErrorRef makeError(CFIndex which) 105{ 106 CFDictionaryRef userInfo = NULL; 107 return CFErrorCreate(kCFAllocatorDefault, sErrorDomain, which, userInfo); 108} 109 110#pragma mark ----- SPI ----- 111 112void initXPCConnection() 113{ 114 pdebug("initXPCConnection\n"); 115 116 xpc_queue = dispatch_queue_create(xpcServiceName, DISPATCH_QUEUE_SERIAL); 117 118 serviceConnection = xpc_connection_create_mach_service(xpcServiceName, xpc_queue, 0); 119 120// serviceConnection = xpc_connection_create(xpcServiceName, xpc_queue); 121 pdebug("serviceConnection: %p\n", serviceConnection); 122 123 xpc_connection_set_event_handler(serviceConnection, ^(xpc_object_t event) 124 { 125 pdebug("xpc_connection_set_event_handler\n"); 126 handle_xpc_event(serviceConnection, event); 127 }); 128 129 xpc_connection_resume(serviceConnection); 130 xpc_retain(serviceConnection); 131} 132 133void closeXPCConnection() 134{ 135 pdebug("closeXPCConnection\n"); 136 xpc_release(serviceConnection); 137} 138 139void setItemsChangedBlock(CloudKeychainReplyBlock icb) 140{ 141 if (icb != itemsChangedBlock) 142 { 143 if (itemsChangedBlock) 144 Block_release(itemsChangedBlock); 145 itemsChangedBlock = icb; 146 Block_copy(itemsChangedBlock); 147 } 148} 149 150// typedef void (^CloudKeychainReplyBlock)(CFDictionaryRef returnedValues, CFErrorRef error); 151 152static bool handle_xpc_event(const xpc_connection_t peer, xpc_object_t event) 153{ 154 CFErrorRef localError = NULL; 155 pdebug(">>>>> handle_connection_event via event_handler <<<<<\n"); 156 bool result = false; 157 if ((result = xpc_event_filter(peer, event, &localError))) 158 { 159 const char *operation = xpc_dictionary_get_string(event, kMessageKeyOperation); 160 if (!operation || strcmp(operation, kMessageOperationItemChanged)) // some op we don't care about 161 { 162 pdebug("operation: %s", operation); 163 return result; 164 } 165 166 xpc_object_t xrv = xpc_dictionary_get_value(event, kMessageKeyValue); 167 if (!xrv) 168 { 169 pdebug("xrv null for kMessageKeyValue"); 170 return result; 171 } 172 describeXPCObject("xrv", xrv); 173 174 CFDictionaryRef returnedValues = _CFXPCCreateCFObjectFromXPCObject(xrv); 175 pdebug("returnedValues: %@", returnedValues); 176 177 if (itemsChangedBlock) 178 itemsChangedBlock(returnedValues, localError); 179 } 180 CFReleaseSafe(localError); 181 182 return result; 183} 184 185static bool xpc_event_filter(const xpc_connection_t peer, xpc_object_t event, CFErrorRef *error) 186{ 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) 192 { 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); 197#if 0 198 if (error) 199 *error = makeError(kSOSOUnexpectedConnectionEvent); // FIX 200 assert(true); 201#endif 202 } 203 else 204 if (XPC_TYPE_ERROR == xtype) 205 { 206 pdebug("default: xpc error: %s\n", xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION)); 207 if (error) 208 *error = makeError(kSOSOUnexpectedConnectionEvent); // FIX 209 } 210 else 211 if (XPC_TYPE_DICTIONARY == xtype) 212 { 213 pdebug("received dictionary event %p\n", event); 214 return true; 215 } 216 else 217 { 218 pdebug("default: unexpected connection event %p\n", event); 219 describeXPCObject("handle_xpc_event: obj : ", event); 220 if (error) 221 *error = makeError(kSOSOUnexpectedXPCEvent); 222 } 223 return false; 224} 225 226static void talkWithKVS(xpc_object_t message, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock) 227{ 228 secerror("start"); 229 __block CFErrorRef error = NULL; 230 __block CFTypeRef object = NULL; 231 232 dispatch_block_t callback = ^{ 233 secerror("callback"); 234 if (replyBlock) 235 replyBlock(object, error); 236 // if (object) 237 // CFRelease(object); 238 if (error) 239 { 240 secerror("callback error: %@", error); 241 // CFRelease(error); 242 } 243 dispatch_release(processQueue); 244 }; 245 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"); 250 251 Block_copy(callback); 252 253//#if !WANTXPCREPLY 254 // xpc_connection_send_message(serviceConnection, message); // Send message; don't want a reply 255//#else 256 xpc_connection_send_message_with_reply(serviceConnection, message, xpc_queue, ^(xpc_object_t reply) 257 { 258 secerror("xpc_connection_send_message_with_reply handler called back"); 259 if (xpc_event_filter(serviceConnection, reply, &error) && reply) 260 { 261 describeXPCObject("getValuesFromKVS: reply : ", reply); 262 xpc_object_t xrv = xpc_dictionary_get_value(reply, kMessageKeyValue); 263 if (xrv) 264 { 265 describeXPCObject("talkWithKVS: xrv: ", xrv); 266 /* 267 * The given XPC object must be one that was previously returned by 268 * _CFXPCCreateXPCMessageWithCFObject(). 269 */ 270 object = _CFXPCCreateCFObjectFromXPCObject(xrv); // CF object is retained; release in callback 271 secerror("converted CF object: %@", object); 272 } 273 else 274 secerror("missing value reply"); 275 } 276 dispatch_async(processQueue, callback); 277 }); 278//#endif 279 280//sleep(5); // DEBUG DEBUG FIX 281 // xpc_release(message); 282 return; 283 284xit: 285 secerror("talkWithKVS error: %@", error); 286 if (replyBlock) 287 dispatch_async(processQueue, callback); 288} 289 290void putValuesWithXPC(CFDictionaryRef values, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock) 291{ 292 CFErrorRef error = NULL; 293 294 require_action(values, xit, error = makeError(kSOSObjectNotFoundError)); 295 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); 299 300 xpc_object_t xobject = _CFXPCCreateXPCObjectFromCFObject(values); 301 require_action(xobject, xit, error = makeError(kSOSObjectCantBeConvertedToXPCObject)); 302 xpc_dictionary_set_value(message, kMessageKeyValue, xobject); 303 304 talkWithKVS(message, processQueue, replyBlock); 305 return; 306 307xit: 308 if (replyBlock) 309 replyBlock(NULL, error); 310} 311 312void synchronizeKVS(dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock) 313{ 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); 318} 319 320void clearAll(dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock) 321{ 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); 326} 327 328/* 329extern 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); 333*/ 334 335void getValuesFromKVS(CFArrayRef keysToGet, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock) 336{ 337 secerror("start"); 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(); 341 342 require_action(xkeysToGet, xit, error = makeError(kSOSObjectNotFoundError)); 343 344 xpc_dictionary_set_value(xkeysOfInterest, kMessageKeyKeysToGet, xkeysToGet); 345 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); 350 351 talkWithKVS(message, processQueue, replyBlock); 352 353 xpc_release(message); 354 return; 355 356xit: 357 if (replyBlock) 358 replyBlock(NULL, error); 359} 360 361void registerKeysForKVS(CFArrayRef keysToGet, CFStringRef clientIdentifier, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock) 362{ 363 secerror("start"); 364 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); 368 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); 373 374 if (clientIdentifier) 375 { 376 char *clientid = CFStringToCString(clientIdentifier); 377 if (clientid) 378 { 379 xpc_dictionary_set_string(message, kMessageKeyClientIdentifier, clientid); 380 free(clientid); 381 } 382 } 383 384 setItemsChangedBlock(replyBlock); 385 talkWithKVS(message, processQueue, replyBlock); 386 387 xpc_release(message); 388} 389 390void unregisterKeysForKVS(CFArrayRef keysToUnregister, CFStringRef clientIdentifier, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock) 391{ 392#if NO_SERVERz 393 if (gCKD->unregisterKeys) { 394 return gCKD->unregisterKeys(...); 395 } 396#endif 397 398 secerror("start"); 399 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); 403 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); 408 409 if (clientIdentifier) 410 { 411 char *clientid = CFStringToCString(clientIdentifier); 412 if (clientid) 413 { 414 xpc_dictionary_set_string(message, kMessageKeyClientIdentifier, clientid); 415 free(clientid); 416 } 417 } 418 419 talkWithKVS(message, processQueue, replyBlock); 420 421 xpc_release(message); 422} 423 424#pragma mark ----- CF-XPC Utilities ----- 425 426 427#pragma mark ----- DEBUG Utilities ----- 428 429//------------------------------------------------------------------------------------------------ 430// DEBUG only 431//------------------------------------------------------------------------------------------------ 432 433void clearStore() 434{ 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); 440} 441 442void describeXPCObject(char *prefix, xpc_object_t object) 443{ 444//#ifndef NDEBUG 445 // This is useful for debugging. 446 if (object) 447 { 448 char *desc = xpc_copy_description(object); 449 pdebug("%s%s\n", prefix, desc); 450 free(desc); 451 } 452 else 453 pdebug("%s<NULL>\n", prefix); 454//#endif 455} 456 457void describeXPCType(char *prefix, xpc_type_t xtype) 458{ 459 /* 460 Add these as necessary: 461 XPC_TYPE_ENDPOINT 462 XPC_TYPE_NULL 463 XPC_TYPE_BOOL 464 XPC_TYPE_INT64 465 XPC_TYPE_UINT64 466 XPC_TYPE_DOUBLE 467 XPC_TYPE_DATE 468 XPC_TYPE_DATA 469 XPC_TYPE_STRING 470 XPC_TYPE_UUID 471 XPC_TYPE_FD 472 XPC_TYPE_SHMEM 473 XPC_TYPE_ARRAY 474 */ 475 476#ifndef NDEBUG 477 // This is useful for debugging. 478 char msg[256]={0,}; 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"); 485 else 486 strcpy(msg, "<unknown>"); 487 488 pdebug("%s type:%s\n", prefix, msg); 489#endif 490} 491 492 493 494