]> git.saurik.com Git - apple/security.git/blobdiff - sec/ipc/client.c
Security-55471.14.18.tar.gz
[apple/security.git] / sec / ipc / client.c
index 241ee0c451406d6d0372ce1c4d43720a11bbaba8..96dafb04bdb71ea2e9c66a4d2928b4135ce397a5 100644 (file)
@@ -1,15 +1,15 @@
 /*
  * Copyright (c) 2007-2009 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,
  * 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@
  */
 
-#include <mach/mach.h>
-#include <mach/message.h>
-#include <mach/mach_error.h>
-#include <servers/bootstrap.h>
-
-#include <pthread.h>
 #include <stdbool.h>
 #include <sys/queue.h>
+#include <syslog.h>
 
 #include <CoreFoundation/CoreFoundation.h>
 #include <Security/SecItem.h>
 #include <Security/SecBasePriv.h>
 #include <Security/SecInternal.h>
 
-#include <security_utilities/debugging.h>
+#include <utilities/debugging.h>
+#include <utilities/SecCFError.h>
+#include <utilities/SecXPCError.h>
+#include <utilities/SecCFWrappers.h>
+#include <utilities/SecDispatchRelease.h>
+#include <utilities/SecDb.h> // TODO Fixme this gets us SecError().
 #include "securityd_client.h"
-#include "securityd_req.h"
-
-#if NO_SERVER
-#define CHECK_ENTITLEMENTS 0
-#else
-#define CHECK_ENTITLEMENTS 1
-#endif
+#include "SecuritydXPC.h"
 
 struct securityd *gSecurityd;
 
-/* XXX Not thread safe right now. */
-static CFArrayRef gAccessGroups = NULL;
+//
+// MARK: XPC IPC.
+//
 
