gnome-keyring r1429 - in trunk: . pkcs11 pkcs11/rpc pkcs11/rpc-layer



Author: nnielsen
Date: Sun Jan  4 23:07:18 2009
New Revision: 1429
URL: http://svn.gnome.org/viewvc/gnome-keyring?rev=1429&view=rev

Log:
	* configure.in:
	* pkcs11/Makefile.am:
	* pkcs11/rpc-layer/: (renamed from rpc/)
	* pkcs11/rpc-layer/gck-rpc-daemon-standalone.c:
	* pkcs11/rpc-layer/gck-rpc-dispatch.c:
	* pkcs11/rpc-layer/gck-rpc-layer.h:
	* pkcs11/rpc-layer/gck-rpc-message.c:
	* pkcs11/rpc-layer/gck-rpc-module.c:
	* pkcs11/rpc-layer/gck-rpc-private.h:
	* pkcs11/rpc-layer/gck-rpc-util.c:
	* pkcs11/rpc-layer/Makefile.am: Rename 'rpc' to 'rpc-layer' component, and 
	reorganize how the files are installed.


Added:
   trunk/pkcs11/rpc-layer/   (props changed)
      - copied from r1425, /trunk/pkcs11/rpc/
   trunk/pkcs11/rpc-layer/gck-rpc-daemon-standalone.c
   trunk/pkcs11/rpc-layer/gck-rpc-dispatch.c
   trunk/pkcs11/rpc-layer/gck-rpc-layer.h
   trunk/pkcs11/rpc-layer/gck-rpc-message.c
   trunk/pkcs11/rpc-layer/gck-rpc-module.c
   trunk/pkcs11/rpc-layer/gck-rpc-private.h
   trunk/pkcs11/rpc-layer/gck-rpc-util.c
Removed:
   trunk/pkcs11/rpc/
Modified:
   trunk/ChangeLog
   trunk/configure.in
   trunk/pkcs11/Makefile.am
   trunk/pkcs11/rpc-layer/Makefile.am

Modified: trunk/configure.in
==============================================================================
--- trunk/configure.in	(original)
+++ trunk/configure.in	Sun Jan  4 23:07:18 2009
@@ -520,7 +520,7 @@
 pkcs11/ssh-agent/Makefile
 pkcs11/ssh-store/Makefile
 pkcs11/ssh-store/tests/Makefile
-pkcs11/rpc/Makefile
+pkcs11/rpc-layer/Makefile
 pkcs11/tests/Makefile
 po/Makefile.in
 tests/Makefile

Modified: trunk/pkcs11/Makefile.am
==============================================================================
--- trunk/pkcs11/Makefile.am	(original)
+++ trunk/pkcs11/Makefile.am	Sun Jan  4 23:07:18 2009
@@ -47,5 +47,5 @@
 TESTS_DIR = 
 endif
 
-SUBDIRS = . rpc gck ssh-agent ssh-store $(ROOTS_DIR) $(TESTS_DIR)
+SUBDIRS = . gck ssh-agent ssh-store rpc-layer $(ROOTS_DIR) $(TESTS_DIR)
 

Modified: trunk/pkcs11/rpc-layer/Makefile.am
==============================================================================
--- /trunk/pkcs11/rpc/Makefile.am	(original)
+++ trunk/pkcs11/rpc-layer/Makefile.am	Sun Jan  4 23:07:18 2009
@@ -1,17 +1,9 @@
 
 noinst_LTLIBRARIES = \
-	libp11-rpc-daemon.la \
-	libp11-rpc-module.la
-	
-# Have to install this somewhere to get libtool to actually 
-# build the library. Strange stuff.
-moduledir = /tmp
-
-module_LTLIBRARIES = \
-	p11-rpc-test-module.la
+	libgck-rpc-layer.la
 
 noinst_PROGRAMS = \
-	p11-rpc-test-daemon
+	gck-rpc-daemon-standalone
 	
 INCLUDES = -I. \
 	-I$(top_srcdir) \
@@ -20,47 +12,50 @@
 # ------------------------------------------------------------------------------
 # The dispatch code
 
-libp11_rpc_daemon_la_SOURCES = \
-	p11-rpc-private.h \
-	p11-rpc-dispatch.c \
-	p11-rpc-message.c \
-	p11-rpc-util.c
-
-libp11_rpc_daemon_la_LIBADD = \
-	$(top_builddir)/common/libgkr-common-buffer.la 
+libgck_rpc_layer_la_SOURCES = \
+	gck-rpc-private.h \
+	gck-rpc-dispatch.c \
+	gck-rpc-message.c \
+	gck-rpc-util.c
+
+libgck_rpc_layer_la_LIBADD = \
+	$(top_builddir)/common/libgkr-common-buffer.la \
+	$(GOBJECT_LIBS) \
+    	$(GTHREAD_LIBS) \
+	$(GLIB_LIBS) 
+
+libgck_rpc_layer_la_CFLAGS = \
+	$(GOBJECT_CFLAGS) \
+    	$(GTHREAD_CFLAGS) \
+	$(GLIB_CFLAGS) 
 
 # ------------------------------------------------------------------------------
 # The module code 
 
-libp11_rpc_module_la_SOURCES = \
-	p11-rpc-private.h \
-	p11-rpc-module.c \
-	p11-rpc-message.c \
-	p11-rpc-util.c
+moduledir = $(libdir)/gnome-keyring/devel/
 
-libp11_rpc_module_la_LIBADD = \
-	$(top_builddir)/common/libgkr-common-buffer.la 
+module_LTLIBRARIES = \
+	gck-rpc-layer-standalone.la
 
-# ------------------------------------------------------------------------------
-# The test module 
+gck_rpc_layer_standalone_la_SOURCES = \
+	gck-rpc-private.h \
+	gck-rpc-module.c \
+	gck-rpc-message.c \
+	gck-rpc-util.c
+
+gck_rpc_layer_standalone_la_LIBADD = \
+	$(top_builddir)/common/libgkr-common-buffer.la 
 
-p11_rpc_test_module_la_LDFLAGS = \
+gck_rpc_layer_standalone_la_LDFLAGS = \
 	-module -avoid-version \
 	-no-undefined -export-symbols-regex 'C_GetFunctionList'
 
-p11_rpc_test_module_la_SOURCES = \
-	p11-rpc-test-module.c
-
-p11_rpc_test_module_la_LIBADD = \
-	libp11-rpc-module.la
-
 # -----------------------------------------------------------------------------
 # The test daemon 
 
-p11_rpc_test_daemon_SOURCES = \
-	p11-rpc-test-daemon.c
+gck_rpc_daemon_standalone_SOURCES = \
+	gck-rpc-daemon-standalone.c
 	
-p11_rpc_test_daemon_LDADD = \
-	-ldl -lpthread \
-	libp11-rpc-daemon.la
+gck_rpc_daemon_standalone_LDADD = \
+	-ldl -lpthread libgck-rpc-layer.la
 	
\ No newline at end of file

Added: trunk/pkcs11/rpc-layer/gck-rpc-daemon-standalone.c
==============================================================================
--- (empty file)
+++ trunk/pkcs11/rpc-layer/gck-rpc-daemon-standalone.c	Sun Jan  4 23:07:18 2009
@@ -0,0 +1,116 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gck-rpc-daemon-standalone.c - A sample daemon.
+
+   Copyright (C) 2008, Stef Walter
+
+   The Gnome Keyring Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Keyring Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Stef Walter <stef memberwebs com>
+*/
+
+#include "config.h"
+
+#include "pkcs11/pkcs11.h"
+
+#include "gck-rpc-layer.h"
+
+#include <stdio.h>
+#include <err.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <dlfcn.h>
+#include <pthread.h>
+
+#define SOCKET_PATH "/tmp/gck-rpc-daemon.sock"
+
+/* Sample configuration for loading NSS remotely */
+static CK_C_INITIALIZE_ARGS p11_init_args = {
+	NULL,
+        NULL,
+        NULL,
+        NULL,
+        CKF_OS_LOCKING_OK,
+        "init-string = configdir='/tmp' certPrefix='' keyPrefix='' secmod='/tmp/secmod.db' flags="
+};
+
+static int is_running = 1;
+
+static int 
+usage (void)
+{
+	fprintf (stderr, "usage: gck-rpc-daemon pkcs11-module");
+	exit (2);
+}
+
+int
+main (int argc, char *argv[])
+{
+	CK_C_GetFunctionList func_get_list;
+	CK_FUNCTION_LIST_PTR funcs;
+	void *module;
+	fd_set read_fds;
+	int sock, ret;
+	CK_RV rv;
+	
+	/* The module to load is the argument */
+	if (argc != 2)
+		usage();
+
+	/* Load the library */
+	module = dlopen(argv[1], RTLD_NOW);
+	if(!module) 
+		errx (1, "couldn't open library: %s: %s", argv[1], dlerror());
+
+	/* Lookup the appropriate function in library */
+	func_get_list = (CK_C_GetFunctionList)dlsym (module, "C_GetFunctionList");
+	if (!func_get_list)
+		errx (1, "couldn't find C_GetFunctionList in library: %s: %s", 
+		      argv[1], dlerror());
+	
+	/* Get the function list */
+	rv = (func_get_list) (&funcs);
+	if (rv != CKR_OK || !funcs)
+		errx (1, "couldn't get function list from C_GetFunctionList in libary: %s: 0x%08x", 
+		      argv[1], (int)rv);
+	
+	unlink (SOCKET_PATH);
+	sock = gck_rpc_dispatch_init (SOCKET_PATH, funcs, &p11_init_args);
+	if (sock == -1)
+		exit (1);
+	
+	is_running = 1;
+	while (is_running) {
+		FD_ZERO (&read_fds);
+		FD_SET (sock, &read_fds);
+		ret = select (sock + 1, &read_fds, NULL, NULL, NULL);
+		if (ret < 0) {
+			if (errno == EINTR)
+				continue;
+			err (1, "error watching socket");
+		}
+		
+		if (FD_ISSET (sock, &read_fds))
+			gck_rpc_dispatch_accept ();
+	}
+	
+	gck_rpc_dispatch_uninit ();
+	dlclose(module);
+
+	return 0;
+}
\ No newline at end of file

