]> granicus.if.org Git - p11-kit/commitdiff
rpc: Implement PKCS#11 messages/client/server code
authorStef Walter <stefw@gnome.org>
Thu, 23 Aug 2012 13:34:04 +0000 (15:34 +0200)
committerStef Walter <stef@thewalter.net>
Tue, 8 Jul 2014 06:57:30 +0000 (08:57 +0200)
 * This enables passing around bytes which represent PKCS#11 RPC calls.
 * Caller is responsible for connecting/disconnecting and so on.
 * Client side caller gets a mixin from p11_rpc_client_init() to call
   into, which generates callbacks with byte arrays to be transported.
 * Server side calls p11_rpc_server_handle() with a CK_FUNCTION_LIST_PTR
   on which relevant methods get called.
 * Doesn't yet implement the actual daemon or clients etc...

https://bugs.freedesktop.org/show_bug.cgi?id=54105

14 files changed:
common/debug.c
common/debug.h
common/mock.c
common/mock.h
doc/manual/Makefile.am
p11-kit/Makefile.am
p11-kit/rpc-client.c [new file with mode: 0644]
p11-kit/rpc-message.c [new file with mode: 0644]
p11-kit/rpc-message.h [new file with mode: 0644]
p11-kit/rpc-server.c [new file with mode: 0644]
p11-kit/rpc.h [new file with mode: 0644]
p11-kit/tests/Makefile.am
p11-kit/tests/test-mock.c
p11-kit/tests/test-rpc.c [new file with mode: 0644]

index b3327bed74d7e06bef54b7f6b95f1f02082275d9..1fbdc7f4c8454e3779f2bed0b1c451bc3e652e18 100644 (file)
@@ -57,6 +57,7 @@ static struct DebugKey debug_keys[] = {
        { "proxy", P11_DEBUG_PROXY },
        { "trust", P11_DEBUG_TRUST },
        { "tool", P11_DEBUG_TOOL },
+       { "rpc", P11_DEBUG_RPC },
        { 0, }
 };
 
index 0dcfeae89ada0c0ce7d4adcceeb9ace4fc624669..6106f19e6df3a8e26ede239167191ae1c01b6770 100644 (file)
@@ -45,6 +45,7 @@ enum {
        P11_DEBUG_PROXY = 1 << 4,
        P11_DEBUG_TRUST = 1 << 5,
        P11_DEBUG_TOOL = 1 << 6,
+       P11_DEBUG_RPC = 1 << 7,
 };
 
 extern int        p11_debug_current_flags;
index 51b32b6505f136d5562182eef67cfe18dfe3e46a..01e095dd085548fa4782bbd26c26db43dbdaeb23 100644 (file)
@@ -457,6 +457,13 @@ mock_C_Initialize__fails (CK_VOID_PTR init_args)
        return CKR_FUNCTION_FAILED;
 }
 
