summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStef Walter <stefw@gnome.org>2013-02-20 12:24:03 +0100
committerStef Walter <stef@thewalter.net>2014-06-23 11:33:48 +0200
commit525d996ff8490608c573eeaf7bf6e9beb08402a3 (patch)
treef4cc971531b10545f43dcadd0554db8e60de2fed
parentfcb22f3142cff178d7708a57b45d3432b56df08d (diff)
downloadp11-kit-525d996ff8490608c573eeaf7bf6e9beb08402a3.tar.gz
rpc: Implement execution of another tool to transport PKCS#11 RPC
-rw-r--r--p11-kit/Makefile.am3
-rw-r--r--p11-kit/modules.c70
-rw-r--r--p11-kit/rpc-transport.c852
-rw-r--r--p11-kit/rpc.h36
-rw-r--r--p11-kit/tests/Makefile.am19
-rw-r--r--p11-kit/tests/frob-server.c173
-rw-r--r--p11-kit/tests/mock-module-ep2.c56
-rw-r--r--p11-kit/tests/test-transport.c175
8 files changed, 1357 insertions, 27 deletions
diff --git a/p11-kit/Makefile.am b/p11-kit/Makefile.am
index da195ac..88883b5 100644
--- a/p11-kit/Makefile.am
+++ b/p11-kit/Makefile.am
@@ -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)
diff --git a/p11-kit/modules.c b/p11-kit/modules.c
index 7ea4296..5e9a950 100644
--- a/p11-kit/modules.c
+++ b/p11-kit/modules.c
@@ -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
index 0000000..4d8361e
--- /dev/null
+++ b/p11-kit/rpc-transport.c
@@ -0,0 +1,852 @@
+/*
+ * 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 lock;
+ int refs;
+ int last_code;
+ bool read_creds;
+ bool sent_creds;
+
+ /* This data is protected by wait mutex */
+ p11_mutex_t wait;
+ 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->lock);
+ p11_mutex_init (&sock->wait);
+
+ return sock;
+}
+
+#if 0
+static rpc_socket *
+rpc_socket_ref (rpc_socket *sock)
+{
+ assert (sock != NULL);
+
+ p11_mutex_lock (&sock->lock);
+ sock->refs++;
+ p11_mutex_unlock (&sock->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->lock);
+ if (--sock->refs == 0)
+ release = 1;
+ p11_mutex_unlock (&sock->lock);
+
+ if (!release)
+ return;
+
+ assert (sock != NULL);
+ assert (sock->refs == 0);
+
+ rpc_socket_close (sock);
+ p11_mutex_uninit (&sock->lock);
+ p11_mutex_uninit (&sock->wait);
+}
+
+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 ("couldn't send data: %s", strerror (errno));
+ 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 ("couldn't receive data: %s", strerror (errno));
+ 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 ("couldn't send socket credentials: %s", strerror (errno));
+ 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->wait);
+
+ if (!sock->read_creds) {
+ if (read_all (sock->fd, &dummy, 1) != 1) {
+ p11_message ("couldn't read socket credentials: %s", strerror (errno));
+ 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->wait);
+
+ /* Used as a simple wait */
+ FD_SET (sock->fd, &rfds);
+ select (sock->fd + 1, &rfds, NULL, NULL, NULL);
+
+ p11_mutex_lock (&sock->wait);
+ }
+ }
+
+ p11_mutex_unlock (&sock->wait);
+ 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->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->lock);
+
+ rv = rpc_socket_read (sock, &call_code, response);
+
+ p11_mutex_lock (&sock->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->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 ("failed to create pipe for remote: %s", strerror (errno));
+ return CKR_DEVICE_ERROR;
+ }
+
+ pid = fork ();
+ switch (pid) {
+
+ /* Failure */
+ case -1:
+ close (fds[0]);
+ close (fds[1]);
+ p11_message ("failed to fork for remote: %s", strerror (errno));
+ return CKR_DEVICE_ERROR;
+
+ /* Child */
+ case 0:
+ if (dup2 (fds[1], STDIN_FILENO) < 0 ||
+ dup2 (fds[1], STDOUT_FILENO) < 0) {
+ errn = errno;
+ p11_message ("couldn't dup file descriptors in remote child: %s",
+ strerror (errn));
+ _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 ("couldn't execute program for rpc: %s: %s",
+ (char *)rex->argv->elem[0], strerror (errn));
+ _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);
+ }
+}
diff --git a/p11-kit/rpc.h b/p11-kit/rpc.h
index a86e796..b129e61 100644
--- a/p11-kit/rpc.h
+++ b/p11-kit/rpc.h
@@ -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__ */
diff --git a/p11-kit/tests/Makefile.am b/p11-kit/tests/Makefile.am
index 2192fed..0672d62 100644
--- a/p11-kit/tests/Makefile.am
+++ b/p11-kit/tests/Makefile.am
@@ -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
index 0000000..e0e7020
--- /dev/null
+++ b/p11-kit/tests/frob-server.c
@@ -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
index 0000000..ee71711
--- /dev/null
+++ b/p11-kit/tests/mock-module-ep2.c
@@ -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;
+}
diff --git a/p11-kit/tests/test-transport.c b/p11-kit/tests/test-transport.c
new file mode 100644
index 0000000..cbaa7a9
--- /dev/null
+++ b/p11-kit/tests/test-transport.c
@@ -0,0 +1,175 @@
+/*
+ * 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 <stdlib.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: " SRCDIR "/frob-server " SRCDIR "/.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);
+}
+
+#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");
+
+ test_mock_add_tests ("/transport");
+
+ return p11_test_run (argc, argv);
+}