]> granicus.if.org Git - p11-kit/commitdiff
rpc: Implement execution of another tool to transport PKCS#11 RPC
authorStef Walter <stefw@gnome.org>
Wed, 20 Feb 2013 11:24:03 +0000 (12:24 +0100)
committerStef Walter <stef@thewalter.net>
Tue, 8 Jul 2014 06:57:31 +0000 (08:57 +0200)
p11-kit/Makefile.am
p11-kit/modules.c
p11-kit/rpc-transport.c [new file with mode: 0644]
p11-kit/rpc.h
p11-kit/tests/Makefile.am
p11-kit/tests/frob-server.c [new file with mode: 0644]
p11-kit/tests/mock-module-ep2.c [new file with mode: 0644]
p11-kit/tests/test-rpc.c
p11-kit/tests/test-transport.c [new file with mode: 0644]

index da195ac6d5781498c1e1d16a09464252f1a31bce..88883b5a31c836e7998080b43c62e96d5d7f9564 100644 (file)
@@ -36,8 +36,9 @@ MODULE_SRCS = \
        proxy.c proxy.h \
        private.h \
        messages.c \
+       rpc-transport.c rpc.h \
        rpc-message.c rpc-message.h \
-       rpc-client.c rpc-server.c rpc.h \
+       rpc-client.c rpc-server.c \
        uri.c \
        virtual.c virtual.h \
        $(inc_HEADERS)
index cc382b29f8366444a5c36dee28dc3ca28fab8926..7dbb6ed16f502f553c819ccc42149e74c745b0a5 100644 (file)
@@ -51,6 +51,7 @@
 #include "p11-kit.h"
 #include "private.h"
 #include "proxy.h"
+#include "rpc.h"
 #include "virtual.h"
 
 #include <sys/stat.h>
@@ -391,6 +392,36 @@ load_module_from_file_inlock (const char *name,
        return CKR_OK;
 }
 