+CK_RV
+mock_X_Initialize__fails (CK_X_FUNCTION_LIST *self,
+                          CK_VOID_PTR init_args)
+{
+       return mock_C_Initialize__fails (init_args);
+}
+
 CK_RV
 mock_C_Finalize (CK_VOID_PTR reserved)
 {
index 9128a639d7534274106dbc5ea957475fa4208bd4..6253386fc1c4fd968199772508a0e1cb3ee52b17 100644 (file)
@@ -133,6 +133,9 @@ CK_RV        mock_C_Initialize__fails                    (CK_VOID_PTR init_args)
 CK_RV        mock_X_Initialize                           (CK_X_FUNCTION_LIST *self,
                                                           CK_VOID_PTR init_args);
 
+CK_RV        mock_X_Initialize__fails                    (CK_X_FUNCTION_LIST *self,
+                                                          CK_VOID_PTR init_args);
+
 CK_RV        mock_C_Finalize                             (CK_VOID_PTR reserved);
 
 CK_RV        mock_X_Finalize                             (CK_X_FUNCTION_LIST *self,
index c9ff26415b09cd58ac0569a1eac32a9db476e360..dc15075f14ca061450d9bf736e41bc294e46e2f2 100644 (file)
@@ -59,6 +59,8 @@ IGNORE_HFILES= \
        pkcs11x.h \
        private.h \
        proxy.h \
+       rpc.h \
+       rpc-message.h \
        util.h \
        virtual.h \
        array.h \
index 92cddfb269eb898711f636911deeaa0a25a4804f..da195ac6d5781498c1e1d16a09464252f1a31bce 100644 (file)
@@ -36,6 +36,8 @@ MODULE_SRCS = \
        proxy.c proxy.h \
        private.h \
        messages.c \
+       rpc-message.c rpc-message.h \
+       rpc-client.c rpc-server.c rpc.h \
        uri.c \
        virtual.c virtual.h \
        $(inc_HEADERS)
diff --git a/p11-kit/rpc-client.c b/p11-kit/rpc-client.c
new file mode 100644 (file)
index 0000000..810ef12
--- /dev/null
@@ -0,0 +1,2092 @@
+/*
+ * Copyright (C) 2008 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 <stefw@gnome.org>
+ */
+
+#include "config.h"
+
+#define P11_DEBUG_FLAG P11_DEBUG_RPC
+#include "debug.h"
+#include "pkcs11.h"
+#include "pkcs11x.h"
+#include "library.h"
+#include "message.h"
+#include "private.h"
+#include "rpc.h"
+#include "rpc-message.h"
+#include "virtual.h"
+
+#include <assert.h>
+#include <string.h>
+#include <unistd.h>
+
+/* The error used by us when parsing of rpc message fails */
+#define PARSE_ERROR   CKR_DEVICE_ERROR
+
+typedef struct {
+       p11_mutex_t mutex;
+       p11_rpc_client_vtable *vtable;
+       pid_t initialized_pid;
+       bool initialize_done;
+} rpc_client;
+
+/* Allocator for call session buffers */
+static void *
+log_allocator (void *pointer,
+               size_t size)
+{
+       void *result = realloc (pointer, (size_t)size);
+       return_val_if_fail (!size || result != NULL, NULL);
+       return result;
+}
+
+static CK_RV
+call_prepare (rpc_client *module,
+              p11_rpc_message *msg,
+              int call_id)
+{
+       p11_buffer *buffer;
+
+       assert (module != NULL);
+       assert (msg != NULL);
+
+       if (module->initialized_pid == 0)
+               return CKR_CRYPTOKI_NOT_INITIALIZED;
+       if (!module->initialize_done)
+               return CKR_DEVICE_REMOVED;
+
+       buffer = p11_rpc_buffer_new_full (64, log_allocator, free);
+       return_val_if_fail (buffer != NULL, CKR_GENERAL_ERROR);
+
+       /* We use the same buffer for reading and writing */
+       p11_rpc_message_init (msg, buffer, buffer);
+
+       /* Put in the Call ID and signature */
+       if (!p11_rpc_message_prep (msg, call_id, P11_RPC_REQUEST))
+               return_val_if_reached (CKR_HOST_MEMORY);
+
+       p11_debug ("prepared call: %d", call_id);
+       return CKR_OK;
+}
+
+static CK_RV
+call_run (rpc_client *module,
+          p11_rpc_message *msg)
+{
+       CK_RV ret = CKR_OK;
+       CK_ULONG ckerr;
+
+       int call_id;
+
+       assert (module != NULL);
+       assert (msg != NULL);
+
+       /* Did building the call fail? */
+       if (p11_buffer_failed (msg->output))
+               return_val_if_reached (CKR_HOST_MEMORY);
+
+       /* Make sure that the signature is valid */
+       assert (p11_rpc_message_is_verified (msg));
+       call_id = msg->call_id;
+
+       /* Do the transport send and receive */
+       assert (module->vtable->transport != NULL);
+       ret = (module->vtable->transport) (module->vtable,
+                                          msg->output,
+                                          msg->input);
+
+       if (ret != CKR_OK)
+               return ret;
+
+       if (!p11_rpc_message_parse (msg, P11_RPC_RESPONSE))
+               return CKR_DEVICE_ERROR;
+
+       /* If it's an error code then return it */
+       if (msg->call_id == P11_RPC_CALL_ERROR) {
+               if (!p11_rpc_message_read_ulong (msg, &ckerr)) {
+                       p11_message ("invalid rpc error response: too short");
+                       return CKR_DEVICE_ERROR;
+               }
+
+               if (ckerr <= CKR_OK) {
+                       p11_message ("invalid rpc error response: bad error code");
+                       return CKR_DEVICE_ERROR;
+               }
+
+               /* An error code from the other side */
+               return (CK_RV)ckerr;
+       }
+
+       /* Make sure other side answered the right call */
+       if (call_id != msg->call_id) {
+               p11_message ("invalid rpc response: call mismatch");
+               return CKR_DEVICE_ERROR;
+       }
+
+       assert (!p11_buffer_failed (msg->input));
+
+       p11_debug ("parsing response values");
+       return CKR_OK;
+}
+
+static CK_RV
+call_done (rpc_client *module,
+           p11_rpc_message *msg,
+           CK_RV ret)
+{
+       assert (module != NULL);
+       assert (msg != NULL);
+
+       /* Check for parsing errors that were not caught elsewhere */
+       if (ret == CKR_OK) {
+               if (p11_buffer_failed (msg->input)) {
+                       p11_message ("invalid rpc response: bad argument data");
+                       ret = CKR_GENERAL_ERROR;
+               } else {
+                       /* Double check that the signature matched our decoding */
+                       assert (p11_rpc_message_is_verified (msg));
+               }
+       }
+
+       /* We used the same buffer for input/output, so this frees both */
+       assert (msg->input == msg->output);
+       p11_rpc_buffer_free (msg->input);
+
+       p11_rpc_message_clear (msg);
+
+       return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * MODULE SPECIFIC PROTOCOL CODE
+ */
+
+static CK_RV
+proto_read_attribute_array (p11_rpc_message *msg,
+                            CK_ATTRIBUTE_PTR arr,
+                            CK_ULONG len)
+{
+       uint32_t i, num, value, type;
+       CK_ATTRIBUTE_PTR attr;
+       const unsigned char *attrval;
+       size_t attrlen;
+       unsigned char validity;
+       CK_RV ret;
+
+       assert (len != 0);
+       assert (msg != NULL);
+       assert (msg->input != NULL);
+
+       /* Make sure this is in the right order */
+       assert (!msg->signature || p11_rpc_message_verify_part (msg, "aA"));
+
+       /* Get the number of items. We need this value to be correct */
+       if (!p11_rpc_buffer_get_uint32 (msg->input, &msg->parsed, &num))
+               return PARSE_ERROR;
+
+       /*
+        * This should never happen in normal operation. It denotes a goof up
+        * on the other side of our RPC. We should be indicating the exact number
+        * of attributes to the other side. And it should respond with the same
+        * number.
+        */
+       if (len != num) {
+               p11_message ("received an attribute array with wrong number of attributes");
+               return PARSE_ERROR;
+       }
+
+       ret = CKR_OK;
+
+       /* We need to go ahead and read everything in all cases */
+       for (i = 0; i < num; ++i) {
+
+               /* The attribute type */
+               p11_rpc_buffer_get_uint32 (msg->input, &msg->parsed, &type);
+
+               /* Attribute validity */
+               p11_rpc_buffer_get_byte (msg->input, &msg->parsed, &validity);
+
+               /* And the data itself */
+               if (validity) {
+                       if (p11_rpc_buffer_get_uint32 (msg->input, &msg->parsed, &value) &&
+                           p11_rpc_buffer_get_byte_array (msg->input, &msg->parsed, &attrval, &attrlen)) {
+                               if (attrval && value != attrlen) {
+                                       p11_message ("attribute length does not match attribute data");
+                                       return PARSE_ERROR;
+                               }
+                               attrlen = value;
+                       }
+               }
+
+               /* Don't act on this data unless no errors */
+               if (p11_buffer_failed (msg->input))
+                       break;
+
+               /* Try and stuff it in the output data */
+               if (arr) {
+                       attr = &(arr[i]);
+                       if (attr->type != type) {
+                               p11_message ("returned attributes in invalid order");
+                               return PARSE_ERROR;
+                       }
+
+                       if (validity) {
+                               /* Just requesting the attribute size */
+                               if (!attr->pValue) {
+                                       attr->ulValueLen = attrlen;
+
+                               /* Wants attribute data, but too small */
+                               } else if (attr->ulValueLen < attrlen) {
+                                       attr->ulValueLen = attrlen;
+                                       ret = CKR_BUFFER_TOO_SMALL;
+
+                               /* Wants attribute data, value is null */
+                               } else if (attrval == NULL) {
+                                       attr->ulValueLen = 0;
+
+                               /* Wants attribute data, enough space */
+                               } else {
+                                       attr->ulValueLen = attrlen;
+                                       memcpy (attr->pValue, attrval, attrlen);
+                               }
+
+                       /* Not a valid attribute */
+                       } else {
+                               attr->ulValueLen = ((CK_ULONG)-1);
+                       }
+               }
+       }
+
+       if (p11_buffer_failed (msg->input))
+               return PARSE_ERROR;
+
+       /* Read in the code that goes along with these attributes */
+       if (!p11_rpc_message_read_ulong (msg, &ret))
+               return PARSE_ERROR;
+
+       return ret;
+}
+
+static CK_RV
+proto_read_byte_array (p11_rpc_message *msg,
+                       CK_BYTE_PTR arr,
+                       CK_ULONG_PTR len,
+                       CK_ULONG max)
+{
+       const unsigned char *val;
+       unsigned char valid;
+       uint32_t length;
+       size_t vlen;
+
+       assert (len != NULL);
+       assert (msg != NULL);
+       assert (msg->input != NULL);
+
+       /* Make sure this is in the right order */
+       assert (!msg->signature || p11_rpc_message_verify_part (msg, "ay"));
+
+       /* A single byte which determines whether valid or not */
+       if (!p11_rpc_buffer_get_byte (msg->input, &msg->parsed, &valid))
+               return PARSE_ERROR;
+
+       /* If not valid, then just the length is encoded, this can signify CKR_BUFFER_TOO_SMALL */
+       if (!valid) {
+               if (!p11_rpc_buffer_get_uint32 (msg->input, &msg->parsed, &length))
+                       return PARSE_ERROR;
+
+               *len = length;
+
+               if (arr)
+                       return CKR_BUFFER_TOO_SMALL;
+               else
+                       return CKR_OK;
+       }
+
+       /* Get the actual bytes */
+       if (!p11_rpc_buffer_get_byte_array (msg->input, &msg->parsed, &val, &vlen))
+               return PARSE_ERROR;
+
+       *len = vlen;
+
+       /* Just asking us for size */
+       if (!arr)
+               return CKR_OK;
+
+       if (max < vlen)
+               return CKR_BUFFER_TOO_SMALL;
+
+       /* Enough space, yay */
+       memcpy (arr, val, vlen);
+       return CKR_OK;
+}
+
+static CK_RV
+proto_read_ulong_array (p11_rpc_message *msg, CK_ULONG_PTR arr,
+                        CK_ULONG_PTR len, CK_ULONG max)
+{
+       uint32_t i, num;
+       uint64_t val;
+       unsigned char valid;
+
+       assert (len != NULL);
+       assert (msg != NULL);
+       assert (msg->input != NULL);
+
+       /* Make sure this is in the right order */
+       assert (!msg->signature || p11_rpc_message_verify_part (msg, "au"));
+
+       /* A single byte which determines whether valid or not */
+       if (!p11_rpc_buffer_get_byte (msg->input, &msg->parsed, &valid))
+               return PARSE_ERROR;
+
+       /* Get the number of items. */
+       if (!p11_rpc_buffer_get_uint32 (msg->input, &msg->parsed, &num))
+               return PARSE_ERROR;
+
+       *len = num;
+
+       /* If not valid, then just the length is encoded, this can signify CKR_BUFFER_TOO_SMALL */
+       if (!valid) {
+               if (arr)
+                       return CKR_BUFFER_TOO_SMALL;
+               else
+                       return CKR_OK;
+       }
+
+       if (max < num)
+               return CKR_BUFFER_TOO_SMALL;
+
+       /* We need to go ahead and read everything in all cases */
+       for (i = 0; i < num; ++i) {
+               p11_rpc_buffer_get_uint64 (msg->input, &msg->parsed, &val);
+               if (arr)
+                       arr[i] = (CK_ULONG)val;
+       }
+
+       return p11_buffer_failed (msg->input) ? PARSE_ERROR : CKR_OK;
+}
+
+/* Used to override the supported mechanisms in tests */
+CK_MECHANISM_TYPE *p11_rpc_mechanisms_override_supported = NULL;
+
+static bool
+mechanism_has_sane_parameters (CK_MECHANISM_TYPE type)
+{
+       int i;
+
+       /* This can be set from tests, to override default set of supported */
+       if (p11_rpc_mechanisms_override_supported) {
+               for (i = 0; p11_rpc_mechanisms_override_supported[i] != 0; i++) {
+                       if (p11_rpc_mechanisms_override_supported[i] == type)
+                               return true;
+               }
+
+               return false;
+       }
+
+       /* This list is incomplete */
+       switch (type) {
+       case CKM_RSA_PKCS_OAEP:
+       case CKM_RSA_PKCS_PSS:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool
+mechanism_has_no_parameters (CK_MECHANISM_TYPE mech)
+{
+       /* This list is incomplete */
+
+       switch (mech) {
+       case CKM_RSA_PKCS_KEY_PAIR_GEN:
+       case CKM_RSA_X9_31_KEY_PAIR_GEN:
+       case CKM_RSA_PKCS:
+       case CKM_RSA_9796:
+       case CKM_RSA_X_509:
+       case CKM_RSA_X9_31:
+       case CKM_MD2_RSA_PKCS:
+       case CKM_MD5_RSA_PKCS:
+       case CKM_SHA1_RSA_PKCS:
+       case CKM_SHA256_RSA_PKCS:
+       case CKM_SHA384_RSA_PKCS:
+       case CKM_SHA512_RSA_PKCS:
+       case CKM_RIPEMD128_RSA_PKCS:
+       case CKM_RIPEMD160_RSA_PKCS:
+       case CKM_SHA1_RSA_X9_31:
+       case CKM_DSA_KEY_PAIR_GEN:
+       case CKM_DSA_PARAMETER_GEN:
+       case CKM_DSA:
+       case CKM_DSA_SHA1:
+       case CKM_FORTEZZA_TIMESTAMP:
+       case CKM_EC_KEY_PAIR_GEN:
+       case CKM_ECDSA:
+       case CKM_ECDSA_SHA1:
+       case CKM_DH_PKCS_KEY_PAIR_GEN:
+       case CKM_DH_PKCS_PARAMETER_GEN:
+       case CKM_X9_42_DH_KEY_PAIR_GEN:
+       case CKM_X9_42_DH_PARAMETER_GEN:
+       case CKM_KEA_KEY_PAIR_GEN:
+       case CKM_GENERIC_SECRET_KEY_GEN:
+       case CKM_RC2_KEY_GEN:
+       case CKM_RC4_KEY_GEN:
+       case CKM_RC4:
+       case CKM_RC5_KEY_GEN:
+       case CKM_AES_KEY_GEN:
+       case CKM_AES_ECB:
+       case CKM_AES_MAC:
+       case CKM_DES_KEY_GEN:
+       case CKM_DES2_KEY_GEN:
+       case CKM_DES3_KEY_GEN:
+       case CKM_CDMF_KEY_GEN:
+       case CKM_CAST_KEY_GEN:
+       case CKM_CAST3_KEY_GEN:
+       case CKM_CAST128_KEY_GEN:
+       case CKM_IDEA_KEY_GEN:
+       case CKM_SSL3_PRE_MASTER_KEY_GEN:
+       case CKM_TLS_PRE_MASTER_KEY_GEN:
+       case CKM_SKIPJACK_KEY_GEN:
+       case CKM_BATON_KEY_GEN:
+       case CKM_JUNIPER_KEY_GEN:
+       case CKM_RC2_ECB:
+       case CKM_DES_ECB:
+       case CKM_DES3_ECB:
+       case CKM_CDMF_ECB:
+       case CKM_CAST_ECB:
+       case CKM_CAST3_ECB:
+       case CKM_CAST128_ECB:
+       case CKM_RC5_ECB:
+       case CKM_IDEA_ECB:
+       case CKM_RC2_MAC:
+       case CKM_DES_MAC:
+       case CKM_DES3_MAC:
+       case CKM_CDMF_MAC:
+       case CKM_CAST_MAC:
+       case CKM_CAST3_MAC:
+       case CKM_RC5_MAC:
+       case CKM_IDEA_MAC:
+       case CKM_SSL3_MD5_MAC:
+       case CKM_SSL3_SHA1_MAC:
+       case CKM_SKIPJACK_WRAP:
+       case CKM_BATON_WRAP:
+       case CKM_JUNIPER_WRAP:
+       case CKM_MD2:
+       case CKM_MD2_HMAC:
+       case CKM_MD5:
+       case CKM_MD5_HMAC:
+       case CKM_SHA_1:
+       case CKM_SHA_1_HMAC:
+       case CKM_SHA256:
+       case CKM_SHA256_HMAC:
+       case CKM_SHA384:
+       case CKM_SHA384_HMAC:
+       case CKM_SHA512:
+       case CKM_SHA512_HMAC:
+       case CKM_FASTHASH:
+       case CKM_RIPEMD128:
+       case CKM_RIPEMD128_HMAC:
+       case CKM_RIPEMD160:
+       case CKM_RIPEMD160_HMAC:
+       case CKM_KEY_WRAP_LYNKS:
+               return true;
+       default:
+               return false;
+       };
+}
+
+static bool
+mechanism_is_supported (CK_MECHANISM_TYPE mech)
+{
+       if (mechanism_has_no_parameters (mech) ||
+           mechanism_has_sane_parameters (mech))
+               return true;
+       return false;
+}
+static void
+mechanism_list_purge (CK_MECHANISM_TYPE_PTR mechs,
+                      CK_ULONG *n_mechs)
+{
+       int i;
+
+       assert (mechs != NULL);
+       assert (n_mechs != NULL);
+
+       for (i = 0; i < (int)(*n_mechs); ++i) {
+               if (!mechanism_is_supported (mechs[i])) {
+
+                       /* Remove the mechanism from the list */
+                       memmove (&mechs[i], &mechs[i + 1],
+                                (*n_mechs - i) * sizeof (CK_MECHANISM_TYPE));
+
+                       --(*n_mechs);
+                       --i;
+               }
+       }
+}
+
+static CK_RV
+proto_write_mechanism (p11_rpc_message *msg,
+                       CK_MECHANISM_PTR mech)
+{
+       assert (msg != NULL);
+       assert (mech != NULL);
+       assert (msg->output != NULL);
+
+       /* Make sure this is in the right order */
+       assert (!msg->signature || p11_rpc_message_verify_part (msg, "M"));
+
+       /* The mechanism type */
+       p11_rpc_buffer_add_uint32 (msg->output, mech->mechanism);
+
+       /*
+        * PKCS#11 mechanism parameters are not easy to serialize. They're
+        * completely different for so many mechanisms, they contain
+        * pointers to arbitrary memory, and many callers don't initialize
+        * them completely or properly.
+        *
+        * We only support certain mechanisms.
+        *
+        * Also callers do yucky things like leaving parts of the structure
+        * pointing to garbage if they don't think it's going to be used.
+        */
+
+       if (mechanism_has_no_parameters (mech->mechanism))
+               p11_rpc_buffer_add_byte_array (msg->output, NULL, 0);
+       else if (mechanism_has_sane_parameters (mech->mechanism))
+               p11_rpc_buffer_add_byte_array (msg->output, mech->pParameter,
+                                              mech->ulParameterLen);
+       else
+               return CKR_MECHANISM_INVALID;
+
+       return p11_buffer_failed (msg->output) ? CKR_HOST_MEMORY : CKR_OK;
+}
+
+static CK_RV
+proto_read_info (p11_rpc_message *msg,
+                 CK_INFO_PTR info)
+{
+       assert (msg != NULL);
+       assert (info != NULL);
+
+       if (!p11_rpc_message_read_version (msg, &info->cryptokiVersion) ||
+           !p11_rpc_message_read_space_string (msg, info->manufacturerID, 32) ||
+           !p11_rpc_message_read_ulong (msg, &info->flags) ||
+           !p11_rpc_message_read_space_string (msg, info->libraryDescription, 32) ||
+           !p11_rpc_message_read_version (msg, &info->libraryVersion))
+               return PARSE_ERROR;
+
+       return CKR_OK;
+}
+
+static CK_RV
+proto_read_slot_info (p11_rpc_message *msg,
+                      CK_SLOT_INFO_PTR info)
+{
+       assert (msg != NULL);
+       assert (info != NULL);
+
+       if (!p11_rpc_message_read_space_string (msg, info->slotDescription, 64) ||
+           !p11_rpc_message_read_space_string (msg, info->manufacturerID, 32) ||
+           !p11_rpc_message_read_ulong (msg, &info->flags) ||
+           !p11_rpc_message_read_version (msg, &info->hardwareVersion) ||
+           !p11_rpc_message_read_version (msg, &info->firmwareVersion))
+               return PARSE_ERROR;
+
+       return CKR_OK;
+}
+
+static CK_RV
+proto_read_token_info (p11_rpc_message *msg,
+                       CK_TOKEN_INFO_PTR info)
+{
+       assert (msg != NULL);
+       assert (info != NULL);
+
+       if (!p11_rpc_message_read_space_string (msg, info->label, 32) ||
+           !p11_rpc_message_read_space_string (msg, info->manufacturerID, 32) ||
+           !p11_rpc_message_read_space_string (msg, info->model, 16) ||
+           !p11_rpc_message_read_space_string (msg, info->serialNumber, 16) ||
+           !p11_rpc_message_read_ulong (msg, &info->flags) ||
+           !p11_rpc_message_read_ulong (msg, &info->ulMaxSessionCount) ||
+           !p11_rpc_message_read_ulong (msg, &info->ulSessionCount) ||
+           !p11_rpc_message_read_ulong (msg, &info->ulMaxRwSessionCount) ||
+           !p11_rpc_message_read_ulong (msg, &info->ulRwSessionCount) ||
+           !p11_rpc_message_read_ulong (msg, &info->ulMaxPinLen) ||
+           !p11_rpc_message_read_ulong (msg, &info->ulMinPinLen) ||
+           !p11_rpc_message_read_ulong (msg, &info->ulTotalPublicMemory) ||
+           !p11_rpc_message_read_ulong (msg, &info->ulFreePublicMemory) ||
+           !p11_rpc_message_read_ulong (msg, &info->ulTotalPrivateMemory) ||
+           !p11_rpc_message_read_ulong (msg, &info->ulFreePrivateMemory) ||
+           !p11_rpc_message_read_version (msg, &info->hardwareVersion) ||
+           !p11_rpc_message_read_version (msg, &info->firmwareVersion) ||
+           !p11_rpc_message_read_space_string (msg, info->utcTime, 16))
+               return PARSE_ERROR;
+
+       return CKR_OK;
+}
+
+static CK_RV
+proto_read_mechanism_info (p11_rpc_message *msg,
+                           CK_MECHANISM_INFO_PTR info)
+{
+       assert (msg != NULL);
+       assert (info != NULL);
+
+       if (!p11_rpc_message_read_ulong (msg, &info->ulMinKeySize) ||
+           !p11_rpc_message_read_ulong (msg, &info->ulMaxKeySize) ||
+           !p11_rpc_message_read_ulong (msg, &info->flags))
+               return PARSE_ERROR;
+
+       return CKR_OK;
+}
+
+static CK_RV
+proto_read_sesssion_info (p11_rpc_message *msg,
+                          CK_SESSION_INFO_PTR info)
+{
+       assert (msg != NULL);
+       assert (info != NULL);
+
+       if (!p11_rpc_message_read_ulong (msg, &info->slotID) ||
+           !p11_rpc_message_read_ulong (msg, &info->state) ||
+           !p11_rpc_message_read_ulong (msg, &info->flags) ||
+           !p11_rpc_message_read_ulong (msg, &info->ulDeviceError))
+               return PARSE_ERROR;
+
+       return CKR_OK;
+}
+
+/* -------------------------------------------------------------------
+ * CALL MACROS
+ */
+
+#define BEGIN_CALL_OR(call_id, self, if_no_daemon) \
+       p11_debug (#call_id ": enter"); \
+       { \
+               rpc_client *_mod = ((p11_virtual *)self)->lower_module; p11_rpc_message _msg; \
+               CK_RV _ret = call_prepare (_mod, &_msg, P11_RPC_CALL_##call_id); \
+               if (_ret == CKR_DEVICE_REMOVED) return (if_no_daemon); \
+               if (_ret != CKR_OK) return _ret;
+
+#define PROCESS_CALL \
+               _ret = call_run (_mod, &_msg); \
+               if (_ret != CKR_OK) goto _cleanup;
+
+#define RETURN(ret) \
+               _ret = ret; \
+               goto _cleanup;
+
+#define END_CALL \
+       _cleanup: \
+               _ret = call_done (_mod, &_msg, _ret); \
+               p11_debug ("ret: %lu", _ret); \
+               return _ret; \
+       }
+
+#define IN_BYTE(val) \
+       if (!p11_rpc_message_write_byte (&_msg, val)) \
+               { _ret = CKR_HOST_MEMORY; goto _cleanup; }
+
+#define IN_ULONG(val) \
+       if (!p11_rpc_message_write_ulong (&_msg, val)) \
+               { _ret = CKR_HOST_MEMORY; goto _cleanup; }
+
+#define IN_STRING(val) \
+       if (!p11_rpc_message_write_zero_string (&_msg, val)) \
+               { _ret = CKR_HOST_MEMORY; goto _cleanup; }
+
+#define IN_BYTE_BUFFER(arr, len) \
+       if (len == NULL) \
+               { _ret = CKR_ARGUMENTS_BAD; goto _cleanup; } \
+       if (!p11_rpc_message_write_byte_buffer (&_msg, arr ? *len : 0)) \
+               { _ret = CKR_HOST_MEMORY; goto _cleanup; }
+
+#define IN_BYTE_ARRAY(arr, len) \
+       if (len != 0 && arr == NULL) \
+               { _ret = CKR_ARGUMENTS_BAD; goto _cleanup; } \
+       if (!p11_rpc_message_write_byte_array (&_msg, arr, len)) \
+               { _ret = CKR_HOST_MEMORY; goto _cleanup; }
+
+#define IN_ULONG_BUFFER(arr, len) \
+       if (len == NULL) \
+               { _ret = CKR_ARGUMENTS_BAD; goto _cleanup; } \
+       if (!p11_rpc_message_write_ulong_buffer (&_msg, arr ? *len : 0)) \
+               { _ret = CKR_HOST_MEMORY; goto _cleanup; }
+
+#define IN_ULONG_ARRAY(arr, len) \
+       if (len != 0 && arr == NULL) \
+               { _ret = CKR_ARGUMENTS_BAD; goto _cleanup; }\
+       if (!p11_rpc_message_write_ulong_array (&_msg, arr, len)) \
+               { _ret = CKR_HOST_MEMORY; goto _cleanup; }
+
+#define IN_ATTRIBUTE_BUFFER(arr, num) \
+       if (num != 0 && arr == NULL) \
+               { _ret = CKR_ARGUMENTS_BAD; goto _cleanup; } \
+       if (!p11_rpc_message_write_attribute_buffer (&_msg, (arr), (num))) \
+               { _ret = CKR_HOST_MEMORY; goto _cleanup; }
+
+#define IN_ATTRIBUTE_ARRAY(arr, num) \
+       if (num != 0 && arr == NULL) \
+               { _ret = CKR_ARGUMENTS_BAD; goto _cleanup; } \
+       if (!p11_rpc_message_write_attribute_array (&_msg, (arr), (num))) \
+               { _ret = CKR_HOST_MEMORY; goto _cleanup; }
+
+#define IN_MECHANISM_TYPE(val) \
+       if(!mechanism_is_supported (val)) \
+               { _ret = CKR_MECHANISM_INVALID; goto _cleanup; } \
+       if (!p11_rpc_message_write_ulong (&_msg, val)) \
+               { _ret = CKR_HOST_MEMORY; goto _cleanup; }
+
+#define IN_MECHANISM(val) \
+       if (val == NULL) \
+               { _ret = CKR_ARGUMENTS_BAD; goto _cleanup; } \
+       _ret = proto_write_mechanism (&_msg, val); \
+       if (_ret != CKR_OK) goto _cleanup;
+
+
+
+#define OUT_ULONG(val) \
+       if (val == NULL) \
+               _ret = CKR_ARGUMENTS_BAD; \
+       if (_ret == CKR_OK && !p11_rpc_message_read_ulong (&_msg, val)) \
+               _ret = PARSE_ERROR;
+
+#define OUT_BYTE_ARRAY(arr, len)  \
+       if (len == NULL) \
+               _ret = CKR_ARGUMENTS_BAD; \
+       if (_ret == CKR_OK) \
+               _ret = proto_read_byte_array (&_msg, (arr), (len), *(len));
+
+#define OUT_ULONG_ARRAY(a, len) \
+       if (len == NULL) \
+               _ret = CKR_ARGUMENTS_BAD; \
+       if (_ret == CKR_OK) \
+               _ret = proto_read_ulong_array (&_msg, (a), (len), *(len));
+
+#define OUT_ATTRIBUTE_ARRAY(arr, num) \
+       if (_ret == CKR_OK) \
+               _ret = proto_read_attribute_array (&_msg, (arr), (num));
+
+#define OUT_INFO(info) \
+       if (info == NULL) \
+               _ret = CKR_ARGUMENTS_BAD; \
+       if (_ret == CKR_OK) \
+               _ret = proto_read_info (&_msg, info);
+
+#define OUT_SLOT_INFO(info) \
+       if (info == NULL) \
+               _ret = CKR_ARGUMENTS_BAD; \
+       if (_ret == CKR_OK) \
+               _ret = proto_read_slot_info (&_msg, info);
+
+#define OUT_TOKEN_INFO(info) \
+       if (info == NULL) \
+               _ret = CKR_ARGUMENTS_BAD; \
+       if (_ret == CKR_OK) \
+               _ret = proto_read_token_info (&_msg, info);
+
+#define OUT_SESSION_INFO(info) \
+       if (info == NULL) \
+               _ret = CKR_ARGUMENTS_BAD; \
+       if (_ret == CKR_OK) \
+               _ret = proto_read_sesssion_info (&_msg, info);
+
+#define OUT_MECHANISM_TYPE_ARRAY(arr, len) \
+       if (len == NULL) \
+               _ret = CKR_ARGUMENTS_BAD; \
+       if (_ret == CKR_OK) \
+               _ret = proto_read_ulong_array (&_msg, (arr), (len), *(len)); \
+       if (_ret == CKR_OK && arr) \
+               mechanism_list_purge (arr, len);
+
+#define OUT_MECHANISM_INFO(info) \
+       if (info == NULL) \
+               _ret = CKR_ARGUMENTS_BAD; \
+       if (_ret == CKR_OK) \
+               _ret = proto_read_mechanism_info (&_msg, info);
+
+
+/* -------------------------------------------------------------------
+ * INITIALIZATION and 'GLOBAL' CALLS
+ */
+
+static CK_RV
+rpc_C_Initialize (CK_X_FUNCTION_LIST *self,
+                  CK_VOID_PTR init_args)
+{
+       rpc_client *module = ((p11_virtual *)self)->lower_module;
+       CK_C_INITIALIZE_ARGS_PTR args = NULL;
+       void *reserved = NULL;
+       CK_RV ret = CKR_OK;
+       p11_rpc_message msg;
+       pid_t pid;
+
+       assert (module != NULL);
+       p11_debug ("C_Initialize: enter");
+
+       if (init_args != NULL) {
+               int supplied_ok;
+
+               /* pReserved must be NULL */
+               args = init_args;
+
+               /* ALL supplied function pointers need to have the value either NULL or non-NULL. */
+               supplied_ok = (args->CreateMutex == NULL && args->DestroyMutex == NULL &&
+                              args->LockMutex == NULL && args->UnlockMutex == NULL) ||
+                             (args->CreateMutex != NULL && args->DestroyMutex != NULL &&
+                              args->LockMutex != NULL && args->UnlockMutex != NULL);
+               if (!supplied_ok) {
+                       p11_message ("invalid set of mutex calls supplied");
+                       return CKR_ARGUMENTS_BAD;
+               }
+
+               /*
+                * When the CKF_OS_LOCKING_OK flag isn't set return an error.
+                * We must be able to use our mutex functionality.
+                */
+               if (!(args->flags & CKF_OS_LOCKING_OK)) {
+                       p11_message ("can't do without os locking");
+                       return CKR_CANT_LOCK;
+               }
+
+               if (args->pReserved)
+                       reserved = args->pReserved;
+       }
+
+       p11_mutex_lock (&module->mutex);
+
+       pid = getpid ();
+       if (module->initialized_pid != 0) {
+               /* This process has called C_Initialize already */
+               if (pid == module->initialized_pid) {
+                       p11_message ("C_Initialize called twice for same process");
+                       ret = CKR_CRYPTOKI_ALREADY_INITIALIZED;
+                       goto done;
+               }
+       }
+
+       /* Call out to initialize client callback */
+       assert (module->vtable->connect != NULL);
+       ret = (module->vtable->connect) (module->vtable, reserved);
+
+       /* Successfully initialized */
+       if (ret == CKR_OK) {
+               module->initialized_pid = pid;
+               module->initialize_done = true;
+
+       /* Server doesn't exist, initialize but don't call */
+       } else if (ret == CKR_DEVICE_REMOVED) {
+               module->initialized_pid = pid;
+               module->initialize_done = false;
+               ret = CKR_OK;
+               goto done;
+
+       } else {
+               goto done;
+       }
+
+       /* If we don't have read and write fds now, then initialize other side */
+       ret = call_prepare (module, &msg, P11_RPC_CALL_C_Initialize);
+       if (ret == CKR_OK)
+               if (!p11_rpc_message_write_byte_array (&msg, P11_RPC_HANDSHAKE, P11_RPC_HANDSHAKE_LEN))
+                       ret = CKR_HOST_MEMORY;
+       if (ret == CKR_OK)
+               ret = call_run (module, &msg);
+       call_done (module, &msg, ret);
+
+done:
+       /* If failed then unmark initialized */
+       if (ret != CKR_OK && ret != CKR_CRYPTOKI_ALREADY_INITIALIZED)
+               module->initialized_pid = 0;
+
+       /* If we told our caller that we're initialized, but not really, then finalize */
+       if (ret != CKR_OK && module->initialize_done) {
+               module->initialize_done = false;
+               assert (module->vtable->disconnect != NULL);
+               (module->vtable->disconnect) (module->vtable, reserved);
+       }
+
+       p11_mutex_unlock (&module->mutex);
+
+       p11_debug ("C_Initialize: %lu", ret);
+       return ret;
+}
+
+static CK_RV
+rpc_C_Finalize (CK_X_FUNCTION_LIST *self,
+                CK_VOID_PTR reserved)
+{
+       rpc_client *module = ((p11_virtual *)self)->lower_module;
+       CK_RV ret = CKR_OK;
+       p11_rpc_message msg;
+
+       p11_debug ("C_Finalize: enter");
+       return_val_if_fail (module->initialized_pid != 0, CKR_CRYPTOKI_NOT_INITIALIZED);
+       return_val_if_fail (!reserved, CKR_ARGUMENTS_BAD);
+
+       p11_mutex_lock (&module->mutex);
+
+       if (module->initialize_done) {
+               ret = call_prepare (module, &msg, P11_RPC_CALL_C_Finalize);
+               if (ret == CKR_OK)
+                       ret = call_run (module, &msg);
+               call_done (module, &msg, ret);
+               if (ret != CKR_OK)
+                       p11_message ("finalizing rpc module returned an error: %lu", ret);
+
+               module->initialize_done = false;
+               assert (module->vtable->disconnect != NULL);
+               (module->vtable->disconnect) (module->vtable, reserved);
+       }
+
+       module->initialized_pid = 0;
+
+       p11_mutex_unlock (&module->mutex);
+
+       p11_debug ("C_Finalize: %lu", CKR_OK);
+       return CKR_OK;
+}
+
+static CK_RV
+fill_stand_in_info (CK_INFO_PTR info)
+{
+       static CK_INFO stand_in_info = {
+               { CRYPTOKI_VERSION_MAJOR, CRYPTOKI_VERSION_MINOR },
+               "p11-kit                         ",
+               0,
+               "p11-kit (no connection)         ",
+               { 1, 1 },
+       };
+       memcpy (info, &stand_in_info, sizeof (CK_INFO));
+       return CKR_OK;
+
+}
+
+static CK_RV
+rpc_C_GetInfo (CK_X_FUNCTION_LIST *self,
+               CK_INFO_PTR info)
+{
+       return_val_if_fail (info, CKR_ARGUMENTS_BAD);
+
+       BEGIN_CALL_OR (C_GetInfo, self, fill_stand_in_info (info));
+       PROCESS_CALL;
+               OUT_INFO (info);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_GetSlotList (CK_X_FUNCTION_LIST *self,
+                   CK_BBOOL token_present,
+                   CK_SLOT_ID_PTR slot_list,
+                   CK_ULONG_PTR count)
+{
+       return_val_if_fail (count, CKR_ARGUMENTS_BAD);
+
+       BEGIN_CALL_OR (C_GetSlotList, self, (*count = 0, CKR_OK));
+               IN_BYTE (token_present);
+               IN_ULONG_BUFFER (slot_list, count);
+       PROCESS_CALL;
+               OUT_ULONG_ARRAY (slot_list, count);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_GetSlotInfo (CK_X_FUNCTION_LIST *self,
+                   CK_SLOT_ID slot_id,
+                   CK_SLOT_INFO_PTR info)
+{
+       return_val_if_fail (info, CKR_ARGUMENTS_BAD);
+
+       BEGIN_CALL_OR (C_GetSlotInfo, self, CKR_SLOT_ID_INVALID);
+               IN_ULONG (slot_id);
+       PROCESS_CALL;
+               OUT_SLOT_INFO (info);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_GetTokenInfo (CK_X_FUNCTION_LIST *self,
+                    CK_SLOT_ID slot_id,
+                    CK_TOKEN_INFO_PTR info)
+{
+       return_val_if_fail (info, CKR_ARGUMENTS_BAD);
+
+       BEGIN_CALL_OR (C_GetTokenInfo, self, CKR_SLOT_ID_INVALID);
+               IN_ULONG (slot_id);
+       PROCESS_CALL;
+               OUT_TOKEN_INFO (info);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_GetMechanismList (CK_X_FUNCTION_LIST *self,
+                        CK_SLOT_ID slot_id,
+                        CK_MECHANISM_TYPE_PTR mechanism_list,
+                        CK_ULONG_PTR count)
+{
+       return_val_if_fail (count, CKR_ARGUMENTS_BAD);
+
+       BEGIN_CALL_OR (C_GetMechanismList, self, CKR_SLOT_ID_INVALID);
+               IN_ULONG (slot_id);
+               IN_ULONG_BUFFER (mechanism_list, count);
+       PROCESS_CALL;
+               OUT_MECHANISM_TYPE_ARRAY (mechanism_list, count);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_GetMechanismInfo (CK_X_FUNCTION_LIST *self,
+                        CK_SLOT_ID slot_id,
+                        CK_MECHANISM_TYPE type,
+                        CK_MECHANISM_INFO_PTR info)
+{
+       return_val_if_fail (info, CKR_ARGUMENTS_BAD);
+
+       BEGIN_CALL_OR (C_GetMechanismInfo, self, CKR_SLOT_ID_INVALID);
+               IN_ULONG (slot_id);
+               IN_MECHANISM_TYPE (type);
+       PROCESS_CALL;
+               OUT_MECHANISM_INFO (info);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_InitToken (CK_X_FUNCTION_LIST *self,
+                 CK_SLOT_ID slot_id,
+                 CK_UTF8CHAR_PTR pin, CK_ULONG pin_len,
+                 CK_UTF8CHAR_PTR label)
+{
+       BEGIN_CALL_OR (C_InitToken, self, CKR_SLOT_ID_INVALID);
+               IN_ULONG (slot_id);
+               IN_BYTE_ARRAY (pin, pin_len);
+               IN_STRING (label);
+       PROCESS_CALL;
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_WaitForSlotEvent (CK_X_FUNCTION_LIST *self,
+                        CK_FLAGS flags,
+                        CK_SLOT_ID_PTR slot,
+                        CK_VOID_PTR reserved)
+{
+       return_val_if_fail (slot, CKR_ARGUMENTS_BAD);
+
+       BEGIN_CALL_OR (C_WaitForSlotEvent, self, CKR_DEVICE_REMOVED);
+               IN_ULONG (flags);
+       PROCESS_CALL;
+               OUT_ULONG (slot);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_OpenSession (CK_X_FUNCTION_LIST *self,
+                   CK_SLOT_ID slot_id,
+                   CK_FLAGS flags,
+                   CK_VOID_PTR user_data,
+                   CK_NOTIFY callback,
+                   CK_SESSION_HANDLE_PTR session)
+{
+       return_val_if_fail (session, CKR_ARGUMENTS_BAD);
+
+       BEGIN_CALL_OR (C_OpenSession, self, CKR_SLOT_ID_INVALID);
+               IN_ULONG (slot_id);
+               IN_ULONG (flags);
+       PROCESS_CALL;
+               OUT_ULONG (session);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_CloseSession (CK_X_FUNCTION_LIST *self,
+                    CK_SESSION_HANDLE session)
+{
+       BEGIN_CALL_OR (C_CloseSession, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+       PROCESS_CALL;
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_CloseAllSessions (CK_X_FUNCTION_LIST *self,
+                        CK_SLOT_ID slot_id)
+{
+       BEGIN_CALL_OR (C_CloseAllSessions, self, CKR_SLOT_ID_INVALID);
+               IN_ULONG (slot_id);
+       PROCESS_CALL;
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_GetSessionInfo (CK_X_FUNCTION_LIST *self,
+                      CK_SESSION_HANDLE session,
+                      CK_SESSION_INFO_PTR info)
+{
+       return_val_if_fail (info, CKR_ARGUMENTS_BAD);
+
+       BEGIN_CALL_OR (C_GetSessionInfo, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+       PROCESS_CALL;
+               OUT_SESSION_INFO (info);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_InitPIN (CK_X_FUNCTION_LIST *self,
+               CK_SESSION_HANDLE session,
+               CK_UTF8CHAR_PTR pin,
+               CK_ULONG pin_len)
+{
+       BEGIN_CALL_OR (C_InitPIN, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (pin, pin_len);
+       PROCESS_CALL;
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_SetPIN (CK_X_FUNCTION_LIST *self,
+              CK_SESSION_HANDLE session,
+              CK_UTF8CHAR_PTR old_pin,
+              CK_ULONG old_pin_len,
+              CK_UTF8CHAR_PTR new_pin,
+              CK_ULONG new_pin_len)
+{
+       BEGIN_CALL_OR (C_SetPIN, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (old_pin, old_pin_len);
+               IN_BYTE_ARRAY (new_pin, new_pin_len);
+       PROCESS_CALL;
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_GetOperationState (CK_X_FUNCTION_LIST *self,
+                         CK_SESSION_HANDLE session,
+                         CK_BYTE_PTR operation_state,
+                         CK_ULONG_PTR operation_state_len)
+{
+       return_val_if_fail (operation_state_len, CKR_ARGUMENTS_BAD);
+
+       BEGIN_CALL_OR (C_GetOperationState, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_BYTE_BUFFER (operation_state, operation_state_len);
+       PROCESS_CALL;
+               OUT_BYTE_ARRAY (operation_state, operation_state_len);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_SetOperationState (CK_X_FUNCTION_LIST *self,
+                         CK_SESSION_HANDLE session,
+                         CK_BYTE_PTR operation_state,
+                         CK_ULONG operation_state_len,
+                         CK_OBJECT_HANDLE encryption_key,
+                         CK_OBJECT_HANDLE authentication_key)
+{
+       BEGIN_CALL_OR (C_SetOperationState, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (operation_state, operation_state_len);
+               IN_ULONG (encryption_key);
+               IN_ULONG (authentication_key);
+       PROCESS_CALL;
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_Login (CK_X_FUNCTION_LIST *self,
+             CK_SESSION_HANDLE session,
+             CK_USER_TYPE user_type,
+             CK_UTF8CHAR_PTR pin,
+             CK_ULONG pin_len)
+{
+       BEGIN_CALL_OR (C_Login, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_ULONG (user_type);
+               IN_BYTE_ARRAY (pin, pin_len);
+       PROCESS_CALL;
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_Logout (CK_X_FUNCTION_LIST *self,
+              CK_SESSION_HANDLE session)
+{
+       BEGIN_CALL_OR (C_Logout, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+       PROCESS_CALL;
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_CreateObject (CK_X_FUNCTION_LIST *self,
+                    CK_SESSION_HANDLE session,
+                    CK_ATTRIBUTE_PTR template,
+                    CK_ULONG count,
+                    CK_OBJECT_HANDLE_PTR new_object)
+{
+       return_val_if_fail (new_object, CKR_ARGUMENTS_BAD);
+
+       BEGIN_CALL_OR (C_CreateObject, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_ATTRIBUTE_ARRAY (template, count);
+       PROCESS_CALL;
+               OUT_ULONG (new_object);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_CopyObject (CK_X_FUNCTION_LIST *self,
+                  CK_SESSION_HANDLE session,
+                  CK_OBJECT_HANDLE object,
+                  CK_ATTRIBUTE_PTR template,
+                  CK_ULONG count,
+                  CK_OBJECT_HANDLE_PTR new_object)
+{
+       return_val_if_fail (new_object, CKR_ARGUMENTS_BAD);
+
+       BEGIN_CALL_OR (C_CopyObject, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_ULONG (object);
+               IN_ATTRIBUTE_ARRAY (template, count);
+       PROCESS_CALL;
+               OUT_ULONG (new_object);
+       END_CALL;
+}
+
+
+static CK_RV
+rpc_C_DestroyObject (CK_X_FUNCTION_LIST *self,
+                     CK_SESSION_HANDLE session,
+                     CK_OBJECT_HANDLE object)
+{
+       BEGIN_CALL_OR (C_DestroyObject, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_ULONG (object);
+       PROCESS_CALL;
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_GetObjectSize (CK_X_FUNCTION_LIST *self,
+                     CK_SESSION_HANDLE session,
+                     CK_OBJECT_HANDLE object,
+                     CK_ULONG_PTR size)
+{
+       return_val_if_fail (size, CKR_ARGUMENTS_BAD);
+
+       BEGIN_CALL_OR (C_GetObjectSize, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_ULONG (object);
+       PROCESS_CALL;
+               OUT_ULONG (size);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_GetAttributeValue (CK_X_FUNCTION_LIST *self,
+                         CK_SESSION_HANDLE session,
+                         CK_OBJECT_HANDLE object,
+                         CK_ATTRIBUTE_PTR template,
+                         CK_ULONG count)
+{
+       BEGIN_CALL_OR (C_GetAttributeValue, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_ULONG (object);
+               IN_ATTRIBUTE_BUFFER (template, count);
+       PROCESS_CALL;
+               OUT_ATTRIBUTE_ARRAY (template, count);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_SetAttributeValue (CK_X_FUNCTION_LIST *self,
+                         CK_SESSION_HANDLE session,
+                         CK_OBJECT_HANDLE object,
+                         CK_ATTRIBUTE_PTR template,
+                         CK_ULONG count)
+{
+       BEGIN_CALL_OR (C_SetAttributeValue, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_ULONG (object);
+               IN_ATTRIBUTE_ARRAY (template, count);
+       PROCESS_CALL;
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_FindObjectsInit (CK_X_FUNCTION_LIST *self,
+                       CK_SESSION_HANDLE session,
+                       CK_ATTRIBUTE_PTR template,
+                       CK_ULONG count)
+{
+       BEGIN_CALL_OR (C_FindObjectsInit, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_ATTRIBUTE_ARRAY (template, count);
+       PROCESS_CALL;
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_FindObjects (CK_X_FUNCTION_LIST *self,
+                   CK_SESSION_HANDLE session,
+                   CK_OBJECT_HANDLE_PTR objects,
+                   CK_ULONG max_count,
+                   CK_ULONG_PTR count)
+{
+       /* HACK: To fix a stupid gcc warning */
+       CK_ULONG_PTR address_of_max_count = &max_count;
+
+       return_val_if_fail (count, CKR_ARGUMENTS_BAD);
+
+       BEGIN_CALL_OR (C_FindObjects, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_ULONG_BUFFER (objects, address_of_max_count);
+       PROCESS_CALL;
+               *count = max_count;
+               OUT_ULONG_ARRAY (objects, count);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_FindObjectsFinal (CK_X_FUNCTION_LIST *self,
+                        CK_SESSION_HANDLE session)
+{
+       BEGIN_CALL_OR (C_FindObjectsFinal, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+       PROCESS_CALL;
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_EncryptInit (CK_X_FUNCTION_LIST *self,
+                   CK_SESSION_HANDLE session,
+                   CK_MECHANISM_PTR mechanism,
+                   CK_OBJECT_HANDLE key)
+{
+       BEGIN_CALL_OR (C_EncryptInit, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_MECHANISM (mechanism);
+               IN_ULONG (key);
+       PROCESS_CALL;
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_Encrypt (CK_X_FUNCTION_LIST *self,
+               CK_SESSION_HANDLE session,
+               CK_BYTE_PTR data,
+               CK_ULONG data_len,
+               CK_BYTE_PTR encrypted_data,
+               CK_ULONG_PTR encrypted_data_len)
+{
+       return_val_if_fail (encrypted_data_len, CKR_ARGUMENTS_BAD);
+
+       BEGIN_CALL_OR (C_Encrypt, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (data, data_len);
+               IN_BYTE_BUFFER (encrypted_data, encrypted_data_len);
+       PROCESS_CALL;
+               OUT_BYTE_ARRAY (encrypted_data, encrypted_data_len);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_EncryptUpdate (CK_X_FUNCTION_LIST *self,
+                     CK_SESSION_HANDLE session,
+                     CK_BYTE_PTR part,
+                     CK_ULONG part_len,
+                     CK_BYTE_PTR encrypted_part,
+                     CK_ULONG_PTR encrypted_part_len)
+{
+       return_val_if_fail (encrypted_part_len, CKR_ARGUMENTS_BAD);
+
+       BEGIN_CALL_OR (C_EncryptUpdate, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (part, part_len);
+               IN_BYTE_BUFFER (encrypted_part, encrypted_part_len);
+       PROCESS_CALL;
+               OUT_BYTE_ARRAY (encrypted_part, encrypted_part_len);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_EncryptFinal (CK_X_FUNCTION_LIST *self,
+                    CK_SESSION_HANDLE session,
+                    CK_BYTE_PTR last_part,
+                    CK_ULONG_PTR last_part_len)
+{
+       return_val_if_fail (last_part_len, CKR_ARGUMENTS_BAD);
+
+       BEGIN_CALL_OR (C_EncryptFinal, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_BYTE_BUFFER (last_part, last_part_len);
+       PROCESS_CALL;
+               OUT_BYTE_ARRAY (last_part, last_part_len);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_DecryptInit (CK_X_FUNCTION_LIST *self,
+                   CK_SESSION_HANDLE session,
+                   CK_MECHANISM_PTR mechanism,
+                   CK_OBJECT_HANDLE key)
+{
+       BEGIN_CALL_OR (C_DecryptInit, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_MECHANISM (mechanism);
+               IN_ULONG (key);
+       PROCESS_CALL;
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_Decrypt (CK_X_FUNCTION_LIST *self,
+               CK_SESSION_HANDLE session,
+               CK_BYTE_PTR enc_data,
+               CK_ULONG enc_data_len,
+               CK_BYTE_PTR data,
+               CK_ULONG_PTR data_len)
+{
+       return_val_if_fail (data_len, CKR_ARGUMENTS_BAD);
+
+       BEGIN_CALL_OR (C_Decrypt, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (enc_data, enc_data_len);
+               IN_BYTE_BUFFER (data, data_len);
+       PROCESS_CALL;
+               OUT_BYTE_ARRAY (data, data_len);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_DecryptUpdate (CK_X_FUNCTION_LIST *self,
+                     CK_SESSION_HANDLE session,
+                     CK_BYTE_PTR enc_part,
+                     CK_ULONG enc_part_len,
+                     CK_BYTE_PTR part,
+                     CK_ULONG_PTR part_len)
+{
+       return_val_if_fail (part_len, CKR_ARGUMENTS_BAD);
+
+       BEGIN_CALL_OR (C_DecryptUpdate, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (enc_part, enc_part_len);
+               IN_BYTE_BUFFER (part, part_len);
+       PROCESS_CALL;
+               OUT_BYTE_ARRAY (part, part_len);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_DecryptFinal (CK_X_FUNCTION_LIST *self,
+                    CK_SESSION_HANDLE session,
+                    CK_BYTE_PTR last_part,
+                    CK_ULONG_PTR last_part_len)
+{
+       return_val_if_fail (last_part_len, CKR_ARGUMENTS_BAD);
+
+       BEGIN_CALL_OR (C_DecryptFinal, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_BYTE_BUFFER (last_part, last_part_len);
+       PROCESS_CALL;
+               OUT_BYTE_ARRAY (last_part, last_part_len);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_DigestInit (CK_X_FUNCTION_LIST *self,
+                  CK_SESSION_HANDLE session,
+                  CK_MECHANISM_PTR mechanism)
+{
+       BEGIN_CALL_OR (C_DigestInit, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_MECHANISM (mechanism);
+       PROCESS_CALL;
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_Digest (CK_X_FUNCTION_LIST *self,
+              CK_SESSION_HANDLE session,
+              CK_BYTE_PTR data,
+              CK_ULONG data_len,
+              CK_BYTE_PTR digest,
+              CK_ULONG_PTR digest_len)
+{
+       return_val_if_fail (digest_len, CKR_ARGUMENTS_BAD);
+
+       BEGIN_CALL_OR (C_Digest, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (data, data_len);
+               IN_BYTE_BUFFER (digest, digest_len);
+       PROCESS_CALL;
+               OUT_BYTE_ARRAY (digest, digest_len);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_DigestUpdate (CK_X_FUNCTION_LIST *self,
+                    CK_SESSION_HANDLE session,
+                    CK_BYTE_PTR part,
+                    CK_ULONG part_len)
+{
+       BEGIN_CALL_OR (C_DigestUpdate, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (part, part_len);
+       PROCESS_CALL;
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_DigestKey (CK_X_FUNCTION_LIST *self,
+                 CK_SESSION_HANDLE session,
+                 CK_OBJECT_HANDLE key)
+{
+       BEGIN_CALL_OR (C_DigestKey, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_ULONG (key);
+       PROCESS_CALL;
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_DigestFinal (CK_X_FUNCTION_LIST *self,
+                   CK_SESSION_HANDLE session,
+                   CK_BYTE_PTR digest,
+                   CK_ULONG_PTR digest_len)
+{
+       return_val_if_fail (digest_len, CKR_ARGUMENTS_BAD);
+
+       BEGIN_CALL_OR (C_DigestFinal, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_BYTE_BUFFER (digest, digest_len);
+       PROCESS_CALL;
+               OUT_BYTE_ARRAY (digest, digest_len);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_SignInit (CK_X_FUNCTION_LIST *self,
+                CK_SESSION_HANDLE session,
+                CK_MECHANISM_PTR mechanism,
+                CK_OBJECT_HANDLE key)
+{
+       BEGIN_CALL_OR (C_SignInit, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_MECHANISM (mechanism);
+               IN_ULONG (key);
+       PROCESS_CALL;
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_Sign (CK_X_FUNCTION_LIST *self,
+            CK_SESSION_HANDLE session,
+            CK_BYTE_PTR data,
+            CK_ULONG data_len,
+            CK_BYTE_PTR signature,
+            CK_ULONG_PTR signature_len)
+{
+       return_val_if_fail (signature_len, CKR_ARGUMENTS_BAD);
+
+       BEGIN_CALL_OR (C_Sign, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (data, data_len);
+               IN_BYTE_BUFFER (signature, signature_len);
+       PROCESS_CALL;
+               OUT_BYTE_ARRAY (signature, signature_len);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_SignUpdate (CK_X_FUNCTION_LIST *self,
+                  CK_SESSION_HANDLE session,
+                  CK_BYTE_PTR part,
+                  CK_ULONG part_len)
+{
+       return_val_if_fail (part_len, CKR_ARGUMENTS_BAD);
+
+       BEGIN_CALL_OR (C_SignUpdate, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (part, part_len);
+       PROCESS_CALL;
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_SignFinal (CK_X_FUNCTION_LIST *self,
+                 CK_SESSION_HANDLE session,
+                 CK_BYTE_PTR signature,
+                 CK_ULONG_PTR signature_len)
+{
+       return_val_if_fail (signature_len, CKR_ARGUMENTS_BAD);
+
+       BEGIN_CALL_OR (C_SignFinal, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_BYTE_BUFFER (signature, signature_len);
+       PROCESS_CALL;
+               OUT_BYTE_ARRAY (signature, signature_len);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_SignRecoverInit (CK_X_FUNCTION_LIST *self,
+                       CK_SESSION_HANDLE session,
+                       CK_MECHANISM_PTR mechanism,
+                       CK_OBJECT_HANDLE key)
+{
+       BEGIN_CALL_OR (C_SignRecoverInit, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_MECHANISM (mechanism);
+               IN_ULONG (key);
+       PROCESS_CALL;
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_SignRecover (CK_X_FUNCTION_LIST *self,
+                   CK_SESSION_HANDLE session,
+                   CK_BYTE_PTR data,
+                   CK_ULONG data_len,
+                   CK_BYTE_PTR signature, CK_ULONG_PTR signature_len)
+{
+       return_val_if_fail (signature_len, CKR_ARGUMENTS_BAD);
+
+       BEGIN_CALL_OR (C_SignRecover, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (data, data_len);
+               IN_BYTE_BUFFER (signature, signature_len);
+       PROCESS_CALL;
+               OUT_BYTE_ARRAY (signature, signature_len);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_VerifyInit (CK_X_FUNCTION_LIST *self,
+                  CK_SESSION_HANDLE session,
+                  CK_MECHANISM_PTR mechanism,
+                  CK_OBJECT_HANDLE key)
+{
+       BEGIN_CALL_OR (C_VerifyInit, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_MECHANISM (mechanism);
+               IN_ULONG (key);
+       PROCESS_CALL;
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_Verify (CK_X_FUNCTION_LIST *self,
+              CK_SESSION_HANDLE session,
+              CK_BYTE_PTR data,
+              CK_ULONG data_len,
+              CK_BYTE_PTR signature,
+              CK_ULONG signature_len)
+{
+       BEGIN_CALL_OR (C_Verify, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (data, data_len);
+               IN_BYTE_ARRAY (signature, signature_len);
+       PROCESS_CALL;
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_VerifyUpdate (CK_X_FUNCTION_LIST *self,
+                    CK_SESSION_HANDLE session,
+                    CK_BYTE_PTR part,
+                    CK_ULONG part_len)
+{
+       BEGIN_CALL_OR (C_VerifyUpdate, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (part, part_len);
+       PROCESS_CALL;
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_VerifyFinal (CK_X_FUNCTION_LIST *self,
+                   CK_SESSION_HANDLE session,
+                   CK_BYTE_PTR signature,
+                   CK_ULONG signature_len)
+{
+       BEGIN_CALL_OR (C_VerifyFinal, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (signature, signature_len);
+       PROCESS_CALL;
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_VerifyRecoverInit (CK_X_FUNCTION_LIST *self,
+                         CK_SESSION_HANDLE session,
+                         CK_MECHANISM_PTR mechanism,
+                         CK_OBJECT_HANDLE key)
+{
+       BEGIN_CALL_OR (C_VerifyRecoverInit, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_MECHANISM (mechanism);
+               IN_ULONG (key);
+       PROCESS_CALL;
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_VerifyRecover (CK_X_FUNCTION_LIST *self,
+                     CK_SESSION_HANDLE session,
+                     CK_BYTE_PTR signature,
+                     CK_ULONG signature_len,
+                     CK_BYTE_PTR data,
+                     CK_ULONG_PTR data_len)
+{
+       return_val_if_fail (data_len, CKR_ARGUMENTS_BAD);
+
+       BEGIN_CALL_OR (C_VerifyRecover, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (signature, signature_len);
+               IN_BYTE_BUFFER (data, data_len);
+       PROCESS_CALL;
+               OUT_BYTE_ARRAY (data, data_len);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_DigestEncryptUpdate (CK_X_FUNCTION_LIST *self,
+                           CK_SESSION_HANDLE session,
+                           CK_BYTE_PTR part,
+                           CK_ULONG part_len,
+                           CK_BYTE_PTR enc_part,
+                           CK_ULONG_PTR enc_part_len)
+{
+       return_val_if_fail (enc_part_len, CKR_ARGUMENTS_BAD);
+
+       BEGIN_CALL_OR (C_DigestEncryptUpdate, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (part, part_len);
+               IN_BYTE_BUFFER (enc_part, enc_part_len);
+       PROCESS_CALL;
+               OUT_BYTE_ARRAY (enc_part, enc_part_len);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_DecryptDigestUpdate (CK_X_FUNCTION_LIST *self,
+                           CK_SESSION_HANDLE session,
+                           CK_BYTE_PTR enc_part,
+                           CK_ULONG enc_part_len,
+                           CK_BYTE_PTR part,
+                           CK_ULONG_PTR part_len)
+{
+       return_val_if_fail (part_len, CKR_ARGUMENTS_BAD);
+
+       BEGIN_CALL_OR (C_DecryptDigestUpdate, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (enc_part, enc_part_len);
+               IN_BYTE_BUFFER (part, part_len);
+       PROCESS_CALL;
+               OUT_BYTE_ARRAY (part, part_len);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_SignEncryptUpdate (CK_X_FUNCTION_LIST *self,
+                         CK_SESSION_HANDLE session,
+                         CK_BYTE_PTR part,
+                         CK_ULONG part_len,
+                         CK_BYTE_PTR enc_part,
+                         CK_ULONG_PTR enc_part_len)
+{
+       return_val_if_fail (enc_part_len, CKR_ARGUMENTS_BAD);
+
+       BEGIN_CALL_OR (C_SignEncryptUpdate, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (part, part_len);
+               IN_BYTE_BUFFER (enc_part, enc_part_len);
+       PROCESS_CALL;
+               OUT_BYTE_ARRAY (enc_part, enc_part_len);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_DecryptVerifyUpdate (CK_X_FUNCTION_LIST *self,
+                           CK_SESSION_HANDLE session,
+                           CK_BYTE_PTR enc_part,
+                           CK_ULONG enc_part_len,
+                           CK_BYTE_PTR part,
+                           CK_ULONG_PTR part_len)
+{
+       return_val_if_fail (part_len, CKR_ARGUMENTS_BAD);
+
+       BEGIN_CALL_OR (C_DecryptVerifyUpdate, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (enc_part, enc_part_len);
+               IN_BYTE_BUFFER (part, part_len);
+       PROCESS_CALL;
+               OUT_BYTE_ARRAY (part, part_len);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_GenerateKey (CK_X_FUNCTION_LIST *self,
+                   CK_SESSION_HANDLE session,
+                   CK_MECHANISM_PTR mechanism,
+                   CK_ATTRIBUTE_PTR template,
+                   CK_ULONG count,
+                   CK_OBJECT_HANDLE_PTR key)
+{
+       BEGIN_CALL_OR (C_GenerateKey, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_MECHANISM (mechanism);
+               IN_ATTRIBUTE_ARRAY (template, count);
+       PROCESS_CALL;
+               OUT_ULONG (key);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_GenerateKeyPair (CK_X_FUNCTION_LIST *self,
+                       CK_SESSION_HANDLE session,
+                       CK_MECHANISM_PTR mechanism,
+                       CK_ATTRIBUTE_PTR pub_template,
+                       CK_ULONG pub_count,
+                       CK_ATTRIBUTE_PTR priv_template,
+                       CK_ULONG priv_count,
+                       CK_OBJECT_HANDLE_PTR pub_key,
+                       CK_OBJECT_HANDLE_PTR priv_key)
+{
+       BEGIN_CALL_OR (C_GenerateKeyPair, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_MECHANISM (mechanism);
+               IN_ATTRIBUTE_ARRAY (pub_template, pub_count);
+               IN_ATTRIBUTE_ARRAY (priv_template, priv_count);
+       PROCESS_CALL;
+               OUT_ULONG (pub_key);
+               OUT_ULONG (priv_key);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_WrapKey (CK_X_FUNCTION_LIST *self,
+               CK_SESSION_HANDLE session,
+               CK_MECHANISM_PTR mechanism,
+               CK_OBJECT_HANDLE wrapping_key,
+               CK_OBJECT_HANDLE key,
+               CK_BYTE_PTR wrapped_key,
+               CK_ULONG_PTR wrapped_key_len)
+{
+       return_val_if_fail (wrapped_key_len, CKR_ARGUMENTS_BAD);
+
+       BEGIN_CALL_OR (C_WrapKey, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_MECHANISM (mechanism);
+               IN_ULONG (wrapping_key);
+               IN_ULONG (key);
+               IN_BYTE_BUFFER (wrapped_key, wrapped_key_len);
+       PROCESS_CALL;
+               OUT_BYTE_ARRAY (wrapped_key, wrapped_key_len);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_UnwrapKey (CK_X_FUNCTION_LIST *self,
+                 CK_SESSION_HANDLE session,
+                 CK_MECHANISM_PTR mechanism,
+                 CK_OBJECT_HANDLE unwrapping_key,
+                 CK_BYTE_PTR wrapped_key,
+                 CK_ULONG wrapped_key_len,
+                 CK_ATTRIBUTE_PTR template,
+                 CK_ULONG count,
+                 CK_OBJECT_HANDLE_PTR key)
+{
+       BEGIN_CALL_OR (C_UnwrapKey, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_MECHANISM (mechanism);
+               IN_ULONG (unwrapping_key);
+               IN_BYTE_ARRAY (wrapped_key, wrapped_key_len);
+               IN_ATTRIBUTE_ARRAY (template, count);
+       PROCESS_CALL;
+               OUT_ULONG (key);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_DeriveKey (CK_X_FUNCTION_LIST *self,
+                 CK_SESSION_HANDLE session,
+                 CK_MECHANISM_PTR mechanism,
+                 CK_OBJECT_HANDLE base_key,
+                 CK_ATTRIBUTE_PTR template,
+                 CK_ULONG count,
+                 CK_OBJECT_HANDLE_PTR key)
+{
+       BEGIN_CALL_OR (C_DeriveKey, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_MECHANISM (mechanism);
+               IN_ULONG (base_key);
+               IN_ATTRIBUTE_ARRAY (template, count);
+       PROCESS_CALL;
+               OUT_ULONG (key);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_SeedRandom (CK_X_FUNCTION_LIST *self,
+                  CK_SESSION_HANDLE session,
+                  CK_BYTE_PTR seed,
+                  CK_ULONG seed_len)
+{
+       BEGIN_CALL_OR (C_SeedRandom, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (seed, seed_len);
+       PROCESS_CALL;
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_GenerateRandom (CK_X_FUNCTION_LIST *self,
+                      CK_SESSION_HANDLE session,
+                      CK_BYTE_PTR random_data,
+                      CK_ULONG random_len)
+{
+       CK_ULONG_PTR address = &random_len;
+
+       BEGIN_CALL_OR (C_GenerateRandom, self, CKR_SESSION_HANDLE_INVALID);
+               IN_ULONG (session);
+               IN_BYTE_BUFFER (random_data, address);
+       PROCESS_CALL;
+               OUT_BYTE_ARRAY (random_data, address);
+       END_CALL;
+}
+
+static CK_X_FUNCTION_LIST rpc_functions = {
+       { -1, -1 },
+       rpc_C_Initialize,
+       rpc_C_Finalize,
+       rpc_C_GetInfo,
+       rpc_C_GetSlotList,
+       rpc_C_GetSlotInfo,
+       rpc_C_GetTokenInfo,
+       rpc_C_GetMechanismList,
+       rpc_C_GetMechanismInfo,
+       rpc_C_InitToken,
+       rpc_C_InitPIN,
+       rpc_C_SetPIN,
+       rpc_C_OpenSession,
+       rpc_C_CloseSession,
+       rpc_C_CloseAllSessions,
+       rpc_C_GetSessionInfo,
+       rpc_C_GetOperationState,
+       rpc_C_SetOperationState,
+       rpc_C_Login,
+       rpc_C_Logout,
+       rpc_C_CreateObject,
+       rpc_C_CopyObject,
+       rpc_C_DestroyObject,
+       rpc_C_GetObjectSize,
+       rpc_C_GetAttributeValue,
+       rpc_C_SetAttributeValue,
+       rpc_C_FindObjectsInit,
+       rpc_C_FindObjects,
+       rpc_C_FindObjectsFinal,
+       rpc_C_EncryptInit,
+       rpc_C_Encrypt,
+       rpc_C_EncryptUpdate,
+       rpc_C_EncryptFinal,
+       rpc_C_DecryptInit,
+       rpc_C_Decrypt,
+       rpc_C_DecryptUpdate,
+       rpc_C_DecryptFinal,
+       rpc_C_DigestInit,
+       rpc_C_Digest,
+       rpc_C_DigestUpdate,
+       rpc_C_DigestKey,
+       rpc_C_DigestFinal,
+       rpc_C_SignInit,
+       rpc_C_Sign,
+       rpc_C_SignUpdate,
+       rpc_C_SignFinal,
+       rpc_C_SignRecoverInit,
+       rpc_C_SignRecover,
+       rpc_C_VerifyInit,
+       rpc_C_Verify,
+       rpc_C_VerifyUpdate,
+       rpc_C_VerifyFinal,
+       rpc_C_VerifyRecoverInit,
+       rpc_C_VerifyRecover,
+       rpc_C_DigestEncryptUpdate,
+       rpc_C_DecryptDigestUpdate,
+       rpc_C_SignEncryptUpdate,
+       rpc_C_DecryptVerifyUpdate,
+       rpc_C_GenerateKey,
+       rpc_C_GenerateKeyPair,
+       rpc_C_WrapKey,
+       rpc_C_UnwrapKey,
+       rpc_C_DeriveKey,
+       rpc_C_SeedRandom,
+       rpc_C_GenerateRandom,
+       rpc_C_WaitForSlotEvent,
+};
+
+static void
+rpc_client_free (void *data)
+{
+       rpc_client *client = data;
+       p11_mutex_uninit (&client->mutex);
+       free (client);
+}
+
+bool
+p11_rpc_client_init (p11_virtual *virt,
+                     p11_rpc_client_vtable *vtable)
+{
+       rpc_client *client;
+
+       p11_message_clear ();
+
+       return_val_if_fail (vtable != NULL, false);
+       return_val_if_fail (vtable->connect != NULL, false);
+       return_val_if_fail (vtable->transport != NULL, false);
+       return_val_if_fail (vtable->disconnect != NULL, false);
+
+       P11_RPC_CHECK_CALLS ();
+
+       client = calloc (1, sizeof (rpc_client));
+       return_val_if_fail (client != NULL, false);
+
+       p11_mutex_init (&client->mutex);
+       client->vtable = vtable;
+
+       p11_virtual_init (virt, &rpc_functions, client, rpc_client_free);
+       return true;
+}
diff --git a/p11-kit/rpc-message.c b/p11-kit/rpc-message.c
new file mode 100644 (file)
index 0000000..30c331e
--- /dev/null
@@ -0,0 +1,769 @@
+/*
+ * Copyright (C) 2008 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 <stefw@gnome.org>
+ */
+
+#include "config.h"
+
+#include "debug.h"
+#include "library.h"
+#include "message.h"
+#include "private.h"
+#include "rpc-message.h"
+
+#include <assert.h>
+#include <string.h>
+
+void
+p11_rpc_message_init (p11_rpc_message *msg,
+                      p11_buffer *input,
+                      p11_buffer *output)
+{
+       assert (input != NULL);
+       assert (output != NULL);
+       assert (output->ffree != NULL);
+       assert (output->frealloc != NULL);
+
+       memset (msg, 0, sizeof (*msg));
+
+       msg->output = output;
+       msg->input = input;
+}
+
+void
+p11_rpc_message_clear (p11_rpc_message *msg)
+{
+       void *allocated;
+       void **data;
+
+       assert (msg != NULL);
+
+       /* Free up the extra allocated memory */
+       allocated = msg->extra;
+       while (allocated != NULL) {
+               data = (void **)allocated;
+
+               /* Pointer to the next allocation */
+               allocated = *data;
+               assert (msg->output->ffree);
+               (msg->output->ffree) (data);
+       }
+
+       msg->output = NULL;
+       msg->input = NULL;
+       msg->extra = NULL;
+}
+
+void *
+p11_rpc_message_alloc_extra (p11_rpc_message *msg,
+                             size_t length)
+{
+       void **data;
+
+       assert (msg != NULL);
+
+       if (length > 0x7fffffff)
+               return NULL;
+
+       assert (msg->output->frealloc != NULL);
+       data = (msg->output->frealloc) (NULL, sizeof (void *) + length);
+       if (data == NULL)
+               return NULL;
+
+       /* Munch up the memory to help catch bugs */
+       memset (data, 0xff, sizeof (void *) + length);
+
+       /* Store pointer to next allocated block at beginning */
+       *data = msg->extra;
+       msg->extra = data;
+
+       /* Data starts after first pointer */
+       return (void *)(data + 1);
+}
+
+bool
+p11_rpc_message_prep (p11_rpc_message *msg,
+                      int call_id,
+                      p11_rpc_message_type type)
+{
+       int len;
+
+       assert (type != 0);
+       assert (call_id >= P11_RPC_CALL_ERROR);
+       assert (call_id < P11_RPC_CALL_MAX);
+
+       p11_buffer_reset (msg->output, 0);
+       msg->signature = NULL;
+
+       /* The call id and signature */
+       if (type == P11_RPC_REQUEST)
+               msg->signature = p11_rpc_calls[call_id].request;
+       else if (type == P11_RPC_RESPONSE)
+               msg->signature = p11_rpc_calls[call_id].response;
+       else
+               assert_not_reached ();
+       assert (msg->signature != NULL);
+       msg->sigverify = msg->signature;
+
+       msg->call_id = call_id;
+       msg->call_type = type;
+
+       /* Encode the two of them */
+       p11_rpc_buffer_add_uint32 (msg->output, call_id);
+       if (msg->signature) {
+               len = strlen (msg->signature);
+               p11_rpc_buffer_add_byte_array (msg->output, (unsigned char*)msg->signature, len);
+       }
+
+       msg->parsed = 0;
+       return !p11_buffer_failed (msg->output);
+}
+
+bool
+p11_rpc_message_parse (p11_rpc_message *msg,
+                       p11_rpc_message_type type)
+{
+       const unsigned char *val;
+       size_t len;
+       uint32_t call_id;
+
+       assert (msg != NULL);
+       assert (msg->input != NULL);
+
+       msg->parsed = 0;
+
+       /* Pull out the call identifier */
+       if (!p11_rpc_buffer_get_uint32 (msg->input, &msg->parsed, &call_id)) {
+               p11_message ("invalid message: couldn't read call identifier");
+               return false;
+       }
+
+       msg->signature = msg->sigverify = NULL;
+
+       /* The call id and signature */
+       if (call_id < 0 || call_id >= P11_RPC_CALL_MAX) {
+               p11_message ("invalid message: bad call id: %d", call_id);
+               return false;
+       }
+       if (type == P11_RPC_REQUEST)
+               msg->signature = p11_rpc_calls[call_id].request;
+       else if (type == P11_RPC_RESPONSE)
+               msg->signature = p11_rpc_calls[call_id].response;
+       else
+               assert_not_reached ();
+       assert (msg->signature != NULL);
+       msg->call_id = call_id;
+       msg->call_type = type;
+       msg->sigverify = msg->signature;
+
+       /* Verify the incoming signature */
+       if (!p11_rpc_buffer_get_byte_array (msg->input, &msg->parsed, &val, &len)) {
+               p11_message ("invalid message: couldn't read signature");
+               return false;
+       }
+
+       if ((strlen (msg->signature) != len) || (memcmp (val, msg->signature, len) != 0)) {
+               p11_message ("invalid message: signature doesn't match");
+               return false;
+       }
+
+       return true;
+}
+
+bool
+p11_rpc_message_verify_part (p11_rpc_message *msg,
+                             const char* part)
+{
+       int len;
+       bool ok;
+
+       if (!msg->sigverify)
+               return true;
+
+       len = strlen (part);
+       ok = (strncmp (msg->sigverify, part, len) == 0);
+       if (ok)
+               msg->sigverify += len;
+       return ok;
+}
+
+bool
+p11_rpc_message_write_attribute_buffer (p11_rpc_message *msg,
+                                        CK_ATTRIBUTE_PTR arr,
+                                        CK_ULONG num)
+{
+       CK_ATTRIBUTE_PTR attr;
+       CK_ULONG i;
+
+       assert (num == 0 || arr != NULL);
+       assert (msg != NULL);
+       assert (msg->output != NULL);
+
+       /* Make sure this is in the rigth order */
+       assert (!msg->signature || p11_rpc_message_verify_part (msg, "fA"));
+
+       /* Write the number of items */
+       p11_rpc_buffer_add_uint32 (msg->output, num);
+
+       for (i = 0; i < num; ++i) {
+               attr = &(arr[i]);
+
+               /* The attribute type */
+               p11_rpc_buffer_add_uint32 (msg->output, attr->type);
+
+               /* And the attribute buffer length */
+               p11_rpc_buffer_add_uint32 (msg->output, attr->pValue ? attr->ulValueLen : 0);
+       }
+
+       return !p11_buffer_failed (msg->output);
+}
+
+bool
+p11_rpc_message_write_attribute_array (p11_rpc_message *msg,
+                                       CK_ATTRIBUTE_PTR arr,
+                                       CK_ULONG num)
+{
+       CK_ULONG i;
+       CK_ATTRIBUTE_PTR attr;
+       unsigned char validity;
+
+       assert (num == 0 || arr != NULL);
+       assert (msg != NULL);
+       assert (msg->output != NULL);
+
+       /* Make sure this is in the rigth order */
+       assert (!msg->signature || p11_rpc_message_verify_part (msg, "aA"));
+
+       /* Write the number of items */
+       p11_rpc_buffer_add_uint32 (msg->output, num);
+
+       for (i = 0; i < num; ++i) {
+               attr = &(arr[i]);
+
+               /* The attribute type */
+               p11_rpc_buffer_add_uint32 (msg->output, attr->type);
+
+               /* Write out the attribute validity */
+               validity = (((CK_LONG)attr->ulValueLen) == -1) ? 0 : 1;
+               p11_rpc_buffer_add_byte (msg->output, validity);
+
+               /* The attribute length and value */
+               if (validity) {
+                       p11_rpc_buffer_add_uint32 (msg->output, attr->ulValueLen);
+                       p11_rpc_buffer_add_byte_array (msg->output, attr->pValue, attr->ulValueLen);
+               }
+       }
+
+       return !p11_buffer_failed (msg->output);
+}
+
+bool
+p11_rpc_message_read_byte (p11_rpc_message *msg,
+                           CK_BYTE *val)
+{
+       assert (msg != NULL);
+       assert (msg->input != NULL);
+
+       /* Make sure this is in the right order */
+       assert (!msg->signature || p11_rpc_message_verify_part (msg, "y"));
+       return p11_rpc_buffer_get_byte (msg->input, &msg->parsed, val);
+}
+
+bool
+p11_rpc_message_write_byte (p11_rpc_message *msg,
+                            CK_BYTE val)
+{
+       assert (msg != NULL);
+       assert (msg->output != NULL);
+
+       /* Make sure this is in the right order */
+       assert (!msg->signature || p11_rpc_message_verify_part (msg, "y"));
+       p11_rpc_buffer_add_byte (msg->output, val);
+       return !p11_buffer_failed (msg->output);
+}
+
+bool
+p11_rpc_message_read_ulong (p11_rpc_message *msg,
+                            CK_ULONG *val)
+{
+       uint64_t v;
+
+       assert (msg != NULL);
+       assert (msg->input != NULL);
+
+       /* Make sure this is in the right order */
+       assert (!msg->signature || p11_rpc_message_verify_part (msg, "u"));
+
+       if (!p11_rpc_buffer_get_uint64 (msg->input, &msg->parsed, &v))
+               return false;
+       if (val)
+               *val = (CK_ULONG)v;
+       return true;
+}
+
+bool
+p11_rpc_message_write_ulong (p11_rpc_message *msg,
+                             CK_ULONG val)
+{
+       assert (msg != NULL);
+       assert (msg->output != NULL);
+
+       /* Make sure this is in the rigth order */
+       assert (!msg->signature || p11_rpc_message_verify_part (msg, "u"));
+       p11_rpc_buffer_add_uint64 (msg->output, val);
+       return !p11_buffer_failed (msg->output);
+}
+
+bool
+p11_rpc_message_write_byte_buffer (p11_rpc_message *msg,
+                                   CK_ULONG count)
+{
+       assert (msg != NULL);
+       assert (msg->output != NULL);
+
+       /* Make sure this is in the right order */
+       assert (!msg->signature || p11_rpc_message_verify_part (msg, "fy"));
+       p11_rpc_buffer_add_uint32 (msg->output, count);
+       return !p11_buffer_failed (msg->output);
+}
+
+bool
+p11_rpc_message_write_byte_array (p11_rpc_message *msg,
+                                  CK_BYTE_PTR arr,
+                                  CK_ULONG num)
+{
+       assert (msg != NULL);
+       assert (msg->output != NULL);
+
+       /* Make sure this is in the right order */
+       assert (!msg->signature || p11_rpc_message_verify_part (msg, "ay"));
+
+       /* No array, no data, just length */
+       if (!arr) {
+               p11_rpc_buffer_add_byte (msg->output, 0);
+               p11_rpc_buffer_add_uint32 (msg->output, num);
+       } else {
+               p11_rpc_buffer_add_byte (msg->output, 1);
+               p11_rpc_buffer_add_byte_array (msg->output, arr, num);
+       }
+
+       return !p11_buffer_failed (msg->output);
+}
+
+bool
+p11_rpc_message_write_ulong_buffer (p11_rpc_message *msg,
+                                    CK_ULONG count)
+{
+       assert (msg != NULL);
+       assert (msg->output != NULL);
+
+       /* Make sure this is in the right order */
+       assert (!msg->signature || p11_rpc_message_verify_part (msg, "fu"));
+       p11_rpc_buffer_add_uint32 (msg->output, count);
+       return !p11_buffer_failed (msg->output);
+}
+
+bool
+p11_rpc_message_write_ulong_array (p11_rpc_message *msg,
+                                   CK_ULONG_PTR array,
+                                   CK_ULONG n_array)
+{
+       CK_ULONG i;
+
+       assert (msg != NULL);
+       assert (msg->output != NULL);
+
+       /* Check that we're supposed to have this at this point */
+       assert (!msg->signature || p11_rpc_message_verify_part (msg, "au"));
+
+       /* We send a byte which determines whether there's actual data present or not */
+       p11_rpc_buffer_add_byte (msg->output, array ? 1 : 0);
+       p11_rpc_buffer_add_uint32 (msg->output, n_array);
+
+       /* Now send the data if valid */
+       if (array) {
+               for (i = 0; i < n_array; ++i)
+                       p11_rpc_buffer_add_uint64 (msg->output, array[i]);
+       }
+
+       return !p11_buffer_failed (msg->output);
+}
+
+bool
+p11_rpc_message_read_version (p11_rpc_message *msg,
+                              CK_VERSION *version)
+{
+       assert (msg != NULL);
+       assert (msg->input != NULL);
+       assert (version != NULL);
+
+       /* Check that we're supposed to have this at this point */
+       assert (!msg->signature || p11_rpc_message_verify_part (msg, "v"));
+
+       return p11_rpc_buffer_get_byte (msg->input, &msg->parsed, &version->major) &&
+              p11_rpc_buffer_get_byte (msg->input, &msg->parsed, &version->minor);
+}
+
+bool
+p11_rpc_message_write_version (p11_rpc_message *msg,
+                               CK_VERSION *version)
+{
+       assert (msg != NULL);
+       assert (msg->output != NULL);
+       assert (version != NULL);
+
+       /* Check that we're supposed to have this at this point */
+       assert (!msg->signature || p11_rpc_message_verify_part (msg, "v"));
+
+       p11_rpc_buffer_add_byte (msg->output, version->major);
+       p11_rpc_buffer_add_byte (msg->output, version->minor);
+
+       return !p11_buffer_failed (msg->output);
+}
+
+bool
+p11_rpc_message_read_space_string (p11_rpc_message *msg,
+                                   CK_UTF8CHAR *buffer,
+                                   CK_ULONG length)
+{
+       const unsigned char *data;
+       size_t n_data;
+
+       assert (msg != NULL);
+       assert (msg->input != NULL);
+       assert (buffer != NULL);
+       assert (length != 0);
+
+       assert (!msg->signature || p11_rpc_message_verify_part (msg, "s"));
+
+       if (!p11_rpc_buffer_get_byte_array (msg->input, &msg->parsed, &data, &n_data))
+               return false;
+
+       if (n_data != length) {
+               p11_message ("invalid length space padded string received: %d != %d",
+                            (int)length, (int)n_data);
+               return false;
+       }
+
+       memcpy (buffer, data, length);
+       return true;
+}
+
+bool
+p11_rpc_message_write_space_string (p11_rpc_message *msg,
+                                    CK_UTF8CHAR *data,
+                                    CK_ULONG length)
+{
+       assert (msg != NULL);
+       assert (msg->output != NULL);
+       assert (data != NULL);
+       assert (length != 0);
+
+       assert (!msg->signature || p11_rpc_message_verify_part (msg, "s"));
+
+       p11_rpc_buffer_add_byte_array (msg->output, data, length);
+       return !p11_buffer_failed (msg->output);
+}
+
+bool
+p11_rpc_message_write_zero_string (p11_rpc_message *msg,
+                                   CK_UTF8CHAR *string)
+{
+       assert (msg != NULL);
+       assert (msg->output != NULL);
+       assert (string != NULL);
+
+       assert (!msg->signature || p11_rpc_message_verify_part (msg, "z"));
+
+       p11_rpc_buffer_add_byte_array (msg->output, string,
+                                      string ? strlen ((char *)string) : 0);
+       return !p11_buffer_failed (msg->output);
+}
+
+static void *
+log_allocator (void *pointer,
+               size_t size)
+{
+       void *result = realloc (pointer, (size_t)size);
+       return_val_if_fail (!size || result != NULL, NULL);
+       return result;
+}
+
+p11_buffer *
+p11_rpc_buffer_new (size_t reserve)
+{
+       return p11_rpc_buffer_new_full (reserve, log_allocator, free);
+}
+
+p11_buffer *
+p11_rpc_buffer_new_full (size_t reserve,
+                         void * (* frealloc) (void *data, size_t size),
+                         void (* ffree) (void *data))
+{
+       p11_buffer *buffer;
+
+       buffer = calloc (1, sizeof (p11_buffer));
+       return_val_if_fail (buffer != NULL, NULL);
+
+       p11_buffer_init_full (buffer, NULL, 0, 0, frealloc, ffree);
+       if (!p11_buffer_reset (buffer, reserve))
+               return_val_if_reached (NULL);
+
+       return buffer;
+}
+
+void
+p11_rpc_buffer_free (p11_buffer *buf)
+{
+       if (buf == NULL)
+               return;
+
+       p11_buffer_uninit (buf);
+       free (buf);
+}
+
+void
+p11_rpc_buffer_add_byte (p11_buffer *buf,
+                         unsigned char value)
+{
+       p11_buffer_add (buf, &value, 1);
+}
+
+int
+p11_rpc_buffer_get_byte (p11_buffer *buf,
+                         size_t *offset,
+                         unsigned char *val)
+{
+       unsigned char *ptr;
+       if (buf->len < 1 || *offset > buf->len - 1) {
+               p11_buffer_fail (buf);
+               return 0;
+       }
+       ptr = (unsigned char *)buf->data + *offset;
+       if (val != NULL)
+               *val = *ptr;
+       *offset = *offset + 1;
+       return 1;
+}
+
+void
+p11_rpc_buffer_encode_uint16 (unsigned char* data,
+                              uint16_t value)
+{
+       data[0] = (value >> 8) & 0xff;
+       data[1] = (value >> 0) & 0xff;
+}
+
+uint16_t
+p11_rpc_buffer_decode_uint16 (unsigned char* data)
+{
+       uint16_t value = data[0] << 8 | data[1];
+       return value;
+}
+
+void
+p11_rpc_buffer_add_uint16 (p11_buffer *buffer,
+                           uint16_t value)
+{
+       size_t offset = buffer->len;
+       if (!p11_buffer_append (buffer, 2))
+               return_if_reached ();
+       p11_rpc_buffer_set_uint16 (buffer, offset, value);
+}
+
+bool
+p11_rpc_buffer_set_uint16 (p11_buffer *buffer,
+                           size_t offset,
+                           uint16_t value)
+{
+       unsigned char *ptr;
+       if (buffer->len < 2 || offset > buffer->len - 2) {
+               p11_buffer_fail (buffer);
+               return false;
+       }
+       ptr = (unsigned char *)buffer->data + offset;
+       p11_rpc_buffer_encode_uint16 (ptr, value);
+       return true;
+}
+
+bool
+p11_rpc_buffer_get_uint16 (p11_buffer *buf,
+                           size_t *offset,
+                           uint16_t *value)
+{
+       unsigned char *ptr;
+       if (buf->len < 2 || *offset > buf->len - 2) {
+               p11_buffer_fail (buf);
+               return false;
+       }
+       ptr = (unsigned char*)buf->data + *offset;
+       if (value != NULL)
+               *value = p11_rpc_buffer_decode_uint16 (ptr);
+       *offset = *offset + 2;
+       return true;
+}
+
+void
+p11_rpc_buffer_encode_uint32 (unsigned char* data,
+                          uint32_t value)
+{
+       data[0] = (value >> 24) & 0xff;
+       data[1] = (value >> 16) & 0xff;
+       data[2] = (value >> 8) & 0xff;
+       data[3] = (value >> 0) & 0xff;
+}
+
+uint32_t
+p11_rpc_buffer_decode_uint32 (unsigned char* ptr)
+{
+       uint32_t val = ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3];
+       return val;
+}
+
+void
+p11_rpc_buffer_add_uint32 (p11_buffer *buffer,
+                           uint32_t value)
+{
+       size_t offset = buffer->len;
+       if (!p11_buffer_append (buffer, 4))
+               return_val_if_reached ();
+       p11_rpc_buffer_set_uint32 (buffer, offset, value);
+}
+
+bool
+p11_rpc_buffer_set_uint32 (p11_buffer *buffer,
+                           size_t offset,
+                           uint32_t value)
+{
+       unsigned char *ptr;
+       if (buffer->len < 4 || offset > buffer->len - 4) {
+               p11_buffer_fail (buffer);
+               return false;
+       }
+       ptr = (unsigned char*)buffer->data + offset;
+       p11_rpc_buffer_encode_uint32 (ptr, value);
+       return true;
+}
+
+bool
+p11_rpc_buffer_get_uint32 (p11_buffer *buf,
+                           size_t *offset,
+                           uint32_t *value)
+{
+       unsigned char *ptr;
+       if (buf->len < 4 || *offset > buf->len - 4) {
+               p11_buffer_fail (buf);
+               return false;
+       }
+       ptr = (unsigned char*)buf->data + *offset;
+       if (value != NULL)
+               *value = p11_rpc_buffer_decode_uint32 (ptr);
+       *offset = *offset + 4;
+       return true;
+}
+
+void
+p11_rpc_buffer_add_uint64 (p11_buffer *buffer,
+                           uint64_t value)
+{
+       p11_rpc_buffer_add_uint32 (buffer, ((value >> 32) & 0xffffffff));
+       p11_rpc_buffer_add_uint32 (buffer, (value & 0xffffffff));
+}
+
+bool
+p11_rpc_buffer_get_uint64 (p11_buffer *buf,
+                           size_t *offset,
+                           uint64_t *value)
+{
+       size_t off = *offset;
+       uint32_t a, b;
+       if (!p11_rpc_buffer_get_uint32 (buf, &off, &a) ||
+           !p11_rpc_buffer_get_uint32 (buf, &off, &b))
+               return false;
+       if (value != NULL)
+               *value = ((uint64_t)a) << 32 | b;
+       *offset = off;
+       return true;
+}
+
+void
+p11_rpc_buffer_add_byte_array (p11_buffer *buffer,
+                               const unsigned char *data,
+                               size_t length)
+{
+       if (data == NULL) {
+               p11_rpc_buffer_add_uint32 (buffer, 0xffffffff);
+               return;
+       } else if (length >= 0x7fffffff) {
+               p11_buffer_fail (buffer);
+               return;
+       }
+       p11_rpc_buffer_add_uint32 (buffer, length);
+       p11_buffer_add (buffer, data, length);
+}
+
+bool
+p11_rpc_buffer_get_byte_array (p11_buffer *buf,
+                               size_t *offset,
+                               const unsigned char **data,
+                               size_t *length)
+{
+       size_t off = *offset;
+       uint32_t len;
+       if (!p11_rpc_buffer_get_uint32 (buf, &off, &len))
+               return false;
+       if (len == 0xffffffff) {
+               *offset = off;
+               if (data)
+                       *data = NULL;
+               if (length)
+                       *length = 0;
+               return true;
+       } else if (len >= 0x7fffffff) {
+               p11_buffer_fail (buf);
+               return false;
+       }
+
+       if (buf->len < len || *offset > buf->len - len) {
+               p11_buffer_fail (buf);
+               return false;
+       }
+
+       if (data)
+               *data = (unsigned char *)buf->data + off;
+       if (length)
+               *length = len;
+       *offset = off + len;
+
+       return true;
+}
diff --git a/p11-kit/rpc-message.h b/p11-kit/rpc-message.h
new file mode 100644 (file)
index 0000000..f85265b
--- /dev/null
@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) 2008 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 <stefw@gnome.org>
+ */
+
+#ifndef _RPC_MESSAGE_H
+#define _RPC_MESSAGE_H
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdint.h>
+
+#include "buffer.h"
+#include "pkcs11.h"
+
+/* The calls, must be in sync with array below */
+enum {
+       P11_RPC_CALL_ERROR = 0,
+
+       P11_RPC_CALL_C_Initialize,
+       P11_RPC_CALL_C_Finalize,
+       P11_RPC_CALL_C_GetInfo,
+       P11_RPC_CALL_C_GetSlotList,
+       P11_RPC_CALL_C_GetSlotInfo,
+       P11_RPC_CALL_C_GetTokenInfo,
+       P11_RPC_CALL_C_GetMechanismList,
+       P11_RPC_CALL_C_GetMechanismInfo,
+       P11_RPC_CALL_C_InitToken,
+       P11_RPC_CALL_C_OpenSession,
+       P11_RPC_CALL_C_CloseSession,
+       P11_RPC_CALL_C_CloseAllSessions,
+       P11_RPC_CALL_C_GetSessionInfo,
+       P11_RPC_CALL_C_InitPIN,
+       P11_RPC_CALL_C_SetPIN,
+       P11_RPC_CALL_C_GetOperationState,
+       P11_RPC_CALL_C_SetOperationState,
+       P11_RPC_CALL_C_Login,
+       P11_RPC_CALL_C_Logout,
+       P11_RPC_CALL_C_CreateObject,
+       P11_RPC_CALL_C_CopyObject,
+       P11_RPC_CALL_C_DestroyObject,
+       P11_RPC_CALL_C_GetObjectSize,
+       P11_RPC_CALL_C_GetAttributeValue,
+       P11_RPC_CALL_C_SetAttributeValue,
+       P11_RPC_CALL_C_FindObjectsInit,
+       P11_RPC_CALL_C_FindObjects,
+       P11_RPC_CALL_C_FindObjectsFinal,
+       P11_RPC_CALL_C_EncryptInit,
+       P11_RPC_CALL_C_Encrypt,
+       P11_RPC_CALL_C_EncryptUpdate,
+       P11_RPC_CALL_C_EncryptFinal,
+       P11_RPC_CALL_C_DecryptInit,
+       P11_RPC_CALL_C_Decrypt,
+       P11_RPC_CALL_C_DecryptUpdate,
+       P11_RPC_CALL_C_DecryptFinal,
+       P11_RPC_CALL_C_DigestInit,
+       P11_RPC_CALL_C_Digest,
+       P11_RPC_CALL_C_DigestUpdate,
+       P11_RPC_CALL_C_DigestKey,
+       P11_RPC_CALL_C_DigestFinal,
+       P11_RPC_CALL_C_SignInit,
+       P11_RPC_CALL_C_Sign,
+       P11_RPC_CALL_C_SignUpdate,
+       P11_RPC_CALL_C_SignFinal,
+       P11_RPC_CALL_C_SignRecoverInit,
+       P11_RPC_CALL_C_SignRecover,
+       P11_RPC_CALL_C_VerifyInit,
+       P11_RPC_CALL_C_Verify,
+       P11_RPC_CALL_C_VerifyUpdate,
+       P11_RPC_CALL_C_VerifyFinal,
+       P11_RPC_CALL_C_VerifyRecoverInit,
+       P11_RPC_CALL_C_VerifyRecover,
+       P11_RPC_CALL_C_DigestEncryptUpdate,
+       P11_RPC_CALL_C_DecryptDigestUpdate,
+       P11_RPC_CALL_C_SignEncryptUpdate,
+       P11_RPC_CALL_C_DecryptVerifyUpdate,
+       P11_RPC_CALL_C_GenerateKey,
+       P11_RPC_CALL_C_GenerateKeyPair,
+       P11_RPC_CALL_C_WrapKey,
+       P11_RPC_CALL_C_UnwrapKey,
+       P11_RPC_CALL_C_DeriveKey,
+       P11_RPC_CALL_C_SeedRandom,
+       P11_RPC_CALL_C_GenerateRandom,
+       P11_RPC_CALL_C_WaitForSlotEvent,
+
+       P11_RPC_CALL_MAX
+};
+
+typedef struct {
+       int call_id;
+       const char* name;
+       const char* request;
+       const char* response;
+} p11_rpc_call;
+
+/*
+ *  a_ = prefix denotes array of _
+ *  A  = CK_ATTRIBUTE
+ *  f_ = prefix denotes buffer for _
+ *  M  = CK_MECHANISM
+ *  u  = CK_ULONG
+ *  s  = space padded string
+ *  v  = CK_VERSION
+ *  y  = CK_BYTE
+ *  z  = null terminated string
+ */
+
+static const p11_rpc_call p11_rpc_calls[] = {
+       { P11_RPC_CALL_ERROR,                  "ERROR",                  NULL,      "u"                    },
+       { P11_RPC_CALL_C_Initialize,           "C_Initialize",           "ay",      ""                     },
+       { P11_RPC_CALL_C_Finalize,             "C_Finalize",             "",        ""                     },
+       { P11_RPC_CALL_C_GetInfo,              "C_GetInfo",              "",        "vsusv"                },
+       { P11_RPC_CALL_C_GetSlotList,          "C_GetSlotList",          "yfu",     "au"                   },
+       { P11_RPC_CALL_C_GetSlotInfo,          "C_GetSlotInfo",          "u",       "ssuvv"                },
+       { P11_RPC_CALL_C_GetTokenInfo,         "C_GetTokenInfo",         "u",       "ssssuuuuuuuuuuuvvs"   },
+       { P11_RPC_CALL_C_GetMechanismList,     "C_GetMechanismList",     "ufu",     "au"                   },
+       { P11_RPC_CALL_C_GetMechanismInfo,     "C_GetMechanismInfo",     "uu",      "uuu"                  },
+       { P11_RPC_CALL_C_InitToken,            "C_InitToken",            "uayz",    ""                     },
+       { P11_RPC_CALL_C_OpenSession,          "C_OpenSession",          "uu",      "u"                    },
+       { P11_RPC_CALL_C_CloseSession,         "C_CloseSession",         "u",       ""                     },
+       { P11_RPC_CALL_C_CloseAllSessions,     "C_CloseAllSessions",     "u",       ""                     },
+       { P11_RPC_CALL_C_GetSessionInfo,       "C_GetSessionInfo",       "u",       "uuuu"                 },
+       { P11_RPC_CALL_C_InitPIN,              "C_InitPIN",              "uay",     ""                     },
+       { P11_RPC_CALL_C_SetPIN,               "C_SetPIN",               "uayay",   ""                     },
+       { P11_RPC_CALL_C_GetOperationState,    "C_GetOperationState",    "ufy",     "ay"                   },
+       { P11_RPC_CALL_C_SetOperationState,    "C_SetOperationState",    "uayuu",   ""                     },
+       { P11_RPC_CALL_C_Login,                "C_Login",                "uuay",    ""                     },
+       { P11_RPC_CALL_C_Logout,               "C_Logout",               "u",       ""                     },
+       { P11_RPC_CALL_C_CreateObject,         "C_CreateObject",         "uaA",     "u"                    },
+       { P11_RPC_CALL_C_CopyObject,           "C_CopyObject",           "uuaA",    "u"                    },
+       { P11_RPC_CALL_C_DestroyObject,        "C_DestroyObject",        "uu",      ""                     },
+       { P11_RPC_CALL_C_GetObjectSize,        "C_GetObjectSize",        "uu",      "u"                    },
+       { P11_RPC_CALL_C_GetAttributeValue,    "C_GetAttributeValue",    "uufA",    "aAu"                  },
+       { P11_RPC_CALL_C_SetAttributeValue,    "C_SetAttributeValue",    "uuaA",    ""                     },
+       { P11_RPC_CALL_C_FindObjectsInit,      "C_FindObjectsInit",      "uaA",     ""                     },
+       { P11_RPC_CALL_C_FindObjects,          "C_FindObjects",          "ufu",     "au"                   },
+       { P11_RPC_CALL_C_FindObjectsFinal,     "C_FindObjectsFinal",     "u",       ""                     },
+       { P11_RPC_CALL_C_EncryptInit,          "C_EncryptInit",          "uMu",     ""                     },
+       { P11_RPC_CALL_C_Encrypt,              "C_Encrypt",              "uayfy",   "ay"                   },
+       { P11_RPC_CALL_C_EncryptUpdate,        "C_EncryptUpdate",        "uayfy",   "ay"                   },
+       { P11_RPC_CALL_C_EncryptFinal,         "C_EncryptFinal",         "ufy",     "ay"                   },
+       { P11_RPC_CALL_C_DecryptInit,          "C_DecryptInit",          "uMu",     ""                     },
+       { P11_RPC_CALL_C_Decrypt,              "C_Decrypt",              "uayfy",   "ay"                   },
+       { P11_RPC_CALL_C_DecryptUpdate,        "C_DecryptUpdate",        "uayfy",   "ay"                   },
+       { P11_RPC_CALL_C_DecryptFinal,         "C_DecryptFinal",         "ufy",     "ay"                   },
+       { P11_RPC_CALL_C_DigestInit,           "C_DigestInit",           "uM",      ""                     },
+       { P11_RPC_CALL_C_Digest,               "C_Digest",               "uayfy",   "ay"                   },
+       { P11_RPC_CALL_C_DigestUpdate,         "C_DigestUpdate",         "uay",     ""                     },
+       { P11_RPC_CALL_C_DigestKey,            "C_DigestKey",            "uu",      ""                     },
+       { P11_RPC_CALL_C_DigestFinal,          "C_DigestFinal",          "ufy",     "ay"                   },
+       { P11_RPC_CALL_C_SignInit,             "C_SignInit",             "uMu",     ""                     },
+       { P11_RPC_CALL_C_Sign,                 "C_Sign",                 "uayfy",   "ay"                   },
+       { P11_RPC_CALL_C_SignUpdate,           "C_SignUpdate",           "uay",     ""                     },
+       { P11_RPC_CALL_C_SignFinal,            "C_SignFinal",            "ufy",     "ay"                   },
+       { P11_RPC_CALL_C_SignRecoverInit,      "C_SignRecoverInit",      "uMu",     ""                     },
+       { P11_RPC_CALL_C_SignRecover,          "C_SignRecover",          "uayfy",   "ay"                   },
+       { P11_RPC_CALL_C_VerifyInit,           "C_VerifyInit",           "uMu",     ""                     },
+       { P11_RPC_CALL_C_Verify,               "C_Verify",               "uayay",   ""                     },
+       { P11_RPC_CALL_C_VerifyUpdate,         "C_VerifyUpdate",         "uay",     ""                     },
+       { P11_RPC_CALL_C_VerifyFinal,          "C_VerifyFinal",          "uay",     ""                     },
+       { P11_RPC_CALL_C_VerifyRecoverInit,    "C_VerifyRecoverInit",    "uMu",     ""                     },
+       { P11_RPC_CALL_C_VerifyRecover,        "C_VerifyRecover",        "uayfy",   "ay"                   },
+       { P11_RPC_CALL_C_DigestEncryptUpdate,  "C_DigestEncryptUpdate",  "uayfy",   "ay"                   },
+       { P11_RPC_CALL_C_DecryptDigestUpdate,  "C_DecryptDigestUpdate",  "uayfy",   "ay"                   },
+       { P11_RPC_CALL_C_SignEncryptUpdate,    "C_SignEncryptUpdate",    "uayfy",   "ay"                   },
+       { P11_RPC_CALL_C_DecryptVerifyUpdate,  "C_DecryptVerifyUpdate",  "uayfy",   "ay"                   },
+       { P11_RPC_CALL_C_GenerateKey,          "C_GenerateKey",          "uMaA",    "u"                    },
+       { P11_RPC_CALL_C_GenerateKeyPair,      "C_GenerateKeyPair",      "uMaAaA",  "uu"                   },
+       { P11_RPC_CALL_C_WrapKey,              "C_WrapKey",              "uMuufy",  "ay"                   },
+       { P11_RPC_CALL_C_UnwrapKey,            "C_UnwrapKey",            "uMuayaA", "u"                    },
+       { P11_RPC_CALL_C_DeriveKey,            "C_DeriveKey",            "uMuaA",   "u"                    },
+       { P11_RPC_CALL_C_SeedRandom,           "C_SeedRandom",           "uay",     ""                     },
+       { P11_RPC_CALL_C_GenerateRandom,       "C_GenerateRandom",       "ufy",     "ay"                   },
+       { P11_RPC_CALL_C_WaitForSlotEvent,     "C_WaitForSlotEvent",     "u",       "u"                    },
+};
+
+#ifdef _DEBUG
+#define P11_RPC_CHECK_CALLS() \
+       { int i; for (i = 0; i < P11_RPC_CALL_MAX; ++i) assert (p11_rpc_calls[i].call_id == i); }
+#endif
+
+#define P11_RPC_HANDSHAKE \
+       ((unsigned char *)"PRIVATE-GNOME-KEYRING-PKCS11-PROTOCOL-V-1")
+#define P11_RPC_HANDSHAKE_LEN \
+       (strlen ((char *)P11_RPC_HANDSHAKE))
+
+typedef enum _p11_rpc_message_type {
+       P11_RPC_REQUEST = 1,
+       P11_RPC_RESPONSE
+} p11_rpc_message_type;
+
+typedef struct {
+       int call_id;
+       p11_rpc_message_type call_type;
+       const char *signature;
+       p11_buffer *input;
+       p11_buffer *output;
+       size_t parsed;
+       const char *sigverify;
+       void *extra;
+} p11_rpc_message;
+
+void             p11_rpc_message_init                    (p11_rpc_message *msg,
+                                                          p11_buffer *input,
+                                                          p11_buffer *output);
+
+void             p11_rpc_message_clear                   (p11_rpc_message *msg);
+
+#define          p11_rpc_message_is_verified(msg)        (!(msg)->sigverify || (msg)->sigverify[0] == 0)
+
+void *           p11_rpc_message_alloc_extra             (p11_rpc_message *msg,
+                                                          size_t length);
+
+bool             p11_rpc_message_prep                    (p11_rpc_message *msg,
+                                                          int call_id,
+                                                          p11_rpc_message_type type);
+
+bool             p11_rpc_message_parse                   (p11_rpc_message *msg,
+                                                          p11_rpc_message_type type);
+
+bool             p11_rpc_message_verify_part             (p11_rpc_message *msg,
+                                                          const char* part);
+
+bool             p11_rpc_message_write_byte              (p11_rpc_message *msg,
+                                                          CK_BYTE val);
+
+bool             p11_rpc_message_write_ulong             (p11_rpc_message *msg,
+                                                          CK_ULONG val);
+
+bool             p11_rpc_message_write_zero_string       (p11_rpc_message *msg,
+                                                          CK_UTF8CHAR *string);
+
+bool             p11_rpc_message_write_space_string      (p11_rpc_message *msg,
+                                                          CK_UTF8CHAR *buffer,
+                                                                   CK_ULONG length);
+
+bool             p11_rpc_message_write_byte_buffer       (p11_rpc_message *msg,
+                                                          CK_ULONG count);
+
+bool             p11_rpc_message_write_byte_array        (p11_rpc_message *msg,
+                                                          CK_BYTE_PTR arr,
+                                                          CK_ULONG num);
+
+bool             p11_rpc_message_write_ulong_buffer      (p11_rpc_message *msg,
+                                                          CK_ULONG count);
+
+bool             p11_rpc_message_write_ulong_array       (p11_rpc_message *msg,
+                                                          CK_ULONG_PTR arr,
+                                                          CK_ULONG num);
+
+bool             p11_rpc_message_write_attribute_buffer  (p11_rpc_message *msg,
+                                                          CK_ATTRIBUTE_PTR arr,
+                                                          CK_ULONG num);
+
+bool             p11_rpc_message_write_attribute_array   (p11_rpc_message *msg,
+                                                          CK_ATTRIBUTE_PTR arr,
+                                                          CK_ULONG num);
+
+bool             p11_rpc_message_write_version           (p11_rpc_message *msg,
+                                                          CK_VERSION* version);
+
+bool             p11_rpc_message_read_byte               (p11_rpc_message *msg,
+                                                          CK_BYTE* val);
+
+bool             p11_rpc_message_read_ulong              (p11_rpc_message *msg,
+                                                          CK_ULONG* val);
+
+bool             p11_rpc_message_read_space_string       (p11_rpc_message *msg,
+                                                          CK_UTF8CHAR* buffer,
+                                                          CK_ULONG length);
+
+bool             p11_rpc_message_read_version            (p11_rpc_message *msg,
+                                                          CK_VERSION* version);
+
+p11_buffer *     p11_rpc_buffer_new                      (size_t reserve);
+
+p11_buffer *     p11_rpc_buffer_new_full                 (size_t reserve,
+                                                          void * (* frealloc) (void *data, size_t size),
+                                                          void (* ffree) (void *data));
+
+void             p11_rpc_buffer_free                     (p11_buffer *buf);
+
+void             p11_rpc_buffer_add_byte                 (p11_buffer *buf,
+                                                          unsigned char value);
+
+int              p11_rpc_buffer_get_byte                 (p11_buffer *buf,
+                                                          size_t *offset,
+                                                          unsigned char *val);
+
+void             p11_rpc_buffer_encode_uint32            (unsigned char *data,
+                                                          uint32_t value);
+
+uint32_t         p11_rpc_buffer_decode_uint32            (unsigned char *data);
+
+void             p11_rpc_buffer_add_uint32               (p11_buffer *buffer,
+                                                          uint32_t value);
+
+bool             p11_rpc_buffer_set_uint32               (p11_buffer *buffer,
+                                                          size_t offset,
+                                                          uint32_t value);
+
+bool             p11_rpc_buffer_get_uint32               (p11_buffer *buf,
+                                                          size_t *offset,
+                                                          uint32_t *value);
+
+void             p11_rpc_buffer_encode_uint16            (unsigned char *data,
+                                                          uint16_t value);
+
+uint16_t         p11_rpc_buffer_decode_uint16            (unsigned char *data);
+
+void             p11_rpc_buffer_add_uint16               (p11_buffer *buffer,
+                                                          uint16_t val);
+
+bool             p11_rpc_buffer_set_uint16               (p11_buffer *buffer,
+                                                          size_t offset,
+                                                          uint16_t val);
+
+bool             p11_rpc_buffer_get_uint16               (p11_buffer *buf,
+                                                          size_t *offset,
+                                                          uint16_t *val);
+
+void             p11_rpc_buffer_add_byte_array           (p11_buffer *buffer,
+                                                          const unsigned char *val,
+                                                          size_t len);
+
+bool             p11_rpc_buffer_get_byte_array           (p11_buffer *buf,
+                                                          size_t *offset,
+                                                          const unsigned char **val,
+                                                          size_t *vlen);
+
+void             p11_rpc_buffer_add_uint64               (p11_buffer *buffer,
+                                                          uint64_t val);
+
+bool             p11_rpc_buffer_get_uint64               (p11_buffer *buf,
+                                                          size_t *offset,
+                                                          uint64_t *val);
+
+#endif /* _RPC_MESSAGE_H */
diff --git a/p11-kit/rpc-server.c b/p11-kit/rpc-server.c
new file mode 100644 (file)
index 0000000..a468e7a
--- /dev/null
@@ -0,0 +1,1901 @@
+/*
+ * Copyright (C) 2008 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 <stefw@gnome.org>
+ */
+
+#include "config.h"
+
+#define P11_DEBUG_FLAG P11_DEBUG_RPC
+#include "debug.h"
+#include "pkcs11.h"
+#include "library.h"
+#include "private.h"
+#include "message.h"
+#include "rpc.h"
+#include "rpc-message.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* The error returned on protocol failures */
+#define PARSE_ERROR CKR_DEVICE_ERROR
+#define PREP_ERROR  CKR_DEVICE_MEMORY
+
+static CK_RV
+proto_read_byte_buffer (p11_rpc_message *msg,
+                        CK_BYTE_PTR *buffer,
+                        CK_ULONG *n_buffer)
+{
+       uint32_t length;
+
+       assert (msg != NULL);
+       assert (buffer != NULL);
+       assert (n_buffer != NULL);
+       assert (msg->input != NULL);
+
+       /* Check that we're supposed to be reading this at this point */
+       assert (!msg->signature || p11_rpc_message_verify_part (msg, "fy"));
+
+       /* The number of ulongs there's room for on the other end */
+       if (!p11_rpc_buffer_get_uint32 (msg->input, &msg->parsed, &length))
+               return PARSE_ERROR;
+
+       *n_buffer = length;
+       *buffer = NULL;
+
+       /* If set to zero, then they just want the length */
+       if (length == 0)
+               return CKR_OK;
+
+       *buffer = p11_rpc_message_alloc_extra (msg, length * sizeof (CK_BYTE));
+       if (*buffer == NULL)
+               return CKR_DEVICE_MEMORY;
+
+       return CKR_OK;
+}
+
+static CK_RV
+proto_read_byte_array (p11_rpc_message *msg,
+                       CK_BYTE_PTR *array,
+                       CK_ULONG *n_array)
+{
+       const unsigned char *data;
+       unsigned char valid;
+       size_t n_data;
+
+       assert (msg != NULL);
+       assert (msg->input != NULL);
+
+       /* Check that we're supposed to have this at this point */
+       assert (!msg->signature || p11_rpc_message_verify_part (msg, "ay"));
+
+       /* Read out the byte which says whether data is present or not */
+       if (!p11_rpc_buffer_get_byte (msg->input, &msg->parsed, &valid))
+               return PARSE_ERROR;
+
+       if (!valid) {
+               *array = NULL;
+               *n_array = 0;
+               return CKR_OK;
+       }
+
+       /* Point our arguments into the buffer */
+       if (!p11_rpc_buffer_get_byte_array (msg->input, &msg->parsed, &data, &n_data))
+               return PARSE_ERROR;
+
+       *array = (CK_BYTE_PTR)data;
+       *n_array = n_data;
+       return CKR_OK;
+}
+
+static CK_RV
+proto_write_byte_array (p11_rpc_message *msg,
+                        CK_BYTE_PTR array,
+                        CK_ULONG len,
+                        CK_RV ret)
+{
+       assert (msg != NULL);
+
+       /*
+        * When returning an byte array, in many cases we need to pass
+        * an invalid array along with a length, which signifies CKR_BUFFER_TOO_SMALL.
+        */
+
+       switch (ret) {
+       case CKR_BUFFER_TOO_SMALL:
+               array = NULL;
+               /* fall through */
+       case CKR_OK:
+               break;
+
+       /* Pass all other errors straight through */
+       default:
+               return ret;
+       };
+
+       if (!p11_rpc_message_write_byte_array (msg, array, len))
+               return PREP_ERROR;
+
+       return CKR_OK;
+}
+
+static CK_RV
+proto_read_ulong_buffer (p11_rpc_message *msg,
+                         CK_ULONG_PTR *buffer,
+                         CK_ULONG *n_buffer)
+{
+       uint32_t length;
+
+       assert (msg != NULL);
+       assert (buffer != NULL);
+       assert (n_buffer != NULL);
+       assert (msg->input != NULL);
+
+       /* Check that we're supposed to be reading this at this point */
+       assert (!msg->signature || p11_rpc_message_verify_part (msg, "fu"));
+
+       /* The number of ulongs there's room for on the other end */
+       if (!p11_rpc_buffer_get_uint32 (msg->input, &msg->parsed, &length))
+               return PARSE_ERROR;
+
+       *n_buffer = length;
+       *buffer = NULL;
+
+       /* If set to zero, then they just want the length */
+       if (length == 0)
+               return CKR_OK;
+
+       *buffer = p11_rpc_message_alloc_extra (msg, length * sizeof (CK_ULONG));
+       if (!*buffer)
+               return CKR_DEVICE_MEMORY;
+
+       return CKR_OK;
+}
+
+static CK_RV
+proto_write_ulong_array (p11_rpc_message *msg,
+                         CK_ULONG_PTR array,
+                         CK_ULONG len,
+                         CK_RV ret)
+{
+       assert (msg != NULL);
+
+       /*
+        * When returning an ulong array, in many cases we need to pass
+        * an invalid array along with a length, which signifies CKR_BUFFER_TOO_SMALL.
+        */
+
+       switch (ret) {
+       case CKR_BUFFER_TOO_SMALL:
+               array = NULL;
+               /* fall through */
+       case CKR_OK:
+               break;
+
+       /* Pass all other errors straight through */
+       default:
+               return ret;
+       };
+
+       if (!p11_rpc_message_write_ulong_array (msg, array, len))
+               return PREP_ERROR;
+
+       return CKR_OK;
+}
+
+static CK_RV
+proto_read_attribute_buffer (p11_rpc_message *msg,
+                             CK_ATTRIBUTE_PTR *result,
+                             CK_ULONG *n_result)
+{
+       CK_ATTRIBUTE_PTR attrs;
+       uint32_t n_attrs, i;
+       uint32_t value;
+
+       assert (msg != NULL);
+       assert (result != NULL);
+       assert (n_result != NULL);
+       assert (msg->input != NULL);
+
+       /* Make sure this is in the rigth order */
+       assert (!msg->signature || p11_rpc_message_verify_part (msg, "fA"));
+
+       /* Read the number of attributes */
+       if (!p11_rpc_buffer_get_uint32 (msg->input, &msg->parsed, &n_attrs))
+               return PARSE_ERROR;
+
+       /* Allocate memory for the attribute structures */
+       attrs = p11_rpc_message_alloc_extra (msg, n_attrs * sizeof (CK_ATTRIBUTE));
+       if (attrs == NULL)
+               return CKR_DEVICE_MEMORY;
+
+       /* Now go through and fill in each one */
+       for (i = 0; i < n_attrs; ++i) {
+
+               /* The attribute type */
+               if (!p11_rpc_buffer_get_uint32 (msg->input, &msg->parsed, &value))
+                       return PARSE_ERROR;
+
+               attrs[i].type = value;
+
+               /* The number of bytes to allocate */
+               if (!p11_rpc_buffer_get_uint32 (msg->input, &msg->parsed, &value))
+                       return PARSE_ERROR;
+
+               if (value == 0) {
+                       attrs[i].pValue = NULL;
+                       attrs[i].ulValueLen = 0;
+               } else {
+                       attrs[i].pValue = p11_rpc_message_alloc_extra (msg, value);
+                       if (!attrs[i].pValue)
+                               return CKR_DEVICE_MEMORY;
+                       attrs[i].ulValueLen = value;
+               }
+       }
+
+       *result = attrs;
+       *n_result = n_attrs;
+       return CKR_OK;
+}
+
+static CK_RV
+proto_read_attribute_array (p11_rpc_message *msg,
+                            CK_ATTRIBUTE_PTR *result,
+                            CK_ULONG *n_result)
+{
+       CK_ATTRIBUTE_PTR attrs;
+       const unsigned char *data;
+       unsigned char valid;
+       uint32_t n_attrs, i;
+       uint32_t value;
+       size_t n_data;
+
+       assert (msg != NULL);
+       assert (result != NULL);
+       assert (n_result != NULL);
+       assert (msg->input != NULL);
+
+       /* Make sure this is in the rigth order */
+       assert (!msg->signature || p11_rpc_message_verify_part (msg, "aA"));
+
+       /* Read the number of attributes */
+       if (!p11_rpc_buffer_get_uint32 (msg->input, &msg->parsed, &n_attrs))
+               return PARSE_ERROR;
+
+       /* Allocate memory for the attribute structures */
+       attrs = p11_rpc_message_alloc_extra (msg, n_attrs * sizeof (CK_ATTRIBUTE));
+       if (attrs == NULL)
+               return CKR_DEVICE_MEMORY;
+
+       /* Now go through and fill in each one */
+       for (i = 0; i < n_attrs; ++i) {
+
+               /* The attribute type */
+               if (!p11_rpc_buffer_get_uint32 (msg->input, &msg->parsed, &value))
+                       return PARSE_ERROR;
+
+               attrs[i].type = value;
+
+               /* Whether this one is valid or not */
+               if (!p11_rpc_buffer_get_byte (msg->input, &msg->parsed, &valid))
+                       return PARSE_ERROR;
+
+               if (valid) {
+                       if (!p11_rpc_buffer_get_uint32 (msg->input, &msg->parsed, &value))
+                               return PARSE_ERROR;
+                       if (!p11_rpc_buffer_get_byte_array (msg->input, &msg->parsed, &data, &n_data))
+                               return PARSE_ERROR;
+
+                       if (data != NULL && n_data != value) {
+                               p11_message ("attribute length and data do not match");
+                               return PARSE_ERROR;
+                       }
+
+                       attrs[i].pValue = (CK_VOID_PTR)data;
+                       attrs[i].ulValueLen = value;
+               } else {
+                       attrs[i].pValue = NULL;
+                       attrs[i].ulValueLen = -1;
+               }
+       }
+
+       *result = attrs;
+       *n_result = n_attrs;
+       return CKR_OK;
+}
+
+static CK_RV
+proto_write_attribute_array (p11_rpc_message *msg,
+                             CK_ATTRIBUTE_PTR array,
+                             CK_ULONG len,
+                             CK_RV ret)
+{
+       assert (msg != NULL);
+
+       /*
+        * When returning an attribute array, certain errors aren't
+        * actually real errors, these are passed through to the other
+        * side along with the attribute array.
+        */
+
+       switch (ret) {
+       case CKR_ATTRIBUTE_SENSITIVE:
+       case CKR_ATTRIBUTE_TYPE_INVALID:
+       case CKR_BUFFER_TOO_SMALL:
+       case CKR_OK:
+               break;
+
+       /* Pass all other errors straight through */
+       default:
+               return ret;
+       };
+
+       if (!p11_rpc_message_write_attribute_array (msg, array, len) ||
+           !p11_rpc_message_write_ulong (msg, ret))
+               return PREP_ERROR;
+
+       return CKR_OK;
+}
+
+static CK_RV
+proto_read_null_string (p11_rpc_message *msg,
+                        CK_UTF8CHAR_PTR *val)
+{
+       const unsigned char *data;
+       size_t n_data;
+
+       assert (msg != NULL);
+       assert (val != NULL);
+       assert (msg->input != NULL);
+
+       /* Check that we're supposed to have this at this point */
+       assert (!msg->signature || p11_rpc_message_verify_part (msg, "z"));
+
+       if (!p11_rpc_buffer_get_byte_array (msg->input, &msg->parsed, &data, &n_data))
+               return PARSE_ERROR;
+
+       /* Allocate a block of memory for it */
+       *val = p11_rpc_message_alloc_extra (msg, n_data + 1);
+       if (*val == NULL)
+               return CKR_DEVICE_MEMORY;
+
+       memcpy (*val, data, n_data);
+       (*val)[n_data] = 0;
+
+       return CKR_OK;
+}
+
+static CK_RV
+proto_read_mechanism (p11_rpc_message *msg,
+                      CK_MECHANISM_PTR mech)
+{
+       const unsigned char *data;
+       uint32_t value;
+       size_t n_data;
+
+       assert (msg != NULL);
+       assert (mech != NULL);
+       assert (msg->input != NULL);
+
+       /* Make sure this is in the right order */
+       assert (!msg->signature || p11_rpc_message_verify_part (msg, "M"));
+
+       /* The mechanism type */
+       if (!p11_rpc_buffer_get_uint32 (msg->input, &msg->parsed, &value))
+               return PARSE_ERROR;
+
+       /* The mechanism data */
+       if (!p11_rpc_buffer_get_byte_array (msg->input, &msg->parsed, &data, &n_data))
+               return PARSE_ERROR;
+
+       mech->mechanism = value;
+       mech->pParameter = (CK_VOID_PTR)data;
+       mech->ulParameterLen = n_data;
+       return CKR_OK;
+}
+
+static CK_RV
+proto_write_info (p11_rpc_message *msg,
+                  CK_INFO_PTR info)
+{
+       assert (msg != NULL);
+       assert (info != NULL);
+
+       if (!p11_rpc_message_write_version (msg, &info->cryptokiVersion) ||
+           !p11_rpc_message_write_space_string (msg, info->manufacturerID, 32) ||
+           !p11_rpc_message_write_ulong (msg, info->flags) ||
+           !p11_rpc_message_write_space_string (msg, info->libraryDescription, 32) ||
+           !p11_rpc_message_write_version (msg, &info->libraryVersion))
+               return PREP_ERROR;
+
+       return CKR_OK;
+}
+
+static CK_RV
+proto_write_slot_info (p11_rpc_message *msg,
+                       CK_SLOT_INFO_PTR info)
+{
+       assert (msg != NULL);
+       assert (info != NULL);
+
+       if (!p11_rpc_message_write_space_string (msg, info->slotDescription, 64) ||
+           !p11_rpc_message_write_space_string (msg, info->manufacturerID, 32) ||
+           !p11_rpc_message_write_ulong (msg, info->flags) ||
+           !p11_rpc_message_write_version (msg, &info->hardwareVersion) ||
+           !p11_rpc_message_write_version (msg, &info->firmwareVersion))
+               return PREP_ERROR;
+
+       return CKR_OK;
+}
+
+static CK_RV
+proto_write_token_info (p11_rpc_message *msg,
+                        CK_TOKEN_INFO_PTR info)
+{
+       assert (msg != NULL);
+       assert (info != NULL);
+
+       if (!p11_rpc_message_write_space_string (msg, info->label, 32) ||
+           !p11_rpc_message_write_space_string (msg, info->manufacturerID, 32) ||
+           !p11_rpc_message_write_space_string (msg, info->model, 16) ||
+           !p11_rpc_message_write_space_string (msg, info->serialNumber, 16) ||
+           !p11_rpc_message_write_ulong (msg, info->flags) ||
+           !p11_rpc_message_write_ulong (msg, info->ulMaxSessionCount) ||
+           !p11_rpc_message_write_ulong (msg, info->ulSessionCount) ||
+           !p11_rpc_message_write_ulong (msg, info->ulMaxRwSessionCount) ||
+           !p11_rpc_message_write_ulong (msg, info->ulRwSessionCount) ||
+           !p11_rpc_message_write_ulong (msg, info->ulMaxPinLen) ||
+           !p11_rpc_message_write_ulong (msg, info->ulMinPinLen) ||
+           !p11_rpc_message_write_ulong (msg, info->ulTotalPublicMemory) ||
+           !p11_rpc_message_write_ulong (msg, info->ulFreePublicMemory) ||
+           !p11_rpc_message_write_ulong (msg, info->ulTotalPrivateMemory) ||
+           !p11_rpc_message_write_ulong (msg, info->ulFreePrivateMemory) ||
+           !p11_rpc_message_write_version (msg, &info->hardwareVersion) ||
+           !p11_rpc_message_write_version (msg, &info->firmwareVersion) ||
+           !p11_rpc_message_write_space_string (msg, info->utcTime, 16))
+               return PREP_ERROR;
+
+       return CKR_OK;
+}
+
+static CK_RV
+proto_write_mechanism_info (p11_rpc_message *msg,
+                            CK_MECHANISM_INFO_PTR info)
+{
+       assert (msg != NULL);
+       assert (info != NULL);
+
+       if (!p11_rpc_message_write_ulong (msg, info->ulMinKeySize) ||
+           !p11_rpc_message_write_ulong (msg, info->ulMaxKeySize) ||
+           !p11_rpc_message_write_ulong (msg, info->flags))
+               return PREP_ERROR;
+
+       return CKR_OK;
+}
+
+static CK_RV
+proto_write_session_info (p11_rpc_message *msg,
+                          CK_SESSION_INFO_PTR info)
+{
+       assert (msg != NULL);
+       assert (info != NULL);
+
+       if (!p11_rpc_message_write_ulong (msg, info->slotID) ||
+           !p11_rpc_message_write_ulong (msg, info->state) ||
+           !p11_rpc_message_write_ulong (msg, info->flags) ||
+           !p11_rpc_message_write_ulong (msg, info->ulDeviceError))
+               return PREP_ERROR;
+
+       return CKR_OK;
+}
+
+static CK_RV
+call_ready (p11_rpc_message *msg)
+{
+       assert (msg->output);
+
+       /*
+        * Called right before invoking the actual PKCS#11 function
+        * Reading out of data is complete, get ready to write return values.
+        */
+
+       if (p11_buffer_failed (msg->output)) {
+               p11_message ("invalid request from module, probably too short"); \
+               return PARSE_ERROR;
+       }
+
+       assert (p11_rpc_message_is_verified (msg));
+
+       /* All done parsing input */
+       msg->input = NULL;
+
+       if (!p11_rpc_message_prep (msg, msg->call_id, P11_RPC_RESPONSE)) {
+               p11_message ("couldn't initialize rpc response");
+               return CKR_DEVICE_MEMORY;
+       }
+
+       return CKR_OK;
+}
+
+/* -------------------------------------------------------------------
+ * CALL MACROS
+ */
+
+#define BEGIN_CALL(call_id) \
+       p11_debug (#call_id ": enter"); \
+       assert (msg != NULL); \
+       assert (self != NULL); \
+       {  \
+               CK_X_##call_id _func = self->C_##call_id; \
+               CK_RV _ret = CKR_OK; \
+               if (!_func) { _ret = CKR_GENERAL_ERROR; goto _cleanup; }
+
+#define PROCESS_CALL(args) \
+               _ret = call_ready (msg); \
+               if (_ret != CKR_OK) { goto _cleanup; } \
+               _ret = _func args
+
+#define END_CALL \
+       _cleanup: \
+               p11_debug ("ret: %d", (int)_ret); \
+               return _ret; \
+       }
+
+#define IN_BYTE(val) \
+       if (!p11_rpc_message_read_byte (msg, &val)) \
+               { _ret = PARSE_ERROR; goto _cleanup; }
+
+#define IN_ULONG(val) \
+       if (!p11_rpc_message_read_ulong (msg, &val)) \
+               { _ret = PARSE_ERROR; goto _cleanup; }
+
+#define IN_STRING(val) \
+       _ret = proto_read_null_string (msg, &val); \
+       if (_ret != CKR_OK) goto _cleanup;
+
+#define IN_BYTE_BUFFER(buffer, buffer_len) \
+       _ret = proto_read_byte_buffer (msg, &buffer, &buffer_len); \
+       if (_ret != CKR_OK) goto _cleanup;
+
+#define IN_BYTE_ARRAY(buffer, buffer_len) \
+       _ret = proto_read_byte_array (msg, &buffer, &buffer_len); \
+       if (_ret != CKR_OK) goto _cleanup;
+
+#define IN_ULONG_BUFFER(buffer, buffer_len) \
+       _ret = proto_read_ulong_buffer (msg, &buffer, &buffer_len); \
+       if (_ret != CKR_OK) goto _cleanup;
+
+#define IN_ATTRIBUTE_BUFFER(buffer, buffer_len) \
+       _ret = proto_read_attribute_buffer (msg, &buffer, &buffer_len); \
+       if (_ret != CKR_OK) goto _cleanup;
+
+#define IN_ATTRIBUTE_ARRAY(attrs, n_attrs) \
+       _ret = proto_read_attribute_array (msg, &attrs, &n_attrs); \
+       if (_ret != CKR_OK) goto _cleanup;
+
+#define IN_MECHANISM(mech) \
+       _ret = proto_read_mechanism (msg, &mech); \
+       if (_ret != CKR_OK) goto _cleanup;
+
+
+#define OUT_ULONG(val) \
+       if (_ret == CKR_OK && !p11_rpc_message_write_ulong (msg, val)) \
+               _ret = PREP_ERROR;
+
+#define OUT_BYTE_ARRAY(array, len) \
+       /* Note how we filter return codes */ \
+       _ret = proto_write_byte_array (msg, array, len, _ret);
+
+#define OUT_ULONG_ARRAY(array, len) \
+       /* Note how we filter return codes */ \
+       _ret = proto_write_ulong_array (msg, array, len, _ret);
+
+#define OUT_ATTRIBUTE_ARRAY(array, len) \
+       /* Note how we filter return codes */ \
+       _ret = proto_write_attribute_array (msg, array, len, _ret);
+
+#define OUT_INFO(val) \
+       if (_ret == CKR_OK) \
+               _ret = proto_write_info (msg, &val);
+
+#define OUT_SLOT_INFO(val) \
+       if (_ret == CKR_OK) \
+               _ret = proto_write_slot_info (msg, &val);
+
+#define OUT_TOKEN_INFO(val) \
+       if (_ret == CKR_OK) \
+               _ret = proto_write_token_info (msg, &val);
+
+#define OUT_MECHANISM_INFO(val) \
+       if (_ret == CKR_OK) \
+               _ret = proto_write_mechanism_info (msg, &val);
+
+#define OUT_SESSION_INFO(val) \
+       if (_ret == CKR_OK) \
+               _ret = proto_write_session_info (msg, &val);
+
+/* ---------------------------------------------------------------------------
+ * DISPATCH SPECIFIC CALLS
+ */
+
+static CK_RV
+rpc_C_Initialize (CK_X_FUNCTION_LIST *self,
+                  p11_rpc_message *msg)
+{
+       CK_X_Initialize func;
+       CK_C_INITIALIZE_ARGS init_args;
+       CK_BYTE_PTR handshake;
+       CK_ULONG n_handshake;
+       CK_RV ret = CKR_OK;
+
+       p11_debug ("C_Initialize: enter");
+
+       assert (msg != NULL);
+       assert (self != NULL);
+
+       ret = proto_read_byte_array (msg, &handshake, &n_handshake);
+       if (ret == CKR_OK) {
+
+               /* Check to make sure the header matches */
+               if (n_handshake != P11_RPC_HANDSHAKE_LEN ||
+                   memcmp (handshake, P11_RPC_HANDSHAKE, n_handshake) != 0) {
+                       p11_message ("invalid handshake received from connecting module");
+                       ret = CKR_GENERAL_ERROR;
+               }
+
+               assert (p11_rpc_message_is_verified (msg));
+       }
+
+       memset (&init_args, 0, sizeof (init_args));
+       init_args.flags = CKF_OS_LOCKING_OK;
+
+       func = self->C_Initialize;
+       assert (func != NULL);
+       ret = (func) (self, &init_args);
+
+       /* Empty response */
+       if (ret == CKR_OK)
+               ret = call_ready (msg);
+
+       p11_debug ("ret: %d", (int)ret);
+       return ret;
+}
+
+static CK_RV
+rpc_C_Finalize (CK_X_FUNCTION_LIST *self,
+                p11_rpc_message *msg)
+{
+       BEGIN_CALL (Finalize);
+       PROCESS_CALL ((self, NULL));
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_GetInfo (CK_X_FUNCTION_LIST *self,
+               p11_rpc_message *msg)
+{
+       CK_INFO info;
+
+       BEGIN_CALL (GetInfo);
+       PROCESS_CALL ((self, &info));
+               OUT_INFO (info);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_GetSlotList (CK_X_FUNCTION_LIST *self,
+                   p11_rpc_message *msg)
+{
+       CK_BBOOL token_present;
+       CK_SLOT_ID_PTR slot_list;
+       CK_ULONG count;
+
+       BEGIN_CALL (GetSlotList);
+               IN_BYTE (token_present);
+               IN_ULONG_BUFFER (slot_list, count);
+       PROCESS_CALL ((self, token_present, slot_list, &count));
+               OUT_ULONG_ARRAY (slot_list, count);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_GetSlotInfo (CK_X_FUNCTION_LIST *self,
+                   p11_rpc_message *msg)
+{
+       CK_SLOT_ID slot_id;
+       CK_SLOT_INFO info;
+
+       BEGIN_CALL (GetSlotInfo);
+               IN_ULONG (slot_id);
+       PROCESS_CALL ((self, slot_id, &info));
+               OUT_SLOT_INFO (info);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_GetTokenInfo (CK_X_FUNCTION_LIST *self,
+                    p11_rpc_message *msg)
+{
+       CK_SLOT_ID slot_id;
+       CK_TOKEN_INFO info;
+
+       BEGIN_CALL (GetTokenInfo);
+               IN_ULONG (slot_id);
+       PROCESS_CALL ((self, slot_id, &info));
+               OUT_TOKEN_INFO (info);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_GetMechanismList (CK_X_FUNCTION_LIST *self,
+                        p11_rpc_message *msg)
+{
+       CK_SLOT_ID slot_id;
+       CK_MECHANISM_TYPE_PTR mechanism_list;
+       CK_ULONG count;
+
+       BEGIN_CALL (GetMechanismList);
+               IN_ULONG (slot_id);
+               IN_ULONG_BUFFER (mechanism_list, count);
+       PROCESS_CALL ((self, slot_id, mechanism_list, &count));
+               OUT_ULONG_ARRAY (mechanism_list, count);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_GetMechanismInfo (CK_X_FUNCTION_LIST *self,
+                        p11_rpc_message *msg)
+{
+       CK_SLOT_ID slot_id;
+       CK_MECHANISM_TYPE type;
+       CK_MECHANISM_INFO info;
+
+       BEGIN_CALL (GetMechanismInfo);
+               IN_ULONG (slot_id);
+               IN_ULONG (type);
+       PROCESS_CALL ((self, slot_id, type, &info));
+               OUT_MECHANISM_INFO (info);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_InitToken (CK_X_FUNCTION_LIST *self,
+                 p11_rpc_message *msg)
+{
+       CK_SLOT_ID slot_id;
+       CK_UTF8CHAR_PTR pin;
+       CK_ULONG pin_len;
+       CK_UTF8CHAR_PTR label;
+
+       BEGIN_CALL (InitToken);
+               IN_ULONG (slot_id);
+               IN_BYTE_ARRAY (pin, pin_len);
+               IN_STRING (label);
+       PROCESS_CALL ((self, slot_id, pin, pin_len, label));
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_WaitForSlotEvent (CK_X_FUNCTION_LIST *self,
+                        p11_rpc_message *msg)
+{
+       CK_FLAGS flags;
+       CK_SLOT_ID slot_id;
+
+       BEGIN_CALL (WaitForSlotEvent);
+               IN_ULONG (flags);
+       PROCESS_CALL ((self, flags, &slot_id, NULL));
+               OUT_ULONG (slot_id);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_OpenSession (CK_X_FUNCTION_LIST *self,
+                   p11_rpc_message *msg)
+{
+       CK_SLOT_ID slot_id;
+       CK_FLAGS flags;
+       CK_SESSION_HANDLE session;
+
+       BEGIN_CALL (OpenSession);
+               IN_ULONG (slot_id);
+               IN_ULONG (flags);
+       PROCESS_CALL ((self, slot_id, flags, NULL, NULL, &session));
+               OUT_ULONG (session);
+       END_CALL;
+}
+
+
+static CK_RV
+rpc_C_CloseSession (CK_X_FUNCTION_LIST *self,
+                    p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+
+       BEGIN_CALL (CloseSession);
+               IN_ULONG (session);
+       PROCESS_CALL ((self, session));
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_CloseAllSessions (CK_X_FUNCTION_LIST *self,
+                        p11_rpc_message *msg)
+{
+       CK_SLOT_ID slot_id;
+
+       /* Slot id becomes appartment so lower layers can tell clients apart. */
+
+       BEGIN_CALL (CloseAllSessions);
+               IN_ULONG (slot_id);
+       PROCESS_CALL ((self, slot_id));
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_GetSessionInfo (CK_X_FUNCTION_LIST *self,
+                      p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_SESSION_INFO info;
+
+       BEGIN_CALL (GetSessionInfo);
+               IN_ULONG (session);
+       PROCESS_CALL ((self, session, &info));
+               OUT_SESSION_INFO (info);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_InitPIN (CK_X_FUNCTION_LIST *self,
+               p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_UTF8CHAR_PTR pin;
+       CK_ULONG pin_len;
+
+       BEGIN_CALL (InitPIN);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (pin, pin_len);
+       PROCESS_CALL ((self, session, pin, pin_len));
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_SetPIN (CK_X_FUNCTION_LIST *self,
+              p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_UTF8CHAR_PTR old_pin;
+       CK_ULONG old_len;
+       CK_UTF8CHAR_PTR new_pin;
+       CK_ULONG new_len;
+
+       BEGIN_CALL (SetPIN);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (old_pin, old_len);
+               IN_BYTE_ARRAY (new_pin, new_len);
+       PROCESS_CALL ((self, session, old_pin, old_len, new_pin, new_len));
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_GetOperationState (CK_X_FUNCTION_LIST *self,
+                                p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_BYTE_PTR operation_state;
+       CK_ULONG operation_state_len;
+
+       BEGIN_CALL (GetOperationState);
+               IN_ULONG (session);
+               IN_BYTE_BUFFER (operation_state, operation_state_len);
+       PROCESS_CALL ((self, session, operation_state, &operation_state_len));
+               OUT_BYTE_ARRAY (operation_state, operation_state_len);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_SetOperationState (CK_X_FUNCTION_LIST *self,
+                         p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_BYTE_PTR operation_state;
+       CK_ULONG operation_state_len;
+       CK_OBJECT_HANDLE encryption_key;
+       CK_OBJECT_HANDLE authentication_key;
+
+       BEGIN_CALL (SetOperationState);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (operation_state, operation_state_len);
+               IN_ULONG (encryption_key);
+               IN_ULONG (authentication_key);
+       PROCESS_CALL ((self, session, operation_state, operation_state_len, encryption_key, authentication_key));
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_Login (CK_X_FUNCTION_LIST *self,
+             p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_USER_TYPE user_type;
+       CK_UTF8CHAR_PTR pin;
+       CK_ULONG pin_len;
+
+       BEGIN_CALL (Login);
+               IN_ULONG (session);
+               IN_ULONG (user_type);
+               IN_BYTE_ARRAY (pin, pin_len);
+       PROCESS_CALL ((self, session, user_type, pin, pin_len));
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_Logout (CK_X_FUNCTION_LIST *self,
+              p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+
+       BEGIN_CALL (Logout);
+               IN_ULONG (session);
+       PROCESS_CALL ((self, session));
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_CreateObject (CK_X_FUNCTION_LIST *self,
+                    p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_ATTRIBUTE_PTR template;
+       CK_ULONG count;
+       CK_OBJECT_HANDLE new_object;
+
+       BEGIN_CALL (CreateObject);
+               IN_ULONG (session);
+               IN_ATTRIBUTE_ARRAY (template, count);
+       PROCESS_CALL ((self, session, template, count, &new_object));
+               OUT_ULONG (new_object);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_CopyObject (CK_X_FUNCTION_LIST *self,
+                  p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_OBJECT_HANDLE object;
+       CK_ATTRIBUTE_PTR template;
+       CK_ULONG count;
+       CK_OBJECT_HANDLE new_object;
+
+       BEGIN_CALL (CopyObject);
+               IN_ULONG (session);
+               IN_ULONG (object);
+               IN_ATTRIBUTE_ARRAY (template, count);
+       PROCESS_CALL ((self, session, object, template, count, &new_object));
+               OUT_ULONG (new_object);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_DestroyObject (CK_X_FUNCTION_LIST *self,
+                     p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_OBJECT_HANDLE object;
+
+       BEGIN_CALL (DestroyObject);
+               IN_ULONG (session);
+               IN_ULONG (object);
+       PROCESS_CALL ((self, session, object));
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_GetObjectSize (CK_X_FUNCTION_LIST *self,
+                     p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_OBJECT_HANDLE object;
+       CK_ULONG size;
+
+       BEGIN_CALL (GetObjectSize);
+               IN_ULONG (session);
+               IN_ULONG (object);
+       PROCESS_CALL ((self, session, object, &size));
+               OUT_ULONG (size);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_GetAttributeValue (CK_X_FUNCTION_LIST *self,
+                         p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_OBJECT_HANDLE object;
+       CK_ATTRIBUTE_PTR template;
+       CK_ULONG count;
+
+       BEGIN_CALL (GetAttributeValue);
+               IN_ULONG (session);
+               IN_ULONG (object);
+               IN_ATTRIBUTE_BUFFER (template, count);
+       PROCESS_CALL ((self, session, object, template, count));
+               OUT_ATTRIBUTE_ARRAY (template, count);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_SetAttributeValue (CK_X_FUNCTION_LIST *self,
+                         p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_OBJECT_HANDLE object;
+       CK_ATTRIBUTE_PTR template;
+       CK_ULONG count;
+
+       BEGIN_CALL (SetAttributeValue);
+               IN_ULONG (session);
+               IN_ULONG (object);
+               IN_ATTRIBUTE_ARRAY (template, count);
+       PROCESS_CALL ((self, session, object, template, count));
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_FindObjectsInit (CK_X_FUNCTION_LIST *self,
+                       p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_ATTRIBUTE_PTR template;
+       CK_ULONG count;
+
+       BEGIN_CALL (FindObjectsInit);
+               IN_ULONG (session);
+               IN_ATTRIBUTE_ARRAY (template, count);
+       PROCESS_CALL ((self, session, template, count));
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_FindObjects (CK_X_FUNCTION_LIST *self,
+                    p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_OBJECT_HANDLE_PTR objects;
+       CK_ULONG max_object_count;
+       CK_ULONG object_count;
+
+       BEGIN_CALL (FindObjects);
+               IN_ULONG (session);
+               IN_ULONG_BUFFER (objects, max_object_count);
+       PROCESS_CALL ((self, session, objects, max_object_count, &object_count));
+               OUT_ULONG_ARRAY (objects, object_count);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_FindObjectsFinal (CK_X_FUNCTION_LIST *self,
+                        p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+
+       BEGIN_CALL (FindObjectsFinal);
+               IN_ULONG (session);
+       PROCESS_CALL ((self, session));
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_EncryptInit (CK_X_FUNCTION_LIST *self,
+                   p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_MECHANISM mechanism;
+       CK_OBJECT_HANDLE key;
+
+       BEGIN_CALL (EncryptInit);
+               IN_ULONG (session);
+               IN_MECHANISM (mechanism);
+               IN_ULONG (key);
+       PROCESS_CALL ((self, session, &mechanism, key));
+       END_CALL;
+
+}
+
+static CK_RV
+rpc_C_Encrypt (CK_X_FUNCTION_LIST *self,
+               p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_BYTE_PTR data;
+       CK_ULONG data_len;
+       CK_BYTE_PTR encrypted_data;
+       CK_ULONG encrypted_data_len;
+
+       BEGIN_CALL (Encrypt);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (data, data_len);
+               IN_BYTE_BUFFER (encrypted_data, encrypted_data_len);
+       PROCESS_CALL ((self, session, data, data_len, encrypted_data, &encrypted_data_len));
+               OUT_BYTE_ARRAY (encrypted_data, encrypted_data_len);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_EncryptUpdate (CK_X_FUNCTION_LIST *self,
+                        p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_BYTE_PTR part;
+       CK_ULONG part_len;
+       CK_BYTE_PTR encrypted_part;
+       CK_ULONG encrypted_part_len;
+
+       BEGIN_CALL (EncryptUpdate);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (part, part_len);
+               IN_BYTE_BUFFER (encrypted_part, encrypted_part_len);
+       PROCESS_CALL ((self, session, part, part_len, encrypted_part, &encrypted_part_len));
+               OUT_BYTE_ARRAY (encrypted_part, encrypted_part_len);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_EncryptFinal (CK_X_FUNCTION_LIST *self,
+                    p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_BYTE_PTR last_encrypted_part;
+       CK_ULONG last_encrypted_part_len;
+
+       BEGIN_CALL (EncryptFinal);
+               IN_ULONG (session);
+               IN_BYTE_BUFFER (last_encrypted_part, last_encrypted_part_len);
+       PROCESS_CALL ((self, session, last_encrypted_part, &last_encrypted_part_len));
+               OUT_BYTE_ARRAY (last_encrypted_part, last_encrypted_part_len);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_DecryptInit (CK_X_FUNCTION_LIST *self,
+                    p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_MECHANISM mechanism;
+       CK_OBJECT_HANDLE key;
+
+       BEGIN_CALL (DecryptInit);
+               IN_ULONG (session);
+               IN_MECHANISM (mechanism);
+               IN_ULONG (key);
+       PROCESS_CALL ((self, session, &mechanism, key));
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_Decrypt (CK_X_FUNCTION_LIST *self,
+               p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_BYTE_PTR encrypted_data;
+       CK_ULONG encrypted_data_len;
+       CK_BYTE_PTR data;
+       CK_ULONG data_len;
+
+       BEGIN_CALL (Decrypt);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (encrypted_data, encrypted_data_len);
+               IN_BYTE_BUFFER (data, data_len);
+       PROCESS_CALL ((self, session, encrypted_data, encrypted_data_len, data, &data_len));
+               OUT_BYTE_ARRAY (data, data_len);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_DecryptUpdate (CK_X_FUNCTION_LIST *self,
+                     p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_BYTE_PTR encrypted_part;
+       CK_ULONG encrypted_part_len;
+       CK_BYTE_PTR part;
+       CK_ULONG part_len;
+
+       BEGIN_CALL (DecryptUpdate);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (encrypted_part, encrypted_part_len);
+               IN_BYTE_BUFFER (part, part_len);
+       PROCESS_CALL ((self, session, encrypted_part, encrypted_part_len, part, &part_len));
+               OUT_BYTE_ARRAY (part, part_len);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_DecryptFinal (CK_X_FUNCTION_LIST *self,
+                    p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_BYTE_PTR last_part;
+       CK_ULONG last_part_len;
+
+       BEGIN_CALL (DecryptFinal);
+               IN_ULONG (session);
+               IN_BYTE_BUFFER (last_part, last_part_len);
+       PROCESS_CALL ((self, session, last_part, &last_part_len));
+               OUT_BYTE_ARRAY (last_part, last_part_len);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_DigestInit (CK_X_FUNCTION_LIST *self,
+                  p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_MECHANISM mechanism;
+
+       BEGIN_CALL (DigestInit);
+               IN_ULONG (session);
+               IN_MECHANISM (mechanism);
+       PROCESS_CALL ((self, session, &mechanism));
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_Digest (CK_X_FUNCTION_LIST *self,
+              p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_BYTE_PTR data;
+       CK_ULONG data_len;
+       CK_BYTE_PTR digest;
+       CK_ULONG digest_len;
+
+       BEGIN_CALL (Digest);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (data, data_len);
+               IN_BYTE_BUFFER (digest, digest_len);
+       PROCESS_CALL ((self, session, data, data_len, digest, &digest_len));
+               OUT_BYTE_ARRAY (digest, digest_len);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_DigestUpdate (CK_X_FUNCTION_LIST *self,
+                    p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_BYTE_PTR part;
+       CK_ULONG part_len;
+
+       BEGIN_CALL (DigestUpdate);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (part, part_len);
+       PROCESS_CALL ((self, session, part, part_len));
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_DigestKey (CK_X_FUNCTION_LIST *self,
+                 p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_OBJECT_HANDLE key;
+
+       BEGIN_CALL (DigestKey);
+               IN_ULONG (session);
+               IN_ULONG (key);
+       PROCESS_CALL ((self, session, key));
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_DigestFinal (CK_X_FUNCTION_LIST *self,
+                   p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_BYTE_PTR digest;
+       CK_ULONG digest_len;
+
+       BEGIN_CALL (DigestFinal);
+               IN_ULONG (session);
+               IN_BYTE_BUFFER (digest, digest_len);
+       PROCESS_CALL ((self, session, digest, &digest_len));
+               OUT_BYTE_ARRAY (digest, digest_len);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_SignInit (CK_X_FUNCTION_LIST *self,
+                p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_MECHANISM mechanism;
+       CK_OBJECT_HANDLE key;
+
+       BEGIN_CALL (SignInit);
+               IN_ULONG (session);
+               IN_MECHANISM (mechanism);
+               IN_ULONG (key);
+       PROCESS_CALL ((self, session, &mechanism, key));
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_Sign (CK_X_FUNCTION_LIST *self,
+            p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_BYTE_PTR part;
+       CK_ULONG part_len;
+       CK_BYTE_PTR signature;
+       CK_ULONG signature_len;
+
+       BEGIN_CALL (Sign);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (part, part_len);
+               IN_BYTE_BUFFER (signature, signature_len);
+       PROCESS_CALL ((self, session, part, part_len, signature, &signature_len));
+               OUT_BYTE_ARRAY (signature, signature_len);
+       END_CALL;
+
+}
+
+static CK_RV
+rpc_C_SignUpdate (CK_X_FUNCTION_LIST *self,
+                  p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_BYTE_PTR part;
+       CK_ULONG part_len;
+
+       BEGIN_CALL (SignUpdate);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (part, part_len);
+       PROCESS_CALL ((self, session, part, part_len));
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_SignFinal (CK_X_FUNCTION_LIST *self,
+                 p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_BYTE_PTR signature;
+       CK_ULONG signature_len;
+
+       BEGIN_CALL (SignFinal);
+               IN_ULONG (session);
+               IN_BYTE_BUFFER (signature, signature_len);
+       PROCESS_CALL ((self, session, signature, &signature_len));
+               OUT_BYTE_ARRAY (signature, signature_len);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_SignRecoverInit (CK_X_FUNCTION_LIST *self,
+                       p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_MECHANISM mechanism;
+       CK_OBJECT_HANDLE key;
+
+       BEGIN_CALL (SignRecoverInit);
+               IN_ULONG (session);
+               IN_MECHANISM (mechanism);
+               IN_ULONG (key);
+       PROCESS_CALL ((self, session, &mechanism, key));
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_SignRecover (CK_X_FUNCTION_LIST *self,
+                   p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_BYTE_PTR data;
+       CK_ULONG data_len;
+       CK_BYTE_PTR signature;
+       CK_ULONG signature_len;
+
+       BEGIN_CALL (SignRecover);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (data, data_len);
+               IN_BYTE_BUFFER (signature, signature_len);
+       PROCESS_CALL ((self, session, data, data_len, signature, &signature_len));
+               OUT_BYTE_ARRAY (signature, signature_len);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_VerifyInit (CK_X_FUNCTION_LIST *self,
+                  p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_MECHANISM mechanism;
+       CK_OBJECT_HANDLE key;
+
+       BEGIN_CALL (VerifyInit);
+               IN_ULONG (session);
+               IN_MECHANISM (mechanism);
+               IN_ULONG (key);
+       PROCESS_CALL ((self, session, &mechanism, key));
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_Verify (CK_X_FUNCTION_LIST *self,
+              p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_BYTE_PTR data;
+       CK_ULONG data_len;
+       CK_BYTE_PTR signature;
+       CK_ULONG signature_len;
+
+       BEGIN_CALL (Verify);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (data, data_len);
+               IN_BYTE_ARRAY (signature, signature_len);
+       PROCESS_CALL ((self, session, data, data_len, signature, signature_len));
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_VerifyUpdate (CK_X_FUNCTION_LIST *self,
+                    p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_BYTE_PTR part;
+       CK_ULONG part_len;
+
+       BEGIN_CALL (VerifyUpdate);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (part, part_len);
+       PROCESS_CALL ((self, session, part, part_len));
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_VerifyFinal (CK_X_FUNCTION_LIST *self,
+                   p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_BYTE_PTR signature;
+       CK_ULONG signature_len;
+
+       BEGIN_CALL (VerifyFinal);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (signature, signature_len);
+       PROCESS_CALL ((self, session, signature, signature_len));
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_VerifyRecoverInit (CK_X_FUNCTION_LIST *self,
+                         p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_MECHANISM mechanism;
+       CK_OBJECT_HANDLE key;
+
+       BEGIN_CALL (VerifyRecoverInit);
+               IN_ULONG (session);
+               IN_MECHANISM (mechanism);
+               IN_ULONG (key);
+       PROCESS_CALL ((self, session, &mechanism, key));
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_VerifyRecover (CK_X_FUNCTION_LIST *self,
+                     p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_BYTE_PTR signature;
+       CK_ULONG signature_len;
+       CK_BYTE_PTR data;
+       CK_ULONG data_len;
+
+       BEGIN_CALL (VerifyRecover);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (signature, signature_len);
+               IN_BYTE_BUFFER (data, data_len);
+       PROCESS_CALL ((self, session, signature, signature_len, data, &data_len));
+               OUT_BYTE_ARRAY (data, data_len);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_DigestEncryptUpdate (CK_X_FUNCTION_LIST *self,
+                           p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_BYTE_PTR part;
+       CK_ULONG part_len;
+       CK_BYTE_PTR encrypted_part;
+       CK_ULONG encrypted_part_len;
+
+       BEGIN_CALL (DigestEncryptUpdate);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (part, part_len);
+               IN_BYTE_BUFFER (encrypted_part, encrypted_part_len);
+       PROCESS_CALL ((self, session, part, part_len, encrypted_part, &encrypted_part_len));
+               OUT_BYTE_ARRAY (encrypted_part, encrypted_part_len);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_DecryptDigestUpdate (CK_X_FUNCTION_LIST *self,
+                                    p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_BYTE_PTR encrypted_part;
+       CK_ULONG encrypted_part_len;
+       CK_BYTE_PTR part;
+       CK_ULONG part_len;
+
+       BEGIN_CALL (DecryptDigestUpdate);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (encrypted_part, encrypted_part_len);
+               IN_BYTE_BUFFER (part, part_len);
+       PROCESS_CALL ((self, session, encrypted_part, encrypted_part_len, part, &part_len));
+               OUT_BYTE_ARRAY (part, part_len);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_SignEncryptUpdate (CK_X_FUNCTION_LIST *self,
+                         p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_BYTE_PTR part;
+       CK_ULONG part_len;
+       CK_BYTE_PTR encrypted_part;
+       CK_ULONG encrypted_part_len;
+
+       BEGIN_CALL (SignEncryptUpdate);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (part, part_len);
+               IN_BYTE_BUFFER (encrypted_part, encrypted_part_len);
+       PROCESS_CALL ((self, session, part, part_len, encrypted_part, &encrypted_part_len));
+               OUT_BYTE_ARRAY (encrypted_part, encrypted_part_len);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_DecryptVerifyUpdate (CK_X_FUNCTION_LIST *self,
+                           p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_BYTE_PTR encrypted_part;
+       CK_ULONG encrypted_part_len;
+       CK_BYTE_PTR part;
+       CK_ULONG part_len;
+
+       BEGIN_CALL (DecryptVerifyUpdate);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (encrypted_part, encrypted_part_len);
+               IN_BYTE_BUFFER (part, part_len);
+       PROCESS_CALL ((self, session, encrypted_part, encrypted_part_len, part, &part_len));
+               OUT_BYTE_ARRAY (part, part_len);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_GenerateKey (CK_X_FUNCTION_LIST *self,
+                   p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_MECHANISM mechanism;
+       CK_ATTRIBUTE_PTR template;
+       CK_ULONG count;
+       CK_OBJECT_HANDLE key;
+
+       BEGIN_CALL (GenerateKey);
+               IN_ULONG (session);
+               IN_MECHANISM (mechanism);
+               IN_ATTRIBUTE_ARRAY (template, count);
+       PROCESS_CALL ((self, session, &mechanism, template, count, &key));
+               OUT_ULONG (key);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_GenerateKeyPair (CK_X_FUNCTION_LIST *self,
+                       p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_MECHANISM mechanism;
+       CK_ATTRIBUTE_PTR public_key_template;
+       CK_ULONG public_key_attribute_count;
+       CK_ATTRIBUTE_PTR private_key_template;
+       CK_ULONG private_key_attribute_count;
+       CK_OBJECT_HANDLE public_key;
+       CK_OBJECT_HANDLE private_key;
+
+       BEGIN_CALL (GenerateKeyPair);
+               IN_ULONG (session);
+               IN_MECHANISM (mechanism);
+               IN_ATTRIBUTE_ARRAY (public_key_template, public_key_attribute_count);
+               IN_ATTRIBUTE_ARRAY (private_key_template, private_key_attribute_count);
+       PROCESS_CALL ((self, session, &mechanism, public_key_template, public_key_attribute_count, private_key_template, private_key_attribute_count, &public_key, &private_key));
+               OUT_ULONG (public_key);
+               OUT_ULONG (private_key);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_WrapKey (CK_X_FUNCTION_LIST *self,
+               p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_MECHANISM mechanism;
+       CK_OBJECT_HANDLE wrapping_key;
+       CK_OBJECT_HANDLE key;
+       CK_BYTE_PTR wrapped_key;
+       CK_ULONG wrapped_key_len;
+
+       BEGIN_CALL (WrapKey);
+               IN_ULONG (session);
+               IN_MECHANISM (mechanism);
+               IN_ULONG (wrapping_key);
+               IN_ULONG (key);
+               IN_BYTE_BUFFER (wrapped_key, wrapped_key_len);
+       PROCESS_CALL ((self, session, &mechanism, wrapping_key, key, wrapped_key, &wrapped_key_len));
+               OUT_BYTE_ARRAY (wrapped_key, wrapped_key_len);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_UnwrapKey (CK_X_FUNCTION_LIST *self,
+                 p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_MECHANISM mechanism;
+       CK_OBJECT_HANDLE unwrapping_key;
+       CK_BYTE_PTR wrapped_key;
+       CK_ULONG wrapped_key_len;
+       CK_ATTRIBUTE_PTR template;
+       CK_ULONG attribute_count;
+       CK_OBJECT_HANDLE key;
+
+       BEGIN_CALL (UnwrapKey);
+               IN_ULONG (session);
+               IN_MECHANISM (mechanism);
+               IN_ULONG (unwrapping_key);
+               IN_BYTE_ARRAY (wrapped_key, wrapped_key_len);
+               IN_ATTRIBUTE_ARRAY (template, attribute_count);
+       PROCESS_CALL ((self, session, &mechanism, unwrapping_key, wrapped_key, wrapped_key_len, template, attribute_count, &key));
+               OUT_ULONG (key);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_DeriveKey (CK_X_FUNCTION_LIST *self,
+                 p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_MECHANISM mechanism;
+       CK_OBJECT_HANDLE base_key;
+       CK_ATTRIBUTE_PTR template;
+       CK_ULONG attribute_count;
+       CK_OBJECT_HANDLE key;
+
+       BEGIN_CALL (DeriveKey);
+               IN_ULONG (session);
+               IN_MECHANISM (mechanism);
+               IN_ULONG (base_key);
+               IN_ATTRIBUTE_ARRAY (template, attribute_count);
+       PROCESS_CALL ((self, session, &mechanism, base_key, template, attribute_count, &key));
+               OUT_ULONG (key);
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_SeedRandom (CK_X_FUNCTION_LIST *self,
+                  p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_BYTE_PTR seed;
+       CK_ULONG seed_len;
+
+       BEGIN_CALL (SeedRandom);
+               IN_ULONG (session);
+               IN_BYTE_ARRAY (seed, seed_len);
+       PROCESS_CALL ((self, session, seed, seed_len));
+       END_CALL;
+}
+
+static CK_RV
+rpc_C_GenerateRandom (CK_X_FUNCTION_LIST *self,
+                      p11_rpc_message *msg)
+{
+       CK_SESSION_HANDLE session;
+       CK_BYTE_PTR random_data;
+       CK_ULONG random_len;
+
+       BEGIN_CALL (GenerateRandom);
+               IN_ULONG (session);
+               IN_BYTE_BUFFER (random_data, random_len);
+       PROCESS_CALL ((self, session, random_data, random_len));
+               OUT_BYTE_ARRAY (random_data, random_len);
+       END_CALL;
+}
+
+bool
+p11_rpc_server_handle (CK_X_FUNCTION_LIST *self,
+                       p11_buffer *request,
+                       p11_buffer *response)
+{
+       p11_rpc_message msg;
+       CK_RV ret;
+       int req_id;
+
+       return_val_if_fail (self != NULL, false);
+       return_val_if_fail (request != NULL, false);
+       return_val_if_fail (response != NULL, false);
+
+       p11_message_clear ();
+
+       p11_rpc_message_init (&msg, request, response);
+
+       if (!p11_rpc_message_parse (&msg, P11_RPC_REQUEST)) {
+               p11_rpc_message_clear (&msg);
+               p11_message ("couldn't parse pkcs11 rpc message");
+               return false;
+       }
+
+       /* This should have been checked by the parsing code */
+       assert (msg.call_id > P11_RPC_CALL_ERROR);
+       assert (msg.call_id < P11_RPC_CALL_MAX);
+       req_id = msg.call_id;
+
+       switch(req_id) {
+       #define CASE_CALL(name) \
+       case P11_RPC_CALL_##name: \
+               ret = rpc_##name (self, &msg); \
+               break;
+       CASE_CALL (C_Initialize)
+       CASE_CALL (C_Finalize)
+       CASE_CALL (C_GetInfo)
+       CASE_CALL (C_GetSlotList)
+       CASE_CALL (C_GetSlotInfo)
+       CASE_CALL (C_GetTokenInfo)
+       CASE_CALL (C_GetMechanismList)
+       CASE_CALL (C_GetMechanismInfo)
+       CASE_CALL (C_InitToken)
+       CASE_CALL (C_OpenSession)
+       CASE_CALL (C_CloseSession)
+       CASE_CALL (C_CloseAllSessions)
+       CASE_CALL (C_GetSessionInfo)
+       CASE_CALL (C_InitPIN)
+       CASE_CALL (C_SetPIN)
+       CASE_CALL (C_GetOperationState)
+       CASE_CALL (C_SetOperationState)
+       CASE_CALL (C_Login)
+       CASE_CALL (C_Logout)
+       CASE_CALL (C_CreateObject)
+       CASE_CALL (C_CopyObject)
+       CASE_CALL (C_DestroyObject)
+       CASE_CALL (C_GetObjectSize)
+       CASE_CALL (C_GetAttributeValue)
+       CASE_CALL (C_SetAttributeValue)
+       CASE_CALL (C_FindObjectsInit)
+       CASE_CALL (C_FindObjects)
+       CASE_CALL (C_FindObjectsFinal)
+       CASE_CALL (C_EncryptInit)
+       CASE_CALL (C_Encrypt)
+       CASE_CALL (C_EncryptUpdate)
+       CASE_CALL (C_EncryptFinal)
+       CASE_CALL (C_DecryptInit)
+       CASE_CALL (C_Decrypt)
+       CASE_CALL (C_DecryptUpdate)
+       CASE_CALL (C_DecryptFinal)
+       CASE_CALL (C_DigestInit)
+       CASE_CALL (C_Digest)
+       CASE_CALL (C_DigestUpdate)
+       CASE_CALL (C_DigestKey)
+       CASE_CALL (C_DigestFinal)
+       CASE_CALL (C_SignInit)
+       CASE_CALL (C_Sign)
+       CASE_CALL (C_SignUpdate)
+       CASE_CALL (C_SignFinal)
+       CASE_CALL (C_SignRecoverInit)
+       CASE_CALL (C_SignRecover)
+       CASE_CALL (C_VerifyInit)
+       CASE_CALL (C_Verify)
+       CASE_CALL (C_VerifyUpdate)
+       CASE_CALL (C_VerifyFinal)
+       CASE_CALL (C_VerifyRecoverInit)
+       CASE_CALL (C_VerifyRecover)
+       CASE_CALL (C_DigestEncryptUpdate)
+       CASE_CALL (C_DecryptDigestUpdate)
+       CASE_CALL (C_SignEncryptUpdate)
+       CASE_CALL (C_DecryptVerifyUpdate)
+       CASE_CALL (C_GenerateKey)
+       CASE_CALL (C_GenerateKeyPair)
+       CASE_CALL (C_WrapKey)
+       CASE_CALL (C_UnwrapKey)
+       CASE_CALL (C_DeriveKey)
+       CASE_CALL (C_SeedRandom)
+       CASE_CALL (C_GenerateRandom)
+       CASE_CALL (C_WaitForSlotEvent)
+       #undef CASE_CALL
+       default:
+               /* This should have been caught by the parse code */
+               assert (0 && "Unchecked call");
+               break;
+       };
+
+       if (p11_buffer_failed (msg.output)) {
+               p11_message ("out of memory error putting together message");
+               p11_rpc_message_clear (&msg);
+               return false;
+       }
+
+       /* A filled in response */
+       if (ret == CKR_OK) {
+
+               /*
+                * Since we're dealing with many many functions above generating
+                * these messages we want to make sure each of them actually
+                * does what it's supposed to.
+                */
+               assert (p11_rpc_message_is_verified (&msg));
+               assert (msg.call_type == P11_RPC_RESPONSE);
+               assert (msg.call_id == req_id);
+               assert (p11_rpc_calls[msg.call_id].response);
+               assert (strcmp (p11_rpc_calls[msg.call_id].response, msg.signature) == 0);
+
+       /* Fill in an error respnose */
+       } else {
+               if (!p11_rpc_message_prep (&msg, P11_RPC_CALL_ERROR, P11_RPC_RESPONSE) ||
+                   !p11_rpc_message_write_ulong (&msg, (uint32_t)ret) ||
+                   p11_buffer_failed (msg.output)) {
+                       p11_message ("out of memory responding with error");
+                       p11_rpc_message_clear (&msg);
+                       return false;
+               }
+       }
+
+       p11_rpc_message_clear (&msg);
+       return true;
+}
diff --git a/p11-kit/rpc.h b/p11-kit/rpc.h
new file mode 100644 (file)
index 0000000..a86e796
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * 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 <stefw@gnome.org>
+ */
+
+#ifndef __P11_KIT_RPC_H__
+#define __P11_KIT_RPC_H__
+
+#include "pkcs11.h"
+#include "buffer.h"
+#include "virtual.h"
+
+typedef struct _p11_rpc_client_vtable p11_rpc_client_vtable;
+
+struct _p11_rpc_client_vtable {
+       void *data;
+
+       CK_RV       (* connect)       (p11_rpc_client_vtable *vtable,
+                                      void *init_reserved);
+
+       CK_RV       (* transport)     (p11_rpc_client_vtable *vtable,
+                                      p11_buffer *request,
+                                      p11_buffer *response);
+
+       void        (* disconnect)    (p11_rpc_client_vtable *vtable,
+                                      void *fini_reserved);
+
+       void *reserved[16];
+};
+
+bool                   p11_rpc_client_init         (p11_virtual *virt,
+                                                    p11_rpc_client_vtable *vtable);
+
+bool                   p11_rpc_server_handle       (CK_X_FUNCTION_LIST *funcs,
+                                                    p11_buffer *request,
+                                                    p11_buffer *response);
+
+extern CK_MECHANISM_TYPE *  p11_rpc_mechanisms_override_supported;
+
+#endif /* __P11_KIT_RPC_H__ */
index d270c74cb3a34cec1828937e87d459ac40fb1533..2192fed860e9953efd0825ab45c2fd5d14a528c4 100644 (file)
@@ -26,6 +26,7 @@ CHECK_PROGS = \
        test-deprecated \
        test-proxy \
        test-iter \
+       test-rpc \
        $(NULL)
 
 if WITH_FFI
index 5fba7ec8bc80c7e93027cdcb024edb2d66031a6d..079ff0d6aefafec53c89108244820796a66d47c5 100644 (file)
@@ -55,8 +55,14 @@ test_get_info (void)
        module = setup_mock_module (NULL);
 
        rv = (module->C_GetInfo) (&info);
-       assert (rv == CKR_OK);
-       assert (memcmp (&info, &MOCK_INFO, sizeof (CK_INFO)) == 0);
+       assert_num_eq (rv, CKR_OK);
+       assert_num_eq (MOCK_INFO.cryptokiVersion.major, info.cryptokiVersion.major);
+       assert_num_eq (MOCK_INFO.cryptokiVersion.minor, info.cryptokiVersion.minor);
+       assert (memcmp (MOCK_INFO.manufacturerID, info.manufacturerID, sizeof (info.manufacturerID)) == 0);
+       assert_num_eq (MOCK_INFO.flags, info.flags);
+       assert (memcmp (MOCK_INFO.libraryDescription, info.libraryDescription, sizeof (info.libraryDescription)) == 0);
+       assert_num_eq (MOCK_INFO.libraryVersion.major, info.libraryVersion.major);
+       assert_num_eq (MOCK_INFO.libraryVersion.minor, info.libraryVersion.minor);
 
        teardown_mock_module (module);
 }
diff --git a/p11-kit/tests/test-rpc.c b/p11-kit/tests/test-rpc.c
new file mode 100644 (file)
index 0000000..d945efd
--- /dev/null
@@ -0,0 +1,939 @@
+/*
+ * 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 "debug.h"
+#include "library.h"
+#include "message.h"
+#include "mock.h"
+#include "p11-kit.h"
+#include "private.h"
+#include "rpc.h"
+#include "rpc-message.h"
+#include "virtual.h"
+
+#include <sys/types.h>
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static void
+test_new_free (void)
+{
+       p11_buffer *buf;
+
+       buf = p11_rpc_buffer_new (0);
+
+       assert_ptr_not_null (buf->data);
+       assert_num_eq (0, buf->len);
+       assert_num_eq (0, buf->flags);
+       assert (buf->size == 0);
+       assert_ptr_not_null (buf->ffree);
+       assert_ptr_not_null (buf->frealloc);
+
+       p11_rpc_buffer_free (buf);
+}
+
+static void
+test_uint16 (void)
+{
+       p11_buffer buffer;
+       uint16_t val = 0xFFFF;
+       size_t next;
+       bool ret;
+
+       p11_buffer_init (&buffer, 0);
+
+       next = 0;
+       ret = p11_rpc_buffer_get_uint16 (&buffer, &next, &val);
+       assert_num_eq (false, ret);
+       assert_num_eq (0, next);
+       assert_num_eq (0xFFFF, val);
+
+       p11_buffer_reset (&buffer, 0);
+
+       ret = p11_rpc_buffer_set_uint16 (&buffer, 0, 0x6789);
+       assert_num_eq (false, ret);
+
+       p11_buffer_reset (&buffer, 0);
+
+       p11_buffer_add (&buffer, (unsigned char *)"padding", 7);
+
+       p11_rpc_buffer_add_uint16 (&buffer, 0x6789);
+       assert_num_eq (9, buffer.len);
+       assert (!p11_buffer_failed (&buffer));
+
+       next = 7;
+       ret = p11_rpc_buffer_get_uint16 (&buffer, &next, &val);
+       assert_num_eq (true, ret);
+       assert_num_eq (9, next);
+       assert_num_eq (0x6789, val);
+
+       p11_buffer_uninit (&buffer);
+}
+
+static void
+test_uint16_static (void)
+{
+       p11_buffer buf = { (unsigned char *)"pad0\x67\x89", 6, };
+       uint16_t val = 0xFFFF;
+       size_t next;
+       bool ret;
+
+       next = 4;
+       ret = p11_rpc_buffer_get_uint16 (&buf, &next, &val);
+       assert_num_eq (true, ret);
+       assert_num_eq (6, next);
+       assert_num_eq (0x6789, val);
+}
+
+static void
+test_uint32 (void)
+{
+       p11_buffer buffer;
+       uint32_t val = 0xFFFFFFFF;
+       size_t next;
+       bool ret;
+
+       p11_buffer_init (&buffer, 0);
+
+       next = 0;
+       ret = p11_rpc_buffer_get_uint32 (&buffer, &next, &val);
+       assert_num_eq (false, ret);
+       assert_num_eq (0, next);
+       assert_num_eq (0xFFFFFFFF, val);
+
+       p11_buffer_reset (&buffer, 0);
+
+       ret = p11_rpc_buffer_set_uint32 (&buffer, 0, 0x12345678);
+       assert_num_eq (false, ret);
+
+       p11_buffer_reset (&buffer, 0);
+
+       p11_buffer_add (&buffer, (unsigned char *)"padding", 7);
+
+       p11_rpc_buffer_add_uint32 (&buffer, 0x12345678);
+       assert_num_eq (11, buffer.len);
+       assert (!p11_buffer_failed (&buffer));
+
+       next = 7;
+       ret = p11_rpc_buffer_get_uint32 (&buffer, &next, &val);
+       assert_num_eq (true, ret);
+       assert_num_eq (11, next);
+       assert_num_eq (0x12345678, val);
+
+       p11_buffer_uninit (&buffer);
+}
+
+static void
+test_uint32_static (void)
+{
+       p11_buffer buf = { (unsigned char *)"pad0\x23\x45\x67\x89", 8, };
+       uint32_t val = 0xFFFFFFFF;
+       size_t next;
+       bool ret;
+
+       next = 4;
+       ret = p11_rpc_buffer_get_uint32 (&buf, &next, &val);
+       assert_num_eq (true, ret);
+       assert_num_eq (8, next);
+       assert_num_eq (0x23456789, val);
+}
+
+static void
+test_uint64 (void)
+{
+       p11_buffer buffer;
+       uint64_t val = 0xFFFFFFFFFFFFFFFF;
+       size_t next;
+       bool ret;
+
+       p11_buffer_init (&buffer, 0);
+
+       next = 0;
+       ret = p11_rpc_buffer_get_uint64 (&buffer, &next, &val);
+       assert_num_eq (0, ret);
+       assert_num_eq (0, next);
+       assert (0xFFFFFFFFFFFFFFFF == val);
+
+       p11_buffer_reset (&buffer, 0);
+
+       p11_buffer_add (&buffer, (unsigned char *)"padding", 7);
+
+       p11_rpc_buffer_add_uint64 (&buffer, 0x0123456708ABCDEF);
+       assert_num_eq (15, buffer.len);
+       assert (!p11_buffer_failed (&buffer));
+
+       next = 7;
+       ret = p11_rpc_buffer_get_uint64 (&buffer, &next, &val);
+       assert_num_eq (true, ret);
+       assert_num_eq (15, next);
+       assert (0x0123456708ABCDEF == val);
+
+       p11_buffer_uninit (&buffer);
+}
+
+static void
+test_uint64_static (void)
+{
+       p11_buffer buf = { (unsigned char *)"pad0\x89\x67\x45\x23\x11\x22\x33\x44", 12, };
+       uint64_t val = 0xFFFFFFFFFFFFFFFF;
+       size_t next;
+       bool ret;
+
+       next = 4;
+       ret = p11_rpc_buffer_get_uint64 (&buf, &next, &val);
+       assert_num_eq (true, ret);
+       assert_num_eq (12, next);
+       assert (0x8967452311223344 == val);
+}
+
+static void
+test_byte_array (void)
+{
+       p11_buffer buffer;
+       unsigned char bytes[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+                                 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+                                 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+                                 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F };
+
+       const unsigned char *val;
+       size_t length = ~0;
+       size_t next;
+       bool ret;
+
+       p11_buffer_init (&buffer, 0);
+
+       /* Invalid read */
+
+       next = 0;
+       ret = p11_rpc_buffer_get_byte_array (&buffer, &next, &val, &length);
+       assert_num_eq (false, ret);
+       assert_num_eq (0, next);
+       assert_num_eq (~0, length);
+
+       /* Test full array */
+
+       p11_buffer_reset (&buffer, 0);
+       p11_buffer_add (&buffer, (unsigned char *)"padding", 7);
+
+       p11_rpc_buffer_add_byte_array (&buffer, bytes, 32);
+       assert_num_eq (43, buffer.len);
+       assert (!p11_buffer_failed (&buffer));
+
+       next = 7;
+       ret = p11_rpc_buffer_get_byte_array (&buffer, &next, &val, &length);
+       assert_num_eq (true, ret);
+       assert_num_eq (43, next);
+       assert_num_eq (32, length);
+       assert (memcmp (val, bytes, 32) == 0);
+
+       p11_buffer_uninit (&buffer);
+}
+
+static void
+test_byte_array_null (void)
+{
+       p11_buffer buffer;
+       const unsigned char *val;
+       size_t length = ~0;
+       size_t next;
+       bool ret;
+
+       p11_buffer_init (&buffer, 0);
+
+       p11_buffer_reset (&buffer, 0);
+       p11_buffer_add (&buffer, (unsigned char *)"padding", 7);
+
+       p11_rpc_buffer_add_byte_array (&buffer, NULL, 0);
+       assert_num_eq (11, buffer.len);
+       assert (!p11_buffer_failed (&buffer));
+
+       next = 7;
+       ret = p11_rpc_buffer_get_byte_array (&buffer, &next, &val, &length);
+       assert_num_eq (true, ret);
+       assert_num_eq (11, next);
+       assert_num_eq (0, length);
+       assert_ptr_eq (NULL, (void*)val);
+
+       p11_buffer_uninit (&buffer);
+}
+
+static void
+test_byte_array_too_long (void)
+{
+       p11_buffer buffer;
+       const unsigned char *val = NULL;
+       size_t length = ~0;
+       size_t next;
+       bool ret;
+
+       p11_buffer_init (&buffer, 0);
+
+       p11_buffer_reset (&buffer, 0);
+       p11_buffer_add (&buffer, (unsigned char *)"padding", 7);
+       assert (!p11_buffer_failed (&buffer));
+
+       /* Passing a too short buffer here shouldn't matter, as length is checked for sanity */
+       p11_rpc_buffer_add_byte_array (&buffer, (unsigned char *)"", 0x9fffffff);
+       assert (p11_buffer_failed (&buffer));
+
+       /* Force write a too long byte arary to buffer */
+       p11_buffer_reset (&buffer, 0);
+       p11_rpc_buffer_add_uint32 (&buffer, 0x9fffffff);
+
+       next = 0;
+       ret = p11_rpc_buffer_get_byte_array (&buffer, &next, &val, &length);
+       assert_num_eq (false, ret);
+       assert_num_eq (0, next);
+       assert_num_eq (~0, length);
+       assert_ptr_eq (NULL, (void*)val);
+
+       p11_buffer_uninit (&buffer);
+}
+
+static void
+test_byte_array_static (void)
+{
+       unsigned char data[] = { 'p', 'a', 'd', 0x00, 0x00, 0x00, 0x00, 0x20,
+                                0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+                                0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+                                0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+                                0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F };
+       p11_buffer buf = { data, 0x40, };
+       const unsigned char *val;
+       size_t length = ~0;
+       size_t next;
+       bool ret;
+
+       next = 4;
+       ret = p11_rpc_buffer_get_byte_array (&buf, &next, &val, &length);
+       assert_num_eq (true, ret);
+       assert_num_eq (40, next);
+       assert_num_eq (32, length);
+       assert (memcmp (data + 8, val, 32) == 0);
+}
+
+static p11_virtual base;
+static bool rpc_initialized = false;
+
+static CK_RV
+rpc_initialize (p11_rpc_client_vtable *vtable,
+                void *init_reserved)
+{
+       assert_str_eq (vtable->data, "vtable-data");
+       assert_num_eq (false, rpc_initialized);
+       rpc_initialized = true;
+
+       return CKR_OK;
+}
+
+static CK_RV
+rpc_initialize_fails (p11_rpc_client_vtable *vtable,
+                      void *init_reserved)
+{
+       assert_str_eq (vtable->data, "vtable-data");
+       assert_num_eq (false, rpc_initialized);
+       return CKR_FUNCTION_FAILED;
+}
+
+static CK_RV
+rpc_initialize_device_removed (p11_rpc_client_vtable *vtable,
+                               void *init_reserved)
+{
+       assert_str_eq (vtable->data, "vtable-data");
+       assert_num_eq (false, rpc_initialized);
+       return CKR_DEVICE_REMOVED;
+}
+
+static CK_RV
+rpc_transport (p11_rpc_client_vtable *vtable,
+               p11_buffer *request,
+               p11_buffer *response)
+{
+       bool ret;
+
+       assert_str_eq (vtable->data, "vtable-data");
+
+       /* Just pass directly to the server code */
+       ret = p11_rpc_server_handle (&base.funcs, request, response);
+       assert (ret == true);
+
+       return CKR_OK;
+}
+
+static void
+rpc_finalize (p11_rpc_client_vtable *vtable,
+              void *fini_reserved)
+{
+       assert_str_eq (vtable->data, "vtable-data");
+       assert_num_eq (true, rpc_initialized);
+       rpc_initialized = false;
+}
+
+static void
+test_initialize (void)
+{
+       p11_rpc_client_vtable vtable = { "vtable-data", rpc_initialize, rpc_transport, rpc_finalize };
+       p11_virtual mixin;
+       bool ret;
+       CK_RV rv;
+
+       /* Build up our own function list */
+       rpc_initialized = false;
+       p11_virtual_init (&base, &p11_virtual_base, &mock_module_no_slots, NULL);
+
+       ret = p11_rpc_client_init (&mixin, &vtable);
+       assert_num_eq (true, ret);
+
+       rv = mixin.funcs.C_Initialize (&mixin.funcs, NULL);
+       assert (rv == CKR_OK);
+       assert_num_eq (true, rpc_initialized);
+
+       rv = mixin.funcs.C_Finalize (&mixin.funcs, NULL);
+       assert (rv == CKR_OK);
+       assert_num_eq (false, rpc_initialized);
+
+       p11_virtual_uninit (&mixin);
+}
+
+static void
+test_not_initialized (void)
+{
+       p11_rpc_client_vtable vtable = { "vtable-data", rpc_initialize, rpc_transport, rpc_finalize };
+       p11_virtual mixin;
+       CK_INFO info;
+       bool ret;
+       CK_RV rv;
+
+       /* Build up our own function list */
+       rpc_initialized = false;
+       p11_virtual_init (&base, &p11_virtual_base, &mock_module_no_slots, NULL);
+
+       ret = p11_rpc_client_init (&mixin, &vtable);
+       assert_num_eq (true, ret);
+
+       rv = (mixin.funcs.C_GetInfo) (&mixin.funcs, &info);
+       assert (rv == CKR_CRYPTOKI_NOT_INITIALIZED);
+
+       p11_virtual_uninit (&mixin);
+}
+
+static void
+test_initialize_fails_on_client (void)
+{
+       p11_rpc_client_vtable vtable = { "vtable-data", rpc_initialize_fails, rpc_transport, rpc_finalize };
+       p11_virtual mixin;
+       bool ret;
+       CK_RV rv;
+
+       /* Build up our own function list */
+       rpc_initialized = false;
+       p11_virtual_init (&base, &p11_virtual_base, &mock_module_no_slots, NULL);
+
+       ret = p11_rpc_client_init (&mixin, &vtable);
+       assert_num_eq (true, ret);
+
+       rv = (mixin.funcs.C_Initialize) (&mixin.funcs, NULL);
+       assert (rv == CKR_FUNCTION_FAILED);
+       assert_num_eq (false, rpc_initialized);
+
+       p11_virtual_uninit (&mixin);
+}
+
+static CK_RV
+rpc_transport_fails (p11_rpc_client_vtable *vtable,
+                     p11_buffer *request,
+                     p11_buffer *response)
+{
+       return CKR_FUNCTION_REJECTED;
+}
+
+static void
+test_transport_fails (void)
+{
+       p11_rpc_client_vtable vtable = { "vtable-data", rpc_initialize, rpc_transport_fails, rpc_finalize };
+       p11_virtual mixin;
+       bool ret;
+       CK_RV rv;
+
+       /* Build up our own function list */
+       rpc_initialized = false;
+       p11_virtual_init (&base, &p11_virtual_base, &mock_module_no_slots, NULL);
+
+       ret = p11_rpc_client_init (&mixin, &vtable);
+       assert_num_eq (true, ret);
+
+       rv = (mixin.funcs.C_Initialize) (&mixin.funcs, NULL);
+       assert (rv == CKR_FUNCTION_REJECTED);
+       assert_num_eq (false, rpc_initialized);
+
+       p11_virtual_uninit (&mixin);
+}
+
+static void
+test_initialize_fails_on_server (void)
+{
+       p11_rpc_client_vtable vtable = { "vtable-data", rpc_initialize, rpc_transport, rpc_finalize };
+       p11_virtual mixin;
+       bool ret;
+       CK_RV rv;
+
+       /* Build up our own function list */
+       p11_virtual_init (&base, &p11_virtual_base, &mock_module_no_slots, NULL);
+       base.funcs.C_Initialize = mock_X_Initialize__fails;
+
+       ret = p11_rpc_client_init (&mixin, &vtable);
+       assert_num_eq (true, ret);
+
+       rv = (mixin.funcs.C_Initialize) (&mixin.funcs, NULL);
+       assert (rv == CKR_FUNCTION_FAILED);
+       assert_num_eq (false, rpc_initialized);
+
+       p11_virtual_uninit (&mixin);
+}
+
+static CK_RV
+rpc_transport_bad_parse (p11_rpc_client_vtable *vtable,
+                         p11_buffer *request,
+                         p11_buffer *response)
+{
+       int rc;
+
+       assert_str_eq (vtable->data, "vtable-data");
+
+       /* Just zero bytes is an invalid message */
+       rc = p11_buffer_reset (response, 2);
+       assert (rc >= 0);
+
+       memset (response->data, 0, 2);
+       response->len = 2;
+       return CKR_OK;
+}
+
+static void
+test_transport_bad_parse (void)
+{
+       p11_rpc_client_vtable vtable = { "vtable-data", rpc_initialize, rpc_transport_bad_parse, rpc_finalize };
+       p11_virtual mixin;
+       bool ret;
+       CK_RV rv;
+
+       /* Build up our own function list */
+       rpc_initialized = false;
+       p11_virtual_init (&base, &p11_virtual_base, &mock_module_no_slots, NULL);
+
+       ret = p11_rpc_client_init (&mixin, &vtable);
+       assert_num_eq (true, ret);
+
+       p11_kit_be_quiet ();
+
+       rv = (mixin.funcs.C_Initialize) (&mixin.funcs, NULL);
+       assert (rv == CKR_DEVICE_ERROR);
+       assert_num_eq (0, rpc_initialized);
+
+       p11_message_loud ();
+       p11_virtual_uninit (&mixin);
+}
+
+static CK_RV
+rpc_transport_short_error (p11_rpc_client_vtable *vtable,
+                           p11_buffer *request,
+                           p11_buffer *response)
+{
+       int rc;
+
+       unsigned char data[] = {
+               0x00, 0x00, 0x00, 0x00,       /* RPC_CALL_ERROR */
+               0x00, 0x00, 0x00, 0x01, 0x75, /* signature 'u' */
+               0x00, 0x01,                   /* short error */
+       };
+
+       assert_str_eq (vtable->data, "vtable-data");
+
+       rc = p11_buffer_reset (response, sizeof (data));
+       assert (rc >= 0);
+
+       memcpy (response->data, data, sizeof (data));
+       response->len = sizeof (data);
+       return CKR_OK;
+}
+
+static void
+test_transport_short_error (void)
+{
+       p11_rpc_client_vtable vtable = { "vtable-data", rpc_initialize, rpc_transport_short_error, rpc_finalize };
+       p11_virtual mixin;
+       bool ret;
+       CK_RV rv;
+
+       /* Build up our own function list */
+       p11_virtual_init (&base, &p11_virtual_base, &mock_module_no_slots, NULL);
+
+       ret = p11_rpc_client_init (&mixin, &vtable);
+       assert_num_eq (true, ret);
+
+       p11_kit_be_quiet ();
+
+       rv = (mixin.funcs.C_Initialize) (&mixin.funcs, NULL);
+       assert (rv == CKR_DEVICE_ERROR);
+       assert_num_eq (0, rpc_initialized);
+
+       p11_message_loud ();
+       p11_virtual_uninit (&mixin);
+}
+
+static CK_RV
+rpc_transport_invalid_error (p11_rpc_client_vtable *vtable,
+                             p11_buffer *request,
+                             p11_buffer *response)
+{
+       int rc;
+
+       unsigned char data[] = {
+               0x00, 0x00, 0x00, 0x00,       /* RPC_CALL_ERROR */
+               0x00, 0x00, 0x00, 0x01, 0x75, /* signature 'u' */
+               0x00, 0x00, 0x00, 0x00,       /* a CKR_OK error*/
+               0x00, 0x00, 0x00, 0x00,
+       };
+
+       assert_str_eq (vtable->data, "vtable-data");
+
+       rc = p11_buffer_reset (response, sizeof (data));
+       assert (rc >= 0);
+       memcpy (response->data, data, sizeof (data));
+       response->len = sizeof (data);
+       return CKR_OK;
+}
+
+static void
+test_transport_invalid_error (void)
+{
+       p11_rpc_client_vtable vtable = { "vtable-data", rpc_initialize, rpc_transport_invalid_error, rpc_finalize };
+       p11_virtual mixin;
+       bool ret;
+       CK_RV rv;
+
+       /* Build up our own function list */
+       p11_virtual_init (&base, &p11_virtual_base, &mock_module_no_slots, NULL);
+
+       ret = p11_rpc_client_init (&mixin, &vtable);
+       assert_num_eq (true, ret);
+
+       p11_kit_be_quiet ();
+
+       rv = (mixin.funcs.C_Initialize) (&mixin.funcs, NULL);
+       assert (rv == CKR_DEVICE_ERROR);
+       assert_num_eq (0, rpc_initialized);
+
+       p11_message_loud ();
+       p11_virtual_uninit (&mixin);
+}
+
+static CK_RV
+rpc_transport_wrong_response (p11_rpc_client_vtable *vtable,
+                              p11_buffer *request,
+                              p11_buffer *response)
+{
+       int rc;
+
+       unsigned char data[] = {
+               0x00, 0x00, 0x00, 0x02,       /* RPC_CALL_C_Finalize */
+               0x00, 0x00, 0x00, 0x00,       /* signature '' */
+       };
+
+       assert_str_eq (vtable->data, "vtable-data");
+
+       rc = p11_buffer_reset (response, sizeof (data));
+       assert (rc >= 0);
+       memcpy (response->data, data, sizeof (data));
+       response->len = sizeof (data);
+       return CKR_OK;
+}
+
+static void
+test_transport_wrong_response (void)
+{
+       p11_rpc_client_vtable vtable = { "vtable-data", rpc_initialize, rpc_transport_wrong_response, rpc_finalize };
+       p11_virtual mixin;
+       bool ret;
+       CK_RV rv;
+
+       /* Build up our own function list */
+       p11_virtual_init (&base, &p11_virtual_base, &mock_module_no_slots, NULL);
+
+       ret = p11_rpc_client_init (&mixin, &vtable);
+       assert_num_eq (true, ret);
+
+       p11_kit_be_quiet ();
+
+       rv = (mixin.funcs.C_Initialize) (&mixin.funcs, NULL);
+       assert (rv == CKR_DEVICE_ERROR);
+       assert_num_eq (0, rpc_initialized);
+
+       p11_message_loud ();
+       p11_virtual_uninit (&mixin);
+}
+
+static CK_RV
+rpc_transport_bad_contents (p11_rpc_client_vtable *vtable,
+                            p11_buffer *request,
+                            p11_buffer *response)
+{
+       int rc;
+
+       unsigned char data[] = {
+               0x00, 0x00, 0x00, 0x02,       /* RPC_CALL_C_GetInfo */
+               0x00, 0x00, 0x00, 0x05,       /* signature 'vsusv' */
+               'v', 's', 'u', 's', 'v',
+               0x00, 0x00, 0x00, 0x00,       /* invalid data */
+       };
+
+       assert_str_eq (vtable->data, "vtable-data");
+
+       rc = p11_buffer_reset (response, sizeof (data));
+       assert (rc >= 0);
+       memcpy (response->data, data, sizeof (data));
+       response->len = sizeof (data);
+       return CKR_OK;
+}
+
+static void
+test_transport_bad_contents (void)
+{
+       p11_rpc_client_vtable vtable = { "vtable-data", rpc_initialize, rpc_transport_bad_contents, rpc_finalize };
+       p11_virtual mixin;
+       bool ret;
+       CK_RV rv;
+
+       /* Build up our own function list */
+       p11_virtual_init (&base, &p11_virtual_base, &mock_module_no_slots, NULL);
+
+       ret = p11_rpc_client_init (&mixin, &vtable);
+       assert_num_eq (true, ret);
+
+       p11_kit_be_quiet ();
+
+       rv = (mixin.funcs.C_Initialize) (&mixin.funcs, NULL);
+       assert (rv == CKR_DEVICE_ERROR);
+       assert_num_eq (0, rpc_initialized);
+
+       p11_message_loud ();
+       p11_virtual_uninit (&mixin);
+}
+
+static p11_rpc_client_vtable test_normal_vtable = {
+       NULL,
+       rpc_initialize,
+       rpc_transport,
+       rpc_finalize,
+};
+
+static p11_rpc_client_vtable test_device_removed_vtable = {
+       NULL,
+       rpc_initialize_device_removed,
+       rpc_transport,
+       rpc_finalize,
+};
+
+static void
+mixin_free (void *data)
+{
+       p11_virtual *mixin = data;
+       p11_virtual_uninit (mixin);
+       free (mixin);
+}
+
+static CK_FUNCTION_LIST_PTR
+setup_test_rpc_module (p11_rpc_client_vtable *vtable,
+                       CK_FUNCTION_LIST *module_template,
+                       CK_SESSION_HANDLE *session)
+{
+       CK_FUNCTION_LIST *rpc_module;
+       p11_virtual *mixin;
+       CK_RV rv;
+
+       /* Build up our own function list */
+       p11_virtual_init (&base, &p11_virtual_base, module_template, NULL);
+
+       mixin = calloc (1, sizeof (p11_virtual));
+       assert (mixin != NULL);
+
+       vtable->data = "vtable-data";
+       if (!p11_rpc_client_init (mixin, vtable))
+               assert_not_reached ();
+
+       rpc_module = p11_virtual_wrap (mixin, mixin_free);
+       assert_ptr_not_null (rpc_module);
+
+       rv = p11_kit_module_initialize (rpc_module);
+       assert (rv == CKR_OK);
+
+       if (session) {
+               rv = (rpc_module->C_OpenSession) (MOCK_SLOT_ONE_ID, CKF_RW_SESSION | CKF_SERIAL_SESSION,
+                                                 NULL, NULL, session);
+               assert (rv == CKR_OK);
+       }
+
+       return rpc_module;
+}
+
+static CK_FUNCTION_LIST *
+setup_mock_module (CK_SESSION_HANDLE *session)
+{
+       return setup_test_rpc_module (&test_normal_vtable, &mock_module, session);
+}
+
+static void
+teardown_mock_module (CK_FUNCTION_LIST *rpc_module)
+{
+       p11_kit_module_finalize (rpc_module);
+       p11_virtual_unwrap (rpc_module);
+}
+
+static void
+test_get_info_stand_in (void)
+{
+       CK_FUNCTION_LIST_PTR rpc_module;
+       CK_INFO info;
+       CK_RV rv;
+       char *string;
+
+       rpc_module = setup_test_rpc_module (&test_device_removed_vtable,
+                                           &mock_module_no_slots, NULL);
+
+       rv = (rpc_module->C_GetInfo) (&info);
+       assert (rv == CKR_OK);
+
+       assert_num_eq (CRYPTOKI_VERSION_MAJOR, info.cryptokiVersion.major);
+       assert_num_eq (CRYPTOKI_VERSION_MINOR, info.cryptokiVersion.minor);
+       string = p11_kit_space_strdup (info.manufacturerID, sizeof (info.manufacturerID));
+       assert_str_eq ("p11-kit", string);
+       free (string);
+       string = p11_kit_space_strdup (info.libraryDescription, sizeof (info.libraryDescription));
+       assert_str_eq ("p11-kit (no connection)", string);
+       free (string);
+       assert_num_eq (0, info.flags);
+       assert_num_eq (1, info.libraryVersion.major);
+       assert_num_eq (1, info.libraryVersion.minor);
+
+       teardown_mock_module (rpc_module);
+}
+
+static void
+test_get_slot_list_no_device (void)
+{
+       CK_FUNCTION_LIST_PTR rpc_module;
+       CK_SLOT_ID slot_list[8];
+       CK_ULONG count;
+       CK_RV rv;
+
+       rpc_module = setup_test_rpc_module (&test_device_removed_vtable,
+                                           &mock_module_no_slots, NULL);
+
+       rv = (rpc_module->C_GetSlotList) (CK_TRUE, NULL, &count);
+       assert (rv == CKR_OK);
+       assert_num_eq (0, count);
+       rv = (rpc_module->C_GetSlotList) (CK_FALSE, NULL, &count);
+       assert (rv == CKR_OK);
+       assert_num_eq (0, count);
+
+       count = 8;
+       rv = (rpc_module->C_GetSlotList) (CK_TRUE, slot_list, &count);
+       assert (rv == CKR_OK);
+       assert_num_eq (0, count);
+
+       count = 8;
+       rv = (rpc_module->C_GetSlotList) (CK_FALSE, slot_list, &count);
+       assert (rv == CKR_OK);
+       assert_num_eq (0, count);
+
+       teardown_mock_module (rpc_module);
+}
+
+#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,
+       };
+
+       mock_module_init ();
+       p11_library_init ();
+
+       /* Override the mechanisms that the RPC mechanism will handle */
+       p11_rpc_mechanisms_override_supported = mechanisms;
+
+       p11_test (test_new_free, "/rpc/new-free");
+       p11_test (test_uint16, "/rpc/uint16");
+       p11_test (test_uint16_static, "/rpc/uint16-static");
+       p11_test (test_uint32, "/rpc/uint32");
+       p11_test (test_uint32_static, "/rpc/uint32-static");
+       p11_test (test_uint64, "/rpc/uint64");
+       p11_test (test_uint64_static, "/rpc/uint64-static");
+       p11_test (test_byte_array, "/rpc/byte-array");
+       p11_test (test_byte_array_null, "/rpc/byte-array-null");
+       p11_test (test_byte_array_too_long, "/rpc/byte-array-too-long");
+       p11_test (test_byte_array_static, "/rpc/byte-array-static");
+
+       p11_test (test_initialize_fails_on_client, "/rpc/initialize-fails-on-client");
+       p11_test (test_initialize_fails_on_server, "/rpc/initialize-fails-on-server");
+       p11_test (test_initialize, "/rpc/initialize");
+       p11_test (test_not_initialized, "/rpc/not-initialized");
+       p11_test (test_transport_fails, "/rpc/transport-fails");
+       p11_test (test_transport_bad_parse, "/rpc/transport-bad-parse");
+       p11_test (test_transport_short_error, "/rpc/transport-short-error");
+       p11_test (test_transport_invalid_error, "/rpc/transport-invalid-error");
+       p11_test (test_transport_wrong_response, "/rpc/transport-wrong-response");
+       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");
+
+       test_mock_add_tests ("/rpc");
+
+       return  p11_test_run (argc, argv);
+}