]> git.saurik.com Git - apple/security.git/blobdiff - Security/sec/SOSCircle/CKBridge/CKClient.c
Security-57031.1.35.tar.gz
[apple/security.git] / Security / sec / SOSCircle / CKBridge / CKClient.c
diff --git a/Security/sec/SOSCircle/CKBridge/CKClient.c b/Security/sec/SOSCircle/CKBridge/CKClient.c
new file mode 100644 (file)
index 0000000..f452dfb
--- /dev/null
@@ -0,0 +1,493 @@
+/*
+ * Copyright (c) 2012,2014 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+//  ckdxpcclient.c
+//  ckd-xpc
+//
+
+/*
+    This XPC service is essentially just a proxy to iCloud KVS, which exists since
+    the main security code cannot link against Foundation.
+    
+    See sendTSARequestWithXPC in tsaSupport.c for how to call the service
+    
+    The client of an XPC service does not get connection events, nor does it
+    need to deal with transactions.
+*/
+
+//------------------------------------------------------------------------------------------------
+
+#include <AssertMacros.h>
+
+#include <xpc/xpc.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreFoundation/CFXPCBridge.h>
+#include <sysexits.h>
+#include <syslog.h>
+
+#include <utilities/debugging.h>
+#include <utilities/SecCFWrappers.h>
+
+#include "CKConstants.h"
+
+#define __CKDXPC_CLIENT_PRIVATE_INDIRECT__ 1
+#include "CKClient.h"
+
+
+#define pdebug(format...) secerror(format)
+
+#define verboseCKDDebugging 1
+
+#ifndef NDEBUG
+    #define xpdebug(format...) \
+        do {  \
+            if (verboseCKDDebugging) \
+                printf(format);  \
+        } while (0)
+#else
+    //empty
+    #define xpdebug(format...)
+#endif
+
+
+static xpc_connection_t serviceConnection = NULL;
+static dispatch_queue_t xpc_queue = NULL;
+static CloudKeychainReplyBlock itemsChangedBlock;
+
+static bool handle_xpc_event(const xpc_connection_t peer, xpc_object_t event);
+static bool xpc_event_filter(const xpc_connection_t peer, xpc_object_t event, CFErrorRef *error);
+
+// extern CFTypeRef _CFXPCCreateCFObjectFromXPCObject(xpc_object_t xo);
+// extern xpc_object_t _CFXPCCreateXPCObjectFromCFObject(CFTypeRef cf);
+
+// Debug
+static void describeXPCObject(char *prefix, xpc_object_t object);
+void describeXPCType(char *prefix, xpc_type_t xtype);
+
+static CFStringRef sErrorDomain = CFSTR("com.apple.security.cloudkeychain");
+
+enum {
+    kSOSObjectMallocFailed = 1,
+    kAddDuplicateEntry,
+    kSOSObjectNotFoundError = 1,
+    kSOSObjectCantBeConvertedToXPCObject,
+    kSOSOUnexpectedConnectionEvent,
+    kSOSOUnexpectedXPCEvent,
+    kSOSConnectionNotOpen
+};
+
+#define WANTXPCREPLY 0
+
+#pragma mark ----- utilities -----
+
+static CFErrorRef makeError(CFIndex which)
+{
+    CFDictionaryRef userInfo = NULL;
+    return CFErrorCreate(kCFAllocatorDefault, sErrorDomain, which, userInfo);
+}
+
+#pragma mark ----- SPI -----
+
+void initXPCConnection()
+{
+    pdebug("initXPCConnection\n");
+    
+    xpc_queue = dispatch_queue_create(xpcServiceName, DISPATCH_QUEUE_SERIAL);
+
+    serviceConnection = xpc_connection_create_mach_service(xpcServiceName, xpc_queue, 0);
+
+//    serviceConnection = xpc_connection_create(xpcServiceName, xpc_queue);
+    pdebug("serviceConnection: %p\n", serviceConnection);
+
+    xpc_connection_set_event_handler(serviceConnection, ^(xpc_object_t event)
+    {
+        pdebug("xpc_connection_set_event_handler\n");
+        handle_xpc_event(serviceConnection, event);
+    });
+
+    xpc_connection_resume(serviceConnection);
+    xpc_retain(serviceConnection);
+}
+
+void closeXPCConnection()
+{
+    pdebug("closeXPCConnection\n");
+    xpc_release(serviceConnection);
+}
+
+void setItemsChangedBlock(CloudKeychainReplyBlock icb)
+{
+    if (icb != itemsChangedBlock)
+    {
+        if (itemsChangedBlock)
+            Block_release(itemsChangedBlock);
+        itemsChangedBlock = icb;
+        Block_copy(itemsChangedBlock);
+    }
+}
+
+// typedef void (^CloudKeychainReplyBlock)(CFDictionaryRef returnedValues, CFErrorRef error);
+
+static bool handle_xpc_event(const xpc_connection_t peer, xpc_object_t event)
+{
+    CFErrorRef localError = NULL;
+    pdebug(">>>>> handle_connection_event via event_handler <<<<<\n");
+    bool result = false;
+    if ((result = xpc_event_filter(peer, event, &localError)))
+    {
+        const char *operation = xpc_dictionary_get_string(event, kMessageKeyOperation);
+        if (!operation || strcmp(operation, kMessageOperationItemChanged))  // some op we don't care about
+        {
+            pdebug("operation: %s", operation);
+            return result;
+        }
+        
+        xpc_object_t xrv = xpc_dictionary_get_value(event, kMessageKeyValue);
+        if (!xrv)
+        {
+            pdebug("xrv null for kMessageKeyValue");
+            return result;
+        }
+        describeXPCObject("xrv", xrv);
+        
+        CFDictionaryRef returnedValues = _CFXPCCreateCFObjectFromXPCObject(xrv);
+        pdebug("returnedValues: %@", returnedValues);
+
+        if (itemsChangedBlock)
+            itemsChangedBlock(returnedValues, localError);
+    }
+    CFReleaseSafe(localError);
+
+    return result;
+}
+
+static bool xpc_event_filter(const xpc_connection_t peer, xpc_object_t event, CFErrorRef *error)
+{
+    // return true if the type is XPC_TYPE_DICTIONARY (and therefore something more to process)
+    pdebug("handle_connection_event\n");
+    xpc_type_t xtype = xpc_get_type(event);
+    describeXPCType("handle_xpc_event", xtype);
+    if (XPC_TYPE_CONNECTION == xtype)
+    {
+        pdebug("handle_xpc_event: XPC_TYPE_CONNECTION (unexpected)");
+        // The client of an XPC service does not get connection events
+        // For nwo, we log this and keep going
+        describeXPCObject("handle_xpc_event: XPC_TYPE_CONNECTION, obj : ", event);
+#if 0
+        if (error)
+            *error = makeError(kSOSOUnexpectedConnectionEvent); // FIX
+        assert(true);
+#endif
+    }
+    else
+    if (XPC_TYPE_ERROR == xtype)
+    {
+        pdebug("default: xpc error: %s\n", xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION));
+        if (error)
+            *error = makeError(kSOSOUnexpectedConnectionEvent); // FIX
+    }
+    else
+    if (XPC_TYPE_DICTIONARY == xtype)
+    {
+        pdebug("received dictionary event %p\n", event);
+        return true;
+    }
+    else
+    {
+        pdebug("default: unexpected connection event %p\n", event);
+        describeXPCObject("handle_xpc_event: obj : ", event);
+        if (error)
+            *error = makeError(kSOSOUnexpectedXPCEvent);
+    }
+    return false;
+}
+
+static void talkWithKVS(xpc_object_t message, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
+{
+    secerror("start");
+    __block CFErrorRef error = NULL;
+    __block CFTypeRef object = NULL;
+    
+    dispatch_block_t callback = ^{
+            secerror("callback");
+            if (replyBlock)
+                replyBlock(object, error);
+    //        if (object)
+     //           CFRelease(object);
+            if (error)
+            {
+                secerror("callback error: %@", error);
+     //           CFRelease(error);
+            }
+            dispatch_release(processQueue);
+        };
+    
+    require_action(serviceConnection, xit, error = makeError(kSOSConnectionNotOpen));
+    require_action(message, xit, error = makeError(kSOSObjectNotFoundError));
+    dispatch_retain(processQueue);
+    secerror("xpc_connection_send_message_with_reply called");
+    
+    Block_copy(callback);
+    
+//#if !WANTXPCREPLY
+ //   xpc_connection_send_message(serviceConnection, message);            // Send message; don't want a reply
+//#else
+    xpc_connection_send_message_with_reply(serviceConnection, message, xpc_queue, ^(xpc_object_t reply)
+        {
+            secerror("xpc_connection_send_message_with_reply handler called back");
+            if (xpc_event_filter(serviceConnection, reply, &error) && reply)
+            {
+                describeXPCObject("getValuesFromKVS: reply : ", reply);
+                xpc_object_t xrv = xpc_dictionary_get_value(reply, kMessageKeyValue);
+                if (xrv)
+                {
+                    describeXPCObject("talkWithKVS: xrv: ", xrv);
+                    /*
+                        * The given XPC object must be one that was previously returned by
+                        * _CFXPCCreateXPCMessageWithCFObject().
+                    */
+                    object = _CFXPCCreateCFObjectFromXPCObject(xrv);   // CF object is retained; release in callback
+                    secerror("converted CF object: %@", object);
+                }
+                else
+                    secerror("missing value reply");
+            }
+            dispatch_async(processQueue, callback);
+        });
+//#endif
+
+//sleep(5);   // DEBUG DEBUG FIX
+ //   xpc_release(message);
+    return;
+    
+xit:
+    secerror("talkWithKVS error: %@", error);
+    if (replyBlock)
+        dispatch_async(processQueue, callback);
+}
+
+void putValuesWithXPC(CFDictionaryRef values, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
+{
+    CFErrorRef error = NULL;
+
+    require_action(values, xit, error = makeError(kSOSObjectNotFoundError));
+
+    xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
+    xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion);
+    xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationPUTDictionary);
+    
+    xpc_object_t xobject = _CFXPCCreateXPCObjectFromCFObject(values);
+    require_action(xobject, xit, error = makeError(kSOSObjectCantBeConvertedToXPCObject));
+    xpc_dictionary_set_value(message, kMessageKeyValue, xobject);
+    
+    talkWithKVS(message, processQueue, replyBlock);
+    return;
+
+xit:
+    if (replyBlock)
+        replyBlock(NULL, error);
+}
+
+void synchronizeKVS(dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
+{
+    xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
+    xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion);
+    xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationSynchronize);
+    talkWithKVS(message, processQueue, replyBlock);
+}
+
+void clearAll(dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
+{
+    xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
+    xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion);
+    xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationClearStore);
+    talkWithKVS(message, processQueue, replyBlock);
+}
+
+/*
+extern xpc_object_t xpc_create_reply_with_format(xpc_object_t original, const char * format, ...);
+            xpc_object_t reply = xpc_create_reply_with_format(event, 
+                "{keychain-paths: %value, all-paths: %value, extensions: %value, keychain-home: %value}",
+                keychain_paths, all_paths, sandbox_extensions, home);
+*/
+
+void getValuesFromKVS(CFArrayRef keysToGet, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
+{
+    secerror("start");
+    CFErrorRef error = NULL;
+    xpc_object_t xkeysOfInterest = xpc_dictionary_create(NULL, NULL, 0);
+    xpc_object_t xkeysToGet = keysToGet ? _CFXPCCreateXPCObjectFromCFObject(keysToGet) : xpc_null_create();
+
+    require_action(xkeysToGet, xit, error = makeError(kSOSObjectNotFoundError));
+
+    xpc_dictionary_set_value(xkeysOfInterest, kMessageKeyKeysToGet, xkeysToGet);
+
+    xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
+    xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion);
+    xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationGETv2);
+    xpc_dictionary_set_value(message, kMessageKeyValue, xkeysOfInterest);
+    
+    talkWithKVS(message, processQueue, replyBlock);
+
+    xpc_release(message);
+    return;
+    
+xit:
+    if (replyBlock)
+        replyBlock(NULL, error);
+}
+
+void registerKeysForKVS(CFArrayRef keysToGet, CFStringRef clientIdentifier, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
+{
+    secerror("start");
+
+    xpc_object_t xkeysOfInterest = xpc_dictionary_create(NULL, NULL, 0);
+    xpc_object_t xkeysToRegister = keysToGet ? _CFXPCCreateXPCObjectFromCFObject(keysToGet) : xpc_null_create();
+    xpc_dictionary_set_value(xkeysOfInterest, kMessageKeyKeysToGet, xkeysToRegister);
+
+    xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
+    xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion);
+    xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationRegisterKeysAndGet);
+    xpc_dictionary_set_value(message, kMessageKeyValue, xkeysOfInterest);
+    
+    if (clientIdentifier)
+    {
+        char *clientid = CFStringToCString(clientIdentifier);
+        if (clientid)
+        {
+            xpc_dictionary_set_string(message, kMessageKeyClientIdentifier, clientid);
+            free(clientid);
+        }
+    }
+
+    setItemsChangedBlock(replyBlock);
+    talkWithKVS(message, processQueue, replyBlock);
+
+    xpc_release(message);
+}
+
+void unregisterKeysForKVS(CFArrayRef keysToUnregister, CFStringRef clientIdentifier, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock)
+{
+#if NO_SERVERz
+    if (gCKD->unregisterKeys) {
+        return gCKD->unregisterKeys(...);
+    }
+#endif
+
+    secerror("start");
+
+    xpc_object_t xkeysOfInterest = xpc_dictionary_create(NULL, NULL, 0);
+    xpc_object_t xkeysToUnregister = keysToUnregister ? _CFXPCCreateXPCObjectFromCFObject(keysToUnregister) : xpc_null_create();
+    xpc_dictionary_set_value(xkeysOfInterest, kMessageKeyKeysToGet, xkeysToUnregister);
+
+    xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
+    xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion);
+    xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationUnregisterKeys);
+    xpc_dictionary_set_value(message, kMessageKeyValue, xkeysOfInterest);
+    
+    if (clientIdentifier)
+    {
+        char *clientid = CFStringToCString(clientIdentifier);
+        if (clientid)
+        {
+            xpc_dictionary_set_string(message, kMessageKeyClientIdentifier, clientid);
+            free(clientid);
+        }
+    }
+
+    talkWithKVS(message, processQueue, replyBlock);
+
+    xpc_release(message);
+}
+
+#pragma mark ----- CF-XPC Utilities -----
+
+
+#pragma mark ----- DEBUG Utilities -----
+
+//------------------------------------------------------------------------------------------------
+//          DEBUG only
+//------------------------------------------------------------------------------------------------
+
+void clearStore()
+{
+    xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
+    xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion);
+    xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationClearStore);
+    xpc_connection_send_message(serviceConnection, message);  // Send message; don't wait for a reply
+    xpc_release(message);
+}
+
+void describeXPCObject(char *prefix, xpc_object_t object)
+{
+//#ifndef NDEBUG
+    // This is useful for debugging.
+    if (object)
+    {
+      char *desc = xpc_copy_description(object);
+    pdebug("%s%s\n", prefix, desc);
+    free(desc);
+    }
+    else
+        pdebug("%s<NULL>\n", prefix);
+//#endif
+}
+
+void describeXPCType(char *prefix, xpc_type_t xtype)
+{
+    /*
+        Add these as necessary:
+        XPC_TYPE_ENDPOINT
+        XPC_TYPE_NULL
+        XPC_TYPE_BOOL
+        XPC_TYPE_INT64
+        XPC_TYPE_UINT64
+        XPC_TYPE_DOUBLE
+        XPC_TYPE_DATE
+        XPC_TYPE_DATA
+        XPC_TYPE_STRING
+        XPC_TYPE_UUID
+        XPC_TYPE_FD
+        XPC_TYPE_SHMEM
+        XPC_TYPE_ARRAY
+    */
+    
+#ifndef NDEBUG
+    // This is useful for debugging.
+    char msg[256]={0,};
+    if (XPC_TYPE_CONNECTION == xtype)
+        strcpy(msg, "XPC_TYPE_CONNECTION");
+    else if (XPC_TYPE_ERROR == xtype)
+        strcpy(msg, "XPC_TYPE_ERROR");
+    else if  (XPC_TYPE_DICTIONARY == xtype)
+        strcpy(msg, "XPC_TYPE_DICTIONARY");
+    else
+        strcpy(msg, "<unknown>");
+
+    pdebug("%s type:%s\n", prefix, msg);
+#endif
+}
+
+
+