+static CK_RV
+setup_module_for_remote_inlock (const char *name,
+                                const char *remote,
+                                Module **result)
+{
+       p11_rpc_transport *rpc;
+       Module *mod;
+
+       p11_debug ("remoting module %s using: %s", name, remote);
+
+       mod = alloc_module_unlocked ();
+       return_val_if_fail (mod != NULL, CKR_HOST_MEMORY);
+
+       rpc = p11_rpc_transport_new (&mod->virt, remote, name);
+       if (rpc == NULL) {
+               free_module_unlocked (mod);
+               return CKR_DEVICE_ERROR;
+       }
+
+       mod->loaded_module = rpc;
+       mod->loaded_destroy = p11_rpc_transport_free;
+
+       /* This takes ownership of the module */
+       if (!p11_dict_set (gl.modules, mod, mod))
+               return_val_if_reached (CKR_HOST_MEMORY);
+
+       *result = mod;
+       return CKR_OK;
+}
+
 static int
 is_list_delimiter (char ch)
 {
@@ -452,6 +483,7 @@ take_config_and_load_module_inlock (char **name,
                                     bool critical)
 {
        const char *filename;
+       const char *remote;
        Module *mod;
        CK_RV rv;
 
@@ -463,15 +495,30 @@ take_config_and_load_module_inlock (char **name,
        if (!is_module_enabled_unlocked (*name, *config))
                return CKR_OK;
 
-       filename = p11_dict_get (*config, "module");
-       if (filename == NULL) {
-               p11_debug ("no module path for module, skipping: %s", *name);
-               return CKR_OK;
-       }
+       remote = p11_dict_get (*config, "remote");
+       if (remote != NULL) {
+               rv = setup_module_for_remote_inlock (*name, remote, &mod);
+               if (rv != CKR_OK)
+                       return rv;
 
-       rv = load_module_from_file_inlock (*name, filename, &mod);
-       if (rv != CKR_OK)
-               return CKR_OK;
+       } else {
+               filename = p11_dict_get (*config, "module");
+               if (filename == NULL) {
+                       p11_debug ("no module path for module, skipping: %s", *name);
+                       return CKR_OK;
+               }
+
+               rv = load_module_from_file_inlock (*name, filename, &mod);
+               if (rv != CKR_OK)
+                       return CKR_OK;
+
+               /*
+                * We support setting of CK_C_INITIALIZE_ARGS.pReserved from
+                * 'x-init-reserved' setting in the config. This only works with specific
+                * PKCS#11 modules, and is non-standard use of that field.
+                */
+               mod->init_args.pReserved = p11_dict_get (*config, "x-init-reserved");
+       }
 
        /* Take ownership of thes evariables */
        p11_dict_free (mod->config);
@@ -482,13 +529,6 @@ take_config_and_load_module_inlock (char **name,
        *name = NULL;
        mod->critical = critical;
 
-       /*
-        * We support setting of CK_C_INITIALIZE_ARGS.pReserved from
-        * 'x-init-reserved' setting in the config. This only works with specific
-        * PKCS#11 modules, and is non-standard use of that field.
-        */
-       mod->init_args.pReserved = p11_dict_get (mod->config, "x-init-reserved");
-
        return CKR_OK;
 }
 
diff --git a/p11-kit/rpc-transport.c b/p11-kit/rpc-transport.c
new file mode 100644 (file)
index 0000000..0ff82d8
--- /dev/null
@@ -0,0 +1,850 @@
+/*
+ * Copyright (C) 2012 Stefan Walter
+ * Copyright (C) 2013 Red Hat Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *     * Redistributions of source code must retain the above
+ *       copyright notice, this list of conditions and the
+ *       following disclaimer.
+ *     * Redistributions in binary form must reproduce the
+ *       above copyright notice, this list of conditions and
+ *       the following disclaimer in the documentation and/or
+ *       other materials provided with the distribution.
+ *     * The names of contributors to this software may not be
+ *       used to endorse or promote products derived from this
+ *       software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Author: Stef Walter <stefw@gnome.org>
+ */
+
+#include "config.h"
+
+#include "argv.h"
+#include "compat.h"
+#define P11_DEBUG_FLAG P11_DEBUG_RPC
+#include "debug.h"
+#include "message.h"
+#include "pkcs11.h"
+#include "private.h"
+#include "rpc.h"
+#include "rpc-message.h"
+
+#include <sys/types.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#ifdef OS_UNIX
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/un.h>
+#include <signal.h>
+#include <unistd.h>
+#endif
+
+#ifdef OS_WIN32
+#include <winsock2.h>
+#endif
+
+typedef struct {
+       /* Never changes */
+       int fd;
+
+       /* Protected by the lock */
+       p11_mutex_t write_lock;
+       int refs;
+       int last_code;
+       bool sent_creds;
+
+       /* This data is protected by read mutex */
+       p11_mutex_t read_lock;
+       bool read_creds;
+       uint32_t read_code;
+       uint32_t read_olen;
+       uint32_t read_dlen;
+} rpc_socket;
+
+static rpc_socket *
+rpc_socket_new (int fd)
+{
+       rpc_socket *sock;
+
+       sock = calloc (1, sizeof (rpc_socket));
+       return_val_if_fail (sock != NULL, NULL);
+
+       sock->fd = fd;
+       sock->last_code = 0x10;
+       sock->read_creds = false;
+       sock->sent_creds = false;
+       sock->refs = 1;
+
+       p11_mutex_init (&sock->write_lock);
+       p11_mutex_init (&sock->read_lock);
+
+       return sock;
+}
+
+#if 0
+static rpc_socket *
+rpc_socket_ref (rpc_socket *sock)
+{
+       assert (sock != NULL);
+
+       p11_mutex_lock (&sock->write_lock);
+       sock->refs++;
+       p11_mutex_unlock (&sock->write_lock);
+
+       return sock;
+}
+
+static bool
+rpc_socket_is_open (rpc_socket *sock)
+{
+       assert (sock != NULL);
+       return sock->fd >= 0;
+}
+#endif
+
+static void
+rpc_socket_close (rpc_socket *sock)
+{
+       assert (sock != NULL);
+       if (sock->fd != -1)
+               close (sock->fd);
+       sock->fd = -1;
+}
+
+static void
+rpc_socket_unref (rpc_socket *sock)
+{
+       int release = 0;
+
+       assert (sock != NULL);
+
+       p11_mutex_lock (&sock->write_lock);
+       if (--sock->refs == 0)
+               release = 1;
+       p11_mutex_unlock (&sock->write_lock);
+
+       if (!release)
+               return;
+
+       assert (sock != NULL);
+       assert (sock->refs == 0);
+
+       rpc_socket_close (sock);
+       p11_mutex_uninit (&sock->write_lock);
+       p11_mutex_uninit (&sock->read_lock);
+}
+
+static bool
+write_all (int fd,
+           unsigned char* data,
+           size_t len)
+{
+       int r;
+
+       while (len > 0) {
+               r = write (fd, data, len);
+               if (r == -1) {
+                       if (errno == EPIPE) {
+                               p11_message ("couldn't send data: closed connection");
+                               return false;
+                       } else if (errno != EAGAIN && errno != EINTR) {
+                               p11_message_err (errno, "couldn't send data");
+                               return false;
+                       }
+               } else {
+                       p11_debug ("wrote %d bytes", r);
+                       data += r;
+                       len -= r;
+               }
+       }
+
+       return true;
+}
+
+static bool
+read_all (int fd,
+          unsigned char* data,
+          size_t len)
+{
+       int r;
+
+       while (len > 0) {
+               r = read (fd, data, len);
+               if (r == 0) {
+                       p11_message ("couldn't receive data: closed connection");
+                       return false;
+               } else if (r == -1) {
+                       if (errno != EAGAIN && errno != EINTR) {
+                               p11_message_err (errno, "couldn't receive data");
+                               return false;
+                       }
+               } else {
+                       p11_debug ("read %d bytes", r);
+                       data += r;
+                       len -= r;
+               }
+       }
+
+       return true;
+}
+
+static CK_RV
+rpc_socket_write_inlock (rpc_socket *sock,
+                         int code,
+                         p11_buffer *options,
+                         p11_buffer *buffer)
+{
+       unsigned char header[12];
+       unsigned char dummy = '\0';
+
+       /* The socket is locked and referenced at this point */
+       assert (buffer != NULL);
+
+       /* Place holder byte, will later carry unix credentials (on some systems) */
+       if (!sock->sent_creds) {
+               if (write_all (sock->fd, &dummy, 1) != 1) {
+                       p11_message_err (errno, "couldn't send socket credentials");
+                       return CKR_DEVICE_ERROR;
+               }
+               sock->sent_creds = true;
+       }
+
+       p11_rpc_buffer_encode_uint32 (header, code);
+       p11_rpc_buffer_encode_uint32 (header + 4, options->len);
+       p11_rpc_buffer_encode_uint32 (header + 8, buffer->len);
+
+       if (!write_all (sock->fd, header, 12) ||
+           !write_all (sock->fd, options->data, options->len) ||
+           !write_all (sock->fd, buffer->data, buffer->len))
+               return CKR_DEVICE_ERROR;
+
+       return CKR_OK;
+}
+
+static p11_rpc_status
+write_at (int fd,
+          unsigned char *data,
+          size_t len,
+          size_t offset,
+          size_t *at)
+{
+       p11_rpc_status status;
+       ssize_t num;
+       size_t from;
+       int errn;
+
+       assert (*at >= offset);
+
+       if (*at >= offset + len)
+               return P11_RPC_OK;
+
+       from = *at - offset;
+       assert (from < len);
+
+       num = write (fd, data + from, len - from);
+       errn = errno;
+
+       /* Update state */
+       if (num > 0)
+               *at += num;
+
+       /* Completely written out this block */
+       if (num == len - from) {
+               p11_debug ("ok: wrote block of %d", (int)num);
+               status = P11_RPC_OK;
+
+       /* Partially written out this block */
+       } else if (num >= 0) {
+               p11_debug ("again: partial read of %d", (int)num);
+               status = P11_RPC_AGAIN;
+
+       /* Didn't write out block due to transient issue */
+       } else if (errn == EINTR || errn == EAGAIN || errn == EWOULDBLOCK) {
+               p11_debug ("again: due to %d", errn);
+               status = P11_RPC_AGAIN;
+
+       /* Failure */
+       } else {
+               p11_debug ("error: due to %d", errn);
+               status = P11_RPC_ERROR;
+       }
+
+       errno = errn;
+       return status;
+}
+
+p11_rpc_status
+p11_rpc_transport_write (int fd,
+                         size_t *state,
+                         int call_code,
+                         p11_buffer *options,
+                         p11_buffer *buffer)
+{
+       unsigned char header[12] = { 0, };
+       p11_rpc_status status;
+
+       assert (state != NULL);
+       assert (options != NULL);
+       assert (buffer != NULL);
+
+       if (*state < 12) {
+               p11_rpc_buffer_encode_uint32 (header, call_code);
+               p11_rpc_buffer_encode_uint32 (header + 4, options->len);
+               p11_rpc_buffer_encode_uint32 (header + 8, buffer->len);
+       }
+
+       status = write_at (fd, header, 12, 0, state);
+
+       if (status == P11_RPC_OK) {
+               status = write_at (fd, options->data, options->len,
+                                  12, state);
+       }
+
+       if (status == P11_RPC_OK) {
+               status = write_at (fd, buffer->data, buffer->len,
+                                  12 + options->len, state);
+       }
+
+       /* All done */
+       if (status == P11_RPC_OK)
+               *state = 0;
+
+       return status;
+}
+
+static int
+rpc_socket_read (rpc_socket *sock,
+                 int *code,
+                 p11_buffer *buffer)
+{
+       CK_RV ret = CKR_DEVICE_ERROR;
+       unsigned char header[12];
+       unsigned char dummy;
+       fd_set rfds;
+
+       assert (code != NULL);
+       assert (buffer != NULL);
+
+       /*
+        * We are not in the main socket lock here, but the socket
+        * is referenced, and won't go away
+        */
+
+       p11_mutex_lock (&sock->read_lock);
+
+       if (!sock->read_creds) {
+               if (read_all (sock->fd, &dummy, 1) != 1)
+                       return CKR_DEVICE_ERROR;
+               sock->read_creds = true;
+       }
+
+       for (;;) {
+               /* No message header has been read yet? ... read one in */
+               if (sock->read_code == 0) {
+                       if (!read_all (sock->fd, header, 12))
+                               break;
+
+                       /* Decode and check the message header */
+                       sock->read_code = p11_rpc_buffer_decode_uint32 (header);
+                       sock->read_olen = p11_rpc_buffer_decode_uint32 (header + 4);
+                       sock->read_dlen = p11_rpc_buffer_decode_uint32 (header + 8);
+                       if (sock->read_code == 0) {
+                               p11_message ("received invalid rpc header values: perhaps wrong protocol");
+                               break;
+                       }
+               }
+
+               /* If it's our header (or caller doesn't care), then yay! */
+               if (*code == -1 || sock->read_code == *code) {
+
+                       /* We ignore the options, so read into the same as buffer */
+                       if (!p11_buffer_reset (buffer, sock->read_olen) ||
+                           !p11_buffer_reset (buffer, sock->read_dlen)) {
+                               warn_if_reached ();
+                               break;
+                       }
+
+                       /* Read in the the options first, and then data */
+                       if (!read_all (sock->fd, buffer->data, sock->read_olen) ||
+                           !read_all (sock->fd, buffer->data, sock->read_dlen))
+                               break;
+
+                       buffer->len = sock->read_dlen;
+                       *code = sock->read_code;
+
+                       /* Yay, we got our data, off we go */
+                       sock->read_code = 0;
+                       sock->read_olen = 0;
+                       sock->read_dlen = 0;
+                       ret = CKR_OK;
+                       break;
+               }
+
+               /* Give another thread the chance to read data for this header */
+               if (sock->read_code != 0) {
+                       p11_debug ("received header in wrong thread");
+                       p11_mutex_unlock (&sock->read_lock);
+
+                       /* Used as a simple wait */
+                       FD_ZERO (&rfds);
+                       FD_SET (sock->fd, &rfds);
+                       if (select (sock->fd + 1, &rfds, NULL, NULL, NULL) < 0)
+                               p11_message ("couldn't use select to wait on rpc socket");
+
+                       p11_mutex_lock (&sock->read_lock);
+               }
+       }
+
+       p11_mutex_unlock (&sock->read_lock);
+       return ret;
+}
+
+static p11_rpc_status
+read_at (int fd,
+         unsigned char *data,
+         size_t len,
+         size_t offset,
+         size_t *at)
+{
+       p11_rpc_status status;
+       int errn;
+       ssize_t num;
+       size_t from;
+
+       assert (*at >= offset);
+
+       if (*at >= offset + len)
+               return P11_RPC_OK;
+
+       from = *at - offset;
+       assert (from < len);
+
+       num = read (fd, data + from, len - from);
+       errn = errno;
+
+       /* Update state */
+       if (num > 0)
+               *at += num;
+
+       /* Completely read out this block */
+       if (num == len - from) {
+               p11_debug ("ok: read block of %d", (int)num);
+               status = P11_RPC_OK;
+
+       /* Partially read out this block */
+       } else if (num > 0) {
+               p11_debug ("again: partial read of %d", (int)num);
+               status = P11_RPC_AGAIN;
+
+       /* End of file, valid if at offset zero */
+       } else if (num == 0) {
+               if (offset == 0) {
+                       p11_debug ("eof: read zero bytes");
+                       status = P11_RPC_EOF;
+               } else {
+                       p11_debug ("error: early truncate");
+                       errn = EPROTO;
+                       status = P11_RPC_ERROR;
+               }
+
+       /* Didn't read out block due to transient issue */
+       } else if (errn == EINTR || errn == EAGAIN || errn == EWOULDBLOCK) {
+               p11_debug ("again: due to %d", errn);
+               status = P11_RPC_AGAIN;
+
+       /* Failure */
+       } else {
+               p11_debug ("error: due to %d", errn);
+               status = P11_RPC_ERROR;
+       }
+
+       errno = errn;
+       return status;
+}
+
+p11_rpc_status
+p11_rpc_transport_read (int fd,
+                        size_t *state,
+                        int *call_code,
+                        p11_buffer *options,
+                        p11_buffer *buffer)
+{
+       unsigned char *header;
+       p11_rpc_status status;
+       size_t len;
+
+       assert (state != NULL);
+       assert (call_code != NULL);
+       assert (options != NULL);
+       assert (buffer != NULL);
+
+       /* Reading the header, we read it into @buffer */
+       if (*state < 12) {
+               if (!p11_buffer_reset (buffer, 12))
+                       return_val_if_reached (P11_RPC_ERROR);
+               status = read_at (fd, buffer->data, 12, 0, state);
+               if (status != P11_RPC_OK)
+                       return status;
+
+               /* Parse out the header */
+               header = buffer->data;
+               *call_code = p11_rpc_buffer_decode_uint32 (header);
+               len = p11_rpc_buffer_decode_uint32 (header + 4);
+               if (!p11_buffer_reset (options, len))
+                       return_val_if_reached (P11_RPC_ERROR);
+               options->len = len;
+               len = p11_rpc_buffer_decode_uint32 (header + 8);
+               if (!p11_buffer_reset (buffer, len))
+                       return_val_if_reached (P11_RPC_ERROR);
+               buffer->len = len;
+       }
+
+       /* At this point options has a valid len field */
+       status = read_at (fd, options->data, options->len, 12, state);
+       if (status == P11_RPC_OK) {
+               status = read_at (fd, buffer->data, buffer->len,
+                                 12 + options->len, state);
+       }
+
+       if (status == P11_RPC_OK)
+               *state = 0;
+
+       return status;
+}
+
+struct _p11_rpc_transport {
+       p11_rpc_client_vtable vtable;
+       p11_destroyer destroyer;
+       rpc_socket *socket;
+       p11_buffer options;
+};
+
+static void
+on_rpc_disconnect (p11_rpc_client_vtable *vtable,
+                   void *init_reserved)
+{
+       p11_rpc_transport *rpc = (p11_rpc_transport *)vtable;
+
+       if (rpc->socket)
+               rpc_socket_unref (rpc->socket);
+       rpc->socket = NULL;
+}
+
+static bool
+rpc_transport_init (p11_rpc_transport *rpc,
+                    const char *module_name,
+                    p11_destroyer destroyer)
+{
+       rpc->destroyer = destroyer;
+
+       p11_buffer_init_null (&rpc->options, 0);
+       p11_buffer_add (&rpc->options, module_name, -1);
+       return_val_if_fail (p11_buffer_ok (&rpc->options), false);
+
+       return true;
+}
+
+static void
+rpc_transport_uninit (p11_rpc_transport *rpc)
+{
+       p11_buffer_uninit (&rpc->options);
+}
+
+static CK_RV
+on_rpc_transport (p11_rpc_client_vtable *vtable,
+                  p11_buffer *request,
+                  p11_buffer *response)
+{
+       p11_rpc_transport *rpc = (p11_rpc_transport *)vtable;
+       CK_RV rv = CKR_OK;
+       rpc_socket *sock;
+       int call_code;
+
+       assert (rpc != NULL);
+       assert (request != NULL);
+       assert (response != NULL);
+
+       sock = rpc->socket;
+       assert (sock != NULL);
+
+       p11_mutex_lock (&sock->write_lock);
+       assert (sock->refs > 0);
+       sock->refs++;
+
+       /* Get the next socket reply code */
+       call_code = sock->last_code++;
+
+       if (sock->fd == -1)
+               rv = CKR_DEVICE_ERROR;
+       if (rv == CKR_OK)
+               rv = rpc_socket_write_inlock (sock, call_code, &rpc->options, request);
+
+       /* We unlock the socket mutex while reading a response */
+       if (rv == CKR_OK) {
+               p11_mutex_unlock (&sock->write_lock);
+
+               rv = rpc_socket_read (sock, &call_code, response);
+
+               p11_mutex_lock (&sock->write_lock);
+       }
+
+       if (rv != CKR_OK && sock->fd != -1) {
+               p11_message ("closing socket due to protocol failure");
+               close (sock->fd);
+               sock->fd = -1;
+       }
+
+       sock->refs--;
+       assert (sock->refs > 0);
+       p11_mutex_unlock (&sock->write_lock);
+
+       return rv;
+}
+
+#ifdef OS_UNIX
+
+typedef struct {
+       p11_rpc_transport base;
+       p11_array *argv;
+       pid_t pid;
+} rpc_exec;
+
+static void
+wait_or_terminate (pid_t pid)
+{
+       bool terminated = false;
+       int status;
+       int sig;
+       int ret;
+       int i;
+
+
+       for (i = 0; i < 3 * 1000; i += 100) {
+               ret = waitpid (pid, &status, WNOHANG);
+               if (ret != 0)
+                       break;
+               p11_sleep_ms (100);
+       }
+
+       if (ret == 0) {
+               p11_message ("process %d did not exit, terminating", (int)pid);
+               kill (pid, SIGTERM);
+               terminated = true;
+               ret = waitpid (pid, &status, 0);
+       }
+
+       if (ret < 0) {
+               p11_message_err (errno, "failed to wait for executed child: %d", (int)pid);
+               status = 0;
+       } else if (WIFEXITED (status)) {
+               status = WEXITSTATUS (status);
+               if (status == 0)
+                       p11_debug ("process %d exited with status 0", (int)pid);
+               else
+                       p11_message ("process %d exited with status %d", (int)pid, status);
+       } else if (WIFSIGNALED (status)) {
+               sig = WTERMSIG (status);
+               if (!terminated || sig != SIGTERM)
+                       p11_message ("process %d was terminated with signal %d", (int)pid, sig);
+       }
+}
+
+static void
+on_rpc_exec_disconnect (p11_rpc_client_vtable *vtable,
+                        void *fini_reserved)
+{
+       rpc_exec *rex = (rpc_exec *)vtable;
+
+       if (rex->base.socket)
+               rpc_socket_close (rex->base.socket);
+
+       if (rex->pid)
+               wait_or_terminate (rex->pid);
+       rex->pid = 0;
+
+       /* Do the common disconnect stuff */
+       on_rpc_disconnect (vtable, fini_reserved);
+}
+
+static int
+set_cloexec_on_fd (void *data,
+                   int fd)
+{
+       int *max_fd = data;
+       if (fd >= *max_fd)
+               fcntl (fd, F_SETFD, FD_CLOEXEC);
+       return 0;
+}
+
+static CK_RV
+on_rpc_exec_connect (p11_rpc_client_vtable *vtable,
+                     void *init_reserved)
+{
+       rpc_exec *rex = (rpc_exec *)vtable;
+       pid_t pid;
+       int max_fd;
+       int fds[2];
+       int errn;
+
+       p11_debug ("executing rpc transport: %s", (char *)rex->argv->elem[0]);
+
+       if (socketpair (AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
+               p11_message_err (errno, "failed to create pipe for remote");
+               return CKR_DEVICE_ERROR;
+       }
+
+       pid = fork ();
+       switch (pid) {
+
+       /* Failure */
+       case -1:
+               close (fds[0]);
+               close (fds[1]);
+               p11_message_err (errno, "failed to fork for remote");
+               return CKR_DEVICE_ERROR;
+
+       /* Child */
+       case 0:
+               if (dup2 (fds[1], STDIN_FILENO) < 0 ||
+                   dup2 (fds[1], STDOUT_FILENO) < 0) {
+                       errn = errno;
+                       p11_message_err (errn, "couldn't dup file descriptors in remote child");
+                       _exit (errn);
+               }
+
+               /* Close file descriptors, except for above on exec */
+               max_fd = STDERR_FILENO + 1;
+               fdwalk (set_cloexec_on_fd, &max_fd);
+               execvp (rex->argv->elem[0], (char **)rex->argv->elem);
+
+               errn = errno;
+               p11_message_err (errn, "couldn't execute program for rpc: %s",
+                                (char *)rex->argv->elem[0]);
+               _exit (errn);
+
+       /* The parent */
+       default:
+               break;
+       }
+
+       close (fds[1]);
+       rex->pid = pid;
+       rex->base.socket = rpc_socket_new (fds[0]);
+       return_val_if_fail (rex->base.socket != NULL, CKR_GENERAL_ERROR);
+
+       return CKR_OK;
+}
+
+static void
+rpc_exec_free (void *data)
+{
+       rpc_exec *rex = data;
+       on_rpc_exec_disconnect (data, NULL);
+       rpc_transport_uninit (&rex->base);
+       p11_array_free (rex->argv);
+       free (rex);
+}
+
+static void
+on_argv_parsed (char *argument,
+                void *data)
+{
+       p11_array *argv = data;
+
+       if (!p11_array_push (argv, strdup (argument)))
+               return_if_reached ();
+}
+
+static p11_rpc_transport *
+rpc_exec_init (const char *remote,
+               const char *name)
+{
+       p11_array *argv;
+       rpc_exec *rex;
+
+       argv = p11_array_new (free);
+       if (!p11_argv_parse (remote, on_argv_parsed, argv) || argv->num < 1) {
+               p11_message ("invalid remote command line: %s", remote);
+               p11_array_free (argv);
+               return NULL;
+       }
+
+       rex = calloc (1, sizeof (rpc_exec));
+       return_val_if_fail (rex != NULL, NULL);
+
+       p11_array_push (argv, NULL);
+       rex->argv = argv;
+
+       rex->base.vtable.connect = on_rpc_exec_connect;
+       rex->base.vtable.disconnect = on_rpc_exec_disconnect;
+       rex->base.vtable.transport = on_rpc_transport;
+       rpc_transport_init (&rex->base, name, rpc_exec_free);
+
+       p11_debug ("initialized rpc exec: %s", remote);
+       return &rex->base;
+}
+
+#endif /* OS_UNIX */
+
+p11_rpc_transport *
+p11_rpc_transport_new (p11_virtual *virt,
+                       const char *remote,
+                       const char *name)
+{
+       p11_rpc_transport *rpc;
+
+       return_val_if_fail (virt != NULL, NULL);
+       return_val_if_fail (remote != NULL, NULL);
+       return_val_if_fail (name != NULL, NULL);
+
+#ifdef OS_UNIX
+       /* For now we assume it's all a command line */
+       rpc = rpc_exec_init (remote, name);
+
+#else /* !OS_WIN32 */
+       rpc = NULL;
+       p11_message ("Windows not yet supported for remote");
+
+#endif /* OS_WIN32 */
+
+       if (!rpc)
+               return NULL;
+
+       if (!p11_rpc_client_init (virt, &rpc->vtable))
+               return_val_if_reached (NULL);
+
+       return rpc;
+}
+
+void
+p11_rpc_transport_free (void *data)
+{
+       p11_rpc_transport *rpc = data;
+
+       if (rpc != NULL) {
+               assert (rpc->destroyer);
+               (rpc->destroyer) (data);
+       }
+}
index a86e7967734251abf60459c43db6cbb8b3d52547..b129e6108170042a0d9c0701f70ce802b3c7f855 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2012 Stefan Walter
+ * Copyright (C) 2013 Stefan Walter
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -32,8 +33,8 @@
  * Author: Stef Walter <stefw@gnome.org>
  */
 
-#ifndef __P11_KIT_RPC_H__
-#define __P11_KIT_RPC_H__
+#ifndef __P11_RPC_H__
+#define __P11_RPC_H__
 
 #include "pkcs11.h"
 #include "buffer.h"
@@ -53,8 +54,6 @@ struct _p11_rpc_client_vtable {
 
        void        (* disconnect)    (p11_rpc_client_vtable *vtable,
                                       void *fini_reserved);
-
-       void *reserved[16];
 };
 
 bool                   p11_rpc_client_init         (p11_virtual *virt,
@@ -66,4 +65,31 @@ bool                   p11_rpc_server_handle       (CK_X_FUNCTION_LIST *funcs,
 
 extern CK_MECHANISM_TYPE *  p11_rpc_mechanisms_override_supported;
 
-#endif /* __P11_KIT_RPC_H__ */
+typedef struct _p11_rpc_transport p11_rpc_transport;
+
+p11_rpc_transport *    p11_rpc_transport_new       (p11_virtual *virt,
+                                                    const char *remote,
+                                                    const char *name);
+
+void                   p11_rpc_transport_free      (void *transport);
+
+typedef enum {
+       P11_RPC_OK,
+       P11_RPC_EOF,
+       P11_RPC_AGAIN,
+       P11_RPC_ERROR
+} p11_rpc_status;
+
+p11_rpc_status         p11_rpc_transport_read      (int fd,
+                                                    size_t *state,
+                                                    int *call_code,
+                                                    p11_buffer *options,
+                                                    p11_buffer *buffer);
+
+p11_rpc_status         p11_rpc_transport_write     (int fd,
+                                                    size_t *state,
+                                                    int call_code,
+                                                    p11_buffer *options,
+                                                    p11_buffer *buffer);
+
+#endif /* __P11_RPC_H__ */
index 2192fed860e9953efd0825ab45c2fd5d14a528c4..0672d620ea22f62eb75f2e9946937c55d6aa3591 100644 (file)
@@ -29,20 +29,24 @@ CHECK_PROGS = \
        test-rpc \
        $(NULL)
 
+noinst_PROGRAMS = \
+       print-messages \
+       frob-setuid \
+       $(CHECK_PROGS)
+
 if WITH_FFI
 
 CHECK_PROGS += \
        test-virtual \
        test-managed \
        test-log \
+       test-transport \
        $(NULL)
 
-endif
+noinst_PROGRAMS += \
+       frob-server
 
-noinst_PROGRAMS = \
-       print-messages \
-       frob-setuid \
-       $(CHECK_PROGS)
+endif
 
 TESTS = $(CHECK_PROGS)
 
@@ -67,7 +71,9 @@ mock_one_la_LDFLAGS = \
        -module -avoid-version -rpath /nowhere \
        -no-undefined -export-symbols-regex 'C_GetFunctionList'
 
-mock_two_la_SOURCES = $(mock_one_la_SOURCES)
+mock_two_la_SOURCES = \
+       mock-module-ep2.c
+
 mock_two_la_CFLAGS = $(mock_one_la_CFLAGS)
 mock_two_la_LDFLAGS = $(mock_one_la_LDFLAGS)
 mock_two_la_LIBADD = $(mock_one_la_LIBADD)
@@ -78,6 +84,7 @@ mock_three_la_LDFLAGS = $(mock_one_la_LDFLAGS)
 mock_three_la_LIBADD = $(mock_one_la_LIBADD)
 
 mock_four_la_SOURCES = $(mock_one_la_SOURCES)
+mock_four_la_CFLAGS = $(mock_one_la_CFLAGS)
 mock_four_la_LDFLAGS = $(mock_one_la_LDFLAGS)
 mock_four_la_LIBADD = $(mock_one_la_LIBADD)
 
diff --git a/p11-kit/tests/frob-server.c b/p11-kit/tests/frob-server.c
new file mode 100644 (file)
index 0000000..e0e7020
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2013 Red Hat Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *     * Redistributions of source code must retain the above
+ *       copyright notice, this list of conditions and the
+ *       following disclaimer.
+ *     * Redistributions in binary form must reproduce the
+ *       above copyright notice, this list of conditions and
+ *       the following disclaimer in the documentation and/or
+ *       other materials provided with the distribution.
+ *     * The names of contributors to this software may not be
+ *       used to endorse or promote products derived from this
+ *       software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Author: Stef Walter <stefw@redhat.com>
+ */
+
+#include "config.h"
+
+#include "buffer.h"
+#include "compat.h"
+#include "debug.h"
+#include "p11-kit.h"
+#include "rpc.h"
+#include "virtual.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int
+main (int argc,
+      char *argv[])
+{
+       CK_FUNCTION_LIST *funcs;
+       CK_C_GetFunctionList gfl;
+       p11_rpc_status status;
+       unsigned char version;
+       p11_virtual virt;
+       p11_buffer options;
+       p11_buffer buffer;
+       dl_module_t dl;
+       size_t state;
+       int code;
+       CK_RV rv;
+
+       p11_debug_init ();
+
+       if (argc != 2) {
+               fprintf (stderr, "usage: frob-server module\n");
+               exit (2);
+       }
+
+       dl = p11_dl_open (argv[1]);
+       if (dl == NULL) {
+               fprintf (stderr, "couldn't load module: %s: %s\n",
+                        argv[1], p11_dl_error ());
+               exit (1);
+       }
+
+       gfl = p11_dl_symbol (dl, "C_GetFunctionList");
+       if (!gfl) {
+               fprintf (stderr, "couldn't find C_GetFunctionList entry point in module: %s: %s\n",
+                        argv[1], p11_dl_error ());
+               exit (1);
+       }
+
+       rv = gfl (&funcs);
+       if (rv != CKR_OK) {
+               fprintf (stderr, "call to C_GetFunctiontList failed in module: %s: %s\n",
+                        argv[1], p11_kit_strerror (rv));
+               exit (1);
+       }
+
+       p11_virtual_init (&virt, &p11_virtual_base, funcs, NULL);
+       p11_buffer_init (&options, 0);
+       p11_buffer_init (&buffer, 0);
+
+       switch (read (0, &version, 1)) {
+       case 0:
+               status = P11_RPC_EOF;
+               break;
+       case 1:
+               if (version != 0) {
+                       fprintf (stderr, "unspported version received: %d", (int)version);
+                       exit (1);
+               }
+               break;
+       default:
+               fprintf (stderr, "couldn't read creds: %s", strerror (errno));
+               exit (1);
+       }
+
+       version = 0;
+       switch (write (1, &version, 1)) {
+       case 1:
+               break;
+       default:
+               fprintf (stderr, "couldn't read creds: %s", strerror (errno));
+               exit (1);
+       }
+
+       status = P11_RPC_OK;
+       while (status == P11_RPC_OK) {
+               state = 0;
+               code = 0;
+
+               do {
+                       status = p11_rpc_transport_read (0, &state, &code,
+                                                        &options, &buffer);
+               } while (status == P11_RPC_AGAIN);
+
+               switch (status) {
+               case P11_RPC_OK:
+                       break;
+               case P11_RPC_EOF:
+                       continue;
+               case P11_RPC_AGAIN:
+                       assert_not_reached ();
+               case P11_RPC_ERROR:
+                       fprintf (stderr, "failed to read rpc message: %s\n", strerror (errno));
+                       exit (1);
+               }
+
+               if (!p11_rpc_server_handle (&virt.funcs, &buffer, &buffer)) {
+                       fprintf (stderr, "unexpected error handling rpc message\n");
+                       exit (1);
+               }
+
+               state = 0;
+               options.len = 0;
+               do {
+                       status = p11_rpc_transport_write (1, &state, code,
+                                                         &options, &buffer);
+               } while (status == P11_RPC_AGAIN);
+
+               switch (status) {
+               case P11_RPC_OK:
+                       break;
+               case P11_RPC_EOF:
+               case P11_RPC_AGAIN:
+                       assert_not_reached ();
+               case P11_RPC_ERROR:
+                       fprintf (stderr, "failed to write rpc message: %s\n", strerror (errno));
+                       exit (1);
+               }
+       }
+
+       p11_buffer_uninit (&buffer);
+       p11_buffer_uninit (&options);
+       p11_dl_close (dl);
+
+       return 0;
+}
diff --git a/p11-kit/tests/mock-module-ep2.c b/p11-kit/tests/mock-module-ep2.c
new file mode 100644 (file)
index 0000000..ee71711
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2012 Stefan Walter
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *     * Redistributions of source code must retain the above
+ *       copyright notice, this list of conditions and the
+ *       following disclaimer.
+ *     * Redistributions in binary form must reproduce the
+ *       above copyright notice, this list of conditions and
+ *       the following disclaimer in the documentation and/or
+ *       other materials provided with the distribution.
+ *     * The names of contributors to this software may not be
+ *       used to endorse or promote products derived from this
+ *       software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Author: Stef Walter <stef@thewalter.net>
+ */
+
+#include "config.h"
+
+#define CRYPTOKI_EXPORTS 1
+#include "pkcs11.h"
+
+#include "mock.h"
+
+#include <stdio.h>
+
+#ifdef OS_WIN32
+__declspec(dllexport)
+#endif
+CK_RV
+C_GetFunctionList (CK_FUNCTION_LIST_PTR_PTR list)
+{
+       mock_module_init ();
+       mock_module.C_GetFunctionList = C_GetFunctionList;
+       if (list == NULL)
+               return CKR_ARGUMENTS_BAD;
+       *list = &mock_module;
+       return CKR_OK;
+}
index d945efda3c2dea68d24c07fab552bebcb2a40c76..0ce2c553cfb2b834e30e936af21eb8fc16e26664 100644 (file)
@@ -47,6 +47,7 @@
 #include "virtual.h"
 
 #include <sys/types.h>
+#include <sys/wait.h>
 #include <assert.h>
 #include <string.h>
 #include <stdio.h>
@@ -350,15 +351,17 @@ test_byte_array_static (void)
 }
 
 static p11_virtual base;
-static bool rpc_initialized = false;
+static pid_t rpc_initialized = 0;
 
 static CK_RV
 rpc_initialize (p11_rpc_client_vtable *vtable,
                 void *init_reserved)
 {
+       pid_t pid = getpid ();
+
        assert_str_eq (vtable->data, "vtable-data");
-       assert_num_eq (false, rpc_initialized);
-       rpc_initialized = true;
+       assert_num_cmp (pid, !=, rpc_initialized);
+       rpc_initialized = pid;
 
        return CKR_OK;
 }
@@ -367,8 +370,10 @@ static CK_RV
 rpc_initialize_fails (p11_rpc_client_vtable *vtable,
                       void *init_reserved)
 {
+       pid_t pid = getpid ();
+
        assert_str_eq (vtable->data, "vtable-data");
-       assert_num_eq (false, rpc_initialized);
+       assert_num_cmp (pid, !=, rpc_initialized);
        return CKR_FUNCTION_FAILED;
 }
 
@@ -376,8 +381,10 @@ static CK_RV
 rpc_initialize_device_removed (p11_rpc_client_vtable *vtable,
                                void *init_reserved)
 {
+       pid_t pid = getpid ();
+
        assert_str_eq (vtable->data, "vtable-data");
-       assert_num_eq (false, rpc_initialized);
+       assert_num_cmp (pid, !=, rpc_initialized);
        return CKR_DEVICE_REMOVED;
 }
 
@@ -401,21 +408,24 @@ static void
 rpc_finalize (p11_rpc_client_vtable *vtable,
               void *fini_reserved)
 {
+       pid_t pid = getpid ();
+
        assert_str_eq (vtable->data, "vtable-data");
-       assert_num_eq (true, rpc_initialized);
-       rpc_initialized = false;
+       assert_num_cmp (pid, ==, rpc_initialized);
+       rpc_initialized = 0;
 }
 
 static void
 test_initialize (void)
 {
        p11_rpc_client_vtable vtable = { "vtable-data", rpc_initialize, rpc_transport, rpc_finalize };
+       pid_t pid = getpid ();
        p11_virtual mixin;
        bool ret;
        CK_RV rv;
 
        /* Build up our own function list */
-       rpc_initialized = false;
+       rpc_initialized = 0;
        p11_virtual_init (&base, &p11_virtual_base, &mock_module_no_slots, NULL);
 
        ret = p11_rpc_client_init (&mixin, &vtable);
@@ -423,11 +433,11 @@ test_initialize (void)
 
        rv = mixin.funcs.C_Initialize (&mixin.funcs, NULL);
        assert (rv == CKR_OK);
-       assert_num_eq (true, rpc_initialized);
+       assert_num_eq (pid, rpc_initialized);
 
        rv = mixin.funcs.C_Finalize (&mixin.funcs, NULL);
        assert (rv == CKR_OK);
-       assert_num_eq (false, rpc_initialized);
+       assert_num_cmp (pid, !=, rpc_initialized);
 
        p11_virtual_uninit (&mixin);
 }
@@ -442,7 +452,7 @@ test_not_initialized (void)
        CK_RV rv;
 
        /* Build up our own function list */
-       rpc_initialized = false;
+       rpc_initialized = 0;
        p11_virtual_init (&base, &p11_virtual_base, &mock_module_no_slots, NULL);
 
        ret = p11_rpc_client_init (&mixin, &vtable);
@@ -463,7 +473,7 @@ test_initialize_fails_on_client (void)
        CK_RV rv;
 
        /* Build up our own function list */
-       rpc_initialized = false;
+       rpc_initialized = 0;
        p11_virtual_init (&base, &p11_virtual_base, &mock_module_no_slots, NULL);
 
        ret = p11_rpc_client_init (&mixin, &vtable);
@@ -471,7 +481,7 @@ test_initialize_fails_on_client (void)
 
        rv = (mixin.funcs.C_Initialize) (&mixin.funcs, NULL);
        assert (rv == CKR_FUNCTION_FAILED);
-       assert_num_eq (false, rpc_initialized);
+       assert_num_eq (0, rpc_initialized);
 
        p11_virtual_uninit (&mixin);
 }
@@ -493,7 +503,7 @@ test_transport_fails (void)
        CK_RV rv;
 
        /* Build up our own function list */
-       rpc_initialized = false;
+       rpc_initialized = 0;
        p11_virtual_init (&base, &p11_virtual_base, &mock_module_no_slots, NULL);
 
        ret = p11_rpc_client_init (&mixin, &vtable);
@@ -501,7 +511,7 @@ test_transport_fails (void)
 
        rv = (mixin.funcs.C_Initialize) (&mixin.funcs, NULL);
        assert (rv == CKR_FUNCTION_REJECTED);
-       assert_num_eq (false, rpc_initialized);
+       assert_num_eq (0, rpc_initialized);
 
        p11_virtual_uninit (&mixin);
 }
@@ -523,7 +533,7 @@ test_initialize_fails_on_server (void)
 
        rv = (mixin.funcs.C_Initialize) (&mixin.funcs, NULL);
        assert (rv == CKR_FUNCTION_FAILED);
-       assert_num_eq (false, rpc_initialized);
+       assert_num_eq (0, rpc_initialized);
 
        p11_virtual_uninit (&mixin);
 }
@@ -555,7 +565,7 @@ test_transport_bad_parse (void)
        CK_RV rv;
 
        /* Build up our own function list */
-       rpc_initialized = false;
+       rpc_initialized = 0;
        p11_virtual_init (&base, &p11_virtual_base, &mock_module_no_slots, NULL);
 
        ret = p11_rpc_client_init (&mixin, &vtable);
@@ -886,6 +896,116 @@ test_get_slot_list_no_device (void)
        teardown_mock_module (rpc_module);
 }
 
+static void *
+invoke_in_thread (void *arg)
+{
+       CK_FUNCTION_LIST *rpc_module = arg;
+       CK_INFO info;
+       CK_RV rv;
+
+       rv = (rpc_module->C_GetInfo) (&info);
+       assert_num_eq (rv, CKR_OK);
+
+       assert (memcmp (info.manufacturerID, MOCK_INFO.manufacturerID,
+                       sizeof (info.manufacturerID)) == 0);
+
+       return NULL;
+}
+
+static p11_mutex_t delay_mutex;
+
+static CK_RV
+delayed_C_GetInfo (CK_INFO_PTR info)
+{
+       CK_RV rv;
+
+       p11_sleep_ms (rand () % 100);
+
+       p11_mutex_lock (&delay_mutex);
+       rv = mock_C_GetInfo (info);
+       p11_mutex_unlock (&delay_mutex);
+
+       return rv;
+}
+
+static void
+test_simultaneous_functions (void)
+{
+       CK_FUNCTION_LIST real_module;
+       CK_FUNCTION_LIST *rpc_module;
+       const int num_threads = 128;
+       p11_thread_t threads[num_threads];
+       int i, ret;
+
+       p11_mutex_init (&delay_mutex);
+
+       memcpy (&real_module, &mock_module_no_slots, sizeof (CK_FUNCTION_LIST));
+       real_module.C_GetInfo = delayed_C_GetInfo;
+
+       rpc_module = setup_test_rpc_module (&test_normal_vtable,
+                                           &real_module, NULL);
+
+       /* Make the invoked function (above) wait */
+       p11_mutex_lock (&delay_mutex);
+
+       for (i = 0; i < num_threads; i++) {
+               ret = p11_thread_create (threads + i, invoke_in_thread, rpc_module);
+               assert_num_eq (0, ret);
+       }
+
+       /* Let the invoked functions return */
+       p11_mutex_unlock (&delay_mutex);
+
+       for (i = 0; i < num_threads; i++)
+               p11_thread_join (threads[i]);
+
+       teardown_mock_module (rpc_module);
+       p11_mutex_uninit (&delay_mutex);
+}
+
+static void
+test_fork_and_reinitialize (void)
+{
+       CK_FUNCTION_LIST *rpc_module;
+       CK_INFO info;
+       int status;
+       CK_RV rv;
+       pid_t pid;
+       int i;
+
+       rpc_module = setup_test_rpc_module (&test_normal_vtable,
+                                           &mock_module_no_slots, NULL);
+
+       pid = fork ();
+       assert_num_cmp (pid, >=, 0);
+
+       /* The child */
+       if (pid == 0) {
+               rv = (rpc_module->C_Initialize) (NULL);
+               assert_num_eq (CKR_OK, rv);
+
+               for (i = 0; i < 32; i++) {
+                       rv = (rpc_module->C_GetInfo) (&info);
+                       assert_num_eq (CKR_OK, rv);
+               }
+
+               rv = (rpc_module->C_Finalize) (NULL);
+               assert_num_eq (CKR_OK, rv);
+
+               _exit (66);
+       }
+
+       for (i = 0; i < 128; i++) {
+               rv = (rpc_module->C_GetInfo) (&info);
+               assert_num_eq (CKR_OK, rv);
+       }
+
+       assert_num_eq (waitpid (pid, &status, 0), pid);
+       assert_num_eq (WEXITSTATUS (status), 66);
+
+       teardown_mock_module (rpc_module);
+}
+
 #include "test-mock.c"
 
 int
@@ -932,6 +1052,8 @@ main (int argc,
        p11_test (test_transport_bad_contents, "/rpc/transport-bad-contents");
        p11_test (test_get_info_stand_in, "/rpc/get-info-stand-in");
        p11_test (test_get_slot_list_no_device, "/rpc/get-slot-list-no-device");
+       p11_test (test_simultaneous_functions, "/rpc/simultaneous-functions");
+       p11_test (test_fork_and_reinitialize, "/rpc/fork-and-reinitialize");
 
        test_mock_add_tests ("/rpc");
 
diff --git a/p11-kit/tests/test-transport.c b/p11-kit/tests/test-transport.c
new file mode 100644 (file)
index 0000000..32ec02a
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 2012 Stefan Walter
+ * Copyright (c) 2012 Red Hat Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *     * Redistributions of source code must retain the above
+ *       copyright notice, this list of conditions and the
+ *       following disclaimer.
+ *     * Redistributions in binary form must reproduce the
+ *       above copyright notice, this list of conditions and
+ *       the following disclaimer in the documentation and/or
+ *       other materials provided with the distribution.
+ *     * The names of contributors to this software may not be
+ *       used to endorse or promote products derived from this
+ *       software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Author: Stef Walter <stef@thewalter.net>
+ */
+
+#include "config.h"
+#include "test.h"
+
+#include "library.h"
+#include "mock.h"
+#include "path.h"
+#include "private.h"
+#include "p11-kit.h"
+#include "rpc.h"
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+struct {
+       char *directory;
+       char *user_config;
+       char *user_modules;
+} test;
+
+static void
+setup_remote (void *unused)
+{
+       const char *data;
+
+       test.directory = p11_test_directory ("p11-test-config");
+       test.user_modules = p11_path_build (test.directory, "modules", NULL);
+       if (mkdir (test.user_modules, 0700) < 0)
+               assert_not_reached ();
+
+       data = "user-config: only\n";
+       test.user_config = p11_path_build (test.directory, "pkcs11.conf", NULL);
+       p11_test_file_write (NULL, test.user_config, data, strlen (data));
+
+       data = "remote: " BUILDDIR "/frob-server " BUILDDIR "/.libs/mock-two.so\n";
+       p11_test_file_write (test.user_modules, "remote.module", data, strlen (data));
+
+       p11_config_user_modules = test.user_modules;
+       p11_config_user_file = test.user_config;
+}
+
+static void
+teardown_remote (void *unused)
+{
+       p11_test_directory_delete (test.user_modules);
+       p11_test_directory_delete (test.directory);
+
+       free (test.directory);
+       free (test.user_config);
+       free (test.user_modules);
+}
+
+static CK_FUNCTION_LIST *
+setup_mock_module (CK_SESSION_HANDLE *session)
+{
+       CK_FUNCTION_LIST **modules;
+       CK_FUNCTION_LIST *module;
+       CK_RV rv;
+       int i;
+
+       setup_remote (NULL);
+
+       modules = p11_kit_modules_load (NULL, 0);
+
+       module = p11_kit_module_for_name (modules, "remote");
+       assert (module != NULL);
+
+       rv = p11_kit_module_initialize (module);
+       assert_num_eq (rv, CKR_OK);
+
+       if (session) {
+               rv = (module->C_OpenSession) (MOCK_SLOT_ONE_ID, CKF_RW_SESSION | CKF_SERIAL_SESSION,
+                                             NULL, NULL, session);
+               assert (rv == CKR_OK);
+       }
+
+       /* Release all the other modules */
+       for (i = 0; modules[i] != NULL; i++) {
+               if (modules[i] != module)
+                       p11_kit_module_release (modules[i]);
+       }
+
+       free (modules);
+       return module;
+}
+
+static void
+teardown_mock_module (CK_FUNCTION_LIST *module)
+{
+       p11_kit_module_finalize (module);
+       teardown_remote (NULL);
+}
+
+static void
+test_basic_exec (void)
+{
+       CK_FUNCTION_LIST **modules;
+       CK_FUNCTION_LIST *module;
+       CK_RV rv;
+
+       modules = p11_kit_modules_load (NULL, 0);
+
+       module = p11_kit_module_for_name (modules, "remote");
+       assert (module != NULL);
+
+       rv = p11_kit_module_initialize (module);
+       assert_num_eq (rv, CKR_OK);
+
+       rv = p11_kit_module_finalize (module);
+       assert_num_eq (rv, CKR_OK);
+
+       p11_kit_modules_release (modules);
+}
+
+static void *
+invoke_in_thread (void *arg)
+{
+       CK_FUNCTION_LIST *rpc_module = arg;
+       CK_INFO info;
+       CK_RV rv;
+
+       rv = (rpc_module->C_GetInfo) (&info);
+       assert_num_eq (rv, CKR_OK);
+
+       assert (memcmp (info.manufacturerID, MOCK_INFO.manufacturerID,
+                       sizeof (info.manufacturerID)) == 0);
+
+       return NULL;
+}
+
+static void
+test_simultaneous_functions (void)
+{
+       CK_FUNCTION_LIST **modules;
+       CK_FUNCTION_LIST *module;
+       const int num_threads = 128;
+       p11_thread_t threads[num_threads];
+       int i, ret;
+       CK_RV rv;
+
+       modules = p11_kit_modules_load (NULL, 0);
+
+       module = p11_kit_module_for_name (modules, "remote");
+       assert (module != NULL);
+
+       rv = p11_kit_module_initialize (module);
+       assert_num_eq (rv, CKR_OK);
+
+       for (i = 0; i < num_threads; i++) {
+               ret = p11_thread_create (threads + i, invoke_in_thread, module);
+               assert_num_eq (0, ret);
+       }
+
+       for (i = 0; i < num_threads; i++)
+               p11_thread_join (threads[i]);
+
+       rv = p11_kit_module_finalize (module);
+       assert_num_eq (rv, CKR_OK);
+
+       p11_kit_modules_release (modules);
+}
+
+static void
+test_fork_and_reinitialize (void)
+{
+       CK_FUNCTION_LIST **modules;
+       CK_FUNCTION_LIST *module;
+       CK_INFO info;
+       int status;
+       CK_RV rv;
+       pid_t pid;
+       int i;
+
+       modules = p11_kit_modules_load (NULL, 0);
+
+       module = p11_kit_module_for_name (modules, "remote");
+       assert (module != NULL);
+
+       rv = p11_kit_module_initialize (module);
+       assert_num_eq (rv, CKR_OK);
+
+       pid = fork ();
+       assert_num_cmp (pid, >=, 0);
+
+       /* The child */
+       if (pid == 0) {
+               rv = (module->C_Initialize) (NULL);
+               assert_num_eq (CKR_OK, rv);
+
+               for (i = 0; i < 32; i++) {
+                       rv = (module->C_GetInfo) (&info);
+                       assert_num_eq (CKR_OK, rv);
+               }
+
+               rv = (module->C_Finalize) (NULL);
+               assert_num_eq (CKR_OK, rv);
+
+               _exit (66);
+       }
+
+       for (i = 0; i < 128; i++) {
+               rv = (module->C_GetInfo) (&info);
+               assert_num_eq (CKR_OK, rv);
+       }
+
+       assert_num_eq (waitpid (pid, &status, 0), pid);
+       assert_num_eq (WEXITSTATUS (status), 66);
+
+       rv = p11_kit_module_finalize (module);
+       assert_num_eq (rv, CKR_OK);
+
+       p11_kit_modules_release (modules);
+}
+
+
+#include "test-mock.c"
+
+int
+main (int argc,
+      char *argv[])
+{
+       CK_MECHANISM_TYPE mechanisms[] = {
+               CKM_MOCK_CAPITALIZE,
+               CKM_MOCK_PREFIX,
+               CKM_MOCK_GENERATE,
+               CKM_MOCK_WRAP,
+               CKM_MOCK_DERIVE,
+               CKM_MOCK_COUNT,
+               0,
+       };
+
+       p11_library_init ();
+
+       /* Override the mechanisms that the RPC mechanism will handle */
+       p11_rpc_mechanisms_override_supported = mechanisms;
+
+       p11_fixture (setup_remote, teardown_remote);
+       p11_test (test_basic_exec, "/transport/basic");
+       p11_test (test_simultaneous_functions, "/transport/simultaneous-functions");
+       p11_test (test_fork_and_reinitialize, "/transport/fork-and-reinitialize");
+
+       test_mock_add_tests ("/transport");
+
+       return  p11_test_run (argc, argv);
+}