-CFArrayRef SecAccessGroupsGetCurrent(void) {
-#if !CHECK_ENTITLEMENTS
-    if (!gAccessGroups) {
-        /* Initialize gAccessGroups for tests. */
-        const void *agrps[] = {
-            CFSTR("test"),
-#if 1
-            CFSTR("apple"),
-            CFSTR("lockdown-identities"),
+/* Hardcoded Access Groups for the server itself */
+static CFArrayRef SecServerCopyAccessGroups(void) {
+    return CFArrayCreateForCFTypes(kCFAllocatorDefault,
+#if NO_SERVER
+                                   CFSTR("test"),
+                                   CFSTR("apple"),
+                                   CFSTR("lockdown-identities"),
 #else
-            CFSTR("*"),
-#endif
-        };
-        gAccessGroups = CFArrayCreate(NULL, agrps,
-            sizeof(agrps) / sizeof(*agrps), &kCFTypeArrayCallBacks);
-    }
+                                   CFSTR("sync"),
 #endif
-    return gAccessGroups;
-}
-
-void SecAccessGroupsSetCurrent(CFArrayRef accessGroups) {
-    if (accessGroups)
-        CFRetain(accessGroups);
-    CFReleaseSafe(gAccessGroups);
-    gAccessGroups = accessGroups;
+                                   CFSTR("com.apple.security.sos"),
+                                   NULL);
 }
 
-static mach_port_t securityd_port = MACH_PORT_NULL;
-static pthread_once_t init_once = PTHREAD_ONCE_INIT;
-static pthread_key_t port_key;
-
-static CFStringRef kSecRunLoopModeRef = CFSTR("com.apple.securityd.runloop");
-
-struct request {
-    uint32_t seq_no;
-    uint32_t msgid;
-    SLIST_ENTRY(request) next;
-    int32_t rcode;
-    void *data;
-    size_t length;
-    volatile bool done;
-};
-
-typedef struct per_thread {
-    SLIST_HEAD(request_head, request) head;;
-    CFMachPortRef port;
-    uint32_t seq_no;
-    mach_msg_id_t notification; /* XXX for stacked requests, notifications
-                                    need to be matched up with seq_nos such
-                                    that only failed requests error out/retry */
-} *per_thread_t;
-
-
-static void destroy_per_thread(void *ex)
-{
-    per_thread_t pt = (per_thread_t)ex;
-    CFMachPortInvalidate(pt->port);
-    CFRelease(pt->port);
-    free(pt);
+CFArrayRef SecAccessGroupsGetCurrent(void) {
+    static CFArrayRef gSecServerAccessGroups;
+    static dispatch_once_t only_do_this_once;
+    dispatch_once(&only_do_this_once, ^{
+        gSecServerAccessGroups = SecServerCopyAccessGroups();
+        assert(gSecServerAccessGroups);
+    });
+    return gSecServerAccessGroups;
 }
 
-static void securityd_port_reset(void) 
-{
-    pthread_once_t temp = PTHREAD_ONCE_INIT;
-    init_once = temp;
-    securityd_port = MACH_PORT_NULL;
+static xpc_connection_t securityd_create_connection(const char *name) {
+    if (!name)
+        name = kSecuritydXPCServiceName;
+    xpc_connection_t connection;
+    connection = xpc_connection_create_mach_service(name, NULL, 0);
+    xpc_connection_set_event_handler(connection, ^(xpc_object_t event) {
+        const char *description = xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION);
+        secnotice("xpc", "got event: %s", description);
+    });
+    xpc_connection_resume(connection);
+    return connection;
 }
 
-static void securityd_port_lookup(void) 
-{
-    kern_return_t ret;
-    mach_port_t bootstrap_lookup_port = MACH_PORT_NULL;
-
-    /* bootstrap_port is not initialized on embedded */
-    ret = task_get_bootstrap_port(mach_task_self(), &bootstrap_lookup_port);
-    if (ret != KERN_SUCCESS) {
-        secdebug("client", "task_get_bootstrap_port(): 0x%x: %s\n", ret, mach_error_string(ret));
-    }
-
-    ret = bootstrap_look_up(bootstrap_lookup_port, 
-            SECURITYSERVER_BOOTSTRAP_NAME, 
-            &securityd_port);
-    if (ret != KERN_SUCCESS) {
-        secdebug("client", "bootstrap_look_up(): 0x%x: %s\n", ret, mach_error_string(ret));
-    }
+static xpc_connection_t sSecuritydConnection;
 
-    pthread_key_create(&port_key, destroy_per_thread);
-
-    int err = pthread_atfork(NULL, NULL, securityd_port_reset);
-    if (err) {
-        secdebug("client", "pthread_atfork(): %d: %s\n", errno, strerror(errno));
-    }
+static xpc_connection_t securityd_connection(void) {
+    static dispatch_once_t once;
+    dispatch_once(&once, ^{
+        sSecuritydConnection = securityd_create_connection(NULL);
+    });
+    return sSecuritydConnection;
 }
 
-union max_msg_size_union {
-    union __RequestUnion__securityd_client_securityd_request_subsystem request;
-};
+// NOTE: This is not thread safe, but this SPI is for testing only.
+void SecServerSetMachServiceName(const char *name) {
+    // Make sure sSecXPCServer.queue exists.
+    securityd_connection();
 
-static uint8_t reply_buffer[sizeof(union max_msg_size_union) + MAX_TRAILER_SIZE];
-
-static boolean_t maybe_notification(mach_msg_header_t *request)
-{
-    mach_no_senders_notification_t * notify = (mach_no_senders_notification_t *)request;
-    if ((notify->not_header.msgh_id > MACH_NOTIFY_LAST) ||
-            (notify->not_header.msgh_id < MACH_NOTIFY_FIRST))
-        return false;        /* if this is not a notification message */
-
-    per_thread_t pt = (per_thread_t)pthread_getspecific(port_key);
-    assert(pt);
-    mach_msg_id_t notification_id = notify->not_header.msgh_id;
-
-    switch(notification_id) {
-        case MACH_NOTIFY_SEND_ONCE: 
-            /* our send-once right for a reply died in the hands of another */
-            pt->notification = notification_id;
-            CFRunLoopStop(CFRunLoopGetCurrent());
-            break;
-        default:
-            secdebug("client", "unexpected notification %d", pt->notification);
-            break;
-    }
-    return true;
-}
-
-extern boolean_t securityd_reply_server
-(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP);
-
-static void cfmachport_callback(CFMachPortRef port, void *msg, CFIndex size, void *info)
-{
-    if (!maybe_notification((mach_msg_header_t *)msg))
-        securityd_reply_server(msg, (mach_msg_header_t *)reply_buffer);
+    xpc_connection_t oldConection = sSecuritydConnection;
+    sSecuritydConnection = securityd_create_connection(name);
+    if (oldConection)
+        xpc_release(oldConection);
 }
 
-static int32_t send_receive(uint32_t msg_id, const void *data_in, size_t length_in,
-        void **data_out, size_t *length_out)
+xpc_object_t
+securityd_message_with_reply_sync(xpc_object_t message, CFErrorRef *error)
 {
-    pthread_once(&init_once, securityd_port_lookup);
+    xpc_object_t reply = NULL;
+    xpc_connection_t connection = securityd_connection();
 
-    CFRunLoopRef rl = CFRunLoopGetCurrent();
-
-    per_thread_t pt = (per_thread_t)pthread_getspecific(port_key);
-    if (!pt) {
-        pt = calloc(1, sizeof(*pt));
-        SLIST_INIT(&pt->head);
-        pt->port = CFMachPortCreate (NULL, cfmachport_callback, NULL, false);
-        CFRunLoopSourceRef source = CFMachPortCreateRunLoopSource(NULL, pt->port, 0/*order*/);
-        CFRunLoopAddSource(rl, source, kSecRunLoopModeRef);
-        CFRelease(source);
-        pthread_setspecific(port_key, pt);
-    }
-
-    struct request req = { pt->seq_no++, msg_id, };
-    SLIST_INSERT_HEAD(&pt->head, &req, next);
-
-    int retries = 1;
-    kern_return_t ret;
-    do {
-        /* 64 bits cast: worst case here is the client send a truncated query, which the server will reject */
-        /* Debug check: The size of the request is of type natural_t.
-            The following is correct as long as natural_t is unsigned int */
-        assert(length_in<=UINT_MAX);
-        ret = securityd_client_request(securityd_port, 
-                CFMachPortGetPort(pt->port), req.seq_no, msg_id, 
-                (void *)data_in, (unsigned int) length_in);
-        secdebug("client", "securityd_client_request %d sent, retry %d, result %d\n", 
-            req.seq_no, retries, ret);
-
-        if (!ret) {
-            pt->notification = 0;
-            while (!pt->notification && !req.done) {
-                CFRunLoopRunInMode(kSecRunLoopModeRef, 10000, true);
-                secdebug("client", "return from runloop, notification %d, request %d %sdone\n",
-                    pt->notification, req.seq_no, !req.done ? "not " : "");
-            }
+    for (int try = 1; try <= 2; ++try) {
+        reply = xpc_connection_send_message_with_reply_sync(connection, message);
+        if (try == 1 && reply == XPC_ERROR_CONNECTION_INTERRUPTED) {
+            xpc_release(reply);
         } else {
-            secdebug("client", "failed to send request to securityd (err=%d).", ret);
+            break;
         }
-
-    } while ((ret || pt->notification) && retries--);
-
-    SLIST_REMOVE_HEAD(&pt->head, next);
-
-    struct request *next_head = SLIST_FIRST(&pt->head);
-    if (next_head) {
-        /* stop runloop if "new head" is also already done */
-        if (pt->notification || next_head->done)
-            CFRunLoopStop(rl);
     }
 
-    if (req.done) {
-        if (data_out)
-            *data_out = req.data;
-        if (length_out)
-            *length_out = req.length;
-        return req.rcode;
+    if (xpc_get_type(reply) == XPC_TYPE_ERROR) {
+        CFIndex code =  0;
+        if (reply == XPC_ERROR_CONNECTION_INTERRUPTED || reply == XPC_ERROR_CONNECTION_INVALID)
+            code = kSecXPCErrorConnectionFailed;
+        else if (reply == XPC_ERROR_TERMINATION_IMMINENT)
+            code = kSecXPCErrorUnknown;
+        else
+            code = kSecXPCErrorUnknown;
+
+        char *conn_desc = xpc_copy_description(connection);
+        const char *description = xpc_dictionary_get_string(reply, XPC_ERROR_KEY_DESCRIPTION);
+        SecCFCreateErrorWithFormat(code, sSecXPCErrorDomain, NULL, error, NULL, CFSTR("%s: %s"), conn_desc, description);
+        free(conn_desc);
+        xpc_release(reply);
+        reply = NULL;
     }
 
-    return errSecNotAvailable;
+    return reply;
 }
 
-kern_return_t securityd_server_reply(mach_port_t receiver,
-        uint32_t seq_no, int32_t rcode,
-        uint8_t *msg_data, mach_msg_type_number_t msg_length);
-
-kern_return_t securityd_server_reply(mach_port_t receiver,
-        uint32_t seq_no, int32_t rcode,
-        uint8_t *msg_data, mach_msg_type_number_t msg_length)
+xpc_object_t securityd_create_message(enum SecXPCOperation op, CFErrorRef* error)
 {
-    secdebug("client", "reply from port %d request_id %d data(%d,%p)\n",
-       receiver, seq_no, msg_length, msg_data);
-    per_thread_t pt = (per_thread_t)pthread_getspecific(port_key);
-    assert(pt);
-    struct request *req;
-    SLIST_FOREACH(req, &pt->head, next) {
-        if (req->seq_no == seq_no) {
-            req->rcode = rcode;
-            if (msg_length && msg_data) {
-                req->data = malloc(msg_length);
-                if (req->data) {
-                    req->length = msg_length;
-                    memcpy(req->data, msg_data, msg_length);
-                } else
-                    req->length = 0;
-            }
-            req->done = true;
-            /* if multiple requests were queued during nested invocations 
-               we wait until the last one inserted which is the deepest
-               nested one is done */
-            if (req == SLIST_FIRST(&pt->head))
-                CFRunLoopStop(CFRunLoopGetCurrent());
-            break;
-        }
+    xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
+    if (message) {
+        xpc_dictionary_set_uint64(message, kSecXPCKeyOperation, op);
+    } else {
+        SecCFCreateError(kSecXPCErrorConnectionFailed, sSecXPCErrorDomain,
+                         CFSTR("xpc_dictionary_create returned NULL"), NULL, error);
     }
-    return 0;
+    return message;
 }
 
-OSStatus ServerCommandSendReceive(uint32_t id, CFTypeRef in, CFTypeRef *out)
-{
-    CFDataRef data_in = NULL, data_out = NULL;
-    void *bytes_out = NULL; size_t length_out = 0;
+// Return true if there is no error in message, return false and set *error if there is.
+bool securityd_message_no_error(xpc_object_t message, CFErrorRef *error) {
+    xpc_object_t xpc_error = xpc_dictionary_get_value(message, kSecXPCKeyError);
+    if (xpc_error == NULL)
+        return true;
 
-    if (in) {
-#ifndef NDEBUG
-        CFDataRef query_debug = CFPropertyListCreateXMLData(kCFAllocatorDefault, in);
-        if (query_debug) {
-            secdebug("client", "securityd query: %.*s\n",
-                CFDataGetLength(query_debug), CFDataGetBytePtr(query_debug));
-            CFReleaseSafe(query_debug);
-        }
-#endif
-        CFErrorRef error = NULL;
-        data_in = CFPropertyListCreateData(kCFAllocatorDefault, in,
-                                           kCFPropertyListBinaryFormat_v1_0,
-                                           0, &error);
-        if (!data_in) {
-            secdebug("client", "failed to encode query: %@", error);
-            CFReleaseSafe(error);
-            return errSecItemIllegalQuery;
-        }
+    if (error) {
+        *error = SecCreateCFErrorWithXPCObject(xpc_error);
     }
+    return false;
+}
 
-    OSStatus status = send_receive(id, data_in ? CFDataGetBytePtr(data_in) : NULL, 
-        data_in ? CFDataGetLength(data_in) : 0, &bytes_out, &length_out);
-    if (data_in) CFRelease(data_in);
-
-    if (bytes_out && length_out) {
-        data_out = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, bytes_out, length_out, kCFAllocatorMalloc);
-        if (out)
-            *out = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, data_out, kCFPropertyListImmutable, NULL);
-        CFRelease(data_out);
-    } 
-    else
-        if (!status && out)
-            *out = NULL;
+bool securityd_send_sync_and_do(enum SecXPCOperation op, CFErrorRef *error,
+                                bool (^add_to_message)(xpc_object_t message, CFErrorRef* error),
+                                bool (^handle_response)(xpc_object_t response, CFErrorRef* error)) {
+    xpc_object_t message = securityd_create_message(op, error);
+    bool ok = false;
+    if (message) {
+        if (!add_to_message || add_to_message(message, error)) {
+            xpc_object_t response = securityd_message_with_reply_sync(message, error);
+            if (response) {
+                if (securityd_message_no_error(response, error)) {
+                    ok = (!handle_response || handle_response(response, error));
+                }
+                xpc_release(response);
+            }
+        }
+        xpc_release(message);
+    }
 
-    return status;     
+    return ok;
 }
 
+
 /* vi:set ts=4 sw=4 et: */