Added: trunk/pkcs11/rpc-layer/gck-rpc-dispatch.c
==============================================================================
--- (empty file)
+++ trunk/pkcs11/rpc-layer/gck-rpc-dispatch.c	Sun Jan  4 23:07:18 2009
@@ -0,0 +1,2219 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gck-rpc-dispatch.h - receiver of our PKCS#11 protocol.
+
+   Copyright (C) 2008, Stef Walter
+
+   The Gnome Keyring Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Keyring Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Stef Walter <stef memberwebs com>
+*/
+
+#include "config.h"
+
+#include "gck-rpc-layer.h"
+#include "gck-rpc-private.h"
+
+#include "pkcs11/pkcs11.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <pthread.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+/* Where we dispatch the calls to */
+static CK_FUNCTION_LIST_PTR pkcs11_module = NULL;
+
+/* Argument to pass to C_Initialize */
+static CK_C_INITIALIZE_ARGS *pkcs11_initialize_args = NULL;
+
+/* Mutex for guarding initialization variable */
+static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/* The number of times we've initialized */
+static int pkcs11_initialized = 0;
+
+/* The error returned on protocol failures */
+#define PARSE_ERROR CKR_DEVICE_ERROR
+#define PREP_ERROR  CKR_DEVICE_MEMORY
+
+/* -----------------------------------------------------------------------------
+ * LOGGING and DEBUGGING
+ */
+
+#if DEBUG_OUTPUT
+#define debug(x) gck_rpc_debug x
+#else
+#define debug(x)	
+#endif
+
+#define warning(x) gck_rpc_warn x
+
+#define return_val_if_fail(x, v) \
+	if (!(x)) { rpc_warn ("'%s' not true at %s", #x, __func__); return v; } 
+
+void
+gck_rpc_log (const char *line)
+{
+	g_message ("%s", line);
+}
+
+/* -------------------------------------------------------------------------------
+ * CALL STRUCTURES
+ */
+
+typedef struct _CallState {
+	GckRpcMessage *req;
+	GckRpcMessage *resp;
+	void *allocated;
+} CallState;
+
+static int
+call_init (CallState *cs)
+{
+	assert (cs);
+	
+	cs->req = gck_rpc_message_new ((GkrBufferAllocator)realloc);
+	cs->resp = gck_rpc_message_new ((GkrBufferAllocator)realloc);
+	if (!cs->req || !cs->resp) {
+		gck_rpc_message_free (cs->req);
+		gck_rpc_message_free (cs->resp);
+		return 0;
+	}
+	
+	cs->allocated = NULL;
+	return 1;
+}
+
+static void*
+call_alloc (CallState *cs, size_t length)
+{
+	void **data;
+	
+	assert (cs);
+	
+	if (length > 0x7fffffff)
+		return NULL;
+	
+	data = malloc (sizeof (void*) + length);
+	if (!data)
+		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 = cs->allocated;
+	cs->allocated = data;
+
+	/* Data starts after first pointer */
+	return (void*)(data + 1);
+}
+
+static void
+call_reset (CallState *cs)
+{
+	void *allocated;
+	void **data;
+	
+	assert (cs);
+
+	allocated = cs->allocated;
+	while (allocated) {
+		data = (void**)allocated;
+		
+		/* Pointer to the next allocation */
+		allocated = *data;
+		free (data);
+	}
+	
+	cs->allocated = NULL;
+	gck_rpc_message_reset (cs->req);
+	gck_rpc_message_reset (cs->resp);
+}
+
+static void
+call_uninit (CallState *cs)
+{
+	assert (cs);
+	
+	call_reset (cs);
+	
+	gck_rpc_message_free (cs->req);
+	gck_rpc_message_free (cs->resp);
+}
+
+/* -------------------------------------------------------------------
+ * PROTOCOL CODE
+ */
+
+static CK_RV
+proto_read_byte_buffer (CallState *cs, CK_BYTE_PTR* buffer, CK_ULONG* n_buffer)
+{
+	GckRpcMessage *msg;
+	uint32_t length;
+	
+	assert (cs);
+	assert (buffer);
+	assert (n_buffer);
+	
+	msg = cs->req;
+	
+	/* Check that we're supposed to be reading this at this point */
+	assert (!msg->signature || gck_rpc_message_verify_part (msg, "ay"));
+
+	/* The number of ulongs there's room for on the other end */
+	if (!gkr_buffer_get_uint32 (&msg->buffer, msg->parsed, &msg->parsed, &length))
+		return PARSE_ERROR; 
+
+	*n_buffer = length;
+	*buffer = NULL;
+	
+	/* If set to zero, then they just want the length */ 
+	if (!length)
+		return CKR_OK;
+	
+	*buffer = call_alloc (cs, length * sizeof (CK_BYTE));
+	if (!*buffer)
+		return CKR_DEVICE_MEMORY;
+
+	return CKR_OK;
+}
+
+static CK_RV
+proto_read_byte_array (CallState *cs, CK_BYTE_PTR* array, CK_ULONG* n_array)
+{
+	GckRpcMessage *msg;
+	const unsigned char *data;
+	unsigned char valid;
+	uint32_t n_data;
+	
+	assert (cs);
+	
+	msg = cs->req;
+	
+	/* Check that we're supposed to have this at this point */
+	assert (!msg->signature || gck_rpc_message_verify_part (msg, "ay"));
+
+	/* Read out the byte which says whether data is present or not */
+	if (!gkr_buffer_get_byte (&msg->buffer, msg->parsed, &msg->parsed, &valid))
+		return PARSE_ERROR;
+	
+	/* Module should always send us valid arrays */
+	if (!valid)
+		return PARSE_ERROR;
+
+	/* Point our arguments into the buffer */
+	if (!gkr_buffer_get_byte_array (&msg->buffer, msg->parsed, &msg->parsed,
+	                                &data, &n_data))
+		return PARSE_ERROR;
+	
+	*array = (CK_BYTE_PTR)data;
+	*n_array = n_data;
+	return CKR_OK;
+}
+
+static CK_RV
+proto_read_ulong_buffer (CallState *cs, CK_ULONG_PTR* buffer, CK_ULONG* n_buffer)
+{
+	GckRpcMessage *msg;
+	uint32_t length;
+	
+	assert (cs);
+	assert (buffer);
+	assert (n_buffer);
+	
+	msg = cs->req;
+	
+	/* Check that we're supposed to be reading this at this point */
+	assert (!msg->signature || gck_rpc_message_verify_part (msg, "fu"));
+
+	/* The number of ulongs there's room for on the other end */
+	if (!gkr_buffer_get_uint32 (&msg->buffer, msg->parsed, &msg->parsed, &length))
+		return PARSE_ERROR; 
+
+	*n_buffer = length;
+	*buffer = NULL;
+	
+	/* If set to zero, then they just want the length */ 
+	if (!length)
+		return CKR_OK;
+	
+	*buffer = call_alloc (cs, length * sizeof (CK_ULONG));
+	if (!*buffer)
+		return CKR_DEVICE_MEMORY;
+
+	return CKR_OK;
+}
+
+
+static CK_RV
+proto_read_attribute_buffer (CallState *cs, CK_ATTRIBUTE_PTR* result, CK_ULONG* n_result)
+{
+	CK_ATTRIBUTE_PTR attrs;
+	GckRpcMessage *msg;
+	uint32_t n_attrs, i;
+	uint32_t value;
+	
+	assert (cs);
+	assert (result);
+	assert (n_result);
+	
+	msg = cs->req;
+	
+	/* Make sure this is in the rigth order */
+	assert (!msg->signature || gck_rpc_message_verify_part (msg, "fA"));
+
+	/* Read the number of attributes */
+	if (!gkr_buffer_get_uint32 (&msg->buffer, msg->parsed, &msg->parsed, &n_attrs))
+		return PARSE_ERROR; 
+
+	/* Allocate memory for the attribute structures */
+	attrs = call_alloc (cs, n_attrs * sizeof (CK_ATTRIBUTE));
+	if (!attrs)
+		return CKR_DEVICE_MEMORY;
+	
+	/* Now go through and fill in each one */
+	for (i = 0; i < n_attrs; ++i) {
+		
+		/* The attribute type */
+		if (!gkr_buffer_get_uint32 (&msg->buffer, msg->parsed, &msg->parsed, &value))
+			return PARSE_ERROR;
+		
+		attrs[i].type = value;
+		
+		/* The number of bytes to allocate */
+		if (!gkr_buffer_get_uint32 (&msg->buffer, msg->parsed, &msg->parsed, &value))
+			return PARSE_ERROR;
+		
+		if (value == 0) {
+			attrs[i].pValue = NULL;
+			attrs[i].ulValueLen = 0;
+		} else {
+			attrs[i].pValue = call_alloc (cs, 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 (CallState *cs, CK_ATTRIBUTE_PTR* result, CK_ULONG* n_result)
+{
+	CK_ATTRIBUTE_PTR attrs;
+	const unsigned char *data;
+	unsigned char valid;
+	GckRpcMessage *msg;
+	uint32_t n_attrs, i;
+	uint32_t value;
+	size_t n_data;
+	
+	assert (cs);
+	assert (result);
+	assert (n_result);
+	
+	msg = cs->req;
+	
+	/* Make sure this is in the rigth order */
+	assert (!msg->signature || gck_rpc_message_verify_part (msg, "aA"));
+
+	/* Read the number of attributes */
+	if (!gkr_buffer_get_uint32 (&msg->buffer, msg->parsed, &msg->parsed, &n_attrs))
+		return PARSE_ERROR; 
+
+	/* Allocate memory for the attribute structures */
+	attrs = call_alloc (cs, n_attrs * sizeof (CK_ATTRIBUTE));
+	if (!attrs)
+		return CKR_DEVICE_MEMORY;
+	
+	/* Now go through and fill in each one */
+	for (i = 0; i < n_attrs; ++i) {
+		
+		/* The attribute type */
+		if (!gkr_buffer_get_uint32 (&msg->buffer, msg->parsed, &msg->parsed, &value))
+			return PARSE_ERROR;
+
+		attrs[i].type = value;
+
+		/* Whether this one is valid or not */
+		if (!gkr_buffer_get_byte (&msg->buffer, msg->parsed, &msg->parsed, &valid))
+			return PARSE_ERROR;
+		
+		if (valid) {
+			if (!gkr_buffer_get_byte_array (&msg->buffer, msg->parsed, &msg->parsed, &data, &n_data))
+				return PARSE_ERROR;
+			attrs[i].pValue = (CK_VOID_PTR)data;
+			attrs[i].ulValueLen = n_data;
+		} 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 (CallState *cs, CK_ATTRIBUTE_PTR array, CK_ULONG len, CK_RV ret)
+{
+	assert (cs);
+	
+	/* 
+	 * 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 (!gck_rpc_message_write_attribute_array (cs->resp, array, len) ||
+	    !gck_rpc_message_write_ulong (cs->resp, ret))
+		return PREP_ERROR;
+
+	return CKR_OK;
+}
+
+static CK_RV
+proto_read_null_string (CallState *cs, CK_UTF8CHAR_PTR* val)
+{
+	GckRpcMessage *msg;
+	const unsigned char *data;
+	size_t n_data;
+	
+	assert (cs);
+	assert (val);
+	
+	msg = cs->req;
+	
+	/* Check that we're supposed to have this at this point */
+	assert (!msg->signature || gck_rpc_message_verify_part (msg, "z"));
+
+	if (!gkr_buffer_get_byte_array (&msg->buffer, msg->parsed, &msg->parsed, &data, &n_data))
+		return PARSE_ERROR;
+	
+	/* Allocate a block of memory for it */
+	*val = call_alloc (cs, n_data);
+	if (!*val)
+		return CKR_DEVICE_MEMORY;
+	
+	memcpy (*val, data, n_data);
+	(*val)[n_data] = 0;
+	
+	return CKR_OK;
+}
+
+static CK_RV
+proto_read_mechanism (CallState *cs, CK_MECHANISM_PTR mech)
+{
+	GckRpcMessage *msg;
+	const unsigned char *data;
+	uint32_t value;
+	size_t n_data;
+	
+	assert (cs);
+	assert (mech);
+	
+	msg = cs->req;
+
+	/* Make sure this is in the right order */
+	assert (!msg->signature || gck_rpc_message_verify_part (msg, "M"));
+	
+	/* The mechanism type */
+	if (!gkr_buffer_get_uint32 (&msg->buffer, msg->parsed, &msg->parsed, &value))
+		return PARSE_ERROR;
+	
+	/* The mechanism data */
+	if (!gkr_buffer_get_byte_array (&msg->buffer, msg->parsed, &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 (CallState *cs, CK_INFO_PTR info)
+{
+	GckRpcMessage *msg;
+	
+	assert (cs);
+	assert (info);
+	
+	msg = cs->resp;
+
+	if (!gck_rpc_message_write_version (msg, &info->cryptokiVersion) ||
+	    !gck_rpc_message_write_space_string (msg, info->manufacturerID, 32) ||
+	    !gck_rpc_message_write_ulong (msg, info->flags) ||
+	    !gck_rpc_message_write_space_string (msg, info->libraryDescription, 32) ||
+	    !gck_rpc_message_write_version (msg, &info->libraryVersion))
+		return PREP_ERROR;
+
+	return CKR_OK;
+}
+
+static CK_RV
+proto_write_slot_info (CallState *cs, CK_SLOT_INFO_PTR info)
+{
+	GckRpcMessage *msg;
+	
+	assert (cs);
+	assert (info);
+	
+	msg = cs->resp;
+
+	if (!gck_rpc_message_write_space_string (msg, info->slotDescription, 64) ||
+	    !gck_rpc_message_write_space_string (msg, info->manufacturerID, 32) ||
+	    !gck_rpc_message_write_ulong (msg, info->flags) ||
+	    !gck_rpc_message_write_version (msg, &info->hardwareVersion) ||
+	    !gck_rpc_message_write_version (msg, &info->firmwareVersion))
+		return PREP_ERROR;
+	
+	return CKR_OK;
+}
+
+static CK_RV
+proto_write_token_info (CallState *cs, CK_TOKEN_INFO_PTR info)
+{
+	GckRpcMessage *msg;
+	
+	assert (cs);
+	assert (info);
+	
+	msg = cs->resp;
+
+	if (!gck_rpc_message_write_space_string (msg, info->label, 32) ||
+	    !gck_rpc_message_write_space_string (msg, info->manufacturerID, 32) ||
+	    !gck_rpc_message_write_space_string (msg, info->model, 16) ||
+	    !gck_rpc_message_write_space_string (msg, info->serialNumber, 16) ||
+	    !gck_rpc_message_write_ulong (msg, info->flags) ||
+	    !gck_rpc_message_write_ulong (msg, info->ulMaxSessionCount) ||
+	    !gck_rpc_message_write_ulong (msg, info->ulSessionCount) ||
+	    !gck_rpc_message_write_ulong (msg, info->ulMaxRwSessionCount) ||
+	    !gck_rpc_message_write_ulong (msg, info->ulRwSessionCount) ||
+	    !gck_rpc_message_write_ulong (msg, info->ulMaxPinLen) ||
+	    !gck_rpc_message_write_ulong (msg, info->ulMinPinLen) ||
+	    !gck_rpc_message_write_ulong (msg, info->ulTotalPublicMemory) ||
+	    !gck_rpc_message_write_ulong (msg, info->ulFreePublicMemory) ||
+	    !gck_rpc_message_write_ulong (msg, info->ulTotalPrivateMemory) ||
+	    !gck_rpc_message_write_ulong (msg, info->ulFreePrivateMemory) ||
+	    !gck_rpc_message_write_version (msg, &info->hardwareVersion) ||
+	    !gck_rpc_message_write_version (msg, &info->firmwareVersion) ||
+	    !gck_rpc_message_write_space_string (msg, info->utcTime, 16))
+		return PREP_ERROR;
+
+	return CKR_OK;
+}
+
+static CK_RV
+proto_write_mechanism_info (CallState *cs, CK_MECHANISM_INFO_PTR info)
+{
+	GckRpcMessage *msg;
+	
+	assert (cs);
+	assert (info);
+	
+	msg = cs->resp;
+
+	if (!gck_rpc_message_write_ulong (msg, info->ulMinKeySize) ||
+	    !gck_rpc_message_write_ulong (msg, info->ulMaxKeySize) ||
+	    !gck_rpc_message_write_ulong (msg, info->flags))
+		return PREP_ERROR;
+	
+	return CKR_OK;
+}
+
+static CK_RV
+proto_write_session_info (CallState *cs, CK_SESSION_INFO_PTR info)
+{
+	GckRpcMessage *msg;
+	
+	assert (cs);
+	assert (info);
+	
+	msg = cs->resp;
+
+	if (!gck_rpc_message_write_ulong (msg, info->slotID) ||
+	    !gck_rpc_message_write_ulong (msg, info->state) ||
+	    !gck_rpc_message_write_ulong (msg, info->flags) ||
+	    !gck_rpc_message_write_ulong (msg, info->ulDeviceError))
+		return PREP_ERROR;
+
+	return CKR_OK;
+}
+
+/* -------------------------------------------------------------------
+ * CALL MACROS 
+ */
+
+#define BEGIN_CALL(call_id) \
+	debug ((#call_id ": enter")); \
+	assert (cs); \
+	assert (pkcs11_module); \
+	{  \
+		CK_ ## call_id _func = pkcs11_module-> call_id; \
+		CK_RV _ret = CKR_OK; \
+		if (!_func) { _ret = CKR_GENERAL_ERROR; goto _cleanup; } 
+		
+#define PROCESS_CALL(args)\
+	assert (gck_rpc_message_is_verified (cs->req)); \
+	_ret = _func args 
+	
+#define END_CALL \
+	_cleanup: \
+		debug (("ret: %d", _ret)); \
+		return _ret; \
+	}
+
+#define IN_BYTE(val) \
+	if (!gck_rpc_message_read_byte (cs->req, &val)) \
+		{ _ret = PARSE_ERROR; goto _cleanup; }
+
+#define IN_ULONG(val) \
+	if (!gck_rpc_message_read_ulong (cs->req, &val)) \
+		{ _ret = PARSE_ERROR; goto _cleanup; }
+
+#define IN_STRING(val) \
+	_ret = proto_read_null_string (cs, &val); \
+	if (_ret != CKR_OK) goto _cleanup;
+
+#define IN_BYTE_BUFFER(buffer, buffer_len) \
+	_ret = proto_read_byte_buffer (cs, &buffer, &buffer_len); \
+	if (_ret != CKR_OK) goto _cleanup;
+
+#define IN_BYTE_ARRAY(buffer, buffer_len) \
+	_ret = proto_read_byte_array (cs, &buffer, &buffer_len); \
+	if (_ret != CKR_OK) goto _cleanup;
+
+#define IN_ULONG_BUFFER(buffer, buffer_len) \
+	_ret = proto_read_ulong_buffer (cs, &buffer, &buffer_len); \
+	if (_ret != CKR_OK) goto _cleanup;
+
+#define IN_ATTRIBUTE_BUFFER(buffer, buffer_len) \
+	_ret = proto_read_attribute_buffer (cs, &buffer, &buffer_len); \
+	if (_ret != CKR_OK) goto _cleanup;
+
+#define IN_ATTRIBUTE_ARRAY(attrs, n_attrs) \
+	_ret = proto_read_attribute_array (cs, &attrs, &n_attrs); \
+	if (_ret != CKR_OK) goto _cleanup;
+
+#define IN_MECHANISM(mech) \
+	_ret = proto_read_mechanism (cs, &mech); \
+	if (_ret != CKR_OK) goto _cleanup;
+
+
+#define OUT_ULONG(val) \
+	if (_ret == CKR_OK && !gck_rpc_message_write_ulong (cs->resp, val)) \
+		_ret = PREP_ERROR;
+
+#define OUT_BYTE_ARRAY(array, len) \
+	if (_ret == CKR_OK && !gck_rpc_message_write_byte_array (cs->resp, array, len)) \
+		_ret = PREP_ERROR;
+
+#define OUT_ULONG_ARRAY(array, len) \
+	if (_ret == CKR_OK && !gck_rpc_message_write_ulong_array (cs->resp, array, len)) \
+		_ret = PREP_ERROR;
+
+#define OUT_ATTRIBUTE_ARRAY(array, len) \
+	/* Note how we filter return codes */ \
+	_ret = proto_write_attribute_array (cs, array, len, _ret);
+
+#define OUT_INFO(val) \
+	if (_ret == CKR_OK) \
+		_ret = proto_write_info (cs, &val);
+
+#define OUT_SLOT_INFO(val) \
+	if (_ret == CKR_OK) \
+		_ret = proto_write_slot_info (cs, &val);
+
+#define OUT_TOKEN_INFO(val) \
+	if (_ret == CKR_OK) \
+		_ret = proto_write_token_info (cs, &val);
+	
+#define OUT_MECHANISM_INFO(val) \
+	if (_ret == CKR_OK) \
+		_ret = proto_write_mechanism_info (cs, &val);
+	
+#define OUT_SESSION_INFO(val) \
+	if (_ret == CKR_OK) \
+		_ret = proto_write_session_info (cs, &val);
+	
+/* ---------------------------------------------------------------------------
+ * DISPATCH SPECIFIC CALLS
+ */
+
+static CK_RV
+rpc_C_Initialize (CallState *cs)
+{
+	CK_BYTE_PTR handshake;
+	CK_ULONG n_handshake;
+	CK_RV ret = CKR_OK;
+	
+	debug (("C_Initialize: enter"));
+	
+	assert (cs);
+	assert (pkcs11_module);
+
+	ret = proto_read_byte_array (cs, &handshake, &n_handshake);
+	if (ret == CKR_OK) {
+		
+		/* Check to make sure the header matches */
+		if (n_handshake != GCK_RPC_HANDSHAKE_LEN ||
+		    memcmp (handshake, GCK_RPC_HANDSHAKE, n_handshake) != 0) {
+			gck_rpc_warn ("invalid handshake received from connecting module");
+			ret = CKR_GENERAL_ERROR;
+		}
+
+		assert (gck_rpc_message_is_verified (cs->req));
+	}
+	
+	if (ret == CKR_OK) { 
+		
+		pthread_mutex_lock (&init_mutex);
+	
+			if (pkcs11_initialized == 0)
+				ret = pkcs11_module->C_Initialize (pkcs11_initialize_args);
+
+			if (ret == CKR_OK)
+				++pkcs11_initialized;
+		
+		pthread_mutex_unlock (&init_mutex);
+	}
+	
+	debug (("ret: %d", ret));
+	return ret;
+}
+
+static CK_RV
+rpc_C_Finalize (CallState *cs)
+{
+	CK_RV ret;
+	
+	debug (("C_Finalize: enter"));
+
+	assert (cs);
+	assert (pkcs11_module);
+	
+	pthread_mutex_lock (&init_mutex);
+
+		if (pkcs11_initialized == 1)
+			ret = pkcs11_module->C_Finalize (NULL);
+
+		if (ret == CKR_OK)
+			--pkcs11_initialized;
+	
+	pthread_mutex_unlock (&init_mutex);	
+
+	debug (("ret: %d", ret));
+	return ret;
+}
+
+static CK_RV
+rpc_C_GetInfo (CallState *cs)
+{
+	CK_INFO info;
+	
+	BEGIN_CALL (C_GetInfo);
+	PROCESS_CALL ((&info));
+		OUT_INFO (info);
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_GetSlotList (CallState *cs)
+{
+	CK_BBOOL token_present;
+	CK_SLOT_ID_PTR slot_list;
+	CK_ULONG count;
+	
+	BEGIN_CALL (C_GetSlotList);
+		IN_BYTE (token_present);
+		IN_ULONG_BUFFER (slot_list, count);
+	PROCESS_CALL ((token_present, slot_list, &count));
+		OUT_ULONG_ARRAY (slot_list, count);
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_GetSlotInfo (CallState *cs)
+{
+	CK_SLOT_ID slot_id;
+	CK_SLOT_INFO info;
+	
+	BEGIN_CALL (C_GetSlotInfo);
+		IN_ULONG (slot_id);
+	PROCESS_CALL ((slot_id, &info));
+		OUT_SLOT_INFO (info);
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_GetTokenInfo (CallState *cs)
+{
+	CK_SLOT_ID slot_id;
+	CK_TOKEN_INFO info;
+	
+	BEGIN_CALL (C_GetTokenInfo);
+		IN_ULONG (slot_id);
+	PROCESS_CALL ((slot_id, &info));
+		OUT_TOKEN_INFO (info);
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_GetMechanismList (CallState *cs)
+{
+	CK_SLOT_ID slot_id;
+	CK_MECHANISM_TYPE_PTR mechanism_list;
+	CK_ULONG count;
+	
+	BEGIN_CALL (C_GetMechanismList);
+		IN_ULONG (slot_id);
+		IN_ULONG_BUFFER (mechanism_list, count);
+	PROCESS_CALL ((slot_id, mechanism_list, &count));
+		OUT_ULONG_ARRAY (mechanism_list, count);
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_GetMechanismInfo (CallState *cs)
+{
+	CK_SLOT_ID slot_id;
+	CK_MECHANISM_TYPE type;
+	CK_MECHANISM_INFO info;
+	
+	BEGIN_CALL (C_GetMechanismInfo);
+		IN_ULONG (slot_id);
+		IN_ULONG (type);
+	PROCESS_CALL ((slot_id, type, &info));
+		OUT_MECHANISM_INFO (info);
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_InitToken (CallState *cs)
+{
+	CK_SLOT_ID slot_id;
+	CK_UTF8CHAR_PTR pin;
+	CK_ULONG pin_len;
+	CK_UTF8CHAR_PTR label;
+	
+	BEGIN_CALL (C_InitToken);
+		IN_ULONG (slot_id);
+		IN_BYTE_ARRAY (pin, pin_len);
+		IN_STRING (label);
+	PROCESS_CALL ((slot_id, pin, pin_len, label));
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_WaitForSlotEvent (CallState *cs)
+{
+	CK_FLAGS flags;
+	CK_SLOT_ID slot_id;
+	
+	BEGIN_CALL (C_WaitForSlotEvent);
+		IN_ULONG (flags);
+	PROCESS_CALL ((flags, &slot_id, NULL));
+		OUT_ULONG (slot_id);
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_OpenSession (CallState *cs)
+{
+	CK_SLOT_ID slot_id;
+	CK_FLAGS flags;
+	CK_SESSION_HANDLE session;
+
+	BEGIN_CALL (C_OpenSession);
+		IN_ULONG (slot_id);
+		IN_ULONG (flags);
+	PROCESS_CALL ((slot_id, flags, NULL, NULL, &session));
+		OUT_ULONG (session);
+	END_CALL;
+}
+
+
+static CK_RV
+rpc_C_CloseSession (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+
+	BEGIN_CALL (C_CloseSession);
+		IN_ULONG (session);
+	PROCESS_CALL ((session));
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_CloseAllSessions (CallState *cs)
+{
+	CK_SLOT_ID slot_id;
+
+	BEGIN_CALL (C_CloseAllSessions);
+		IN_ULONG (slot_id);
+	PROCESS_CALL ((slot_id));
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_GetFunctionStatus (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+
+	BEGIN_CALL (C_GetFunctionStatus);
+		IN_ULONG (session);
+	PROCESS_CALL ((session));
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_CancelFunction (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+
+	BEGIN_CALL (C_CancelFunction);
+		IN_ULONG (session);
+	PROCESS_CALL ((session));
+	END_CALL;
+}
+
+static CK_RV 
+rpc_C_GetSessionInfo (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_SESSION_INFO info;
+
+	BEGIN_CALL (C_GetSessionInfo);
+		IN_ULONG (session);
+	PROCESS_CALL ((session, &info));
+		OUT_SESSION_INFO (info);
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_InitPIN (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_UTF8CHAR_PTR pin;
+	CK_ULONG pin_len;
+
+	BEGIN_CALL (C_InitPIN);
+		IN_ULONG (session);
+		IN_BYTE_ARRAY (pin, pin_len);
+	PROCESS_CALL ((session, pin, pin_len));
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_SetPIN (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_UTF8CHAR_PTR old_pin;
+	CK_ULONG old_len;
+	CK_UTF8CHAR_PTR new_pin;
+	CK_ULONG new_len;
+	
+	BEGIN_CALL (C_SetPIN);
+		IN_ULONG (session);
+		IN_BYTE_ARRAY (old_pin, old_len);
+		IN_BYTE_ARRAY (new_pin, new_len);
+	PROCESS_CALL ((session, old_pin, old_len, new_pin, new_len));
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_GetOperationState (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_BYTE_PTR operation_state;
+	CK_ULONG operation_state_len;
+	
+	BEGIN_CALL (C_GetOperationState);
+		IN_ULONG (session);
+		IN_BYTE_BUFFER (operation_state, operation_state_len);
+	PROCESS_CALL ((session, operation_state, &operation_state_len));
+		OUT_BYTE_ARRAY (operation_state, operation_state_len);
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_SetOperationState (CallState *cs)
+{
+	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 (C_SetOperationState);
+		IN_ULONG (session);
+		IN_BYTE_ARRAY (operation_state, operation_state_len);
+		IN_ULONG (encryption_key);
+		IN_ULONG (authentication_key);
+	PROCESS_CALL ((session, operation_state, operation_state_len, encryption_key, authentication_key));
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_Login (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_USER_TYPE user_type;
+	CK_UTF8CHAR_PTR pin;
+	CK_ULONG pin_len;
+
+	BEGIN_CALL (C_Login);
+		IN_ULONG (session);
+		IN_ULONG (user_type);
+		IN_BYTE_ARRAY (pin, pin_len);
+	PROCESS_CALL ((session, user_type, pin, pin_len));
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_Logout (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+
+	BEGIN_CALL (C_Logout);
+		IN_ULONG (session);
+	PROCESS_CALL ((session));
+	END_CALL;
+}
+
+/* -----------------------------------------------------------------------------
+ * OBJECT OPERATIONS
+ */
+
+static CK_RV
+rpc_C_CreateObject (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_ATTRIBUTE_PTR template;
+	CK_ULONG count;
+	CK_OBJECT_HANDLE new_object;
+
+	BEGIN_CALL (C_CreateObject);
+		IN_ULONG (session);
+		IN_ATTRIBUTE_ARRAY (template, count);
+	PROCESS_CALL ((session, template, count, &new_object));
+		OUT_ULONG (new_object);
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_CopyObject (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_OBJECT_HANDLE object;
+	CK_ATTRIBUTE_PTR template;
+	CK_ULONG count;
+	CK_OBJECT_HANDLE new_object;
+
+	BEGIN_CALL (C_CopyObject);
+		IN_ULONG (session);
+		IN_ULONG (object);
+		IN_ATTRIBUTE_ARRAY (template, count);
+	PROCESS_CALL ((session, object, template, count, &new_object));
+		OUT_ULONG (new_object);
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_DestroyObject (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_OBJECT_HANDLE object;
+
+	BEGIN_CALL (C_DestroyObject);
+		IN_ULONG (session);
+		IN_ULONG (object);
+	PROCESS_CALL ((session, object));
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_GetObjectSize (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_OBJECT_HANDLE object;
+	CK_ULONG size;
+
+	BEGIN_CALL (C_GetObjectSize);
+		IN_ULONG (session);
+		IN_ULONG (object);
+	PROCESS_CALL ((session, object, &size));
+		OUT_ULONG (size);
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_GetAttributeValue (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_OBJECT_HANDLE object;
+	CK_ATTRIBUTE_PTR template;
+	CK_ULONG count;
+
+	BEGIN_CALL (C_GetAttributeValue);
+		IN_ULONG (session);
+		IN_ULONG (object);
+		IN_ATTRIBUTE_BUFFER (template, count);
+	PROCESS_CALL ((session, object, template, count));
+		OUT_ATTRIBUTE_ARRAY (template, count);
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_SetAttributeValue (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_OBJECT_HANDLE object;
+	CK_ATTRIBUTE_PTR template;
+	CK_ULONG count;
+	
+	BEGIN_CALL (C_SetAttributeValue);
+		IN_ULONG (session);
+		IN_ULONG (object);
+		IN_ATTRIBUTE_ARRAY (template, count);
+	PROCESS_CALL ((session, object, template, count));
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_FindObjectsInit (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_ATTRIBUTE_PTR template;
+	CK_ULONG count;
+
+	BEGIN_CALL (C_FindObjectsInit);
+		IN_ULONG (session);
+		IN_ATTRIBUTE_ARRAY (template, count);
+	PROCESS_CALL ((session, template, count));
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_FindObjects (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_OBJECT_HANDLE_PTR objects;
+	CK_ULONG max_object_count;
+	CK_ULONG object_count;
+
+	BEGIN_CALL (C_FindObjects);
+		IN_ULONG (session);
+		IN_ULONG_BUFFER (objects, max_object_count);
+	PROCESS_CALL ((session, objects, max_object_count, &object_count));
+		OUT_ULONG_ARRAY (objects, object_count);
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_FindObjectsFinal (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+
+	BEGIN_CALL (C_FindObjectsFinal);
+		IN_ULONG (session);
+	PROCESS_CALL ((session));
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_EncryptInit (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_MECHANISM mechanism;
+	CK_OBJECT_HANDLE key;
+	
+	BEGIN_CALL (C_EncryptInit);
+		IN_ULONG (session);
+		IN_MECHANISM (mechanism);
+		IN_ULONG (key);
+	PROCESS_CALL ((session, &mechanism, key));
+	END_CALL;
+
+}
+
+static CK_RV
+rpc_C_Encrypt (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_BYTE_PTR data;
+	CK_ULONG data_len;
+	CK_BYTE_PTR encrypted_data;
+	CK_ULONG encrypted_data_len;
+	
+	BEGIN_CALL (C_Encrypt);
+		IN_ULONG (session);
+		IN_BYTE_ARRAY (data, data_len);
+		IN_BYTE_BUFFER (encrypted_data, encrypted_data_len);
+	PROCESS_CALL ((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 (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_BYTE_PTR part;
+	CK_ULONG part_len;
+	CK_BYTE_PTR encrypted_part;
+	CK_ULONG encrypted_part_len;
+	
+	BEGIN_CALL (C_EncryptUpdate);
+		IN_ULONG (session);
+		IN_BYTE_ARRAY (part, part_len);
+		IN_BYTE_BUFFER (encrypted_part, encrypted_part_len);
+	PROCESS_CALL ((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 (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_BYTE_PTR last_encrypted_part;
+	CK_ULONG last_encrypted_part_len;
+	
+	BEGIN_CALL (C_EncryptFinal);
+		IN_ULONG (session);
+		IN_BYTE_BUFFER (last_encrypted_part, last_encrypted_part_len);
+	PROCESS_CALL ((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 (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_MECHANISM mechanism;
+	CK_OBJECT_HANDLE key;
+	
+	BEGIN_CALL (C_DecryptInit);
+		IN_ULONG (session);
+		IN_MECHANISM (mechanism);
+		IN_ULONG (key);
+	PROCESS_CALL ((session, &mechanism, key));
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_Decrypt (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_BYTE_PTR encrypted_data;
+	CK_ULONG encrypted_data_len;
+	CK_BYTE_PTR data;
+	CK_ULONG data_len;
+
+	BEGIN_CALL (C_Decrypt);
+		IN_ULONG (session);
+		IN_BYTE_ARRAY (encrypted_data, encrypted_data_len);
+		IN_BYTE_BUFFER (data, data_len);
+	PROCESS_CALL ((session, encrypted_data, encrypted_data_len, data, &data_len));
+		OUT_BYTE_ARRAY (data, data_len);
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_DecryptUpdate (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_BYTE_PTR encrypted_part;
+	CK_ULONG encrypted_part_len;
+	CK_BYTE_PTR part;
+	CK_ULONG part_len;
+
+	BEGIN_CALL (C_DecryptUpdate);
+		IN_ULONG (session);
+		IN_BYTE_ARRAY (encrypted_part, encrypted_part_len);
+		IN_BYTE_BUFFER (part, part_len);
+	PROCESS_CALL ((session, encrypted_part, encrypted_part_len, part, &part_len));
+		OUT_BYTE_ARRAY (part, part_len);
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_DecryptFinal (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_BYTE_PTR last_part;
+	CK_ULONG last_part_len;
+
+	BEGIN_CALL (C_DecryptFinal);
+		IN_ULONG (session);
+		IN_BYTE_BUFFER (last_part, last_part_len);
+	PROCESS_CALL ((session, last_part, &last_part_len));
+		OUT_BYTE_ARRAY (last_part, last_part_len);
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_DigestInit (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_MECHANISM mechanism;
+	
+	BEGIN_CALL (C_DigestInit);
+		IN_ULONG (session);
+		IN_MECHANISM (mechanism);
+	PROCESS_CALL ((session, &mechanism));
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_Digest (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_BYTE_PTR data;
+	CK_ULONG data_len;
+	CK_BYTE_PTR digest;
+	CK_ULONG digest_len;
+	
+	BEGIN_CALL (C_Digest);
+		IN_ULONG (session);
+		IN_BYTE_ARRAY (data, data_len);
+		IN_BYTE_BUFFER (digest, digest_len);
+	PROCESS_CALL ((session, data, data_len, digest, &digest_len));
+		OUT_BYTE_ARRAY (digest, digest_len);
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_DigestUpdate (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_BYTE_PTR part;
+	CK_ULONG part_len;
+
+	BEGIN_CALL (C_DigestUpdate);
+		IN_ULONG (session);
+		IN_BYTE_ARRAY (part, part_len);
+	PROCESS_CALL ((session, part, part_len));
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_DigestKey (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_OBJECT_HANDLE key;
+	
+	BEGIN_CALL (C_DigestKey);
+		IN_ULONG (session);
+		IN_ULONG (key);
+	PROCESS_CALL ((session, key));
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_DigestFinal (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_BYTE_PTR digest;
+	CK_ULONG digest_len;
+	
+	BEGIN_CALL (C_DigestFinal);
+		IN_ULONG (session);
+		IN_BYTE_BUFFER (digest, digest_len);
+	PROCESS_CALL ((session, digest, &digest_len));
+		OUT_BYTE_ARRAY (digest, digest_len);
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_SignInit (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_MECHANISM mechanism;
+	CK_OBJECT_HANDLE key;
+	
+	BEGIN_CALL (C_SignInit);
+		IN_ULONG (session);
+		IN_MECHANISM (mechanism);
+		IN_ULONG (key);
+	PROCESS_CALL ((session, &mechanism, key));
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_Sign (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_BYTE_PTR part;
+	CK_ULONG part_len;
+	CK_BYTE_PTR signature;
+	CK_ULONG signature_len;
+
+	BEGIN_CALL (C_Sign);
+		IN_ULONG (session);
+		IN_BYTE_ARRAY (part, part_len);
+		IN_BYTE_BUFFER (signature, signature_len);
+	PROCESS_CALL ((session, part, part_len, signature, &signature_len));
+		OUT_BYTE_ARRAY (signature, signature_len);
+	END_CALL;
+
+}
+
+static CK_RV
+rpc_C_SignUpdate (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_BYTE_PTR part;
+	CK_ULONG part_len;
+
+	BEGIN_CALL (C_SignUpdate);
+		IN_ULONG (session);
+		IN_BYTE_ARRAY (part, part_len);
+	PROCESS_CALL ((session, part, part_len));
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_SignFinal (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_BYTE_PTR signature;
+	CK_ULONG signature_len;
+	
+	BEGIN_CALL (C_SignFinal);
+		IN_ULONG (session);
+		IN_BYTE_BUFFER (signature, signature_len);
+	PROCESS_CALL ((session, signature, &signature_len));
+		OUT_BYTE_ARRAY (signature, signature_len);
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_SignRecoverInit (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_MECHANISM mechanism;
+	CK_OBJECT_HANDLE key;
+	
+	BEGIN_CALL (C_SignRecoverInit);
+		IN_ULONG (session);
+		IN_MECHANISM (mechanism);
+		IN_ULONG (key);
+	PROCESS_CALL ((session, &mechanism, key));
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_SignRecover (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_BYTE_PTR data;
+	CK_ULONG data_len;
+	CK_BYTE_PTR signature;
+	CK_ULONG signature_len;
+	
+	BEGIN_CALL (C_SignRecover);
+		IN_ULONG (session);
+		IN_BYTE_ARRAY (data, data_len);
+		IN_BYTE_BUFFER (signature, signature_len);
+	PROCESS_CALL ((session, data, data_len, signature, &signature_len));
+		OUT_BYTE_ARRAY (signature, signature_len);
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_VerifyInit (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_MECHANISM mechanism;
+	CK_OBJECT_HANDLE key;
+	
+	BEGIN_CALL (C_VerifyInit);
+		IN_ULONG (session);
+		IN_MECHANISM (mechanism);
+		IN_ULONG (key);
+	PROCESS_CALL ((session, &mechanism, key));
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_Verify (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_BYTE_PTR data;
+	CK_ULONG data_len;
+	CK_BYTE_PTR signature;
+	CK_ULONG signature_len;
+	
+	BEGIN_CALL (C_Verify);
+		IN_ULONG (session);
+		IN_BYTE_ARRAY (data, data_len);
+		IN_BYTE_ARRAY (signature, signature_len);
+	PROCESS_CALL ((session, data, data_len, signature, signature_len));
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_VerifyUpdate (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_BYTE_PTR part;
+	CK_ULONG part_len;
+	
+	BEGIN_CALL (C_VerifyUpdate);
+		IN_ULONG (session);
+		IN_BYTE_ARRAY (part, part_len);
+	PROCESS_CALL ((session, part, part_len));
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_VerifyFinal (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_BYTE_PTR signature;
+	CK_ULONG signature_len;
+	
+	BEGIN_CALL (C_VerifyFinal);
+		IN_ULONG (session);
+		IN_BYTE_ARRAY (signature, signature_len);
+	PROCESS_CALL ((session, signature, signature_len));
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_VerifyRecoverInit (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_MECHANISM mechanism;
+	CK_OBJECT_HANDLE key;
+	
+	BEGIN_CALL (C_VerifyRecoverInit);
+		IN_ULONG (session);
+		IN_MECHANISM (mechanism);
+		IN_ULONG (key);
+	PROCESS_CALL ((session, &mechanism, key));
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_VerifyRecover (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_BYTE_PTR signature;
+	CK_ULONG signature_len;
+	CK_BYTE_PTR data;
+	CK_ULONG data_len;
+	
+	BEGIN_CALL (C_VerifyRecover);
+		IN_ULONG (session);
+		IN_BYTE_ARRAY (signature, signature_len);
+		IN_BYTE_BUFFER (data, data_len);
+	PROCESS_CALL ((session, signature, signature_len, data, &data_len));
+		OUT_BYTE_ARRAY (data, data_len);
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_DigestEncryptUpdate (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_BYTE_PTR part;
+	CK_ULONG part_len;
+	CK_BYTE_PTR encrypted_part;
+	CK_ULONG encrypted_part_len;
+
+	BEGIN_CALL (C_DigestEncryptUpdate);
+		IN_ULONG (session);
+		IN_BYTE_ARRAY (part, part_len);
+		IN_BYTE_BUFFER (encrypted_part, encrypted_part_len);
+	PROCESS_CALL ((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 (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_BYTE_PTR encrypted_part;
+	CK_ULONG encrypted_part_len;
+	CK_BYTE_PTR part;
+	CK_ULONG part_len;
+	
+	BEGIN_CALL (C_DecryptDigestUpdate);
+		IN_ULONG (session);
+		IN_BYTE_ARRAY (encrypted_part, encrypted_part_len);
+		IN_BYTE_BUFFER (part, part_len);
+	PROCESS_CALL ((session, encrypted_part, encrypted_part_len, part, &part_len));
+		OUT_BYTE_ARRAY (part, part_len);
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_SignEncryptUpdate (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_BYTE_PTR part;
+	CK_ULONG part_len;
+	CK_BYTE_PTR encrypted_part;
+	CK_ULONG encrypted_part_len;
+
+	BEGIN_CALL (C_SignEncryptUpdate);
+		IN_ULONG (session);
+		IN_BYTE_ARRAY (part, part_len);
+		IN_BYTE_BUFFER (encrypted_part, encrypted_part_len);
+	PROCESS_CALL ((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 (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_BYTE_PTR encrypted_part;
+	CK_ULONG encrypted_part_len;
+	CK_BYTE_PTR part;
+	CK_ULONG part_len;
+	
+	BEGIN_CALL (C_DecryptVerifyUpdate);
+		IN_ULONG (session);
+		IN_BYTE_ARRAY (encrypted_part, encrypted_part_len);
+		IN_BYTE_BUFFER (part, part_len);
+	PROCESS_CALL ((session, encrypted_part, encrypted_part_len, part, &part_len));
+		OUT_BYTE_ARRAY (part, part_len);
+	END_CALL;
+}
+
+/* -----------------------------------------------------------------------------
+ * KEY OPERATIONS
+ */
+
+static CK_RV
+rpc_C_GenerateKey (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_MECHANISM mechanism;
+	CK_ATTRIBUTE_PTR template;
+	CK_ULONG count;
+	CK_OBJECT_HANDLE key;
+	
+	BEGIN_CALL (C_GenerateKey);
+		IN_ULONG (session);
+		IN_MECHANISM (mechanism);
+		IN_ATTRIBUTE_ARRAY (template, count);
+	PROCESS_CALL ((session, &mechanism, template, count, &key));
+		OUT_ULONG (key);
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_GenerateKeyPair (CallState *cs)
+{
+	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 (C_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 ((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 (CallState *cs)
+{
+	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 (C_WrapKey);
+		IN_ULONG (session);
+		IN_MECHANISM (mechanism);
+		IN_ULONG (wrapping_key);
+		IN_ULONG (key);
+		IN_BYTE_BUFFER (wrapped_key, wrapped_key_len);
+	PROCESS_CALL ((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 (CallState *cs)
+{
+	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 (C_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 ((session, &mechanism, unwrapping_key, wrapped_key, wrapped_key_len, template, attribute_count, &key));
+		OUT_ULONG (key);
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_DeriveKey (CallState *cs)
+{
+	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 (C_DeriveKey);
+		IN_ULONG (session);
+		IN_MECHANISM (mechanism);
+		IN_ULONG (base_key);
+		IN_ATTRIBUTE_ARRAY (template, attribute_count);
+	PROCESS_CALL ((session, &mechanism, base_key, template, attribute_count, &key));
+		OUT_ULONG (key);
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_SeedRandom (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_BYTE_PTR seed;
+	CK_ULONG seed_len;
+	
+	BEGIN_CALL (C_SeedRandom);
+		IN_ULONG (session);
+		IN_BYTE_ARRAY (seed, seed_len);
+	PROCESS_CALL ((session, seed, seed_len));
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_GenerateRandom (CallState *cs)
+{
+	CK_SESSION_HANDLE session;
+	CK_BYTE_PTR random_data;
+	CK_ULONG random_len;
+	
+	BEGIN_CALL (C_GenerateRandom);
+		IN_ULONG (session);
+		IN_BYTE_BUFFER (random_data, random_len);
+	PROCESS_CALL ((session, random_data, random_len));
+		OUT_BYTE_ARRAY (random_data, random_len);
+	END_CALL;
+}
+
+/* ---------------------------------------------------------------------------
+ * DISPATCH THREAD HANDLING
+ */
+
+static int
+dispatch_call (CallState *cs)
+{
+	GckRpcMessage *req, *resp;
+	CK_RV ret = CKR_OK;
+
+	assert (cs);
+	
+	req = cs->req;
+	resp = cs->resp;
+
+	/* This should have been checked by the parsing code */
+	assert (req->call_id > GCK_RPC_CALL_ERROR);
+	assert (req->call_id < GCK_RPC_CALL_MAX);
+	
+	/* Prepare a response for the function to fill in */
+	if (!gck_rpc_message_prep (resp, req->call_id, GCK_RPC_RESPONSE)) {
+		gck_rpc_warn ("couldn't prepare message");
+		return 0;
+	}
+	
+	switch(req->call_id) {
+	
+	#define CASE_CALL(name) \
+		case GCK_RPC_CALL_##name: \
+			ret = rpc_##name (cs); \
+			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_WaitForSlotEvent)
+	CASE_CALL(C_OpenSession)
+	CASE_CALL(C_CloseSession)
+	CASE_CALL(C_CloseAllSessions)
+	CASE_CALL(C_GetFunctionStatus)
+	CASE_CALL(C_CancelFunction)
+	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)
+	#undef CASE_CALL
+	
+	default:
+		/* This should have been caught by the parse code */
+		assert (0 && "Unchecked call");
+		break;
+	};
+
+	if (ret == CKR_OK) {
+		
+		/* Parsing errors? */
+		if (gck_rpc_message_buffer_error (req)) {
+			gck_rpc_warn ("invalid request from module, probably too short");
+			ret = PARSE_ERROR;
+		}
+
+		/* Out of memory errors? */
+		if (gck_rpc_message_buffer_error (resp)) {
+			gck_rpc_warn ("out of memory error putting together message");
+			ret = PREP_ERROR;
+		}
+	}
+	
+	/* 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 (gck_rpc_message_is_verified (resp));
+		assert (resp->call_type == GCK_RPC_RESPONSE);
+		assert (resp->call_id == req->call_id);
+		assert (gck_rpc_calls[resp->call_id].response);
+		assert (strcmp (gck_rpc_calls[resp->call_id].response, 
+		                resp->signature) == 0);
+		
+	/* Fill in an error respnose */
+	} else {
+		if (!gck_rpc_message_prep (resp, GCK_RPC_CALL_ERROR, GCK_RPC_RESPONSE) ||
+		    !gck_rpc_message_write_ulong (resp, (uint32_t)ret) ||
+		    gck_rpc_message_buffer_error (resp)) {
+			gck_rpc_warn ("out of memory responding with error");
+			return 0;
+		}	
+	}
+	
+	return 1;
+}
+
+static int
+read_all (int sock, unsigned char* data, size_t len)
+{
+	int r;
+	
+	assert (sock >= 0);
+	assert (data);
+	assert (len > 0);
+
+	while (len > 0) {
+	
+		r = read (sock, data, len);
+			
+		if (r == 0) {
+			/* Connection was closed on client */
+			return 0;
+		} else if (r == -1) {
+			if (errno != EAGAIN && errno != EINTR) {
+				gck_rpc_warn ("couldn't receive data: %s", strerror (errno));
+				return 0;
+			}
+		} else {
+			data += r;
+			len -= r;
+		}
+	}
+	
+	return 1;
+}
+
+static int
+write_all (int sock, unsigned char* data, size_t len)
+{
+	int r;
+
+	assert (sock >= 0);
+	assert (data);
+	assert (len > 0);
+
+	while (len > 0) {
+	
+		r = write (sock, data, len);
+		
+		if (r == -1) {
+			if (errno == EPIPE) {
+				/* Connection closed from client */
+				return 0;
+			} else if (errno != EAGAIN && errno != EINTR) {
+				gck_rpc_warn ("couldn't send data: %s", strerror (errno));
+				return 0;
+			}
+		} else {
+			data += r;
+			len -= r;
+		}
+	}
+	
+	return 1;
+}
+
+static void 
+run_dispatch_loop (int sock)
+{
+	CallState cs;
+	unsigned char buf[4];
+	uint32_t len;
+	
+	assert (sock != -1);
+
+	/* TODO: Read credentials */
+	
+	/* Setup our buffers */
+	if (!call_init (&cs)) {
+		gck_rpc_warn ("out of memory");
+		return;
+	}
+	
+	/* The main thread loop */
+	while (TRUE) {
+	
+		call_reset (&cs);
+		
+		/* Read the number of bytes ... */
+		if (!read_all (sock, buf, 4))
+			break;
+
+		/* Calculate the number of bytes */
+		len = gkr_buffer_decode_uint32 (buf);
+		if (len >= 0x0FFFFFFF) { 
+			gck_rpc_warn ("invalid message size from module: %u bytes", len); 
+			break;
+		}
+		
+		/* Allocate memory */
+		gkr_buffer_reserve (&cs.req->buffer, cs.req->buffer.len + len); 
+		if (gkr_buffer_has_error (&cs.req->buffer)) {
+			gck_rpc_warn ("error allocating buffer for message");
+			break;
+		}
+		
+		/* ... and read/parse in the actual message */
+		if (!read_all (sock, cs.req->buffer.buf, len))
+			break;
+		
+		gkr_buffer_add_empty (&cs.req->buffer, len);
+		
+		if (!gck_rpc_message_parse (cs.req, GCK_RPC_REQUEST))
+			break;
+		
+		/* ... send for processing ... */
+		if (!dispatch_call (&cs)) 
+			break;
+		
+		/* .. send back response length, and then response data */
+		gkr_buffer_encode_uint32 (buf, cs.resp->buffer.len);
+		if(!write_all (sock, buf, 4) ||
+		   !write_all (sock, cs.resp->buffer.buf, cs.resp->buffer.len))
+			break;
+	}
+	
+	call_uninit (&cs);
+}
+
+static void*
+run_dispatch_thread (void *arg)
+{
+	int *sock = arg;
+	assert (*sock != -1);
+
+	/* Try and initialize the PKCS#11 module */
+	if (!pkcs11_initialized) {
+		
+	}
+	
+	run_dispatch_loop (*sock);
+	
+	/* The thread closes the socket and marks as done */
+	assert (*sock != -1);
+	close (*sock);
+	*sock = -1;
+	
+	return NULL;
+}	
+
+/* ---------------------------------------------------------------------------
+ * MAIN THREAD
+ */
+
+typedef struct _DispatchState {
+	struct _DispatchState *next;
+	GThread *thread;
+	int socket;
+} DispatchState;
+
+/* The main daemon socket that we're listening on */
+static int pkcs11_socket = -1;
+
+/* The unix socket path, that we listen on */
+static char pkcs11_socket_path[MAXPATHLEN] = { 0, };
+
+/* A linked list of dispatcher threads */
+static DispatchState *pkcs11_dispatchers = NULL;
+
+
+void 
+gck_rpc_dispatch_accept (void)
+{
+	struct sockaddr_un addr;
+	DispatchState *ds, **here;
+	GError *error = NULL;
+	socklen_t addrlen;
+	int new_fd;
+
+	assert (pkcs11_socket != -1);
+
+	/* Cleanup any completed dispatch threads */
+	for (here = &pkcs11_dispatchers, ds = *here; ds != NULL; ds = *here) {
+		if (ds->socket == -1) {
+			g_thread_join (ds->thread);
+			*here = ds->next; 
+			free (ds);
+		} else {
+			here = &ds->next;
+		}
+	}
+	
+	addrlen = sizeof (addr);
+	new_fd = accept (pkcs11_socket, (struct sockaddr*) &addr, &addrlen);
+	if (new_fd < 0) {
+		gck_rpc_warn ("cannot accept pkcs11 connection: %s", strerror (errno));
+		return;
+	}
+	
+	ds = calloc (1, sizeof (DispatchState));
+	if (ds == NULL) {
+		gck_rpc_warn ("out of memory");
+		close (new_fd);
+		return;
+	}
+	
+	ds->socket = new_fd;
+	
+	ds->thread = g_thread_create (run_dispatch_thread, &(ds->socket), TRUE, &error);
+	if (!ds->thread) {
+		gck_rpc_warn ("couldn't start thread: %s", error && error->message ? error->message : "");
+		close (new_fd);
+		free (ds);
+		return;
+	}
+	
+	ds->next = pkcs11_dispatchers;
+	pkcs11_dispatchers = ds;
+}
+
+int
+gck_rpc_dispatch_init (const char *socket_path, CK_FUNCTION_LIST_PTR module, 
+                       CK_C_INITIALIZE_ARGS_PTR init_args)
+{
+	struct sockaddr_un addr;
+	int sock;
+
+#ifdef _DEBUG
+	GCK_RPC_CHECK_CALLS ();
+#endif
+
+	assert (module);
+	assert (socket_path);
+
+	/* cannot be called more than once */
+	assert (!pkcs11_module);
+	assert (pkcs11_socket == -1);
+	assert (pkcs11_dispatchers == NULL);
+	
+	snprintf (pkcs11_socket_path, sizeof (pkcs11_socket_path), 
+	          "%s", socket_path);
+
+	sock = socket(AF_UNIX, SOCK_STREAM, 0);
+	if (sock < 0) {
+		gck_rpc_warn ("couldn't create pkcs11 socket: %s", strerror (errno));
+		return -1;
+	}
+	
+	memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+	strncpy (addr.sun_path, pkcs11_socket_path, sizeof (addr.sun_path));
+	if (bind (sock, (struct sockaddr*)&addr, sizeof (addr)) < 0) {
+		gck_rpc_warn ("couldn't bind to pkcs11 socket: %s: %s", 
+		                  pkcs11_socket_path, strerror (errno));
+		return -1;
+	}
+	
+	if (listen (sock, 128) < 0) {
+		gck_rpc_warn ("couldn't listen on pkcs11 socket: %s: %s", 
+		                  pkcs11_socket_path, strerror (errno));
+		return -1;
+	}
+	
+	pkcs11_module = module;
+	pkcs11_initialize_args = init_args;
+	pkcs11_socket = sock;
+	pkcs11_dispatchers = NULL;
+	
+	return sock;
+}
+
+void
+gck_rpc_dispatch_uninit (void)
+{
+	DispatchState *ds, *next;
+	
+	if (!pkcs11_module)
+		return;
+
+	/* Close our main listening socket */
+	if (pkcs11_socket != -1) 
+		close (pkcs11_socket);
+	pkcs11_socket = -1;
+
+	/* Delete our unix socket */
+	if(pkcs11_socket_path[0])
+		unlink (pkcs11_socket_path);
+	pkcs11_socket_path[0] = 0;
+
+	/* Stop all of the dispatch threads */
+	for (ds = pkcs11_dispatchers; ds; ds = next) {
+		next = ds->next;
+		
+		/* Forcibly shutdown the connection */
+		if (ds->socket)
+			shutdown (ds->socket, SHUT_RDWR);
+		g_thread_join (ds->thread);
+		
+		/* This is always closed by dispatch thread */
+		assert (ds->socket == -1);
+		free (ds);
+	}
+	
+	pkcs11_module = NULL;
+	pkcs11_initialize_args = NULL;
+}

Added: trunk/pkcs11/rpc-layer/gck-rpc-layer.h
==============================================================================
--- (empty file)
+++ trunk/pkcs11/rpc-layer/gck-rpc-layer.h	Sun Jan  4 23:07:18 2009
@@ -0,0 +1,21 @@
+#ifndef GCKRPC_H_
+#define GCKRPC_H_
+
+#include "pkcs11/pkcs11.h"
+
+/* ------------------------------------------------------------------
+ * DISPATCHER 
+ */
+
+/* Call to initialize the module and start listening, returns socket or -1 */
+int                gck_rpc_dispatch_init                (const char *socket_prefix, 
+                                                         CK_FUNCTION_LIST_PTR module, 
+                                                         CK_C_INITIALIZE_ARGS_PTR init_args);
+
+/* Should be called to cleanup dispatcher */
+void               gck_rpc_dispatch_uninit              (void);
+
+/* Accept a new connection. Should be called when above fd has read */
+void               gck_rpc_dispatch_accept              (void);
+
+#endif /* GCKRPC_H_ */

Added: trunk/pkcs11/rpc-layer/gck-rpc-message.c
==============================================================================
--- (empty file)
+++ trunk/pkcs11/rpc-layer/gck-rpc-message.c	Sun Jan  4 23:07:18 2009
@@ -0,0 +1,469 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* p11-rpc-message.c - our marshalled PKCS#11 protocol.
+
+   Copyright (C) 2008, Stef Walter
+
+   The Gnome Keyring Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Keyring Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Stef Walter <stef memberwebs com>
+*/
+
+#include "config.h"
+
+#include "gck-rpc-layer.h"
+#include "gck-rpc-private.h"
+
+#include <string.h>
+
+#ifdef G_DISABLE_ASSERT
+#define assert(x)
+#else
+#include <assert.h>
+#endif
+
+GckRpcMessage*
+gck_rpc_message_new (GkrBufferAllocator allocator)
+{
+	GckRpcMessage *msg;
+	
+	assert (allocator);
+	
+	msg = (GckRpcMessage*) (allocator)(NULL, sizeof (GckRpcMessage));
+	if (!msg)
+		return NULL;
+	memset (msg, 0, sizeof (*msg));
+	
+	if (!gkr_buffer_init_full (&msg->buffer, 64, allocator)) {
+		(allocator) (msg, 0); /* Frees allocation */
+		return NULL;
+	}
+	
+	gck_rpc_message_reset (msg);
+	
+	return msg;
+}
+
+void 
+gck_rpc_message_free (GckRpcMessage *msg)
+{
+	GkrBufferAllocator allocator;
+	
+	if (msg) {
+		assert (msg->buffer.allocator);
+		allocator = msg->buffer.allocator; 
+		gkr_buffer_uninit (&msg->buffer);
+		
+		/* frees data buffer */
+		(allocator) (msg, 0);
+	}
+}
+
+void 
+gck_rpc_message_reset (GckRpcMessage *msg)
+{
+	assert (msg);
+	
+	msg->call_id = 0;
+	msg->call_type = 0;
+	msg->signature = NULL;
+	msg->sigverify = NULL;
+	msg->parsed = 0;
+	
+	gkr_buffer_reset (&msg->buffer);
+}
+
+int
+gck_rpc_message_prep (GckRpcMessage *msg, int call_id, GckRpcMessageType type)
+{
+	int len;
+
+	assert (type);
+	assert (call_id >= GCK_RPC_CALL_ERROR);
+	assert (call_id < GCK_RPC_CALL_MAX);
+	
+	gck_rpc_message_reset (msg);
+
+	if (call_id != GCK_RPC_CALL_ERROR) {
+
+		/* The call id and signature */
+		if (type == GCK_RPC_REQUEST) 
+			msg->signature = gck_rpc_calls[call_id].request;
+		else if (type == GCK_RPC_RESPONSE)
+			msg->signature = gck_rpc_calls[call_id].response;
+		else
+			assert (0 && "invalid message type");
+		msg->sigverify = msg->signature;
+	}
+	
+	msg->call_id = call_id;
+	msg->call_type = type;
+
+	/* Encode the two of them */
+	gkr_buffer_add_uint32 (&msg->buffer, call_id);
+	if (msg->signature) {
+		len = strlen (msg->signature);
+		gkr_buffer_add_byte_array (&msg->buffer, (unsigned char*)msg->signature, len);
+	}
+
+	msg->parsed = 0;
+	return !gkr_buffer_has_error (&msg->buffer);
+}
+
+int 
+gck_rpc_message_parse (GckRpcMessage *msg, GckRpcMessageType type)
+{
+	const unsigned char *val;
+	size_t len;
+	uint32_t call_id;
+
+	msg->parsed = 0;
+
+	/* Pull out the call identifier */
+	if (!gkr_buffer_get_uint32 (&msg->buffer, msg->parsed, &(msg->parsed), &call_id)) {
+		gck_rpc_warn ("invalid message: couldn't read call identifier");
+		return 0;
+	}
+
+	msg->signature = msg->sigverify = NULL;
+
+	/* If it's an error code then no more processing */
+	if (call_id == GCK_RPC_CALL_ERROR) {
+		if (type == GCK_RPC_REQUEST) {
+			gck_rpc_warn ("invalid message: error code in request");
+			return 0;
+		}
+		
+		return 1;
+	}
+
+	/* The call id and signature */
+	if (call_id <= 0 || call_id >= GCK_RPC_CALL_MAX) {
+		gck_rpc_warn ("invalid message: bad call id: %d", call_id);
+		return 0;
+	}
+	if (type == GCK_RPC_REQUEST) 
+		msg->signature = gck_rpc_calls[call_id].request;
+	else if (type == GCK_RPC_RESPONSE)
+		msg->signature = gck_rpc_calls[call_id].response;
+	else
+		assert (0 && "invalid message type");
+	msg->call_id = call_id;
+	msg->call_type = type;
+	msg->sigverify = msg->signature;
+
+	/* Verify the incoming signature */
+	if (!gkr_buffer_get_byte_array (&msg->buffer, msg->parsed, &(msg->parsed), &val, &len)) {
+		gck_rpc_warn ("invalid message: couldn't read signature");
+		return 0;
+	}
+	
+	if ((strlen (msg->signature) != len) || (memcmp (val, msg->signature, len) != 0)) {
+		gck_rpc_warn ("invalid message: signature doesn't match");
+		return 0;
+	}
+	
+	return 1;
+}
+
+int
+gck_rpc_message_equals (GckRpcMessage *m1, GckRpcMessage *m2)
+{
+	assert (m1 && m2);
+	
+	/* Any errors and messages are never equal */
+	if (gkr_buffer_has_error (&m1->buffer) || 
+	    gkr_buffer_has_error (&m2->buffer))
+		return 0;
+
+	/* Calls and signatures must be identical */	
+	if (m1->call_id != m2->call_id)
+		return 0;
+	if (m1->call_type != m2->call_type)
+		return 0;
+	if (m1->signature && m2->signature) {
+		if (strcmp (m1->signature, m2->signature) != 0)
+			return 0;
+	} else if (m1->signature != m2->signature) {
+		return 0;
+	}
+		
+	/* Data in buffer must be identical */
+	return gkr_buffer_equal (&m1->buffer, &m2->buffer);
+}
+
+int 
+gck_rpc_message_verify_part (GckRpcMessage *msg, const char* part)
+{
+	int len, ok;
+	
+	if (!msg->sigverify)
+		return 1;
+
+	len = strlen (part);
+	ok = (strncmp (msg->sigverify, part, len) == 0);
+	if (ok)
+		msg->sigverify += len;
+	return ok;
+}
+
+int
+gck_rpc_message_write_attribute_buffer (GckRpcMessage *msg, CK_ATTRIBUTE_PTR arr, 
+                                        CK_ULONG num)
+{
+	CK_ATTRIBUTE_PTR attr;
+	CK_ULONG i;
+
+	assert (!num || arr);
+	assert (msg);
+
+	/* Make sure this is in the rigth order */
+	assert (!msg->signature || gck_rpc_message_verify_part (msg, "fA"));
+	
+	/* Write the number of items */
+	gkr_buffer_add_uint32 (&msg->buffer, num);
+	
+	for (i = 0; i < num; ++i) {
+		attr = &(arr[i]);
+
+		/* The attribute type */
+		gkr_buffer_add_uint32 (&msg->buffer, attr->type);
+
+		/* And the attribute buffer length */
+		gkr_buffer_add_uint32 (&msg->buffer, attr->pValue ? attr->ulValueLen : 0);
+	}
+
+	return !gkr_buffer_has_error (&msg->buffer);	
+}
+
+int
+gck_rpc_message_write_attribute_array (GckRpcMessage *msg, 
+                                           CK_ATTRIBUTE_PTR arr, CK_ULONG num)
+{
+	CK_ULONG i;
+	CK_ATTRIBUTE_PTR attr;
+	unsigned char validity;
+
+	assert (!num || arr);
+	assert (msg);
+
+	/* Make sure this is in the rigth order */
+	assert (!msg->signature || gck_rpc_message_verify_part (msg, "aA"));
+	
+	/* Write the number of items */
+	gkr_buffer_add_uint32 (&msg->buffer, num);
+	
+	for (i = 0; i < num; ++i) {
+		attr = &(arr[i]);
+
+		/* The attribute type */
+		gkr_buffer_add_uint32 (&msg->buffer, attr->type);
+
+		/* Write out the attribute validity */
+		validity = (((CK_LONG)attr->ulValueLen) == -1) ? 0 : 1;
+		gkr_buffer_add_byte (&msg->buffer, validity);
+
+		/* The attribute value */
+		if (validity)
+			gkr_buffer_add_byte_array (&msg->buffer, attr->pValue, attr->ulValueLen);
+	}
+
+	return !gkr_buffer_has_error (&msg->buffer);
+}
+
+int
+gck_rpc_message_read_byte (GckRpcMessage *msg, CK_BYTE *val)
+{
+	assert (msg);
+
+	/* Make sure this is in the right order */
+	assert (!msg->signature || gck_rpc_message_verify_part (msg, "y"));
+	return gkr_buffer_get_byte (&msg->buffer, msg->parsed, &msg->parsed, val);
+}
+
+int
+gck_rpc_message_write_byte (GckRpcMessage *msg, CK_BYTE val)
+{
+	assert (msg);
+
+	/* Make sure this is in the right order */
+	assert (!msg->signature || gck_rpc_message_verify_part (msg, "y"));
+	return gkr_buffer_add_byte (&msg->buffer, val);
+}
+
+int
+gck_rpc_message_read_ulong (GckRpcMessage *msg, CK_ULONG *val)
+{
+	uint32_t v;
+	assert (msg);
+	
+	/* Make sure this is in the right order */
+	assert (!msg->signature || gck_rpc_message_verify_part (msg, "u"));
+
+	if (!gkr_buffer_get_uint32 (&msg->buffer, msg->parsed, &msg->parsed, &v))
+		return 0;
+	if (val)
+		*val = v;
+	return 1;
+}
+
+int
+gck_rpc_message_write_ulong (GckRpcMessage *msg, CK_ULONG val)
+{
+	assert (msg);
+
+	/* Make sure this is in the rigth order */
+	assert (!msg->signature || gck_rpc_message_verify_part (msg, "u"));
+	return gkr_buffer_add_uint32 (&msg->buffer, val);
+}
+
+int
+gck_rpc_message_write_byte_buffer (GckRpcMessage *msg, CK_ULONG count)
+{
+	assert (msg);
+
+	/* Make sure this is in the right order */
+	assert (!msg->signature || gck_rpc_message_verify_part (msg, "fy"));
+	return gkr_buffer_add_uint32 (&msg->buffer, count);
+}
+
+int
+gck_rpc_message_write_byte_array (GckRpcMessage *msg, CK_BYTE_PTR arr, CK_ULONG num)
+{
+	assert (msg);
+	assert (!num || arr);
+
+	/* Make sure this is in the right order */
+	assert (!msg->signature || gck_rpc_message_verify_part (msg, "ay"));
+	
+	/* No array, no data, just length */
+	if (!arr) {
+		gkr_buffer_add_byte (&msg->buffer, 0);
+		gkr_buffer_add_uint32 (&msg->buffer, num);
+	} else {
+		gkr_buffer_add_byte (&msg->buffer, 1);
+		gkr_buffer_add_byte_array (&msg->buffer, arr, num);
+	}
+	
+	return !gkr_buffer_has_error (&msg->buffer);
+}
+
+int
+gck_rpc_message_write_ulong_buffer (GckRpcMessage *msg, CK_ULONG count)
+{
+	assert (msg);
+
+	/* Make sure this is in the right order */
+	assert (!msg->signature || gck_rpc_message_verify_part (msg, "fu"));
+	return gkr_buffer_add_uint32 (&msg->buffer, count);
+}
+
+int
+gck_rpc_message_write_ulong_array (GckRpcMessage *msg, CK_ULONG_PTR array, CK_ULONG n_array)
+{
+	CK_ULONG i;
+	
+	assert (msg);
+	
+	/* Check that we're supposed to have this at this point */
+	assert (!msg->signature || gck_rpc_message_verify_part (msg, "au"));
+
+	/* We send a byte which determines whether there's actual data present or not */
+	gkr_buffer_add_byte (&msg->buffer, array ? 1 : 0);
+	gkr_buffer_add_uint32 (&msg->buffer, n_array);
+	
+	/* Now send the data if valid */
+	if (array) {
+		for (i = 0; i < n_array; ++i)
+			gkr_buffer_add_uint32 (&msg->buffer, array[i]); 
+	}
+	
+	return !gkr_buffer_has_error (&msg->buffer);
+}
+
+int
+gck_rpc_message_read_version (GckRpcMessage *msg, CK_VERSION* version)
+{
+	assert (msg);
+	assert (version);
+	
+	/* Check that we're supposed to have this at this point */
+	assert (!msg->signature || gck_rpc_message_verify_part (msg, "v"));
+
+	return gkr_buffer_get_byte (&msg->buffer, msg->parsed, &msg->parsed, &version->major) &&
+	       gkr_buffer_get_byte (&msg->buffer, msg->parsed, &msg->parsed, &version->minor);
+}
+
+int
+gck_rpc_message_write_version (GckRpcMessage *msg, CK_VERSION* version)
+{
+	assert (msg);
+	assert (version);
+	
+	/* Check that we're supposed to have this at this point */
+	assert (!msg->signature || gck_rpc_message_verify_part (msg, "v"));
+
+	gkr_buffer_add_byte (&msg->buffer, version->major);
+	gkr_buffer_add_byte (&msg->buffer, version->minor);
+
+	return !gkr_buffer_has_error (&msg->buffer);	
+}
+
+int
+gck_rpc_message_read_space_string (GckRpcMessage *msg, CK_UTF8CHAR* buffer, CK_ULONG length)
+{
+	const unsigned char *data;
+	size_t n_data;
+	
+	assert (msg);
+	assert (buffer);
+	assert (length);
+	
+	assert (!msg->signature || gck_rpc_message_verify_part (msg, "s"));
+	
+	if (!gkr_buffer_get_byte_array (&msg->buffer, msg->parsed, &msg->parsed, &data, &n_data))
+		return 0;
+	
+	if (n_data != length) {
+		gck_rpc_warn ("invalid length space padded string received: %d != %d", length, n_data);
+		return 0;
+	}
+	
+	memcpy (buffer, data, length);
+	return 1;
+}
+
+int
+gck_rpc_message_write_space_string (GckRpcMessage *msg, CK_UTF8CHAR* buffer, CK_ULONG length)
+{
+	assert (msg);
+	assert (buffer);
+	assert (length);
+	
+	assert (!msg->signature || gck_rpc_message_verify_part (msg, "s"));
+	
+	return gkr_buffer_add_byte_array (&msg->buffer, buffer, length);
+}
+
+int
+gck_rpc_message_write_zero_string (GckRpcMessage *msg, CK_UTF8CHAR* string)
+{
+	assert (msg);
+	assert (string);
+
+	assert (!msg->signature || gck_rpc_message_verify_part (msg, "z"));
+	
+	return gkr_buffer_add_string (&msg->buffer, (const char*)string);
+}

Added: trunk/pkcs11/rpc-layer/gck-rpc-module.c
==============================================================================
--- (empty file)
+++ trunk/pkcs11/rpc-layer/gck-rpc-module.c	Sun Jan  4 23:07:18 2009
@@ -0,0 +1,2103 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gkr-pkcs11-rpc-module.c - a PKCS#11 module which communicates with another process
+
+   Copyright (C) 2008, Stefan Walter
+
+   The Gnome Keyring Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Keyring Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Stef Walter <stef memberwebs com>
+*/
+
+#include "config.h"
+
+#include "gck-rpc-layer.h"
+#include "gck-rpc-private.h"
+
+#include "pkcs11/pkcs11.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <stdlib.h>
+#include <limits.h>
+#include <ctype.h>
+#include <stdint.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+/* -------------------------------------------------------------------
+ * GLOBALS / DEFINES
+ */
+
+/* Various mutexes */
+static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/* Our per thread key */
+static pthread_key_t pkcs11_per_thread = 0;
+
+/* Whether we've been initialized, and on what process id it happened */
+static int pkcs11_initialized = 0;
+static pid_t pkcs11_initialized_pid = 0;
+
+/* The socket to connect to */
+static char pkcs11_socket_path[MAXPATHLEN] = { 0, };
+
+/* The error used by us when parsing of rpc message fails */
+#define PARSE_ERROR   CKR_DEVICE_ERROR
+
+/* -----------------------------------------------------------------------------
+ * LOGGING and DEBUGGING
+ */
+
+#if DEBUG_OUTPUT
+#define debug(x) gck_rpc_debug x
+#else
+#define debug(x)	
+#endif 
+#define warning(x) gck_rpc_warn x
+
+#define return_val_if_fail(x, v) \
+	if (!(x)) { gck_rpc_warn ("'%s' not true at %s", #x, __func__); return v; } 
+
+void 
+gck_rpc_log (const char *line)
+{
+	fprintf (stderr, "%s\n", line);
+}
+
+/* -----------------------------------------------------------------------------
+ * CALL SESSION
+ */
+
+enum CallStatus {
+	CALL_INVALID,
+	CALL_READY,
+	CALL_PREP,
+	CALL_TRANSIT,
+	CALL_PARSE
+};
+
+typedef struct _CallState {
+	int socket;                     /* The connection we're sending on */
+	GckRpcMessage *req;          /* The current request */
+	GckRpcMessage *resp;         /* The current response */
+	int call_status;
+} CallState;
+
+/* Allocator for call session buffers */
+static void*
+call_allocator (void* p, unsigned long sz)
+{
+	void* res = realloc (p, (size_t)sz);
+	if (!res && sz)
+		warning (("memory allocation of %lu bytes failed", sz));
+	return res;	
+}
+
+static CK_RV
+call_connect (CallState *cs)
+{
+	struct sockaddr_un addr;
+	int sock;
+
+	assert (cs);
+	assert (cs->socket == -1);
+	assert (cs->call_status == CALL_INVALID);
+	assert (pkcs11_socket_path[0]);
+	
+	debug (("connecting to: %s", pkcs11_socket_path));
+		
+	addr.sun_family = AF_UNIX;
+	strncpy (addr.sun_path, pkcs11_socket_path, sizeof (addr.sun_path));
+	
+	sock = socket (AF_UNIX, SOCK_STREAM, 0);
+	if (sock < 0) {
+		warning (("couldn't open socket: %s", strerror (errno)));
+		return CKR_DEVICE_ERROR;
+	}
+
+	/* close on exec */
+	if (fcntl (sock, F_SETFD, 1) == -1) {
+		close (sock);
+		warning (("couldn't secure socket: %s", strerror (errno)));
+		return CKR_DEVICE_ERROR;
+	}
+
+	if (connect (sock, (struct sockaddr*) &addr, sizeof (addr)) < 0) {
+		close (sock);
+		warning (("couldn't connect to: %s: %s", pkcs11_socket_path, strerror (errno)));
+		return CKR_DEVICE_ERROR;
+	}
+
+	/* TODO: Write credentials */
+#if 0
+	if (!gck_rpc_write_credentials (sock)) {
+		close (sock);
+		warning (("couldn't send socket credentials: %s", strerror (errno)));
+		return CKR_DEVICE_ERROR;
+	}
+#endif
+
+	cs->socket = sock;
+	cs->call_status = CALL_READY;
+	debug (("connected socket"));
+	
+	return CKR_OK;
+}
+
+static void
+call_disconnect (CallState *cs)
+{
+	assert (cs);
+	
+	if (cs->socket != -1) {
+		debug (("disconnected socket"));
+		close (cs->socket);
+		cs->socket = -1;
+	}
+}
+
+static void
+call_destroy (void *value)
+{
+	CallState *cs = value;
+	
+	if (value) {
+		call_disconnect (cs);
+		assert (cs->socket == -1);
+
+		gck_rpc_message_free (cs->req);
+		gck_rpc_message_free (cs->resp);
+		
+		free (cs);
+		
+		debug (("destroyed state"));
+	}
+}
+
+static CK_RV
+call_create (void)
+{
+	CallState *cs;
+	
+	assert (pkcs11_per_thread);
+	
+	cs = calloc(1, sizeof (CallState));
+	if (!cs)
+		return CKR_HOST_MEMORY;
+	cs->socket = -1;
+	cs->call_status = CALL_INVALID;
+	
+	assert (!pthread_getspecific (pkcs11_per_thread));
+	pthread_setspecific (pkcs11_per_thread, cs);
+	
+	return CKR_OK;
+}
+
+static CK_RV
+call_lookup (CallState **ret)
+{
+	CallState *cs;
+	CK_RV rv;
+	
+	assert (ret);
+	assert (pkcs11_per_thread);
+	
+	cs = pthread_getspecific (pkcs11_per_thread);
+	if (cs == NULL) {
+		rv = call_create ();
+		if (rv != CKR_OK)
+			return rv;
+
+		cs = pthread_getspecific (pkcs11_per_thread);
+		assert (cs);
+	}
+
+	if (cs->call_status == CALL_INVALID) {
+		rv = call_connect (cs);
+		if (rv != CKR_OK)
+			return rv;
+	}
+	
+	assert (cs->call_status == CALL_READY);
+	assert (cs->socket != -1);
+	*ret = cs;
+	return CKR_OK;
+}
+
+/* Perform the initial setup for a new call. */
+static CK_RV
+call_prepare (CallState *cs, int call_id)
+{
+	assert (cs);
+	assert (cs->call_status == CALL_READY);
+
+	/* Allocate a new request if we've lost the old one */
+	if (!cs->req) {
+		cs->req = gck_rpc_message_new (call_allocator);
+		if (!cs->req) {
+			warning (("cannot allocate request buffer: out of memory"));
+			return CKR_HOST_MEMORY;
+		}
+	}
+	
+	/* Put in the Call ID and signature */
+	gck_rpc_message_reset (cs->req);
+	if (!gck_rpc_message_prep (cs->req, call_id, GCK_RPC_REQUEST))
+		return CKR_HOST_MEMORY;
+	
+	debug (("prepared call: %d", call_id));
+
+	/* Ready to fill in arguments */
+	cs->call_status = CALL_PREP;
+	return CKR_OK;
+}
+
+/* Write all data to session socket.  */
+static CK_RV
+call_write (CallState *cs, unsigned char* data, size_t len)
+{
+	int fd, r;
+
+	assert (cs);
+	assert (data);
+	assert (len > 0);
+
+	while (len > 0) {
+	
+		fd = cs->socket;
+		if (fd == -1) {
+			warning (("couldn't send data: socket has been closed"));
+			return CKR_DEVICE_ERROR;
+		}
+		
+		r = write (fd, data, len);
+		
+		if (r == -1) {
+			if (errno == EPIPE) {
+				warning (("couldn't send data: daemon closed connection"));
+				call_disconnect (cs);
+				return CKR_DEVICE_ERROR;
+			} else if (errno != EAGAIN && errno != EINTR) {
+				warning (("couldn't send data: %s", strerror (errno)));
+				return CKR_DEVICE_ERROR;
+			}
+		} else {
+			debug (("wrote %d bytes", r));
+			data += r;
+			len -= r;
+		}
+	}
+	
+	return CKR_OK;
+}
+
+/* Read a certain amount of data from session socket. */
+static CK_RV
+call_read (CallState *cs, unsigned char* data, size_t len)
+{
+	int fd, r;
+
+	assert (cs);
+	assert (data);
+	assert (len > 0);
+
+	while (len > 0) {
+	
+		fd = cs->socket;
+		if (fd == -1) {
+			warning (("couldn't receive data: session socket has been closed"));
+			return CKR_DEVICE_ERROR;
+		}
+		
+		r = read (fd, data, len);
+		
+		if (r == 0) {
+			warning (("couldn't receive data: daemon closed connection"));
+			call_disconnect (cs);
+			return CKR_DEVICE_ERROR;
+		} else if (r == -1) {
+			if (errno != EAGAIN && errno != EINTR) {
+				warning (("couldn't receive data: %s", strerror (errno)));
+				return CKR_DEVICE_ERROR;
+			}
+		} else {
+			debug (("read %d bytes", r));
+			data += r;
+			len -= r;
+		}
+	}
+	
+	return CKR_OK;
+}
+
+/* 
+ * Used by call_session_do_call() to actually send the message to the daemon.
+ * Note how we unlock and relock the session during the call. 
+ */
+static CK_RV
+call_send_recv (CallState *cs)
+{
+	GckRpcMessage *req, *resp;
+	unsigned char buf[4];
+	uint32_t len;
+	CK_RV ret;
+	
+	assert (cs);
+	assert (cs->req);
+	assert (cs->call_status == CALL_PREP);
+	
+	cs->call_status = CALL_TRANSIT;
+	
+	/* Setup the response buffer properly */
+	if (!cs->resp) {
+		/* TODO: Do secrets or passwords ever flow through here? */
+		cs->resp = gck_rpc_message_new (call_allocator);
+		if (!cs->resp) {
+			warning (("couldn't allocate response buffer: out of memory"));
+			return CKR_HOST_MEMORY;
+		}
+	}
+	gck_rpc_message_reset (cs->resp);
+	
+	/* 
+	 * Now as an additional check to make sure nothing nasty will
+	 * happen while we are unlocked, we remove the request and 
+	 * response from the session during the action.
+	 */
+	req = cs->req;
+	resp = cs->resp;
+	cs->req = cs->resp = NULL;
+
+	/* Send the number of bytes, and then the data */
+	gkr_buffer_encode_uint32 (buf, req->buffer.len);
+	ret = call_write (cs, buf, 4);
+	if (ret != CKR_OK)
+		goto cleanup;
+	ret = call_write (cs, req->buffer.buf, req->buffer.len);
+	if (ret != CKR_OK)
+		goto cleanup;
+	
+	/* Now read out the number of bytes, and then the data */
+	ret = call_read (cs, buf, 4);
+	if (ret != CKR_OK) 
+		goto cleanup;
+	len = gkr_buffer_decode_uint32 (buf);
+	if (!gkr_buffer_reserve (&resp->buffer, len + resp->buffer.len)) {
+		warning (("couldn't allocate %u byte response area: out of memory", len));
+		ret = CKR_HOST_MEMORY;
+		goto cleanup;
+	}
+	ret = call_read (cs, resp->buffer.buf, len);
+	if (ret != CKR_OK)
+		goto cleanup;
+	
+	gkr_buffer_add_empty (&resp->buffer, len);
+	if (!gck_rpc_message_parse (resp, GCK_RPC_RESPONSE))
+		goto cleanup;
+	
+	debug (("received response from daemon"));
+	
+cleanup:
+	/* Make sure nobody else used this thread while unlocked */
+	assert (cs->call_status == CALL_TRANSIT);
+	assert (cs->resp == NULL);
+	cs->resp = resp;
+	assert (cs->req == NULL);
+	cs->req = req;
+	
+	return ret;
+}
+
+/* 
+ * At this point the request is ready. So we validate it, and we send it to 
+ * the daemon for a response. 
+ */
+static CK_RV
+call_run (CallState *cs)
+{
+	CK_RV ret = CKR_OK;
+	CK_ULONG ckerr;
+
+	assert (cs);
+	assert (cs->req);
+	assert (cs->call_status == CALL_PREP);
+	assert (cs->socket != -1);
+
+	/* Did building the call fail? */
+	if (gck_rpc_message_buffer_error (cs->req)) {
+		warning (("couldn't allocate request area: out of memory"));
+		return CKR_HOST_MEMORY;
+	}
+
+	/* Make sure that the signature is valid */
+	assert (gck_rpc_message_is_verified (cs->req));
+
+	/* Do the dialog with daemon */
+	ret = call_send_recv (cs);
+	
+	cs->call_status = CALL_PARSE;
+	
+	if (ret != CKR_OK)
+		return ret;
+
+	/* If it's an error code then return it */
+	if (cs->resp->call_id == GCK_RPC_CALL_ERROR) {
+
+		if (!gck_rpc_message_read_ulong (cs->resp, &ckerr)) {
+			warning (("invalid error response from gnome-keyring-daemon: too short"));
+			return CKR_DEVICE_ERROR;
+		}
+
+		if (ckerr <= CKR_OK) {
+			warning (("invalid error response from gnome-keyring-daemon: bad error code"));
+			return CKR_DEVICE_ERROR;
+		}
+
+		/* An error code from the daemon */
+		return (CK_RV)ckerr;
+	}
+	
+	/* Make sure daemon answered the right call */
+	if (cs->req->call_id != cs->resp->call_id) {
+		warning (("invalid response from gnome-keyring-daemon: call mismatch"));
+		return CKR_DEVICE_ERROR;
+	}
+
+	assert (!gck_rpc_message_buffer_error (cs->resp));
+	debug (("parsing response values"));
+
+	return CKR_OK;
+}
+
+static CK_RV
+call_done (CallState *cs, CK_RV ret)
+{
+	assert (cs);
+	assert (cs->call_status > CALL_INVALID);
+
+	if (cs->call_status == CALL_PARSE && cs->req && cs->resp) {
+
+		/* Check for parsing errors that were not caught elsewhere */
+		if (ret == CKR_OK) {
+
+			if (gck_rpc_message_buffer_error (cs->resp)) {
+				warning (("invalid response from gnome-keyring-daemon: bad argument data"));
+				return CKR_GENERAL_ERROR;
+			}
+
+			/* Double check that the signature matched our decoding */
+			assert (gck_rpc_message_is_verified (cs->resp));
+		} 
+	}
+
+	/* Some cleanup */
+	if (cs->socket == -1)
+		cs->call_status = CALL_INVALID;
+	else
+		cs->call_status = CALL_READY;
+
+	return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * MODULE SPECIFIC PROTOCOL CODE
+ */
+
+static CK_RV
+proto_read_attribute_array (GckRpcMessage *msg, CK_ATTRIBUTE_PTR arr, CK_ULONG len)
+{
+	uint32_t i, num, val;
+	CK_ATTRIBUTE_PTR attr;
+	const unsigned char *attrval;
+	size_t attrlen;
+	unsigned char validity;
+	CK_RV ret;
+
+	assert (len);
+	assert (msg);
+
+	/* Make sure this is in the right order */
+	assert (!msg->signature || gck_rpc_message_verify_part (msg, "aA"));
+	
+	/* Get the number of items. We need this value to be correct */
+	if (!gkr_buffer_get_uint32 (&msg->buffer, msg->parsed, &msg->parsed, &num))
+		return PARSE_ERROR; 
+
+	if (len != num) {
+		
+		/*
+		 * 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.
+		 */
+
+		warning (("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 */
+		gkr_buffer_get_uint32 (&msg->buffer, msg->parsed,
+		                       &msg->parsed, &val);
+
+		/* Attribute validity */
+		gkr_buffer_get_byte (&msg->buffer, msg->parsed,
+		                     &msg->parsed, &validity);
+
+		/* And the data itself */
+		if (validity)
+			gkr_buffer_get_byte_array (&msg->buffer, msg->parsed,
+			                           &msg->parsed, &attrval, &attrlen);
+			
+		/* Don't act on this data unless no errors */
+		if (gkr_buffer_has_error (&msg->buffer))
+			break;
+
+		/* Try and stuff it in the output data */
+		if (arr) {
+			attr = &(arr[i]);
+			attr->type = val;
+
+			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);
+			}
+		}
+	}
+
+	return gkr_buffer_has_error (&msg->buffer) ? PARSE_ERROR : ret;	
+}
+
+static CK_RV
+proto_read_byte_array (GckRpcMessage *msg, CK_BYTE_PTR arr,
+                       CK_ULONG_PTR len, CK_ULONG max)
+{
+	const unsigned char *val;
+	unsigned char valid;
+	size_t vlen;
+
+	assert (len);
+	assert (msg);
+
+	/* Make sure this is in the right order */
+	assert (!msg->signature || gck_rpc_message_verify_part (msg, "ay"));
+
+	/* A single byte which determines whether valid or not */
+	if (!gkr_buffer_get_byte (&msg->buffer, msg->parsed, &msg->parsed, &valid))
+		return PARSE_ERROR;
+	
+	/* If not valid, then just the length is encoded */
+	if (!valid) {
+		if (!gkr_buffer_get_uint32 (&msg->buffer, msg->parsed, &msg->parsed, &vlen))
+			return PARSE_ERROR;
+		
+		if (arr) {
+			
+			/*
+			 * This should never happen in normal operation. It denotes a goof up 
+			 * on the other side of our RPC. We should be sending an empty buffer 
+			 * only in the case where there's no array to be filled, which is what 
+			 * indicates the other side to reply with an invalid array.
+			 */
+
+			warning (("received an invalid array, but caller expected filled"));
+			return PARSE_ERROR;
+		}
+		
+		/* Just return the length */
+		*len = vlen;
+		return CKR_OK;
+	} 
+
+	/* Get the actual bytes */
+	if (!gkr_buffer_get_byte_array (&msg->buffer, msg->parsed, &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 (GckRpcMessage *msg, CK_ULONG_PTR arr,
+                        CK_ULONG_PTR len, CK_ULONG max)
+{
+	uint32_t i, num, val;
+	unsigned char valid;
+
+	assert (len);
+	assert (msg);
+
+	/* Make sure this is in the right order */
+	assert (!msg->signature || gck_rpc_message_verify_part (msg, "au"));
+
+	/* A single byte which determines whether valid or not */
+	if (!gkr_buffer_get_byte (&msg->buffer, msg->parsed, &msg->parsed, &valid))
+		return PARSE_ERROR;
+
+	/* Get the number of items. */
+	if (!gkr_buffer_get_uint32 (&msg->buffer, msg->parsed, &msg->parsed, &num))
+		return PARSE_ERROR;
+
+	*len = num;
+
+	if (!valid) {
+
+		if (arr) {
+			
+			/*
+			 * This should never happen in normal operation. It denotes a goof up 
+			 * on the other side of our RPC. We should be sending an empty buffer 
+			 * only in the case where there's no array to be filled, which is what 
+			 * indicates the other side to reply with an invalid array.
+			 */
+
+			warning (("received an invalid array, but caller expected filled"));
+			return PARSE_ERROR;
+		}
+
+		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) {
+		gkr_buffer_get_uint32 (&msg->buffer, msg->parsed, &msg->parsed, &val);
+		if (arr)
+			arr[i] = val;
+	}
+
+	return gkr_buffer_has_error (&msg->buffer) ? PARSE_ERROR : CKR_OK;
+}
+
+static CK_RV
+proto_write_mechanism (GckRpcMessage *msg, CK_MECHANISM_PTR mech)
+{
+	assert (msg);
+	assert (mech);
+
+	/* Make sure this is in the right order */
+	assert (!msg->signature || gck_rpc_message_verify_part (msg, "M"));
+	
+	/* The mechanism type */
+	gkr_buffer_add_uint32 (&msg->buffer, 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 (gck_rpc_mechanism_has_no_parameters (mech->mechanism))
+		gkr_buffer_add_byte_array (&msg->buffer, NULL, 0);
+	else if (gck_rpc_mechanism_has_sane_parameters (mech->mechanism))
+		gkr_buffer_add_byte_array (&msg->buffer, mech->pParameter, 
+		                           mech->ulParameterLen);
+	else
+		return CKR_MECHANISM_INVALID; 
+
+	return gkr_buffer_has_error (&msg->buffer) ? CKR_HOST_MEMORY : CKR_OK;
+}
+
+static CK_RV
+proto_read_info (GckRpcMessage *msg, CK_INFO_PTR info)
+{
+	assert (msg);
+	assert (info);
+
+	if (!gck_rpc_message_read_version (msg, &info->cryptokiVersion) || 
+	    !gck_rpc_message_read_space_string (msg, info->manufacturerID, 32) ||
+	    !gck_rpc_message_read_ulong (msg, &info->flags) ||
+	    !gck_rpc_message_read_space_string (msg, info->libraryDescription, 32) ||
+	    !gck_rpc_message_read_version (msg, &info->libraryVersion))
+		return PARSE_ERROR;
+
+	return CKR_OK;
+}
+
+static CK_RV
+proto_read_slot_info (GckRpcMessage *msg, CK_SLOT_INFO_PTR info)
+{
+	assert (msg);
+	assert (info);
+
+	if (!gck_rpc_message_read_space_string (msg, info->slotDescription, 64) ||
+	    !gck_rpc_message_read_space_string (msg, info->manufacturerID, 32) ||
+	    !gck_rpc_message_read_ulong (msg, &info->flags) ||
+	    !gck_rpc_message_read_version (msg, &info->hardwareVersion) ||
+	    !gck_rpc_message_read_version (msg, &info->firmwareVersion))
+		return PARSE_ERROR;
+
+	return CKR_OK;
+}
+
+static CK_RV
+proto_read_token_info (GckRpcMessage *msg, CK_TOKEN_INFO_PTR info)
+{
+	assert (msg);
+	assert (info);
+
+	if (!gck_rpc_message_read_space_string (msg, info->label, 32) ||
+	    !gck_rpc_message_read_space_string (msg, info->manufacturerID, 32) ||
+	    !gck_rpc_message_read_space_string (msg, info->model, 16) ||
+	    !gck_rpc_message_read_space_string (msg, info->serialNumber, 16) ||
+	    !gck_rpc_message_read_ulong (msg, &info->flags) ||
+	    !gck_rpc_message_read_ulong (msg, &info->ulMaxSessionCount) ||
+	    !gck_rpc_message_read_ulong (msg, &info->ulSessionCount) ||
+	    !gck_rpc_message_read_ulong (msg, &info->ulMaxRwSessionCount) ||
+	    !gck_rpc_message_read_ulong (msg, &info->ulRwSessionCount) ||
+	    !gck_rpc_message_read_ulong (msg, &info->ulMaxPinLen) ||
+	    !gck_rpc_message_read_ulong (msg, &info->ulMinPinLen) ||
+	    !gck_rpc_message_read_ulong (msg, &info->ulTotalPublicMemory) ||
+	    !gck_rpc_message_read_ulong (msg, &info->ulFreePublicMemory) ||
+	    !gck_rpc_message_read_ulong (msg, &info->ulTotalPrivateMemory) ||
+	    !gck_rpc_message_read_ulong (msg, &info->ulFreePrivateMemory) ||
+	    !gck_rpc_message_read_version (msg, &info->hardwareVersion) ||
+	    !gck_rpc_message_read_version (msg, &info->firmwareVersion) ||
+	    !gck_rpc_message_read_space_string (msg, info->utcTime, 16))
+		return PARSE_ERROR;
+	
+	return CKR_OK;
+}
+
+static CK_RV
+proto_read_mechanism_info (GckRpcMessage *msg, CK_MECHANISM_INFO_PTR info)
+{
+	assert (msg);
+	assert (info);
+	
+	if (!gck_rpc_message_read_ulong (msg, &info->ulMinKeySize) ||
+	    !gck_rpc_message_read_ulong (msg, &info->ulMaxKeySize) ||
+	    !gck_rpc_message_read_ulong (msg, &info->flags))
+		return PARSE_ERROR;
+	
+	return CKR_OK;
+}
+
+static CK_RV
+proto_read_sesssion_info (GckRpcMessage *msg, CK_SESSION_INFO_PTR info)
+{
+	assert (msg);
+	assert (info);
+
+	if (!gck_rpc_message_read_ulong (msg, &info->slotID) ||
+	    !gck_rpc_message_read_ulong (msg, &info->state) ||
+	    !gck_rpc_message_read_ulong (msg, &info->flags) ||
+	    !gck_rpc_message_read_ulong (msg, &info->ulDeviceError))
+		return PARSE_ERROR;
+
+	return CKR_OK;
+}
+
+/* -------------------------------------------------------------------
+ * CALL MACROS 
+ */
+
+#define BEGIN_CALL(call_id) \
+	debug ((#call_id ": enter")); \
+	return_val_if_fail (pkcs11_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); \
+	{  \
+		CallState *_cs; \
+		CK_RV _ret = CKR_OK; \
+		_ret = call_lookup (&_cs); \
+		if (_ret != CKR_OK) return _ret; \
+		_ret = call_prepare (_cs, GCK_RPC_CALL_##call_id); \
+		if (_ret != CKR_OK) goto _cleanup;
+
+#define PROCESS_CALL \
+		_ret = call_run (_cs); \
+		if (_ret != CKR_OK) goto _cleanup;
+
+#define RETURN(ret) \
+		_ret = ret; \
+		goto _cleanup;
+		
+#define END_CALL \
+	_cleanup: \
+		_ret = call_done (_cs, _ret); \
+		debug (("ret: %d", _ret)); \
+		return _ret; \
+	} 
+
+#define IN_BYTE(val) \
+	if (!gck_rpc_message_write_byte (_cs->req, val)) \
+		{ _ret = CKR_HOST_MEMORY; goto _cleanup; }
+
+#define IN_ULONG(val) \
+	if (!gck_rpc_message_write_ulong (_cs->req, val)) \
+		{ _ret = CKR_HOST_MEMORY; goto _cleanup; }
+	
+#define IN_STRING(val) \
+	if (!gck_rpc_message_write_zero_string (_cs->req, val)) \
+		{ _ret = CKR_HOST_MEMORY; goto _cleanup; }
+
+#define IN_BYTE_BUFFER(arr, len) \
+	if (len == NULL) \
+		{ _ret = CKR_ARGUMENTS_BAD; goto _cleanup; } \
+	if (!gck_rpc_message_write_byte_buffer (_cs->req, arr ? *len : 0)) \
+		{ _ret = CKR_HOST_MEMORY; goto _cleanup; }
+	
+#define IN_BYTE_ARRAY(arr, len) \
+	if (len != 0 && arr == NULL) \
+		_ret = CKR_ARGUMENTS_BAD; \
+	if (!gck_rpc_message_write_byte_array (_cs->req, arr, len)) \
+		{ _ret = CKR_HOST_MEMORY; goto _cleanup; }
+
+#define IN_ULONG_BUFFER(arr, len) \
+	if (len == NULL) \
+		{ _ret = CKR_ARGUMENTS_BAD; goto _cleanup; } \
+	if (!gck_rpc_message_write_ulong_buffer (_cs->req, arr ? *len : 0)) \
+		{ _ret = CKR_HOST_MEMORY; goto _cleanup; }
+	
+#define IN_ULONG_ARRAY(arr, len) \
+	if (len != 0 && arr == NULL) \
+		_ret = CKR_ARGUMENTS_BAD; \
+	if (!gck_rpc_message_write_ulong_array (_cs->req, 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 (!gck_rpc_message_write_attribute_buffer (_cs->req, (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 (!gck_rpc_message_write_attribute_array (_cs->req, (arr), (num))) \
+		{ _ret = CKR_HOST_MEMORY; goto _cleanup; }
+
+#define IN_MECHANISM_TYPE(val) \
+	if(!gck_rpc_mechanism_is_supported (val)) \
+		{ _ret = CKR_MECHANISM_INVALID; goto _cleanup; } \
+	if (!gck_rpc_message_write_ulong (_cs->req, val)) \
+		{ _ret = CKR_HOST_MEMORY; goto _cleanup; }
+
+#define IN_MECHANISM(val) \
+	if (val == NULL) \
+		_ret = CKR_ARGUMENTS_BAD; \
+	_ret = proto_write_mechanism (_cs->req, val); \
+	if (_ret != CKR_OK) goto _cleanup;
+
+
+
+#define OUT_ULONG(val) \
+	if (val == NULL) \
+		_ret = CKR_ARGUMENTS_BAD; \
+	if (_ret == CKR_OK && !gck_rpc_message_read_ulong (_cs->resp, val)) \
+		_ret = PARSE_ERROR;
+
+#define OUT_RETURN_CODE() { \
+	CK_RV r; \
+	_ret = gck_rpc_message_read_ulong (_cs->resp, (_ret == CKR_OK) ? &r : NULL); \
+	if (_ret == CKR_OK) _ret = r; \
+	if (_ret != CKR_OK) goto _cleanup; \
+	}
+
+#define OUT_BYTE_ARRAY(arr, len)  \
+	if (len == NULL) \
+		_ret = CKR_ARGUMENTS_BAD; \
+	if (_ret == CKR_OK) \
+		_ret = proto_read_byte_array (_cs->resp, (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 (_cs->resp, (a), (len), *(len)); 
+
+#define OUT_ATTRIBUTE_ARRAY(arr, num) \
+	if (_ret == CKR_OK) \
+		_ret = proto_read_attribute_array (_cs->resp, (arr), (num));
+
+#define OUT_INFO(info) \
+	if (info == NULL) \
+		_ret = CKR_ARGUMENTS_BAD; \
+	if (_ret == CKR_OK) \
+		_ret = proto_read_info (_cs->resp, info); 
+
+#define OUT_SLOT_INFO(info) \
+	if (info == NULL) \
+		_ret = CKR_ARGUMENTS_BAD; \
+	if (_ret == CKR_OK) \
+		_ret = proto_read_slot_info (_cs->resp, info);
+
+#define OUT_TOKEN_INFO(info) \
+	if (info == NULL) \
+		_ret = CKR_ARGUMENTS_BAD; \
+	if (_ret == CKR_OK) \
+		_ret = proto_read_token_info (_cs->resp, info);
+
+#define OUT_SESSION_INFO(info) \
+	if (info == NULL) \
+		_ret = CKR_ARGUMENTS_BAD; \
+	if (_ret == CKR_OK) \
+		_ret = proto_read_sesssion_info (_cs->resp, info); 
+
+#define OUT_MECHANISM_TYPE_ARRAY(arr, len) \
+	if (len == NULL) \
+		_ret = CKR_ARGUMENTS_BAD; \
+	if (_ret == CKR_OK) \
+		_ret = proto_read_ulong_array (_cs->resp, (arr), (len), *(len)); \
+	if (_ret == CKR_OK && arr) \
+		gck_rpc_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 (_cs->resp, info); 
+	
+
+/* -------------------------------------------------------------------
+ * INITIALIZATION and 'GLOBAL' CALLS
+ */
+
+static CK_RV
+rpc_C_Initialize (CK_VOID_PTR init_args)
+{
+	CK_C_INITIALIZE_ARGS_PTR args = NULL;
+	CK_RV ret = CKR_OK;
+	const char *path;
+	CallState *cs;
+	pid_t pid;
+	int err;
+	
+	debug (("C_Initialize: enter"));
+
+#ifdef _DEBUG 
+	GCK_RPC_CHECK_CALLS();
+#endif
+
+	pthread_mutex_lock (&init_mutex);
+
+		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) {
+				warning (("invalid set of mutex calls supplied"));
+				ret = CKR_ARGUMENTS_BAD;
+				goto done;
+			}
+
+			/*
+			 * When the CKF_OS_LOCKING_OK flag isn't set return an error.  
+			 * We must be able to use our pthread functionality.
+			 */
+			if (!(args->flags & CKF_OS_LOCKING_OK)) {
+				warning (("can't do without os locking"));
+				ret = CKR_CANT_LOCK;
+				goto done;
+			}
+		}
+	
+		pid = getpid ();
+		if (pkcs11_initialized) {
+
+			/* This process has called C_Initialize already */
+			if (pid == pkcs11_initialized_pid) {
+				warning (("C_Initialize called twice for same process"));
+				ret = CKR_CRYPTOKI_ALREADY_INITIALIZED;
+				goto done;
+			}
+		}
+			
+		/* Create the necessary per thread key */
+		if (pkcs11_per_thread == 0) {
+			err = pthread_key_create (&pkcs11_per_thread, call_destroy);
+			if (err != 0) {
+				ret = CKR_GENERAL_ERROR;
+				goto done;
+			}
+		}
+
+		path = gck_rpc_module_init (args);
+		if (!path || !path[0]) {
+			warning (("missing pkcs11 socket path in environment"));
+			ret = CKR_GENERAL_ERROR;
+			goto done;
+		}
+		
+		/* Make a copy of the socket path */
+		snprintf (pkcs11_socket_path, sizeof (pkcs11_socket_path), "%s", path);
+			
+		/* Call through and initialize the daemon */
+		ret = call_lookup (&cs);
+		if (ret == CKR_OK) { 
+			ret = call_prepare (cs, GCK_RPC_CALL_C_Initialize);
+			if (ret == CKR_OK)
+				if (!gck_rpc_message_write_byte_array (cs->req, GCK_RPC_HANDSHAKE, GCK_RPC_HANDSHAKE_LEN))
+					ret = CKR_HOST_MEMORY;
+			if (ret == CKR_OK) {
+				ret = call_run (cs);
+				if (ret == CKR_CRYPTOKI_ALREADY_INITIALIZED)
+					ret = CKR_OK;
+			}
+			call_done (cs, ret);
+		}
+
+done:
+	/* Mark us as officially initialized */
+	if (ret == CKR_OK) {
+		pkcs11_initialized = 1;
+		pkcs11_initialized_pid = pid;
+	} else if (ret != CKR_CRYPTOKI_ALREADY_INITIALIZED) {
+		pkcs11_initialized = 0;
+		pkcs11_initialized_pid = 0;
+		pkcs11_socket_path[0] = 0;
+	}
+			
+	pthread_mutex_unlock (&init_mutex);
+
+	debug (("C_Initialize: %d", ret));
+	return ret;
+}
+
+static CK_RV
+rpc_C_Finalize (CK_VOID_PTR reserved)
+{
+	CallState *cs;
+	CK_RV ret;
+	
+	debug (("C_Finalize: enter"));
+	return_val_if_fail (pkcs11_initialized, CKR_CRYPTOKI_NOT_INITIALIZED);
+	return_val_if_fail (!reserved, CKR_ARGUMENTS_BAD);
+
+	pthread_mutex_lock (&init_mutex);
+
+		ret = call_lookup (&cs);
+		if (ret == CKR_OK) { 
+			ret = call_prepare (cs, GCK_RPC_CALL_C_Finalize);
+			if (ret == CKR_OK) {
+				ret = call_run (cs);
+			}
+			call_done (cs, ret);
+		}
+		
+		if (ret != CKR_OK)
+			warning (("finalizing the daemon returned an error: %d", ret));
+		
+		/* This should stop all other calls in */
+		pkcs11_initialized = 0;
+		pkcs11_initialized_pid = 0;
+		pkcs11_socket_path[0] = 0;
+		
+	pthread_mutex_unlock (&init_mutex);
+	
+	debug (("C_Finalize: %d", CKR_OK));
+	return CKR_OK;
+}
+
+static CK_RV
+rpc_C_GetInfo (CK_INFO_PTR info)
+{
+	return_val_if_fail (info, CKR_ARGUMENTS_BAD);
+
+	BEGIN_CALL (C_GetInfo);
+	PROCESS_CALL;
+		OUT_INFO (info);
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_GetFunctionList (CK_FUNCTION_LIST_PTR_PTR list)
+{
+	/* This would be a strange call to receive */
+	return C_GetFunctionList (list);
+}
+
+static CK_RV
+rpc_C_GetSlotList (CK_BBOOL token_present, CK_SLOT_ID_PTR slot_list, CK_ULONG_PTR count)
+{
+	return_val_if_fail (count, CKR_ARGUMENTS_BAD);
+
+	BEGIN_CALL (C_GetSlotList);
+		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_SLOT_ID id, CK_SLOT_INFO_PTR info)
+{
+	return_val_if_fail (info, CKR_ARGUMENTS_BAD);
+
+	BEGIN_CALL (C_GetSlotInfo);
+		IN_ULONG (id);
+	PROCESS_CALL;
+		OUT_SLOT_INFO (info);
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_GetTokenInfo (CK_SLOT_ID id, CK_TOKEN_INFO_PTR info)
+{
+	return_val_if_fail (info, CKR_ARGUMENTS_BAD);
+
+	BEGIN_CALL (C_GetTokenInfo);
+		IN_ULONG (id);
+	PROCESS_CALL;
+		OUT_TOKEN_INFO (info);
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_GetMechanismList (CK_SLOT_ID id, CK_MECHANISM_TYPE_PTR mechanism_list,
+                        CK_ULONG_PTR count)
+{
+	return_val_if_fail (count, CKR_ARGUMENTS_BAD);
+
+	BEGIN_CALL (C_GetMechanismList);
+		IN_ULONG (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_SLOT_ID id, CK_MECHANISM_TYPE type, 
+                        CK_MECHANISM_INFO_PTR info)
+{
+	return_val_if_fail (info, CKR_ARGUMENTS_BAD);
+
+	BEGIN_CALL (C_GetMechanismInfo);
+		IN_ULONG (id);
+		IN_MECHANISM_TYPE (type);
+	PROCESS_CALL;
+		OUT_MECHANISM_INFO (info);
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_InitToken (CK_SLOT_ID id, CK_UTF8CHAR_PTR pin, CK_ULONG pin_len, 
+                 CK_UTF8CHAR_PTR label)
+{
+	BEGIN_CALL (C_InitToken);
+		IN_ULONG (id);
+		IN_BYTE_ARRAY (pin, pin_len);
+		IN_STRING (label);
+	PROCESS_CALL;
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_WaitForSlotEvent (CK_FLAGS flags, CK_SLOT_ID_PTR slot, CK_VOID_PTR reserved)
+{
+	return_val_if_fail (slot, CKR_ARGUMENTS_BAD);
+
+	BEGIN_CALL (C_WaitForSlotEvent);
+		IN_ULONG (flags);
+	PROCESS_CALL;
+		OUT_ULONG (slot);
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_OpenSession (CK_SLOT_ID 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 (C_OpenSession);
+		IN_ULONG (id);
+		IN_ULONG (flags);
+	PROCESS_CALL;
+		OUT_ULONG (session);
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_CloseSession (CK_SESSION_HANDLE session)
+{
+	BEGIN_CALL (C_CloseSession);
+		IN_ULONG (session);
+	PROCESS_CALL;
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_CloseAllSessions (CK_SLOT_ID id)
+{
+	BEGIN_CALL (C_CloseAllSessions);
+		IN_ULONG (id);
+	PROCESS_CALL;
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_GetFunctionStatus (CK_SESSION_HANDLE session)
+{
+	BEGIN_CALL (C_GetFunctionStatus);
+		IN_ULONG (session);
+	PROCESS_CALL;
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_CancelFunction (CK_SESSION_HANDLE session)
+{
+	BEGIN_CALL (C_CancelFunction);
+		IN_ULONG (session);
+	PROCESS_CALL;
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_GetSessionInfo(CK_SESSION_HANDLE session, CK_SESSION_INFO_PTR info)
+{
+	return_val_if_fail (info, CKR_ARGUMENTS_BAD);
+
+	BEGIN_CALL (C_GetSessionInfo);
+		IN_ULONG (session);
+	PROCESS_CALL;
+		OUT_SESSION_INFO (info);
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_InitPIN (CK_SESSION_HANDLE session, CK_UTF8CHAR_PTR pin, 
+               CK_ULONG pin_len)
+{
+	BEGIN_CALL (C_InitPIN);
+		IN_ULONG (session);
+		IN_BYTE_ARRAY (pin, pin_len);
+	PROCESS_CALL;
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_SetPIN (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 (C_SetPIN);
+		IN_ULONG (session);
+		IN_BYTE_ARRAY (old_pin, old_pin_len);
+		IN_BYTE_ARRAY (new_pin, old_pin_len);
+	PROCESS_CALL;
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_GetOperationState (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 (C_GetOperationState);
+		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_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 (C_SetOperationState);
+		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_SESSION_HANDLE session, CK_USER_TYPE user_type,
+             CK_UTF8CHAR_PTR pin, CK_ULONG pin_len)
+{
+	BEGIN_CALL (C_Login);
+		IN_ULONG (session);
+		IN_ULONG (user_type);
+		IN_BYTE_ARRAY (pin, pin_len);
+	PROCESS_CALL;
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_Logout (CK_SESSION_HANDLE session)
+{
+	BEGIN_CALL (C_Logout);
+		IN_ULONG (session);
+	PROCESS_CALL;
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_CreateObject (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 (C_CreateObject);
+		IN_ULONG (session);
+		IN_ATTRIBUTE_ARRAY (template, count);
+	PROCESS_CALL;
+		OUT_ULONG (new_object);
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_CopyObject (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 (C_CopyObject);
+		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_SESSION_HANDLE session, CK_OBJECT_HANDLE object)
+{
+	BEGIN_CALL (C_DestroyObject);
+		IN_ULONG (session);
+		IN_ULONG (object);
+	PROCESS_CALL;
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_GetObjectSize (CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object,
+                     CK_ULONG_PTR size)
+{
+	return_val_if_fail (size, CKR_ARGUMENTS_BAD);
+
+	BEGIN_CALL (C_GetObjectSize);
+		IN_ULONG (session);
+		IN_ULONG (object);
+	PROCESS_CALL;
+		OUT_ULONG (size);
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_GetAttributeValue (CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object,
+                         CK_ATTRIBUTE_PTR template, CK_ULONG count)
+{
+	BEGIN_CALL (C_GetAttributeValue);
+		IN_ULONG (session);
+		IN_ULONG (object);
+		IN_ATTRIBUTE_BUFFER (template, count);
+	PROCESS_CALL;
+		OUT_ATTRIBUTE_ARRAY (template, count);
+		OUT_RETURN_CODE ();
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_SetAttributeValue (CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object,
+                         CK_ATTRIBUTE_PTR template, CK_ULONG count)
+{
+	BEGIN_CALL (C_SetAttributeValue);
+		IN_ULONG (session);
+		IN_ULONG (object);
+		IN_ATTRIBUTE_ARRAY (template, count);
+	PROCESS_CALL;
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_FindObjectsInit (CK_SESSION_HANDLE session, CK_ATTRIBUTE_PTR template,
+                       CK_ULONG count)
+{
+	BEGIN_CALL (C_FindObjectsInit);
+		IN_ULONG (session);
+		IN_ATTRIBUTE_ARRAY (template, count);
+	PROCESS_CALL;
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_FindObjects (CK_SESSION_HANDLE session, CK_OBJECT_HANDLE_PTR objects,
+                   CK_ULONG max_count, CK_ULONG_PTR count)
+{
+	return_val_if_fail (count, CKR_ARGUMENTS_BAD);
+
+	BEGIN_CALL (C_FindObjects);
+		IN_ULONG (session);
+		IN_ULONG_BUFFER (objects, &max_count);
+	PROCESS_CALL;
+		*count = max_count;
+		OUT_ULONG_ARRAY (objects, count);
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_FindObjectsFinal (CK_SESSION_HANDLE session)
+{
+	BEGIN_CALL (C_FindObjectsFinal);
+		IN_ULONG (session);
+	PROCESS_CALL;
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_EncryptInit (CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism,
+                   CK_OBJECT_HANDLE key)
+{
+	BEGIN_CALL (C_EncryptInit);
+		IN_ULONG (session);
+		IN_MECHANISM (mechanism);
+		IN_ULONG (key);
+	PROCESS_CALL;
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_Encrypt (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 (C_Encrypt);
+		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_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 (C_EncryptUpdate);
+		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_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 (C_EncryptFinal);
+		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_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism,
+                   CK_OBJECT_HANDLE key)
+{
+	BEGIN_CALL (C_DecryptInit);
+		IN_ULONG (session);
+		IN_MECHANISM (mechanism);
+		IN_ULONG (key);
+	PROCESS_CALL;
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_Decrypt (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 (C_Decrypt);
+		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_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 (C_DecryptUpdate);
+		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_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 (C_DecryptFinal);
+		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_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism)
+{
+	BEGIN_CALL (C_DigestInit);
+		IN_ULONG (session);
+		IN_MECHANISM (mechanism);
+	PROCESS_CALL;
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_Digest (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 (C_Digest);
+		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_SESSION_HANDLE session, CK_BYTE_PTR part, CK_ULONG part_len)
+{
+	BEGIN_CALL (C_DigestUpdate);
+		IN_ULONG (session);
+		IN_BYTE_ARRAY (part, part_len);
+	PROCESS_CALL;
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_DigestKey (CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key)
+{
+	BEGIN_CALL (C_DigestKey);
+		IN_ULONG (session);
+		IN_ULONG (key);
+	PROCESS_CALL;
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_DigestFinal (CK_SESSION_HANDLE session, CK_BYTE_PTR digest,
+                   CK_ULONG_PTR digest_len)
+{
+	return_val_if_fail (digest_len, CKR_ARGUMENTS_BAD);
+
+	BEGIN_CALL (C_DigestFinal);
+		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_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism,
+                CK_OBJECT_HANDLE key)
+{
+	BEGIN_CALL (C_SignInit);
+		IN_ULONG (session);
+		IN_MECHANISM (mechanism);
+		IN_ULONG (key);
+	PROCESS_CALL;
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_Sign (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 (C_Sign);
+		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_SESSION_HANDLE session, CK_BYTE_PTR part, CK_ULONG part_len)
+{
+	return_val_if_fail (part_len, CKR_ARGUMENTS_BAD);
+
+	BEGIN_CALL (C_SignUpdate);
+		IN_ULONG (session);
+		IN_BYTE_ARRAY (part, part_len);
+	PROCESS_CALL;
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_SignFinal (CK_SESSION_HANDLE session, CK_BYTE_PTR signature,
+                 CK_ULONG_PTR signature_len)
+{
+	return_val_if_fail (signature_len, CKR_ARGUMENTS_BAD);
+
+	BEGIN_CALL (C_SignFinal);
+		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_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism,
+                       CK_OBJECT_HANDLE key)
+{
+	BEGIN_CALL (C_SignRecoverInit);
+		IN_ULONG (session);
+		IN_MECHANISM (mechanism);
+		IN_ULONG (key);
+	PROCESS_CALL;
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_SignRecover (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 (C_SignRecover);
+		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_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism,
+                  CK_OBJECT_HANDLE key)
+{
+	BEGIN_CALL (C_VerifyInit);
+		IN_ULONG (session);
+		IN_MECHANISM (mechanism);
+		IN_ULONG (key);
+	PROCESS_CALL;
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_Verify (CK_SESSION_HANDLE session, CK_BYTE_PTR data, CK_ULONG data_len,
+              CK_BYTE_PTR signature, CK_ULONG signature_len)
+{
+	BEGIN_CALL (C_Verify);
+		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_SESSION_HANDLE session, CK_BYTE_PTR part, CK_ULONG part_len)
+{
+	BEGIN_CALL (C_VerifyUpdate);
+		IN_ULONG (session);
+		IN_BYTE_ARRAY (part, part_len);
+	PROCESS_CALL;
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_VerifyFinal (CK_SESSION_HANDLE session, CK_BYTE_PTR signature,
+                   CK_ULONG signature_len)
+{
+	BEGIN_CALL (C_VerifyFinal);
+		IN_ULONG (session);
+		IN_BYTE_ARRAY (signature, signature_len);
+	PROCESS_CALL;
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_VerifyRecoverInit (CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism,
+                         CK_OBJECT_HANDLE key)
+{
+	BEGIN_CALL (C_VerifyRecoverInit);
+		IN_ULONG (session);
+		IN_MECHANISM (mechanism);
+		IN_ULONG (key);
+	PROCESS_CALL;
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_VerifyRecover (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 (C_VerifyRecover);
+		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_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 (C_DigestEncryptUpdate);
+		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_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 (C_DecryptDigestUpdate);
+		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_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 (C_SignEncryptUpdate);
+		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_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 (C_DecryptVerifyUpdate);
+		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_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism,
+                   CK_ATTRIBUTE_PTR template, CK_ULONG count, 
+                   CK_OBJECT_HANDLE_PTR key)
+{
+	BEGIN_CALL (C_GenerateKey);
+		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_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 (C_GenerateKeyPair);
+		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_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 (C_WrapKey);
+		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_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 (C_UnwrapKey);
+		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_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 (C_DeriveKey);
+		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_SESSION_HANDLE session, CK_BYTE_PTR seed, CK_ULONG seed_len)
+{
+	BEGIN_CALL (C_SeedRandom);
+		IN_ULONG (session);
+		IN_BYTE_ARRAY (seed, seed_len);
+	PROCESS_CALL;
+	END_CALL;
+}
+
+static CK_RV
+rpc_C_GenerateRandom (CK_SESSION_HANDLE session, CK_BYTE_PTR random_data,
+                      CK_ULONG random_len)
+{
+	BEGIN_CALL (C_GenerateRandom);
+		IN_ULONG (session);
+		IN_BYTE_BUFFER (random_data, &random_len);
+	PROCESS_CALL;
+		OUT_BYTE_ARRAY (random_data, &random_len);
+	END_CALL;
+}
+
+/* --------------------------------------------------------------------
+ * MODULE ENTRY POINT
+ */
+
+/* 
+ * PKCS#11 is broken here. It states that Unix compilers automatically byte 
+ * pack structures. This is wrong. GCC on Linux aligns to 4 by default. 
+ * 
+ * This results in incompatibilities. Where this structure's first version
+ * members take up too much or too little space depending on how this module
+ * is compiled.
+ */
+
+static CK_FUNCTION_LIST functionList = {
+	{ CRYPTOKI_VERSION_MAJOR, CRYPTOKI_VERSION_MINOR },  /* version */
+	rpc_C_Initialize,
+	rpc_C_Finalize,
+	rpc_C_GetInfo,
+	rpc_C_GetFunctionList,
+	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_GetFunctionStatus,
+	rpc_C_CancelFunction,
+	rpc_C_WaitForSlotEvent
+};
+
+CK_RV
+C_GetFunctionList (CK_FUNCTION_LIST_PTR_PTR list)
+{
+	return_val_if_fail (list, CKR_ARGUMENTS_BAD);
+
+	*list = &functionList;
+	return CKR_OK;
+}

Added: trunk/pkcs11/rpc-layer/gck-rpc-private.h
==============================================================================
--- (empty file)
+++ trunk/pkcs11/rpc-layer/gck-rpc-private.h	Sun Jan  4 23:07:18 2009
@@ -0,0 +1,342 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* p11-rpc-private.h - various ids and signatures for our protocol
+
+   Copyright (C) 2008, Stef Walter
+
+   The Gnome Keyring Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Keyring Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Stef Walter <stef memberwebs com>
+*/
+
+#ifndef GCK_RPC_CALLS_H
+#define GCK_RPC_CALLS_H
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "common/gkr-buffer.h"
+
+#include "pkcs11/pkcs11.h"
+
+
+/* Whether to print debug output or not */
+#define DEBUG_OUTPUT 1
+
+
+/* The calls, must be in sync with array below */
+enum {
+	GCK_RPC_CALL_ERROR = 0,
+	
+	GCK_RPC_CALL_C_Initialize,
+	GCK_RPC_CALL_C_Finalize,
+	GCK_RPC_CALL_C_GetInfo,
+	GCK_RPC_CALL_C_GetSlotList,
+	GCK_RPC_CALL_C_GetSlotInfo,
+	GCK_RPC_CALL_C_GetTokenInfo,
+	GCK_RPC_CALL_C_GetMechanismList,
+	GCK_RPC_CALL_C_GetMechanismInfo,
+	GCK_RPC_CALL_C_InitToken,
+	GCK_RPC_CALL_C_WaitForSlotEvent,
+	
+	GCK_RPC_CALL_C_OpenSession,
+	
+	GCK_RPC_CALL_C_CloseSession,
+	GCK_RPC_CALL_C_CloseAllSessions,
+	GCK_RPC_CALL_C_GetFunctionStatus,
+	GCK_RPC_CALL_C_CancelFunction,
+	
+	GCK_RPC_CALL_C_GetSessionInfo,
+	GCK_RPC_CALL_C_InitPIN,
+	GCK_RPC_CALL_C_SetPIN,
+	GCK_RPC_CALL_C_GetOperationState,
+	GCK_RPC_CALL_C_SetOperationState,
+	GCK_RPC_CALL_C_Login,
+	GCK_RPC_CALL_C_Logout,
+	GCK_RPC_CALL_C_CreateObject,
+	GCK_RPC_CALL_C_CopyObject,
+	GCK_RPC_CALL_C_DestroyObject,
+	GCK_RPC_CALL_C_GetObjectSize,
+	GCK_RPC_CALL_C_GetAttributeValue,
+	GCK_RPC_CALL_C_SetAttributeValue,
+	GCK_RPC_CALL_C_FindObjectsInit,
+	GCK_RPC_CALL_C_FindObjects,
+	GCK_RPC_CALL_C_FindObjectsFinal,
+	GCK_RPC_CALL_C_EncryptInit,
+	GCK_RPC_CALL_C_Encrypt,
+	GCK_RPC_CALL_C_EncryptUpdate,
+	GCK_RPC_CALL_C_EncryptFinal,
+	GCK_RPC_CALL_C_DecryptInit,
+	GCK_RPC_CALL_C_Decrypt,
+	GCK_RPC_CALL_C_DecryptUpdate,
+	GCK_RPC_CALL_C_DecryptFinal,
+	GCK_RPC_CALL_C_DigestInit,
+	GCK_RPC_CALL_C_Digest,
+	GCK_RPC_CALL_C_DigestUpdate,
+	GCK_RPC_CALL_C_DigestKey,
+	GCK_RPC_CALL_C_DigestFinal,
+	GCK_RPC_CALL_C_SignInit,
+	GCK_RPC_CALL_C_Sign,
+	GCK_RPC_CALL_C_SignUpdate,
+	GCK_RPC_CALL_C_SignFinal,
+	GCK_RPC_CALL_C_SignRecoverInit,
+	GCK_RPC_CALL_C_SignRecover,
+	GCK_RPC_CALL_C_VerifyInit,
+	GCK_RPC_CALL_C_Verify,
+	GCK_RPC_CALL_C_VerifyUpdate,
+	GCK_RPC_CALL_C_VerifyFinal,
+	GCK_RPC_CALL_C_VerifyRecoverInit,
+	GCK_RPC_CALL_C_VerifyRecover,
+	GCK_RPC_CALL_C_DigestEncryptUpdate,
+	GCK_RPC_CALL_C_DecryptDigestUpdate,
+	GCK_RPC_CALL_C_SignEncryptUpdate,
+	GCK_RPC_CALL_C_DecryptVerifyUpdate,
+	GCK_RPC_CALL_C_GenerateKey,
+	GCK_RPC_CALL_C_GenerateKeyPair,
+	GCK_RPC_CALL_C_WrapKey,
+	GCK_RPC_CALL_C_UnwrapKey,
+	GCK_RPC_CALL_C_DeriveKey,
+	GCK_RPC_CALL_C_SeedRandom,
+	GCK_RPC_CALL_C_GenerateRandom,
+	
+	GCK_RPC_CALL_MAX
+};
+
+typedef struct _GckRpcCall {
+	int call_id;
+	const char* name;
+	const char* request;
+	const char* response;
+} GckRpcCall;
+
+/*
+ *  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
+ */
+
+const static GckRpcCall gck_rpc_calls[] = {
+	{ GCK_RPC_CALL_ERROR,                  "ERROR",                  NULL,      NULL                   },
+	{ GCK_RPC_CALL_C_Initialize,           "C_Initialize",           "ay",      ""                     },
+	{ GCK_RPC_CALL_C_Finalize,             "C_Finalize",             "",        ""                     },
+	{ GCK_RPC_CALL_C_GetInfo,              "C_GetInfo",              "",        "vsusv"                },
+	{ GCK_RPC_CALL_C_GetSlotList,          "C_GetSlotList",          "yfu",     "au"                   },
+	{ GCK_RPC_CALL_C_GetSlotInfo,          "C_GetSlotInfo",          "u",       "ssuvv"                },
+	{ GCK_RPC_CALL_C_GetTokenInfo,         "C_GetTokenInfo",         "u",       "ssssuuuuuuuuuuuvvs"   },
+	{ GCK_RPC_CALL_C_GetMechanismList,     "C_GetMechanismList",     "ufu",     "au"                   },
+	{ GCK_RPC_CALL_C_GetMechanismInfo,     "C_GetMechanismInfo",     "uu",      "uuu"                  },
+	{ GCK_RPC_CALL_C_InitToken,            "C_InitToken",            "uayz",    ""                     },
+	{ GCK_RPC_CALL_C_WaitForSlotEvent,     "C_WaitForSlotEvent",     "u",       "u"                    },
+	{ GCK_RPC_CALL_C_OpenSession,          "C_OpenSession",          "uu",      "u"                    },
+	{ GCK_RPC_CALL_C_CloseSession,         "C_CloseSession",         "u",       ""                     },
+	{ GCK_RPC_CALL_C_CloseAllSessions,     "C_CloseAllSessions",     "u",       ""                     },
+	{ GCK_RPC_CALL_C_GetFunctionStatus,    "C_GetFunctionStatus",    "u",       ""                     },
+	{ GCK_RPC_CALL_C_CancelFunction,       "C_CancelFunction",       "u",       ""                     },
+	{ GCK_RPC_CALL_C_GetSessionInfo,       "C_GetSessionInfo",       "u",       "uuuu"                 },
+	{ GCK_RPC_CALL_C_InitPIN,              "C_InitPIN",              "uay",     ""                     },
+	{ GCK_RPC_CALL_C_SetPIN,               "C_SetPIN",               "uayay",   ""                     },
+	{ GCK_RPC_CALL_C_GetOperationState,    "C_GetOperationState",    "ufy",     "ay"                   },
+	{ GCK_RPC_CALL_C_SetOperationState,    "C_SetOperationState",    "uayuu",   ""                     },
+	{ GCK_RPC_CALL_C_Login,                "C_Login",                "uuay",    ""                     },
+	{ GCK_RPC_CALL_C_Logout,               "C_Logout",               "u",       ""                     },
+	{ GCK_RPC_CALL_C_CreateObject,         "C_CreateObject",         "uaA",     "u"                    },
+	{ GCK_RPC_CALL_C_CopyObject,           "C_CopyObject",           "uuaA",    "u"                    },
+	{ GCK_RPC_CALL_C_DestroyObject,        "C_DestroyObject",        "uu",      ""                     },
+	{ GCK_RPC_CALL_C_GetObjectSize,        "C_GetObjectSize",        "uu",      "u"                    },
+	{ GCK_RPC_CALL_C_GetAttributeValue,    "C_GetAttributeValue",    "uufA",    "aAu"                  },
+	{ GCK_RPC_CALL_C_SetAttributeValue,    "C_SetAttributeValue",    "uuaA",    ""                     },
+	{ GCK_RPC_CALL_C_FindObjectsInit,      "C_FindObjectsInit",      "uaA",     ""                     },
+	{ GCK_RPC_CALL_C_FindObjects,          "C_FindObjects",          "ufu",     "au"                   },
+	{ GCK_RPC_CALL_C_FindObjectsFinal,     "C_FindObjectsFinal",     "u",       ""                     },
+	{ GCK_RPC_CALL_C_EncryptInit,          "C_EncryptInit",          "uMu"      ""                     },
+	{ GCK_RPC_CALL_C_Encrypt,              "C_Encrypt",              "uayfy",   "ay"                   },
+	{ GCK_RPC_CALL_C_EncryptUpdate,        "C_EncryptUpdate",        "uayfy",   "ay"                   },
+	{ GCK_RPC_CALL_C_EncryptFinal,         "C_EncryptFinal",         "ufy",     "ay"                   },
+	{ GCK_RPC_CALL_C_DecryptInit,          "C_DecryptInit",          "uMu",     ""                     },
+	{ GCK_RPC_CALL_C_Decrypt,              "C_Decrypt",              "uayfy",   "ay"                   },
+	{ GCK_RPC_CALL_C_DecryptUpdate,        "C_DecryptUpdate",        "uayfy",   "ay"                   },
+	{ GCK_RPC_CALL_C_DecryptFinal,         "C_DecryptFinal",         "ufy",     "ay"                   },
+	{ GCK_RPC_CALL_C_DigestInit,           "C_DigestInit",           "uM",      ""                     },
+	{ GCK_RPC_CALL_C_Digest,               "C_Digest",               "uayfy",   "ay"                   },
+	{ GCK_RPC_CALL_C_DigestUpdate,         "C_DigestUpdate",         "uay",     ""                     },
+	{ GCK_RPC_CALL_C_DigestKey,            "C_DigestKey",            "uu",      ""                     },
+	{ GCK_RPC_CALL_C_DigestFinal,          "C_DigestFinal",          "ufy",     "ay"                   },
+	{ GCK_RPC_CALL_C_SignInit,             "C_SignInit",             "uMu",     ""                     },
+	{ GCK_RPC_CALL_C_Sign,                 "C_Sign",                 "uayfy",   "ay"                   },
+	{ GCK_RPC_CALL_C_SignUpdate,           "C_SignUpdate",           "uay",     ""                     },
+	{ GCK_RPC_CALL_C_SignFinal,            "C_SignFinal",            "ufy",     "ay"                   },
+	{ GCK_RPC_CALL_C_SignRecoverInit,      "C_SignRecoverInit",      "uMu",     ""                     },
+	{ GCK_RPC_CALL_C_SignRecover,          "C_SignRecover",          "uayfy",   "ay"                   },
+	{ GCK_RPC_CALL_C_VerifyInit,           "C_VerifyInit",           "uMu",     ""                     },
+	{ GCK_RPC_CALL_C_Verify,               "C_Verify",               "uayay",   ""                     },
+	{ GCK_RPC_CALL_C_VerifyUpdate,         "C_VerifyUpdate",         "uay",     ""                     },
+	{ GCK_RPC_CALL_C_VerifyFinal,          "C_VerifyFinal",          "uay",     ""                     },
+	{ GCK_RPC_CALL_C_VerifyRecoverInit,    "C_VerifyRecoverInit",    "uMu",     ""                     },
+	{ GCK_RPC_CALL_C_VerifyRecover,        "C_VerifyRecover",        "uayfy",   "ay"                   },
+	{ GCK_RPC_CALL_C_DigestEncryptUpdate,  "C_DigestEncryptUpdate",  "uayfy",   "ay"                   }, 
+	{ GCK_RPC_CALL_C_DecryptDigestUpdate,  "C_DecryptDigestUpdate",  "uayfy",   "ay"                   },
+	{ GCK_RPC_CALL_C_SignEncryptUpdate,    "C_SignEncryptUpdate",    "uayfy",   "ay"                   },
+	{ GCK_RPC_CALL_C_DecryptVerifyUpdate,  "C_DecryptVerifyUpdate",  "uayfy",   "ay"                   },
+	{ GCK_RPC_CALL_C_GenerateKey,          "C_GenerateKey",          "uMaA",    "u"                    },
+	{ GCK_RPC_CALL_C_GenerateKeyPair,      "C_GenerateKeyPair",      "uMaAaA",  "uu"                   },
+	{ GCK_RPC_CALL_C_WrapKey,              "C_WrapKey",              "uMuufy",  "ay"                   },
+	{ GCK_RPC_CALL_C_UnwrapKey,            "C_UnwrapKey",            "uMuayaA", "u"                    },
+	{ GCK_RPC_CALL_C_DeriveKey,            "C_DeriveKey",            "uMuaA",   "u"                    },
+	{ GCK_RPC_CALL_C_SeedRandom,           "C_SeedRandom",           "uay",     ""                     },
+	{ GCK_RPC_CALL_C_GenerateRandom,       "C_GenerateRandom",       "ufy",     "ay"                   },
+};
+
+#ifdef _DEBUG 
+#define GCK_RPC_CHECK_CALLS() \
+	{ int i; for (i = 0; i < GCK_RPC_CALL_MAX; ++i) assert (gck_rpc_calls[i].call_id == i); }
+#endif 
+
+#define GCK_RPC_HANDSHAKE \
+	((unsigned char*)"PRIVATE-GNOME-KEYRING-PKCS11-PROTOCOL-V-1")
+#define GCK_RPC_HANDSHAKE_LEN \
+	(sizeof (GCK_RPC_HANDSHAKE) - 1)
+
+#define GCK_RPC_SOCKET_EXT 	"pkcs11"
+
+typedef enum _GckRpcMessageType {
+	GCK_RPC_REQUEST = 1,
+	GCK_RPC_RESPONSE
+} GckRpcMessageType;
+
+typedef struct _GckRpcMessage {
+	int call_id;
+	GckRpcMessageType call_type;
+	const char *signature;
+	GkrBuffer buffer;
+
+	size_t parsed;
+	const char *sigverify;
+} GckRpcMessage;
+
+GckRpcMessage*           gck_rpc_message_new                     (GkrBufferAllocator allocator);
+
+void                     gck_rpc_message_free                    (GckRpcMessage *msg);
+
+void                     gck_rpc_message_reset                   (GckRpcMessage *msg);
+
+int                      gck_rpc_message_equals                  (GckRpcMessage *m1, 
+                                                                  GckRpcMessage *m2);
+
+#define                  gck_rpc_message_is_verified(msg)        ((msg)->sigverify[0] == 0)
+
+#define                  gck_rpc_message_buffer_error(msg)       (gkr_buffer_has_error(&(msg)->buffer))
+
+int                      gck_rpc_message_prep                    (GckRpcMessage *msg, 
+                                                                  int call_id, 
+                                                                  GckRpcMessageType type);
+
+int                      gck_rpc_message_parse                   (GckRpcMessage *msg, 
+                                                                  GckRpcMessageType type);
+
+int                      gck_rpc_message_verify_part             (GckRpcMessage *msg, 
+                                                                  const char* part);
+
+int                      gck_rpc_message_write_byte              (GckRpcMessage *msg,
+                                                                  CK_BYTE val);
+
+int                      gck_rpc_message_write_ulong             (GckRpcMessage *msg, 
+                                                                  CK_ULONG val);
+
+int                      gck_rpc_message_write_zero_string       (GckRpcMessage *msg,
+                                                                  CK_UTF8CHAR* string);
+
+int                      gck_rpc_message_write_space_string      (GckRpcMessage *msg,
+                                                                  CK_UTF8CHAR* buffer,
+                                                                  CK_ULONG length);
+
+int                      gck_rpc_message_write_byte_buffer       (GckRpcMessage *msg, 
+                                                                  CK_ULONG count);
+
+int                      gck_rpc_message_write_byte_array        (GckRpcMessage *msg, 
+                                                                  CK_BYTE_PTR arr, 
+                                                                  CK_ULONG num);
+
+int                      gck_rpc_message_write_ulong_buffer      (GckRpcMessage *msg, 
+                                                                  CK_ULONG count);
+
+int                      gck_rpc_message_write_ulong_array       (GckRpcMessage *msg, 
+                                                                  CK_ULONG_PTR arr, 
+                                                                  CK_ULONG num);
+
+int                      gck_rpc_message_write_attribute_buffer  (GckRpcMessage *msg, 
+                                                                  CK_ATTRIBUTE_PTR arr, 
+                                                                  CK_ULONG num);
+
+int                      gck_rpc_message_write_attribute_array   (GckRpcMessage *msg, 
+                                                                  CK_ATTRIBUTE_PTR arr, 
+                                                                  CK_ULONG num);
+
+int                      gck_rpc_message_write_version           (GckRpcMessage *msg,
+                                                                  CK_VERSION* version);
+
+
+int                      gck_rpc_message_read_byte               (GckRpcMessage *msg,
+                                                                  CK_BYTE* val);
+
+int                      gck_rpc_message_read_ulong              (GckRpcMessage *msg, 
+                                                                  CK_ULONG* val);
+
+int                      gck_rpc_message_read_space_string       (GckRpcMessage *msg,
+                                                                  CK_UTF8CHAR* buffer,
+                                                                  CK_ULONG length);
+
+int                      gck_rpc_message_read_version            (GckRpcMessage *msg,
+                                                                  CK_VERSION* version);
+
+
+
+void                     gck_rpc_log                             (const char *line);
+
+void                     gck_rpc_warn                            (const char* msg, ...);
+
+void                     gck_rpc_debug                           (const char* msg, ...);
+
+#ifdef G_DISABLE_ASSERT
+#define assert(x)
+#else
+#include <assert.h>
+#endif
+
+/*
+ * 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.
+ */
+
+int    gck_rpc_mechanism_is_supported        (CK_MECHANISM_TYPE mech);
+void   gck_rpc_mechanism_list_purge          (CK_MECHANISM_TYPE_PTR mechs, CK_ULONG_PTR n_mechs);
+int    gck_rpc_mechanism_has_sane_parameters (CK_MECHANISM_TYPE type);
+int    gck_rpc_mechanism_has_no_parameters   (CK_MECHANISM_TYPE mech);
+
+#endif /* GCK_RPC_CALLS_H */

Added: trunk/pkcs11/rpc-layer/gck-rpc-util.c
==============================================================================
--- (empty file)
+++ trunk/pkcs11/rpc-layer/gck-rpc-util.c	Sun Jan  4 23:07:18 2009
@@ -0,0 +1,207 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* p11-rpc-util.c - utilities for module and dispatcher
+
+   Copyright (C) 2008, Stef Walter
+
+   The Gnome Keyring Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Keyring Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Stef Walter <stef memberwebs com>
+*/
+
+#include "config.h"
+
+#include "gck-rpc-layer.h"
+#include "gck-rpc-private.h"
+
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+
+static void
+do_log (const char *pref, const char *msg, va_list va)
+{
+	char buffer[1024];
+	size_t len = 0;
+	
+	if (pref) {
+		snprintf (buffer, sizeof (buffer), "%s: ", pref);
+		len = strlen (buffer);
+	}
+	
+	vsnprintf (buffer + len, sizeof (buffer) - len, msg, va);
+	gck_rpc_log (buffer);
+}
+
+void 
+gck_rpc_warn (const char* msg, ...)
+{
+	va_list va;
+	va_start (va, msg);
+	do_log ("WARNING", msg, va);
+	va_end (va);
+}
+
+void 
+gck_rpc_debug (const char* msg, ...)
+{
+	va_list va;
+	va_start (va, msg);
+	do_log ("DEBUG", msg, va);
+	va_end (va);
+}
+
+int 
+gck_rpc_mechanism_is_supported (CK_MECHANISM_TYPE mech)
+{
+	if (gck_rpc_mechanism_has_no_parameters (mech) ||
+	    gck_rpc_mechanism_has_sane_parameters (mech))
+		return 1;
+	return 0;
+}
+void
+gck_rpc_mechanism_list_purge (CK_MECHANISM_TYPE_PTR mechs, CK_ULONG* n_mechs)
+{
+	int i;
+
+	assert (mechs);
+	assert (n_mechs);
+	
+	for (i = 0; i < (int)(*mechs); ++i) {
+		if (!gck_rpc_mechanism_has_no_parameters (mechs[i]) &&
+		    !gck_rpc_mechanism_has_sane_parameters (mechs[i])) {
+			
+			/* Remove the mechanism from the list */
+			memmove (&mechs[i], &mechs[i + 1], (*n_mechs - i) * sizeof (CK_MECHANISM_TYPE));
+			
+			--(*n_mechs);
+			--i;
+		}
+	}
+}
+
+int 
+gck_rpc_mechanism_has_sane_parameters (CK_MECHANISM_TYPE type)
+{
+	/* This list is incomplete */
+	switch (type) {
+	case CKM_RSA_PKCS_OAEP:
+	case CKM_RSA_PKCS_PSS:
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+int 
+gck_rpc_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 1;
+	default:
+		return 0;
+	};
+}
\ No newline at end of file



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]