summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStef Walter <stefw@collabora.co.uk>2012-02-07 14:43:13 +0100
committerStef Walter <stefw@gnome.org>2012-08-23 14:14:19 +0200
commit7ad8913c7c3a6af1167cfe4afc8b874a048ecd8b (patch)
treee715a8f85058cd882bbdbe7f66fa05c07bab9466
parent56860b7f72c444eed5923e11d735b85b630a171d (diff)
downloadp11-kit-7ad8913c7c3a6af1167cfe4afc8b874a048ecd8b.tar.gz
WIP rpc
-rw-r--r--egg/egg-unix-credentials.c256
-rw-r--r--egg/egg-unix-credentials.h38
-rw-r--r--p11-kit/Makefile.am4
-rw-r--r--p11-kit/buffer.c612
-rw-r--r--p11-kit/buffer.h194
-rw-r--r--p11-kit/daemon.c1405
-rw-r--r--p11-kit/mapping.c206
-rw-r--r--p11-kit/modules.c6
-rw-r--r--p11-kit/rpc-client.c2227
-rw-r--r--p11-kit/rpc-message.c473
-rw-r--r--p11-kit/rpc-private.h344
-rw-r--r--p11-kit/rpc-util.c207
12 files changed, 5969 insertions, 3 deletions
diff --git a/egg/egg-unix-credentials.c b/egg/egg-unix-credentials.c
new file mode 100644
index 0000000..43da594
--- /dev/null
+++ b/egg/egg-unix-credentials.c
@@ -0,0 +1,256 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* egg-unix-credentials.c - write and read unix credentials on socket
+
+ Copyright (C) 2003 Red Hat, Inc
+
+ Gnome keyring is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ Gnome keyring 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Author: Alexander Larsson <alexl@redhat.com>
+ Author: Stef Walter <stef@memberwebs.com>
+*/
+
+#include "config.h"
+
+#include "egg-unix-credentials.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#if defined(HAVE_GETPEERUCRED)
+#include <ucred.h>
+#endif
+
+int
+egg_unix_credentials_read (int sock,
+ pid_t *pid,
+ uid_t *uid)
+{
+ struct msghdr msg;
+ struct iovec iov;
+ char buf;
+ int ret;
+
+#if defined(HAVE_CMSGCRED) || defined(LOCAL_CREDS)
+ /* Prefer CMSGCRED over LOCAL_CREDS because the former provides the
+ * remote PID. */
+#if defined(HAVE_CMSGCRED)
+ struct cmsgcred *cred;
+#else /* defined(LOCAL_CREDS) */
+ struct sockcred *cred;
+#endif
+ union {
+ struct cmsghdr hdr;
+ char cred[CMSG_SPACE (sizeof *cred)];
+ } cmsg;
+#endif
+
+ *pid = 0;
+ *uid = 0;
+
+ /* If LOCAL_CREDS are used in this platform, they have already been
+ * initialized by init_connection prior to sending of the credentials
+ * byte we receive below. */
+
+ iov.iov_base = &buf;
+ iov.iov_len = 1;
+
+ memset (&msg, 0, sizeof (msg));
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+#if defined(HAVE_CMSGCRED) || defined(LOCAL_CREDS)
+ memset (&cmsg, 0, sizeof (cmsg));
+ msg.msg_control = (caddr_t) &cmsg;
+ msg.msg_controllen = CMSG_SPACE(sizeof *cred);
+#endif
+
+ again:
+ ret = recvmsg (sock, &msg, 0);
+
+ if (ret < 0) {
+ if (errno == EINTR)
+ goto again;
+ return -1;
+
+ } else if (ret == 0) {
+ /* Disconnected */
+ return -1;
+ }
+
+ if (buf != '\0') {
+ fprintf (stderr, "credentials byte was not nul\n");
+ return -1;
+ }
+
+#if defined(HAVE_CMSGCRED) || defined(LOCAL_CREDS)
+ if (cmsg.hdr.cmsg_len < CMSG_LEN (sizeof *cred) ||
+ cmsg.hdr.cmsg_type != SCM_CREDS) {
+ fprintf (stderr, "message from recvmsg() was not SCM_CREDS\n");
+ return -1;
+ }
+#endif
+
+ {
+#ifdef SO_PEERCRED
+#ifndef __OpenBSD__
+ struct ucred cr;
+#else
+ struct sockpeercred cr;
+#endif
+ socklen_t cr_len = sizeof (cr);
+
+ if (getsockopt (sock, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len) == 0 &&
+ cr_len == sizeof (cr)) {
+ *pid = cr.pid;
+ *uid = cr.uid;
+ } else {
+ fprintf (stderr, "failed to getsockopt() credentials, returned len %d/%d\n",
+ cr_len, (int) sizeof (cr));
+ return -1;
+ }
+#elif defined(HAVE_CMSGCRED)
+ cred = (struct cmsgcred *) CMSG_DATA (&cmsg.hdr);
+ *pid = cred->cmcred_pid;
+ *uid = cred->cmcred_euid;
+#elif defined(LOCAL_CREDS)
+ cred = (struct sockcred *) CMSG_DATA (&cmsg.hdr);
+ *pid = 0;
+ *uid = cred->sc_euid;
+ set_local_creds(sock, 0);
+#elif defined(HAVE_GETPEEREID) /* OpenBSD */
+ uid_t euid;
+ gid_t egid;
+ *pid = 0;
+
+ if (getpeereid (sock, &euid, &egid) == 0) {
+ *uid = euid;
+ } else {
+ fprintf (stderr, "getpeereid() failed: %s\n", strerror (errno));
+ return -1;
+ }
+#elif defined(HAVE_GETPEERUCRED)
+ ucred_t *uc = NULL;
+
+ if (getpeerucred (sock, &uc) == 0) {
+ *pid = ucred_getpid (uc);
+ *uid = ucred_geteuid (uc);
+ ucred_free (uc);
+ } else {
+ fprintf (stderr, "getpeerucred() failed: %s\n", strerror (errno));
+ return -1;
+ }
+#else /* !SO_PEERCRED && !HAVE_CMSGCRED */
+ fprintf (stderr, "socket credentials not supported on this OS\n");
+ return -1;
+#endif
+ }
+
+ return 0;
+}
+
+int
+egg_unix_credentials_write (int socket)
+{
+ char buf;
+ int bytes_written;
+#if defined(HAVE_CMSGCRED) && (!defined(LOCAL_CREDS) || defined(__FreeBSD__))
+ union {
+ struct cmsghdr hdr;
+ char cred[CMSG_SPACE (sizeof (struct cmsgcred))];
+ } cmsg;
+ struct iovec iov;
+ struct msghdr msg;
+#endif
+
+ buf = 0;
+
+#if defined(HAVE_CMSGCRED) && (!defined(LOCAL_CREDS) || defined(__FreeBSD__))
+ iov.iov_base = &buf;
+ iov.iov_len = 1;
+
+ memset (&msg, 0, sizeof (msg));
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ msg.msg_control = (caddr_t) &cmsg;
+ msg.msg_controllen = CMSG_SPACE (sizeof (struct cmsgcred));
+ memset (&cmsg, 0, sizeof (cmsg));
+ cmsg.hdr.cmsg_len = CMSG_LEN (sizeof (struct cmsgcred));
+ cmsg.hdr.cmsg_level = SOL_SOCKET;
+ cmsg.hdr.cmsg_type = SCM_CREDS;
+#endif
+
+again:
+
+#if defined(HAVE_CMSGCRED) && (!defined(LOCAL_CREDS) || defined(__FreeBSD__))
+ bytes_written = sendmsg (socket, &msg, 0);
+#else
+ bytes_written = write (socket, &buf, 1);
+#endif
+
+ if (bytes_written < 0 && errno == EINTR)
+ goto again;
+
+ if (bytes_written <= 0)
+ return -1;
+
+ return 0;
+}
+
+int
+egg_unix_credentials_setup (int sock)
+{
+ int retval = 0;
+#if defined(LOCAL_CREDS) && !defined(HAVE_CMSGCRED)
+ int val = 1;
+ if (setsockopt (sock, 0, LOCAL_CREDS, &val, sizeof (val)) < 0) {
+ fprintf (stderr, "unable to set LOCAL_CREDS socket option on fd %d\n", fd);
+ retval = -1;
+ }
+#endif
+ return retval;
+}
+
+char *
+egg_unix_credentials_executable (pid_t pid)
+{
+ char *result = NULL;
+
+ /* Try and figure out the path from the pid */
+#if defined(__linux__) || defined(__FreeBSD__)
+ char path[1024];
+ char buffer[64];
+ int count;
+
+#if defined(__linux__)
+ snprintf (buffer, sizeof (buffer), "/proc/%d/exe", (int)pid);
+#elif defined(__FreeBSD__)
+ snprintf (buffer, sizeof (buffer), "/proc/%d/file", (int)pid);
+#endif
+
+ count = readlink (buffer, path, sizeof (path));
+ if (count < 0)
+ fprintf (stderr, "readlink failed for file: %s", buffer);
+ else
+ result = strndup (path, count);
+#endif
+
+ return result;
+}
diff --git a/egg/egg-unix-credentials.h b/egg/egg-unix-credentials.h
new file mode 100644
index 0000000..57826d0
--- /dev/null
+++ b/egg/egg-unix-credentials.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* egg-unix-credentials.h - write and read unix credentials on socket
+
+ Copyright (C) 2008 Stefan Walter
+
+ Gnome keyring is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ Gnome keyring 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Author: Stef Walter <stef@memberwebs.com>
+*/
+
+#ifndef EGGUNIXCREDENTIALS_H_
+#define EGGUNIXCREDENTIALS_H_
+
+#include <unistd.h>
+
+int egg_unix_credentials_read (int sock,
+ pid_t *pid,
+ uid_t *uid);
+
+int egg_unix_credentials_write (int sock);
+
+int egg_unix_credentials_setup (int sock);
+
+char* egg_unix_credentials_executable (pid_t pid);
+
+#endif /*EGGUNIXCREDENTIALS_H_*/
diff --git a/p11-kit/Makefile.am b/p11-kit/Makefile.am
index 2985d85..6366d1f 100644
--- a/p11-kit/Makefile.am
+++ b/p11-kit/Makefile.am
@@ -15,7 +15,7 @@ inc_HEADERS = \
pkcs11.h
MODULE_SRCS = \
- util.c util.h \
+ buffer.c buffer.h \
conf.c conf.h \
debug.c debug.h \
hashmap.c hashmap.h \
@@ -25,7 +25,9 @@ MODULE_SRCS = \
private.h \
ptr-array.c ptr-array.h \
messages.c \
+ rpc-private.h rpc-module.c rpc-message.c rpc-util.c \
uri.c \
+ util.c util.h \
$(top_srcdir)/common/compat.c \
$(top_srcdir)/common/compat.h \
$(inc_HEADERS)
diff --git a/p11-kit/buffer.c b/p11-kit/buffer.c
new file mode 100644
index 0000000..e79fe39
--- /dev/null
+++ b/p11-kit/buffer.c
@@ -0,0 +1,612 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* buffer.c - Generic data buffer, used by openssh, gnome-keyring
+
+ Copyright (C) 2007 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 <string.h>
+#include <stdarg.h>
+
+#include "buffer.h"
+
+#define DEFAULT_ALLOCATOR ((buffer_allocator)realloc)
+
+int
+_p11_buffer_init (buffer_t *buffer,
+ size_t reserve)
+{
+ return _p11_buffer_init_full (buffer, reserve, NULL);
+}
+
+int
+_p11_buffer_init_full (buffer_t *buffer,
+ size_t reserve,
+ buffer_allocator allocator)
+{
+ memset (buffer, 0, sizeof (*buffer));
+
+ if (!allocator)
+ allocator = DEFAULT_ALLOCATOR;
+ if (reserve == 0)
+ reserve = 64;
+
+ buffer->buf = (allocator) (NULL, reserve);
+ if (!buffer->buf) {
+ buffer->failures++;
+ return 0;
+ }
+
+ buffer->len = 0;
+ buffer->allocated_len = reserve;
+ buffer->failures = 0;
+ buffer->allocator = allocator;
+
+ return 1;
+}
+
+void
+_p11_buffer_init_static (buffer_t *buffer,
+ const unsigned char *buf,
+ size_t len)
+{
+ memset (buffer, 0, sizeof (*buffer));
+
+ buffer->buf = (unsigned char*)buf;
+ buffer->len = len;
+ buffer->allocated_len = len;
+ buffer->failures = 0;
+
+ /* A null allocator, and the buffer can't change in size */
+ buffer->allocator = NULL;
+}
+
+void
+_p11_buffer_init_allocated (buffer_t *buffer,
+ unsigned char *buf,
+ size_t len,
+ buffer_allocator allocator)
+{
+ memset (buffer, 0, sizeof (*buffer));
+
+ if (!allocator)
+ allocator = DEFAULT_ALLOCATOR;
+
+ buffer->buf = buf;
+ buffer->len = len;
+ buffer->allocated_len = len;
+ buffer->failures = 0;
+ buffer->allocator = allocator;
+}
+
+void
+_p11_buffer_reset (buffer_t *buffer)
+{
+ memset (buffer->buf, 0, buffer->allocated_len);
+ buffer->len = 0;
+ buffer->failures = 0;
+}
+
+void
+_p11_buffer_uninit (buffer_t *buffer)
+{
+ if (!buffer)
+ return;
+
+ /*
+ * Free the memory block using allocator. If no allocator,
+ * then this memory is ownerd elsewhere and not to be freed.
+ */
+ if (buffer->buf && buffer->allocator)
+ (buffer->allocator) (buffer->buf, 0);
+
+ memset (buffer, 0, sizeof (*buffer));
+}
+
+unsigned char *
+_p11_buffer_uninit_steal (buffer_t *buffer,
+ size_t *n_result)
+{
+ unsigned char *result;
+
+ if (n_result)
+ *n_result = buffer->len;
+ result = buffer->buf;
+
+ memset (buffer, 0, sizeof (*buffer));
+
+ return result;
+}
+
+int
+_p11_buffer_set_allocator (buffer_t *buffer,
+ buffer_allocator allocator)
+{
+ unsigned char *buf = NULL;
+
+ if (!allocator)
+ allocator = DEFAULT_ALLOCATOR;
+ if (buffer->allocator == allocator)
+ return 1;
+
+ if (buffer->allocated_len) {
+ /* Reallocate memory block using new allocator */
+ buf = (allocator) (NULL, buffer->allocated_len);
+ if (buf == NULL)
+ return 0;
+
+ /* Copy stuff into new memory */
+ memcpy (buf, buffer->buf, buffer->allocated_len);
+ }
+
+ /* If old wasn't static, then free it */
+ if (buffer->allocator && buffer->buf)
+ (buffer->allocator) (buffer->buf, 0);
+
+ buffer->buf = buf;
+ buffer->allocator = allocator;
+
+ return 1;
+}
+
+int
+_p11_buffer_equal (buffer_t *b1,
+ buffer_t *b2)
+{
+ if (b1->len != b2->len)
+ return 0;
+ return memcmp (b1->buf, b2->buf, b1->len) == 0;
+}
+
+int
+_p11_buffer_reserve (buffer_t *buffer, size_t len)
+{
+ unsigned char *newbuf;
+ size_t newlen;
+
+ if (len < buffer->allocated_len)
+ return 1;
+
+ /* Calculate a new length, minimize number of buffer allocations */
+ newlen = buffer->allocated_len * 2;
+ if (len > newlen)
+ newlen += len;
+
+ /* Memory owned elsewhere can't be reallocated */
+ if (!buffer->allocator) {
+ buffer->failures++;
+ return 0;
+ }
+
+ /* Reallocate built in buffer using allocator */
+ newbuf = (buffer->allocator) (buffer->buf, newlen);
+ if (!newbuf) {
+ buffer->failures++;
+ return 0;
+ }
+
+ buffer->buf = newbuf;
+ buffer->allocated_len = newlen;
+
+ return 1;
+}
+
+int
+_p11_buffer_resize (buffer_t *buffer,
+ size_t len)
+{
+ if (!_p11_buffer_reserve (buffer, len))
+ return 0;
+
+ buffer->len = len;
+ return 1;
+}
+
+unsigned char*
+_p11_buffer_add_empty (buffer_t *buffer,
+ size_t len)
+{
+ size_t pos = buffer->len;
+ if (!_p11_buffer_reserve (buffer, buffer->len + len))
+ return NULL;
+ buffer->len += len;
+ return buffer->buf + pos;
+}
+
+int
+_p11_buffer_append (buffer_t *buffer,
+ const unsigned char *val,
+ size_t len)
+{
+ if (!_p11_buffer_reserve (buffer, buffer->len + len))
+ return 0; /* failures already incremented */
+ memcpy (buffer->buf + buffer->len, val, len);
+ buffer->len += len;
+ return 1;
+}
+
+int
+_p11_buffer_add_byte (buffer_t *buffer,
+ unsigned char val)
+{
+ if (!_p11_buffer_reserve (buffer, buffer->len + 1))
+ return 0; /* failures already incremented */
+ buffer->buf[buffer->len] = val;
+ buffer->len++;
+ return 1;
+}
+
+int
+_p11_buffer_get_byte (buffer_t *buffer,
+ size_t offset,
+ size_t *next_offset,
+ unsigned char *val)
+{
+ unsigned char *ptr;
+ if (buffer->len < 1 || offset > buffer->len - 1) {
+ buffer->failures++;
+ return 0;
+ }
+ ptr = (unsigned char*)buffer->buf + offset;
+ if (val != NULL)
+ *val = *ptr;
+ if (next_offset != NULL)
+ *next_offset = offset + 1;
+ return 1;
+}
+
+void
+_p11_buffer_encode_uint16 (unsigned char* buf,
+ uint16_t val)
+{
+ buf[0] = (val >> 8) & 0xff;
+ buf[1] = (val >> 0) & 0xff;
+}
+
+uint16_t
+_p11_buffer_decode_uint16 (unsigned char* buf)
+{
+ uint16_t val = buf[0] << 8 | buf[1];
+ return val;
+}
+
+int
+_p11_buffer_add_uint16 (buffer_t *buffer,
+ uint16_t val)
+{
+ if (!_p11_buffer_reserve (buffer, buffer->len + 2))
+ return 0; /* failures already incremented */
+ buffer->len += 2;
+ _p11_buffer_set_uint16 (buffer, buffer->len - 2, val);
+ return 1;
+}
+
+int
+_p11_buffer_set_uint16 (buffer_t *buffer,
+ size_t offset,
+ uint16_t val)
+{
+ unsigned char *ptr;
+ if (buffer->len < 2 || offset > buffer->len - 2) {
+ buffer->failures++;
+ return 0;
+ }
+ ptr = (unsigned char*)buffer->buf + offset;
+ _p11_buffer_encode_uint16 (ptr, val);
+ return 1;
+}
+
+int
+_p11_buffer_get_uint16 (buffer_t *buffer,
+ size_t offset,
+ size_t *next_offset,
+ uint16_t *val)
+{
+ unsigned char *ptr;
+ if (buffer->len < 2 || offset > buffer->len - 2) {
+ buffer->failures++;
+ return 0;
+ }
+ ptr = (unsigned char*)buffer->buf + offset;
+ if (val != NULL)
+ *val = _p11_buffer_decode_uint16 (ptr);
+ if (next_offset != NULL)
+ *next_offset = offset + 2;
+ return 1;
+}
+
+void
+_p11_buffer_encode_uint32 (unsigned char* buf,
+ uint32_t val)
+{
+ buf[0] = (val >> 24) & 0xff;
+ buf[1] = (val >> 16) & 0xff;
+ buf[2] = (val >> 8) & 0xff;
+ buf[3] = (val >> 0) & 0xff;
+}
+
+uint32_t
+_p11_buffer_decode_uint32 (unsigned char* ptr)
+{
+ uint32_t val = ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3];
+ return val;
+}
+
+int
+_p11_buffer_add_uint32 (buffer_t *buffer,
+ uint32_t val)
+{
+ if (!_p11_buffer_reserve (buffer, buffer->len + 4))
+ return 0; /* failures already incremented */
+ buffer->len += 4;
+ _p11_buffer_set_uint32 (buffer, buffer->len - 4, val);
+ return 1;
+}
+
+int
+_p11_buffer_set_uint32 (buffer_t *buffer,
+ size_t offset,
+ uint32_t val)
+{
+ unsigned char *ptr;
+ if (buffer->len < 4 || offset > buffer->len - 4) {
+ buffer->failures++;
+ return 0;
+ }
+ ptr = (unsigned char*)buffer->buf + offset;
+ _p11_buffer_encode_uint32 (ptr, val);
+ return 1;
+}
+
+int
+_p11_buffer_get_uint32 (buffer_t *buffer,
+ size_t offset,
+ size_t *next_offset,
+ uint32_t *val)
+{
+ unsigned char *ptr;
+ if (buffer->len < 4 || offset > buffer->len - 4) {
+ buffer->failures++;
+ return 0;
+ }
+ ptr = (unsigned char*)buffer->buf + offset;
+ if (val != NULL)
+ *val = _p11_buffer_decode_uint32 (ptr);
+ if (next_offset != NULL)
+ *next_offset = offset + 4;
+ return 1;
+}
+
+int
+_p11_buffer_add_uint64 (buffer_t *buffer,
+ uint64_t val)
+{
+ if (!_p11_buffer_add_uint32 (buffer, ((val >> 32) & 0xffffffff)))
+ return 0;
+ return _p11_buffer_add_uint32 (buffer, (val & 0xffffffff));
+}
+
+int
+_p11_buffer_get_uint64 (buffer_t *buffer,
+ size_t offset,
+ size_t *next_offset,
+ uint64_t *val)
+{
+ uint32_t a, b;
+ if (!_p11_buffer_get_uint32 (buffer, offset, &offset, &a))
+ return 0;
+ if (!_p11_buffer_get_uint32 (buffer, offset, &offset, &b))
+ return 0;
+ if (val != NULL)
+ *val = ((uint64_t)a) << 32 | b;
+ if (next_offset != NULL)
+ *next_offset = offset;
+ return 1;
+}
+
+int
+_p11_buffer_add_byte_array (buffer_t *buffer,
+ const unsigned char *val,
+ size_t len)
+{
+ if (val == NULL)
+ return _p11_buffer_add_uint32 (buffer, 0xffffffff);
+ if (len >= 0x7fffffff) {
+ buffer->failures++;
+ return 0;
+ }
+ if (!_p11_buffer_add_uint32 (buffer, len))
+ return 0;
+ return _p11_buffer_append (buffer, val, len);
+}
+
+int
+_p11_buffer_get_byte_array (buffer_t *buffer,
+ size_t offset,
+ size_t *next_offset,
+ const unsigned char **val,
+ size_t *vlen)
+{
+ uint32_t len;
+ if (!_p11_buffer_get_uint32 (buffer, offset, &offset, &len))
+ return 0;
+ if (len == 0xffffffff) {
+ if (next_offset)
+ *next_offset = offset;
+ if (val)
+ *val = NULL;
+ if (vlen)
+ *vlen = 0;
+ return 1;
+ } else if (len >= 0x7fffffff) {
+ buffer->failures++;
+ return 0;
+ }
+
+ if (buffer->len < len || offset > buffer->len - len) {
+ buffer->failures++;
+ return 0;
+ }
+
+ if (val)
+ *val = buffer->buf + offset;
+ if (vlen)
+ *vlen = len;
+ if (next_offset)
+ *next_offset = offset + len;
+
+ return 1;
+}
+
+int
+_p11_buffer_add_string (buffer_t *buffer,
+ const char *str)
+{
+ if (str == NULL) {
+ return _p11_buffer_add_uint32 (buffer, 0xffffffff);
+ } else {
+ size_t len = strlen (str);
+ if (len >= 0x7fffffff)
+ return 0;
+ if (!_p11_buffer_add_uint32 (buffer, len))
+ return 0;
+ return _p11_buffer_append (buffer, (unsigned char*)str, len);
+ }
+}
+
+int
+_p11_buffer_get_string (buffer_t *buffer,
+ size_t offset,
+ size_t *next_offset,
+ char **str_ret,
+ buffer_allocator allocator)
+{
+ uint32_t len;
+
+ if (!allocator)
+ allocator = buffer->allocator;
+ if (!allocator)
+ allocator = DEFAULT_ALLOCATOR;
+
+ if (!_p11_buffer_get_uint32 (buffer, offset, &offset, &len)) {
+ return 0;
+ }
+ if (len == 0xffffffff) {
+ *next_offset = offset;
+ *str_ret = NULL;
+ return 1;
+ } else if (len >= 0x7fffffff) {
+ return 0;
+ }
+
+ if (buffer->len < len ||
+ offset > buffer->len - len) {
+ return 0;
+ }
+
+ /* Make sure no null characters in string */
+ if (memchr (buffer->buf + offset, 0, len) != NULL)
+ return 0;
+
+ /* The passed allocator may be for non-pageable memory */
+ *str_ret = (allocator) (NULL, len + 1);
+ if (!*str_ret)
+ return 0;
+ memcpy (*str_ret, buffer->buf + offset, len);
+
+ /* Always zero terminate */
+ (*str_ret)[len] = 0;
+ *next_offset = offset + len;
+
+ return 1;
+}
+
+int
+_p11_buffer_add_stringv (buffer_t *buffer,
+ const char** strv)
+{
+ const char **v;
+ uint32_t n = 0;
+
+ if (!strv)
+ return 0;
+
+ /* Add the number of strings coming */
+ for (v = strv; *v; ++v)
+ ++n;
+ if (!_p11_buffer_add_uint32 (buffer, n))
+ return 0;
+
+ /* Add the individual strings */
+ for (v = strv; *v; ++v) {
+ if (!_p11_buffer_add_string (buffer, *v))
+ return 0;
+ }
+
+ return 1;
+}
+
+int
+_p11_buffer_get_stringv (buffer_t *buffer,
+ size_t offset,
+ size_t *next_offset,
+ char ***strv_ret,
+ buffer_allocator allocator)
+{
+ uint32_t n, i, j;
+ size_t len;
+
+ if (!allocator)
+ allocator = buffer->allocator;
+ if (!allocator)
+ allocator = DEFAULT_ALLOCATOR;
+
+ /* First the number of environment variable lines */
+ if (!_p11_buffer_get_uint32 (buffer, offset, &offset, &n))
+ return 0;
+
+ /* Then that number of strings */
+ len = (n + 1) * sizeof (char*);
+ *strv_ret = (char**)(allocator) (NULL, len);
+ if (!*strv_ret)
+ return 0;
+
+ /* All null strings */
+ memset (*strv_ret, 0, len);
+
+ for (i = 0; i < n; ++i) {
+ if (!_p11_buffer_get_string (buffer, offset, &offset,
+ &((*strv_ret)[i]), allocator)) {
+
+ /* Free all the strings on failure */
+ for (j = 0; j < i; ++j) {
+ if ((*strv_ret)[j])
+ (allocator) ((*strv_ret)[j], 0);
+ }
+
+ return 0;
+ }
+ }
+
+ if (next_offset != NULL)
+ *next_offset = offset;
+
+ return 1;
+}
diff --git a/p11-kit/buffer.h b/p11-kit/buffer.h
new file mode 100644
index 0000000..25205d2
--- /dev/null
+++ b/p11-kit/buffer.h
@@ -0,0 +1,194 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* buffer.h - Generic data buffer, used by openssh, gnome-keyring
+
+ Copyright (C) 2007, 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>
+*/
+
+#ifndef P11_BUFFER_H
+#define P11_BUFFER_H
+
+#include <stdlib.h>
+#include <stdint.h>
+
+/* -------------------------------------------------------------------
+ * buffer_t
+ *
+ * IMPORTANT: This is pure vanila standard C, no glib. We need this
+ * because certain consumers of this protocol need to be built
+ * without linking in any special libraries. ie: the PKCS#11 module.
+ *
+ * Memory Allocation
+ *
+ * Callers can set their own allocator. If NULL is used then standard
+ * C library heap memory is used and failures will not be fatal. Memory
+ * failures will instead result in a zero return value or
+ * _p11_buffer_has_error() returning one.
+ *
+ * If you use something like g_realloc as the allocator, then memory
+ * failures become fatal just like in a standard GTK program.
+ *
+ * Don't change the allocator manually in the buffer_t structure. The
+ * _p11_buffer_set_allocator() func will reallocate and handle things
+ * properly.
+ *
+ * Pointers into the Buffer
+ *
+ * Any write operation has the posibility of reallocating memory
+ * and invalidating any direct pointers into the buffer.
+ */
+
+/* The allocator for the buffer_t. This follows the realloc() syntax and logic */
+typedef void* (*buffer_allocator) (void* p, size_t len);
+
+typedef struct _buffer_t {
+ unsigned char *buf;
+ size_t len;
+ size_t allocated_len;
+ int failures;
+ buffer_allocator allocator;
+} buffer_t;
+
+#define P11_BUFFER_EMPTY { NULL, 0, 0, 0, NULL }
+
+int _p11_buffer_init (buffer_t *buffer,
+ size_t reserve);
+
+int _p11_buffer_init_full (buffer_t *buffer,
+ size_t reserve,
+ buffer_allocator allocator);
+
+void _p11_buffer_init_static (buffer_t *buffer,
+ const unsigned char *buf,
+ size_t len);
+
+void _p11_buffer_init_allocated (buffer_t *buffer,
+ unsigned char *buf,
+ size_t len,
+ buffer_allocator allocator);
+
+void _p11_buffer_uninit (buffer_t *buffer);
+
+unsigned char * _p11_buffer_uninit_steal (buffer_t *buffer,
+ size_t *n_result);
+
+int _p11_buffer_set_allocator (buffer_t *buffer,
+ buffer_allocator allocator);
+
+void _p11_buffer_reset (buffer_t *buffer);
+
+int _p11_buffer_equal (buffer_t *b1,
+ buffer_t *b2);
+
+int _p11_buffer_reserve (buffer_t *buffer,
+ size_t len);
+
+int _p11_buffer_resize (buffer_t *buffer,
+ size_t len);
+
+int _p11_buffer_append (buffer_t *buffer,
+ const unsigned char *val,
+ size_t len);
+
+unsigned char * _p11_buffer_add_empty (buffer_t *buffer,
+ size_t len);
+
+int _p11_buffer_add_byte (buffer_t *buffer,
+ unsigned char val);
+
+int _p11_buffer_get_byte (buffer_t *buffer,
+ size_t offset,
+ size_t *next_offset,
+ unsigned char *val);
+
+void _p11_buffer_encode_uint32 (unsigned char *buf,
+ uint32_t val);
+
+uint32_t _p11_buffer_decode_uint32 (unsigned char *buf);
+
+int _p11_buffer_add_uint32 (buffer_t *buffer,
+ uint32_t val);
+
+int _p11_buffer_set_uint32 (buffer_t *buffer,
+ size_t offset,
+ uint32_t val);
+
+int _p11_buffer_get_uint32 (buffer_t *buffer,
+ size_t offset,
+ size_t *next_offset,
+ uint32_t *val);
+
+void _p11_buffer_encode_uint16 (unsigned char *buf,
+ uint16_t val);
+
+uint16_t _p11_buffer_decode_uint16 (unsigned char *buf);
+
+int _p11_buffer_add_uint16 (buffer_t *buffer,
+ uint16_t val);
+
+int _p11_buffer_set_uint16 (buffer_t *buffer,
+ size_t offset,
+ uint16_t val);
+
+int _p11_buffer_get_uint16 (buffer_t *buffer,
+ size_t offset,
+ size_t *next_offset,
+ uint16_t *val);
+
+int _p11_buffer_add_byte_array (buffer_t *buffer,
+ const unsigned char *val,
+ size_t len);
+
+int _p11_buffer_get_byte_array (buffer_t *buffer,
+ size_t offset,
+ size_t *next_offset,
+ const unsigned char **val,
+ size_t *vlen);
+
+int _p11_buffer_add_string (buffer_t *buffer,
+ const char *str);
+
+int _p11_buffer_get_string (buffer_t *buffer,
+ size_t offset,
+ size_t *next_offset,
+ char **str_ret,
+ buffer_allocator allocator);
+
+int _p11_buffer_add_stringv (buffer_t *buffer,
+ const char **strv);
+
+int _p11_buffer_get_stringv (buffer_t *buffer,
+ size_t offset,
+ size_t *next_offset,
+ char ***strv_ret,
+ buffer_allocator allocator);
+
+int _p11_buffer_add_uint64 (buffer_t *buffer,
+ uint64_t val);
+
+int _p11_buffer_get_uint64 (buffer_t *buffer,
+ size_t offset,
+ size_t *next_offset,
+ uint64_t *val);
+
+#define _p11_buffer_length(b) ((b)->len)
+
+#define _p11_buffer_has_error(b) ((b)->failures > 0)
+
+#endif /* P11_BUFFER_H */
diff --git a/p11-kit/daemon.c b/p11-kit/daemon.c
new file mode 100644
index 0000000..882924e
--- /dev/null
+++ b/p11-kit/daemon.c
@@ -0,0 +1,1405 @@
+/*
+ * Copyright (C) 2008 Stefan Walter
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ * * The names of contributors to this software may not be
+ * used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Author: Stef Walter <stefw@collabora.co.uk>
+ */
+
+#include "config.h"
+
+#define DEBUG_FLAG DEBUG_PROXY
+#include "debug.h"
+#include "hashmap.h"
+#define CRYPTOKI_EXPORTS
+#include "pkcs11.h"
+#include "p11-kit.h"
+#include "private.h"
+#include "util.h"
+
+#include <sys/types.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+/* Start wrap slots slightly higher for testing */
+#define MAPPING_OFFSET 0x10
+#define FIRST_HANDLE 0x10
+
+typedef struct _Mapping {
+ CK_SLOT_ID wrap_slot;
+ CK_SLOT_ID real_slot;
+ CK_FUNCTION_LIST_PTR funcs;
+} Mapping;
+
+typedef struct _Session {
+ CK_SESSION_HANDLE wrap_session;
+ CK_SESSION_HANDLE real_session;
+ CK_SLOT_ID wrap_slot;
+} Session;
+
+/* Forward declaration */
+static CK_FUNCTION_LIST proxy_function_list;
+
+/*
+ * Shared data between threads, protected by the mutex, a structure so
+ * we can audit thread safety easier.
+ */
+static struct _Shared {
+ Mapping *mappings;
+ unsigned int n_mappings;
+ int mappings_refs;
+ hashmap *sessions;
+ CK_ULONG last_handle;
+} gl = { NULL, 0, 0, NULL, FIRST_HANDLE };
+
+#define MANUFACTURER_ID "PKCS#11 Kit "
+#define LIBRARY_DESCRIPTION "PKCS#11 Kit Proxy Module "
+#define LIBRARY_VERSION_MAJOR 1
+#define LIBRARY_VERSION_MINOR 1
+
+/* -----------------------------------------------------------------------------
+ * PKCS#11 PROXY MODULE
+ */
+
+static CK_RV
+map_slot_unlocked (CK_SLOT_ID slot, Mapping *mapping)
+{
+ assert (mapping);
+
+ if (slot < MAPPING_OFFSET)
+ return CKR_SLOT_ID_INVALID;
+ slot -= MAPPING_OFFSET;
+
+ if (slot > gl.n_mappings) {
+ return CKR_SLOT_ID_INVALID;
+ } else {
+ assert (gl.mappings);
+ memcpy (mapping, &gl.mappings[slot], sizeof (Mapping));
+ return CKR_OK;
+ }
+}
+
+static CK_RV
+map_slot_to_real (CK_SLOT_ID_PTR slot, Mapping *mapping)
+{
+ CK_RV rv;
+
+ assert (mapping);
+
+ _p11_lock ();
+
+ if (!gl.mappings)
+ rv = CKR_CRYPTOKI_NOT_INITIALIZED;
+ else
+ rv = map_slot_unlocked (*slot, mapping);
+ if (rv == CKR_OK)
+ *slot = mapping->real_slot;
+
+ _p11_unlock ();
+
+ return rv;
+}
+
+static CK_RV
+map_session_to_real (CK_SESSION_HANDLE_PTR handle, Mapping *mapping, Session *session)
+{
+ CK_RV rv = CKR_OK;
+ Session *sess;
+
+ assert (handle);
+ assert (mapping);
+
+ _p11_lock ();
+
+ if (!gl.sessions) {
+ rv = CKR_CRYPTOKI_NOT_INITIALIZED;
+ } else {
+ assert (gl.sessions);
+ sess = _p11_hash_get (gl.sessions, handle);
+ if (sess != NULL) {
+ *handle = sess->real_session;
+ rv = map_slot_unlocked (sess->wrap_slot, mapping);
+ if (session != NULL)
+ memcpy (session, sess, sizeof (Session));
+ } else {
+ rv = CKR_SESSION_HANDLE_INVALID;
+ }
+ }
+
+ _p11_unlock ();
+
+ return rv;
+}
+
+static void
+finalize_mappings_unlocked (void)
+{
+ assert (gl.mappings_refs);
+
+ if (--gl.mappings_refs)
+ return;
+
+ /* No more mappings */
+ free (gl.mappings);
+ gl.mappings = NULL;
+ gl.n_mappings = 0;
+
+ /* no more sessions */
+ _p11_hash_free (gl.sessions);
+ gl.sessions = NULL;
+}
+
+void
+_p11_kit_proxy_after_fork (void)
+{
+ /*
+ * After a fork the callers are supposed to call C_Initialize and all.
+ * In addition the underlying libraries may change their state so free
+ * up any mappings and all
+ */
+
+ _p11_lock ();
+
+ gl.mappings_refs = 1;
+ finalize_mappings_unlocked ();
+ assert (!gl.mappings);
+
+ _p11_unlock ();
+}
+
+static CK_RV
+proxy_C_Finalize (CK_VOID_PTR reserved)
+{
+ CK_RV rv;
+
+ _p11_debug ("in");
+
+ /* WARNING: This function must be reentrant */
+
+ if (reserved) {
+ rv = CKR_ARGUMENTS_BAD;
+
+ } else {
+ _p11_lock ();
+
+ /* WARNING: Reentrancy can occur here */
+ rv = _p11_kit_finalize_registered_unlocked_reentrant ();
+
+ /*
+ * If modules are all gone, then this was the last
+ * finalize, so cleanup our mappings
+ */
+ if (gl.mappings_refs)
+ finalize_mappings_unlocked ();
+
+ _p11_unlock ();
+ }
+
+ _p11_debug ("out: %lu", rv);
+ return rv;
+}
+
+static CK_RV
+initialize_mappings_unlocked_reentrant (void)
+{
+ CK_FUNCTION_LIST_PTR *funcss, *f;
+ CK_FUNCTION_LIST_PTR funcs;
+ Mapping *mappings = NULL;
+ int n_mappings = 0;
+ CK_SLOT_ID_PTR slots;
+ CK_ULONG i, count;
+ CK_RV rv = CKR_OK;
+
+ assert (!gl.mappings);
+
+ funcss = _p11_kit_registered_modules_unlocked ();
+ for (f = funcss; *f; ++f) {
+ funcs = *f;
+
+ assert (funcs);
+ slots = NULL;
+
+ _p11_unlock ();
+
+ /* Ask module for its slots */
+ rv = (funcs->C_GetSlotList) (FALSE, NULL, &count);
+ if (rv == CKR_OK && count) {
+ slots = calloc (sizeof (CK_SLOT_ID), count);
+ if (!slots)
+ rv = CKR_HOST_MEMORY;
+ else
+ rv = (funcs->C_GetSlotList) (FALSE, slots, &count);
+ }
+
+ _p11_lock ();
+
+ if (rv != CKR_OK) {
+ free (slots);
+ break;
+ }
+
+ mappings = _p11_realloc (mappings, sizeof (Mapping) * (n_mappings + count));
+ if (!mappings) {
+ free (slots);
+ rv = CKR_HOST_MEMORY;
+ break;
+ }
+
+ /* And now add a mapping for each of those slots */
+ for (i = 0; i < count; ++i) {
+ mappings[n_mappings].funcs = funcs;
+ mappings[n_mappings].wrap_slot = n_mappings + MAPPING_OFFSET;
+ mappings[n_mappings].real_slot = slots[i];
+ ++n_mappings;
+ }
+
+ free (slots);
+ }
+
+ /* Another thread raced us here due to above reentrancy */
+ if (gl.mappings) {
+ free (mappings);
+ return CKR_OK;
+ }
+
+ assert (!gl.sessions);
+ gl.mappings = mappings;
+ gl.n_mappings = n_mappings;
+ gl.sessions = _p11_hash_create (_p11_hash_ulongptr_hash, _p11_hash_ulongptr_equal, NULL, free);
+ ++gl.mappings_refs;
+
+ /* Any cleanup necessary for failure will happen at caller */
+ return rv;
+}
+
+static CK_RV
+proxy_C_Initialize (CK_VOID_PTR init_args)
+{
+ CK_RV rv;
+
+ _p11_library_init_once ();
+
+ /* WARNING: This function must be reentrant */
+
+ _p11_debug ("in");
+
+ _p11_lock ();
+
+ /* WARNING: Reentrancy can occur here */
+ rv = _p11_kit_initialize_registered_unlocked_reentrant ();
+
+ /* WARNING: Reentrancy can occur here */
+ if (rv == CKR_OK && gl.mappings_refs == 0)
+ rv = initialize_mappings_unlocked_reentrant ();
+
+ _p11_unlock ();
+
+ _p11_debug ("here");
+
+ if (rv != CKR_OK)
+ proxy_C_Finalize (NULL);
+
+ _p11_debug ("out: %lu", rv);
+ return rv;
+}
+
+static CK_RV
+proxy_C_GetInfo (CK_INFO_PTR info)
+{
+ CK_RV rv = CKR_OK;
+
+ _p11_library_init_once ();
+
+ if (info == NULL)
+ return CKR_ARGUMENTS_BAD;
+
+ _p11_lock ();
+
+ if (!gl.mappings)
+ rv = CKR_CRYPTOKI_NOT_INITIALIZED;
+
+ _p11_unlock ();
+
+ if (rv != CKR_OK)
+ return rv;
+
+ info->cryptokiVersion.major = CRYPTOKI_VERSION_MAJOR;
+ info->cryptokiVersion.minor = CRYPTOKI_VERSION_MINOR;
+ info->libraryVersion.major = LIBRARY_VERSION_MAJOR;
+ info->libraryVersion.minor = LIBRARY_VERSION_MINOR;
+ info->flags = 0;
+ strncpy ((char*)info->manufacturerID, MANUFACTURER_ID, 32);
+ strncpy ((char*)info->libraryDescription, LIBRARY_DESCRIPTION, 32);
+ return CKR_OK;
+}
+
+static CK_RV
+proxy_C_GetFunctionList (CK_FUNCTION_LIST_PTR_PTR list)
+{
+ /* Can be called before C_Initialize */
+
+ if (!list)
+ return CKR_ARGUMENTS_BAD;
+ *list = &proxy_function_list;
+ return CKR_OK;
+}
+
+static CK_RV
+proxy_C_GetSlotList (CK_BBOOL token_present, CK_SLOT_ID_PTR slot_list,
+ CK_ULONG_PTR count)
+{
+ CK_SLOT_INFO info;
+ Mapping *mapping;
+ CK_ULONG index;
+ CK_RV rv = CKR_OK;
+ int i;
+
+ if (!count)
+ return CKR_ARGUMENTS_BAD;
+
+ _p11_lock ();
+
+ if (!gl.mappings) {
+ rv = CKR_CRYPTOKI_NOT_INITIALIZED;
+ } else {
+ index = 0;
+
+ /* Go through and build up a map */
+ for (i = 0; i < gl.n_mappings; ++i) {
+ mapping = &gl.mappings[i];
+
+ /* Skip ones without a token if requested */
+ if (token_present) {
+ rv = (mapping->funcs->C_GetSlotInfo) (mapping->real_slot, &info);
+ if (rv != CKR_OK)
+ break;
+ if (!(info.flags & CKF_TOKEN_PRESENT))
+ continue;
+ }
+
+ /* Fill in the slot if we can */
+ if (slot_list && *count > index)
+ slot_list[index] = mapping->wrap_slot;
+
+ ++index;
+ }
+
+ if (slot_list && *count < index)
+ rv = CKR_BUFFER_TOO_SMALL;
+
+ *count = index;
+ }
+
+ _p11_unlock ();
+
+ return rv;
+}
+
+static CK_RV
+proxy_C_GetSlotInfo (CK_SLOT_ID id, CK_SLOT_INFO_PTR info)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_slot_to_real (&id, &map);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_GetSlotInfo) (id, info);
+}
+
+static CK_RV
+proxy_C_GetTokenInfo (CK_SLOT_ID id, CK_TOKEN_INFO_PTR info)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_slot_to_real (&id, &map);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_GetTokenInfo) (id, info);
+}
+
+static CK_RV
+proxy_C_GetMechanismList (CK_SLOT_ID id, CK_MECHANISM_TYPE_PTR mechanism_list,
+ CK_ULONG_PTR count)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_slot_to_real (&id, &map);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_GetMechanismList) (id, mechanism_list, count);
+}
+
+static CK_RV
+proxy_C_GetMechanismInfo (CK_SLOT_ID id, CK_MECHANISM_TYPE type,
+ CK_MECHANISM_INFO_PTR info)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_slot_to_real (&id, &map);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_GetMechanismInfo) (id, type, info);
+}
+
+static CK_RV
+proxy_C_InitToken (CK_SLOT_ID id, CK_UTF8CHAR_PTR pin, CK_ULONG pin_len, CK_UTF8CHAR_PTR label)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_slot_to_real (&id, &map);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_InitToken) (id, pin, pin_len, label);
+}
+
+static CK_RV
+proxy_C_WaitForSlotEvent (CK_FLAGS flags, CK_SLOT_ID_PTR slot, CK_VOID_PTR reserved)
+{
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+static CK_RV
+proxy_C_OpenSession (CK_SLOT_ID id, CK_FLAGS flags, CK_VOID_PTR user_data,
+ CK_NOTIFY callback, CK_SESSION_HANDLE_PTR handle)
+{
+ Session *sess;
+ Mapping map;
+ CK_RV rv;
+
+ if (handle == NULL)
+ return CKR_ARGUMENTS_BAD;
+
+ rv = map_slot_to_real (&id, &map);
+ if (rv != CKR_OK)
+ return rv;
+
+ rv = (map.funcs->C_OpenSession) (id, flags, user_data, callback, handle);
+
+ if (rv == CKR_OK) {
+ _p11_lock ();
+
+ if (!gl.sessions) {
+ /*
+ * The underlying module should have returned an error, so this
+ * code should never be reached with properly behaving modules.
+ * That's why we don't cleanup and close the newly opened session here
+ * or anything like that.
+ */
+ rv = CKR_CRYPTOKI_NOT_INITIALIZED;
+
+ } else {
+ sess = calloc (1, sizeof (Session));
+ sess->wrap_slot = map.wrap_slot;
+ sess->real_session = *handle;
+ sess->wrap_session = ++gl.last_handle; /* TODO: Handle wrapping, and then collisions */
+ _p11_hash_set (gl.sessions, &sess->wrap_session, sess);
+ *handle = sess->wrap_session;
+ }
+
+ _p11_unlock ();
+ }
+
+ return rv;
+}
+
+static CK_RV
+proxy_C_CloseSession (CK_SESSION_HANDLE handle)
+{
+ CK_SESSION_HANDLE key;
+ Mapping map;
+ CK_RV rv;
+
+ key = handle;
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ rv = (map.funcs->C_CloseSession) (handle);
+
+ if (rv == CKR_OK) {
+ _p11_lock ();
+
+ if (gl.sessions)
+ _p11_hash_remove (gl.sessions, &key);
+
+ _p11_unlock ();
+ }
+
+ return rv;
+}
+
+static CK_RV
+proxy_C_CloseAllSessions (CK_SLOT_ID id)
+{
+ CK_SESSION_HANDLE_PTR to_close;
+ CK_RV rv = CKR_OK;
+ Session *sess;
+ CK_ULONG i, count = 0;
+ hashiter iter;
+
+ _p11_lock ();
+
+ if (!gl.sessions) {
+ rv = CKR_CRYPTOKI_NOT_INITIALIZED;
+ } else {
+ to_close = calloc (sizeof (CK_SESSION_HANDLE), _p11_hash_size (gl.sessions));
+ if (!to_close) {
+ rv = CKR_HOST_MEMORY;
+ } else {
+ _p11_hash_iterate (gl.sessions, &iter);
+ count = 0;
+ while (_p11_hash_next (&iter, NULL, (void**)&sess)) {
+ if (sess->wrap_slot == id && to_close)
+ to_close[count++] = sess->wrap_session;
+ }
+ }
+ }
+
+ _p11_unlock ();
+
+ if (rv != CKR_OK)
+ return rv;
+
+ for (i = 0; i < count; ++i)
+ proxy_C_CloseSession (to_close[i]);
+
+ free (to_close);
+ return CKR_OK;
+}
+
+static CK_RV
+proxy_C_GetFunctionStatus (CK_SESSION_HANDLE handle)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_GetFunctionStatus) (handle);
+}
+
+static CK_RV
+proxy_C_CancelFunction (CK_SESSION_HANDLE handle)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_CancelFunction) (handle);
+}
+
+static CK_RV
+proxy_C_GetSessionInfo (CK_SESSION_HANDLE handle, CK_SESSION_INFO_PTR info)
+{
+ Mapping map;
+ CK_RV rv;
+
+ if (info == NULL)
+ return CKR_ARGUMENTS_BAD;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+
+ rv = (map.funcs->C_GetSessionInfo) (handle, info);
+ if (rv == CKR_OK)
+ info->slotID = map.wrap_slot;
+
+ return rv;
+}
+
+static CK_RV
+proxy_C_InitPIN (CK_SESSION_HANDLE handle, CK_UTF8CHAR_PTR pin, CK_ULONG pin_len)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+
+ return (map.funcs->C_InitPIN) (handle, pin, pin_len);
+}
+
+static CK_RV
+proxy_C_SetPIN (CK_SESSION_HANDLE handle, CK_UTF8CHAR_PTR old_pin, CK_ULONG old_pin_len,
+ CK_UTF8CHAR_PTR new_pin, CK_ULONG new_pin_len)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+
+ return (map.funcs->C_SetPIN) (handle, old_pin, old_pin_len, new_pin, new_pin_len);
+}
+
+static CK_RV
+proxy_C_GetOperationState (CK_SESSION_HANDLE handle, CK_BYTE_PTR operation_state, CK_ULONG_PTR operation_state_len)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_GetOperationState) (handle, operation_state, operation_state_len);
+}
+
+static CK_RV
+proxy_C_SetOperationState (CK_SESSION_HANDLE handle, CK_BYTE_PTR operation_state,
+ CK_ULONG operation_state_len, CK_OBJECT_HANDLE encryption_key,
+ CK_OBJECT_HANDLE authentication_key)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_SetOperationState) (handle, operation_state, operation_state_len, encryption_key, authentication_key);
+}
+
+static CK_RV
+proxy_C_Login (CK_SESSION_HANDLE handle, CK_USER_TYPE user_type,
+ CK_UTF8CHAR_PTR pin, CK_ULONG pin_len)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+
+ return (map.funcs->C_Login) (handle, user_type, pin, pin_len);
+}
+
+static CK_RV
+proxy_C_Logout (CK_SESSION_HANDLE handle)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_Logout) (handle);
+}
+
+static CK_RV
+proxy_C_CreateObject (CK_SESSION_HANDLE handle, CK_ATTRIBUTE_PTR template,
+ CK_ULONG count, CK_OBJECT_HANDLE_PTR new_object)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+
+ return (map.funcs->C_CreateObject) (handle, template, count, new_object);
+}
+
+static CK_RV
+proxy_C_CopyObject (CK_SESSION_HANDLE handle, CK_OBJECT_HANDLE object,
+ CK_ATTRIBUTE_PTR template, CK_ULONG count,
+ CK_OBJECT_HANDLE_PTR new_object)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_CopyObject) (handle, object, template, count, new_object);
+}
+
+static CK_RV
+proxy_C_DestroyObject (CK_SESSION_HANDLE handle, CK_OBJECT_HANDLE object)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_DestroyObject) (handle, object);
+}
+
+static CK_RV
+proxy_C_GetObjectSize (CK_SESSION_HANDLE handle, CK_OBJECT_HANDLE object,
+ CK_ULONG_PTR size)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_GetObjectSize) (handle, object, size);
+}
+
+static CK_RV
+proxy_C_GetAttributeValue (CK_SESSION_HANDLE handle, CK_OBJECT_HANDLE object,
+ CK_ATTRIBUTE_PTR template, CK_ULONG count)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_GetAttributeValue) (handle, object, template, count);
+}
+
+static CK_RV
+proxy_C_SetAttributeValue (CK_SESSION_HANDLE handle, CK_OBJECT_HANDLE object,
+ CK_ATTRIBUTE_PTR template, CK_ULONG count)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_SetAttributeValue) (handle, object, template, count);
+}
+
+static CK_RV
+proxy_C_FindObjectsInit (CK_SESSION_HANDLE handle, CK_ATTRIBUTE_PTR template,
+ CK_ULONG count)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_FindObjectsInit) (handle, template, count);
+}
+
+static CK_RV
+proxy_C_FindObjects (CK_SESSION_HANDLE handle, CK_OBJECT_HANDLE_PTR objects,
+ CK_ULONG max_count, CK_ULONG_PTR count)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_FindObjects) (handle, objects, max_count, count);
+}
+
+static CK_RV
+proxy_C_FindObjectsFinal (CK_SESSION_HANDLE handle)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_FindObjectsFinal) (handle);
+}
+
+static CK_RV
+proxy_C_EncryptInit (CK_SESSION_HANDLE handle, CK_MECHANISM_PTR mechanism,
+ CK_OBJECT_HANDLE key)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_EncryptInit) (handle, mechanism, key);
+}
+
+static CK_RV
+proxy_C_Encrypt (CK_SESSION_HANDLE handle, CK_BYTE_PTR data, CK_ULONG data_len,
+ CK_BYTE_PTR encrypted_data, CK_ULONG_PTR encrypted_data_len)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_Encrypt) (handle, data, data_len, encrypted_data, encrypted_data_len);
+}
+
+static CK_RV
+proxy_C_EncryptUpdate (CK_SESSION_HANDLE handle, CK_BYTE_PTR part,
+ CK_ULONG part_len, CK_BYTE_PTR encrypted_part,
+ CK_ULONG_PTR encrypted_part_len)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_EncryptUpdate) (handle, part, part_len, encrypted_part, encrypted_part_len);
+}
+
+static CK_RV
+proxy_C_EncryptFinal (CK_SESSION_HANDLE handle, CK_BYTE_PTR last_part,
+ CK_ULONG_PTR last_part_len)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_EncryptFinal) (handle, last_part, last_part_len);
+}
+
+static CK_RV
+proxy_C_DecryptInit (CK_SESSION_HANDLE handle, CK_MECHANISM_PTR mechanism,
+ CK_OBJECT_HANDLE key)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_DecryptInit) (handle, mechanism, key);
+}
+
+static CK_RV
+proxy_C_Decrypt (CK_SESSION_HANDLE handle, CK_BYTE_PTR enc_data,
+ CK_ULONG enc_data_len, CK_BYTE_PTR data, CK_ULONG_PTR data_len)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_Decrypt) (handle, enc_data, enc_data_len, data, data_len);
+}
+
+static CK_RV
+proxy_C_DecryptUpdate (CK_SESSION_HANDLE handle, CK_BYTE_PTR enc_part,
+ CK_ULONG enc_part_len, CK_BYTE_PTR part, CK_ULONG_PTR part_len)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_DecryptUpdate) (handle, enc_part, enc_part_len, part, part_len);
+}
+
+static CK_RV
+proxy_C_DecryptFinal (CK_SESSION_HANDLE handle, CK_BYTE_PTR last_part,
+ CK_ULONG_PTR last_part_len)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_DecryptFinal) (handle, last_part, last_part_len);
+}
+
+static CK_RV
+proxy_C_DigestInit (CK_SESSION_HANDLE handle, CK_MECHANISM_PTR mechanism)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_DigestInit) (handle, mechanism);
+}
+
+static CK_RV
+proxy_C_Digest (CK_SESSION_HANDLE handle, CK_BYTE_PTR data, CK_ULONG data_len,
+ CK_BYTE_PTR digest, CK_ULONG_PTR digest_len)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_Digest) (handle, data, data_len, digest, digest_len);
+}
+
+static CK_RV
+proxy_C_DigestUpdate (CK_SESSION_HANDLE handle, CK_BYTE_PTR part, CK_ULONG part_len)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_DigestUpdate) (handle, part, part_len);
+}
+
+static CK_RV
+proxy_C_DigestKey (CK_SESSION_HANDLE handle, CK_OBJECT_HANDLE key)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_DigestKey) (handle, key);
+}
+
+static CK_RV
+proxy_C_DigestFinal (CK_SESSION_HANDLE handle, CK_BYTE_PTR digest,
+ CK_ULONG_PTR digest_len)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_DigestFinal) (handle, digest, digest_len);
+}
+
+static CK_RV
+proxy_C_SignInit (CK_SESSION_HANDLE handle, CK_MECHANISM_PTR mechanism,
+ CK_OBJECT_HANDLE key)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_SignInit) (handle, mechanism, key);
+}
+
+static CK_RV
+proxy_C_Sign (CK_SESSION_HANDLE handle, CK_BYTE_PTR data, CK_ULONG data_len,
+ CK_BYTE_PTR signature, CK_ULONG_PTR signature_len)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_Sign) (handle, data, data_len, signature, signature_len);
+}
+
+static CK_RV
+proxy_C_SignUpdate (CK_SESSION_HANDLE handle, CK_BYTE_PTR part, CK_ULONG part_len)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_SignUpdate) (handle, part, part_len);
+}
+
+static CK_RV
+proxy_C_SignFinal (CK_SESSION_HANDLE handle, CK_BYTE_PTR signature,
+ CK_ULONG_PTR signature_len)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_SignFinal) (handle, signature, signature_len);
+}
+
+static CK_RV
+proxy_C_SignRecoverInit (CK_SESSION_HANDLE handle, CK_MECHANISM_PTR mechanism,
+ CK_OBJECT_HANDLE key)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_SignRecoverInit) (handle, mechanism, key);
+}
+
+static CK_RV
+proxy_C_SignRecover (CK_SESSION_HANDLE handle, CK_BYTE_PTR data, CK_ULONG data_len,
+ CK_BYTE_PTR signature, CK_ULONG_PTR signature_len)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_SignRecover) (handle, data, data_len, signature, signature_len);
+}
+
+static CK_RV
+proxy_C_VerifyInit (CK_SESSION_HANDLE handle, CK_MECHANISM_PTR mechanism,
+ CK_OBJECT_HANDLE key)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_VerifyInit) (handle, mechanism, key);
+}
+
+static CK_RV
+proxy_C_Verify (CK_SESSION_HANDLE handle, CK_BYTE_PTR data, CK_ULONG data_len,
+ CK_BYTE_PTR signature, CK_ULONG signature_len)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_Verify) (handle, data, data_len, signature, signature_len);
+}
+
+static CK_RV
+proxy_C_VerifyUpdate (CK_SESSION_HANDLE handle, CK_BYTE_PTR part, CK_ULONG part_len)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_VerifyUpdate) (handle, part, part_len);
+}
+
+static CK_RV
+proxy_C_VerifyFinal (CK_SESSION_HANDLE handle, CK_BYTE_PTR signature,
+ CK_ULONG signature_len)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_VerifyFinal) (handle, signature, signature_len);
+}
+
+static CK_RV
+proxy_C_VerifyRecoverInit (CK_SESSION_HANDLE handle, CK_MECHANISM_PTR mechanism,
+ CK_OBJECT_HANDLE key)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_VerifyRecoverInit) (handle, mechanism, key);
+}
+
+static CK_RV
+proxy_C_VerifyRecover (CK_SESSION_HANDLE handle, CK_BYTE_PTR signature,
+ CK_ULONG signature_len, CK_BYTE_PTR data, CK_ULONG_PTR data_len)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_VerifyRecover) (handle, signature, signature_len, data, data_len);
+}
+
+static CK_RV
+proxy_C_DigestEncryptUpdate (CK_SESSION_HANDLE handle, CK_BYTE_PTR part,
+ CK_ULONG part_len, CK_BYTE_PTR enc_part,
+ CK_ULONG_PTR enc_part_len)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_DigestEncryptUpdate) (handle, part, part_len, enc_part, enc_part_len);
+}
+
+static CK_RV
+proxy_C_DecryptDigestUpdate (CK_SESSION_HANDLE handle, CK_BYTE_PTR enc_part,
+ CK_ULONG enc_part_len, CK_BYTE_PTR part,
+ CK_ULONG_PTR part_len)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_DecryptDigestUpdate) (handle, enc_part, enc_part_len, part, part_len);
+}
+
+static CK_RV
+proxy_C_SignEncryptUpdate (CK_SESSION_HANDLE handle, CK_BYTE_PTR part,
+ CK_ULONG part_len, CK_BYTE_PTR enc_part,
+ CK_ULONG_PTR enc_part_len)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_SignEncryptUpdate) (handle, part, part_len, enc_part, enc_part_len);
+}
+
+static CK_RV
+proxy_C_DecryptVerifyUpdate (CK_SESSION_HANDLE handle, CK_BYTE_PTR enc_part,
+ CK_ULONG enc_part_len, CK_BYTE_PTR part,
+ CK_ULONG_PTR part_len)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_DecryptVerifyUpdate) (handle, enc_part, enc_part_len, part, part_len);
+}
+
+static CK_RV
+proxy_C_GenerateKey (CK_SESSION_HANDLE handle, CK_MECHANISM_PTR mechanism,
+ CK_ATTRIBUTE_PTR template, CK_ULONG count,
+ CK_OBJECT_HANDLE_PTR key)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_GenerateKey) (handle, mechanism, template, count, key);
+}
+
+static CK_RV
+proxy_C_GenerateKeyPair (CK_SESSION_HANDLE handle, 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)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_GenerateKeyPair) (handle, mechanism, pub_template, pub_count, priv_template, priv_count, pub_key, priv_key);
+}
+
+static CK_RV
+proxy_C_WrapKey (CK_SESSION_HANDLE handle, CK_MECHANISM_PTR mechanism,
+ CK_OBJECT_HANDLE wrapping_key, CK_OBJECT_HANDLE key,
+ CK_BYTE_PTR wrapped_key, CK_ULONG_PTR wrapped_key_len)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_WrapKey) (handle, mechanism, wrapping_key, key, wrapped_key, wrapped_key_len);
+}
+
+static CK_RV
+proxy_C_UnwrapKey (CK_SESSION_HANDLE handle, 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)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_UnwrapKey) (handle, mechanism, unwrapping_key, wrapped_key, wrapped_key_len, template, count, key);
+}
+
+static CK_RV
+proxy_C_DeriveKey (CK_SESSION_HANDLE handle, CK_MECHANISM_PTR mechanism,
+ CK_OBJECT_HANDLE base_key, CK_ATTRIBUTE_PTR template,
+ CK_ULONG count, CK_OBJECT_HANDLE_PTR key)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_DeriveKey) (handle, mechanism, base_key, template, count, key);
+}
+
+static CK_RV
+proxy_C_SeedRandom (CK_SESSION_HANDLE handle, CK_BYTE_PTR seed, CK_ULONG seed_len)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_SeedRandom) (handle, seed, seed_len);
+}
+
+static CK_RV
+proxy_C_GenerateRandom (CK_SESSION_HANDLE handle, CK_BYTE_PTR random_data,
+ CK_ULONG random_len)
+{
+ Mapping map;
+ CK_RV rv;
+
+ rv = map_session_to_real (&handle, &map, NULL);
+ if (rv != CKR_OK)
+ return rv;
+ return (map.funcs->C_GenerateRandom) (handle, random_data, random_len);
+}
+
+/* --------------------------------------------------------------------
+ * MODULE ENTRY POINT
+ */
+
+static CK_FUNCTION_LIST proxy_function_list = {
+ { CRYPTOKI_VERSION_MAJOR, CRYPTOKI_VERSION_MINOR }, /* version */
+ proxy_C_Initialize,
+ proxy_C_Finalize,
+ proxy_C_GetInfo,
+ proxy_C_GetFunctionList,
+ proxy_C_GetSlotList,
+ proxy_C_GetSlotInfo,
+ proxy_C_GetTokenInfo,
+ proxy_C_GetMechanismList,
+ proxy_C_GetMechanismInfo,
+ proxy_C_InitToken,
+ proxy_C_InitPIN,
+ proxy_C_SetPIN,
+ proxy_C_OpenSession,
+ proxy_C_CloseSession,
+ proxy_C_CloseAllSessions,
+ proxy_C_GetSessionInfo,
+ proxy_C_GetOperationState,
+ proxy_C_SetOperationState,
+ proxy_C_Login,
+ proxy_C_Logout,
+ proxy_C_CreateObject,
+ proxy_C_CopyObject,
+ proxy_C_DestroyObject,
+ proxy_C_GetObjectSize,
+ proxy_C_GetAttributeValue,
+ proxy_C_SetAttributeValue,
+ proxy_C_FindObjectsInit,
+ proxy_C_FindObjects,
+ proxy_C_FindObjectsFinal,
+ proxy_C_EncryptInit,
+ proxy_C_Encrypt,
+ proxy_C_EncryptUpdate,
+ proxy_C_EncryptFinal,
+ proxy_C_DecryptInit,
+ proxy_C_Decrypt,
+ proxy_C_DecryptUpdate,
+ proxy_C_DecryptFinal,
+ proxy_C_DigestInit,
+ proxy_C_Digest,
+ proxy_C_DigestUpdate,
+ proxy_C_DigestKey,
+ proxy_C_DigestFinal,
+ proxy_C_SignInit,
+ proxy_C_Sign,
+ proxy_C_SignUpdate,
+ proxy_C_SignFinal,
+ proxy_C_SignRecoverInit,
+ proxy_C_SignRecover,
+ proxy_C_VerifyInit,
+ proxy_C_Verify,
+ proxy_C_VerifyUpdate,
+ proxy_C_VerifyFinal,
+ proxy_C_VerifyRecoverInit,
+ proxy_C_VerifyRecover,
+ proxy_C_DigestEncryptUpdate,
+ proxy_C_DecryptDigestUpdate,
+ proxy_C_SignEncryptUpdate,
+ proxy_C_DecryptVerifyUpdate,
+ proxy_C_GenerateKey,
+ proxy_C_GenerateKeyPair,
+ proxy_C_WrapKey,
+ proxy_C_UnwrapKey,
+ proxy_C_DeriveKey,
+ proxy_C_SeedRandom,
+ proxy_C_GenerateRandom,
+ proxy_C_GetFunctionStatus,
+ proxy_C_CancelFunction,
+ proxy_C_WaitForSlotEvent
+};
+
+#ifdef OS_WIN32
+__declspec(dllexport)
+#endif
+
+CK_RV
+C_GetFunctionList (CK_FUNCTION_LIST_PTR_PTR list)
+{
+ _p11_library_init_once ();
+ return proxy_C_GetFunctionList (list);
+}
diff --git a/p11-kit/mapping.c b/p11-kit/mapping.c
new file mode 100644
index 0000000..e4fe2dc
--- /dev/null
+++ b/p11-kit/mapping.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2008 Stefan Walter
+ * Copyright (C) 2011 Collabora Ltd.
+ * Copyright (C) 2012 Red Hat Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ * * The names of contributors to this software may not be
+ * used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Author: Stef Walter <stefw@collabora.co.uk>
+ */
+
+#include "config.h"
+
+#if 0
+#define DEBUG_FLAG DEBUG_PROXY
+#include "debug.h"
+#include "hashmap.h"
+#define CRYPTOKI_EXPORTS
+#include "pkcs11.h"
+#include "p11-kit.h"
+#include "private.h"
+#include "util.h"
+
+#include <sys/types.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#endif
+
+/* Start wrap slots slightly higher for testing */
+#define MAPPING_OFFSET 0x10
+#define FIRST_HANDLE 0x10
+
+typedef struct _Mapping {
+ CK_SLOT_ID wrap_slot;
+ CK_SLOT_ID real_slot;
+ CK_FUNCTION_LIST_PTR funcs;
+} Mapping;
+
+typedef struct _Session {
+ CK_SESSION_HANDLE wrap_session;
+ CK_SESSION_HANDLE real_session;
+ CK_SLOT_ID wrap_slot;
+} Session;
+
+/*
+ * Shared data between threads, protected by the mutex, a structure so
+ * we can audit thread safety easier.
+ */
+static struct _Mappings {
+ Mapping *mappings;
+ unsigned int n_mappings;
+ int mappings_refs;
+ hashmap *sessions;
+ CK_ULONG last_handle;
+} gl = { NULL, 0, 0, NULL, FIRST_HANDLE };
+
+static CK_RV
+map_slot_unlocked (CK_SLOT_ID slot,
+ Mapping *mapping)
+{
+ assert (mapping);
+
+ if (slot < MAPPING_OFFSET)
+ return CKR_SLOT_ID_INVALID;
+ slot -= MAPPING_OFFSET;
+
+ if (slot > gl.n_mappings) {
+ return CKR_SLOT_ID_INVALID;
+ } else {
+ assert (gl.mappings);
+ memcpy (mapping, &gl.mappings[slot], sizeof (Mapping));
+ return CKR_OK;
+ }
+}
+
+static CK_RV
+map_slot_to_real (CK_SLOT_ID_PTR slot,
+ Mapping *mapping)
+{
+ CK_RV rv;
+
+ assert (mapping);
+
+ _p11_lock ();
+
+ if (!gl.mappings)
+ rv = CKR_CRYPTOKI_NOT_INITIALIZED;
+ else
+ rv = map_slot_unlocked (*slot, mapping);
+ if (rv == CKR_OK)
+ *slot = mapping->real_slot;
+
+ _p11_unlock ();
+
+ return rv;
+}
+
+static CK_RV
+map_session_to_real (CK_SESSION_HANDLE_PTR handle,
+ Mapping *mapping,
+ Session *session)
+{
+ CK_RV rv = CKR_OK;
+ Session *sess;
+
+ assert (handle);
+ assert (mapping);
+
+ _p11_lock ();
+
+ if (!gl.sessions) {
+ rv = CKR_CRYPTOKI_NOT_INITIALIZED;
+ } else {
+ assert (gl.sessions);
+ sess = _p11_hash_get (gl.sessions, handle);
+ if (sess != NULL) {
+ *handle = sess->real_session;
+ rv = map_slot_unlocked (sess->wrap_slot, mapping);
+ if (session != NULL)
+ memcpy (session, sess, sizeof (Session));
+ } else {
+ rv = CKR_SESSION_HANDLE_INVALID;
+ }
+ }
+
+ _p11_unlock ();
+
+ return rv;
+}
+
+static void
+finalize_mappings_unlocked (void)
+{
+ assert (gl.mappings_refs);
+
+ if (--gl.mappings_refs)
+ return;
+
+ /* No more mappings */
+ free (gl.mappings);
+ gl.mappings = NULL;
+ gl.n_mappings = 0;
+
+ /* no more sessions */
+ _p11_hash_free (gl.sessions);
+ gl.sessions = NULL;
+}
+
+static CK_RV
+initialize_mappings_unlocked_reentrant (void)
+{
+ Mapping *mappings = NULL;
+ int n_mappings = 0;
+ CK_SLOT_ID_PTR slots;
+ CK_ULONG i, count;
+ CK_RV rv = CKR_OK;
+
+ assert (!gl.mappings);
+
+#if 0
+ /* Another thread raced us here due to above reentrancy */
+ if (gl.mappings) {
+ free (mappings);
+ return CKR_OK;
+ }
+
+ assert (!gl.sessions);
+ gl.mappings = mappings;
+ gl.n_mappings = n_mappings;
+ gl.sessions = _p11_hash_create (_p11_hash_ulongptr_hash, _p11_hash_ulongptr_equal, NULL, free);
+ ++gl.mappings_refs;
+#endif
+
+ /* Any cleanup necessary for failure will happen at caller */
+ return rv;
+}
diff --git a/p11-kit/modules.c b/p11-kit/modules.c
index 547ee02..2521c83 100644
--- a/p11-kit/modules.c
+++ b/p11-kit/modules.c
@@ -595,8 +595,10 @@ reinitialize_after_fork (void)
if (gl.modules) {
_p11_hash_iterate (gl.modules, &iter);
- while (_p11_hash_next (&iter, NULL, (void **)&mod))
- mod->initialize_called = 0;
+ while (_p11_hash_next (&iter, NULL, (void **)&mod)) {
+ if (mod->initialize_called)
+ mod->initialize_called = 0;
+ }
}
_p11_unlock ();
diff --git a/p11-kit/rpc-client.c b/p11-kit/rpc-client.c
new file mode 100644
index 0000000..749103d
--- /dev/null
+++ b/p11-kit/rpc-client.c
@@ -0,0 +1,2227 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* client.c - a PKCS#11 module which communicates with another process
+
+ Copyright (C) 2012 Red Hat Inc.
+
+ 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 <stefw@gnome.org>
+*/
+
+#include "config.h"
+
+#include "rpc-private.h"
+
+#include "pkcs11.h"
+
+#include "egg/egg-unix-credentials.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;
+
+/* 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 = NULL;
+
+/* 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) _p11_rpc_debug x
+#else
+#define debug(x)
+#endif
+#define warning(x) _p11_rpc_warn x
+
+#define return_val_if_fail(x, v) \
+ if (!(x)) { _p11_rpc_warn ("'%s' not true at %s", #x, __func__); return v; }
+
+void
+_p11_rpc_log (const char *line)
+{
+ fprintf (stderr, "%s\n", line);
+}
+
+/* -----------------------------------------------------------------------------
+ * MODULE ARGUMENTS
+ */
+
+static void
+parse_argument (char *arg)
+{
+ char *value;
+
+ value = arg + strcspn (arg, ":=");
+ if (!*value)
+ value = NULL;
+ else
+ *(value++) = 0;
+
+ /* Setup the socket path from the arguments */
+ if (strcmp (arg, "socket") == 0) {
+ free (pkcs11_socket_path);
+ pkcs11_socket_path = strdup (value);
+ } else {
+ warning (("unrecognized argument: %s", arg));
+ }
+}
+
+static void
+parse_arguments (const char *string)
+{
+ char quote = '\0';
+ char *src, *dup, *at, *arg;
+
+ if (!string)
+ return;
+
+ src = dup = strdup (string);
+ if (!dup) {
+ warning (("couldn't allocate memory for argument string"));
+ return;
+ }
+
+ arg = at = src;
+ for (src = dup; *src; src++) {
+
+ /* Matching quote */
+ if (quote == *src) {
+ quote = '\0';
+
+ /* Inside of quotes */
+ } else if (quote != '\0') {
+ if (*src == '\\') {
+ *at++ = *src++;
+ if (!*src) {
+ warning (("couldn't parse argument string: %s", string));
+ goto done;
+ }
+ if (*src != quote)
+ *at++ = '\\';
+ }
+ *at++ = *src;
+
+ /* Space, not inside of quotes */
+ } else if (isspace(*src)) {
+ *at = 0;
+ parse_argument (arg);
+ arg = at;
+
+ /* Other character outside of quotes */
+ } else {
+ switch (*src) {
+ case '\'':
+ case '"':
+ quote = *src;
+ break;
+ case '\\':
+ *at++ = *src++;
+ if (!*src) {
+ warning (("couldn't parse argument string: %s", string));
+ goto done;
+ }
+ /* fall through */
+ default:
+ *at++ = *src;
+ break;
+ }
+ }
+ }
+
+
+ if (at != arg)
+ parse_argument (arg);
+
+done:
+ free (dup);
+}
+
+/* -----------------------------------------------------------------------------
+ * 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 */
+ RpcMessage *req; /* The current request */
+ RpcMessage *resp; /* The current response */
+ int call_status;
+ struct _CallState *next; /* For pooling of completed sockets */
+} CallState;
+
+/* Maximum number of idle calls */
+#define MAX_CALL_STATE_POOL 8
+
+/* All call unused call states are in this list */
+static CallState *call_state_pool = NULL;
+static unsigned int n_call_state_pool = 0;
+
+/* Mutex to protect above call state list */
+static pthread_mutex_t call_state_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/* Allocator for call session buffers */
+static void*
+call_allocator (void* p, size_t 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);
+
+ if (!pkcs11_socket_path)
+ return CKR_DEVICE_REMOVED;
+
+ 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;
+ }
+
+ if (egg_unix_credentials_write (sock) < 0) {
+ close (sock);
+ warning (("couldn't send socket credentials: %s", strerror (errno)));
+ return CKR_DEVICE_ERROR;
+ }
+
+ 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);
+
+ _p11_rpc_message_free (cs->req);
+ _p11_rpc_message_free (cs->resp);
+
+ free (cs);
+
+ debug (("destroyed state"));
+ }
+}
+
+static CK_RV
+call_lookup (CallState **ret)
+{
+ CallState *cs = NULL;
+ CK_RV rv;
+
+ assert (ret);
+
+ pthread_mutex_lock (&call_state_mutex);
+
+ /* Pop one from the pool if possible */
+ if (call_state_pool != NULL) {
+ cs = call_state_pool;
+ call_state_pool = cs->next;
+ cs->next = NULL;
+ assert (n_call_state_pool > 0);
+ --n_call_state_pool;
+ }
+
+ pthread_mutex_unlock (&call_state_mutex);
+
+ if (cs == NULL) {
+ cs = calloc(1, sizeof (CallState));
+ if (cs == NULL)
+ return CKR_HOST_MEMORY;
+ cs->socket = -1;
+ cs->call_status = CALL_INVALID;
+
+ /* Try to connect the call */
+ rv = call_connect (cs);
+ if (rv != CKR_OK) {
+ free (cs);
+ return rv;
+ }
+ }
+
+ assert (cs->call_status == CALL_READY);
+ assert (cs->socket != -1);
+ assert (cs->next == NULL);
+ *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 = _p11_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 */
+ _p11_rpc_message_reset (cs->req);
+ if (!_p11_rpc_message_prep (cs->req, call_id, 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)
+{
+ RpcMessage *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 = _p11_rpc_message_new (call_allocator);
+ if (!cs->resp) {
+ warning (("couldn't allocate response buffer: out of memory"));
+ return CKR_HOST_MEMORY;
+ }
+ }
+ _p11_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 */
+ _p11_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 = _p11_buffer_decode_uint32 (buf);
+ if (!_p11_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;
+
+ _p11_buffer_add_empty (&resp->buffer, len);
+ if (!_p11_rpc_message_parse (resp, 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 (_p11_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 (_p11_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 == RPC_CALL_ERROR) {
+
+ if (!_p11_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 (!_p11_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 (_p11_rpc_message_buffer_error (cs->resp)) {
+ warning (("invalid response from gnome-keyring-daemon: bad argument data"));
+ ret = CKR_GENERAL_ERROR;
+ } else {
+ /* Double check that the signature matched our decoding */
+ assert (_p11_rpc_message_is_verified (cs->resp));
+ }
+ }
+ }
+
+ /* Certain error codes cause us to discard the conenction */
+ if (ret != CKR_DEVICE_ERROR && ret != CKR_DEVICE_REMOVED && cs->socket != -1) {
+
+ /* Try and stash it away for later use */
+ pthread_mutex_lock (&call_state_mutex);
+
+ if (n_call_state_pool < MAX_CALL_STATE_POOL) {
+ cs->call_status = CALL_READY;
+ assert (cs->next == NULL);
+ cs->next = call_state_pool;
+ call_state_pool = cs;
+ ++n_call_state_pool;
+ cs = NULL;
+ }
+
+ pthread_mutex_unlock (&call_state_mutex);
+ }
+
+ if (cs != NULL)
+ call_destroy (cs);
+
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * MODULE SPECIFIC PROTOCOL CODE
+ */
+
+static CK_RV
+proto_read_attribute_array (RpcMessage *msg, CK_ATTRIBUTE_PTR arr, CK_ULONG len)
+{
+ uint32_t i, num, value, type;
+ CK_ATTRIBUTE_PTR attr;
+ const unsigned char *attrval;
+ size_t attrlen;
+ unsigned char validity;
+ CK_RV ret;
+
+ assert (len);
+ assert (msg);
+
+ /* Make sure this is in the right order */
+ assert (!msg->signature || _p11_rpc_message_verify_part (msg, "aA"));
+
+ /* Get the number of items. We need this value to be correct */
+ if (!_p11_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 */
+ _p11_buffer_get_uint32 (&msg->buffer, msg->parsed,
+ &msg->parsed, &type);
+
+ /* Attribute validity */
+ _p11_buffer_get_byte (&msg->buffer, msg->parsed,
+ &msg->parsed, &validity);
+
+ /* And the data itself */
+ if (validity) {
+ if (_p11_buffer_get_uint32 (&msg->buffer, msg->parsed, &msg->parsed, &value) &&
+ _p11_buffer_get_byte_array (&msg->buffer, msg->parsed, &msg->parsed, &attrval, &attrlen)) {
+ if (attrval && value != attrlen) {
+ warning (("attribute length does not match attribute data"));
+ return PARSE_ERROR;
+ }
+ attrlen = value;
+ }
+ }
+
+ /* Don't act on this data unless no errors */
+ if (_p11_buffer_has_error (&msg->buffer))
+ break;
+
+ /* Try and stuff it in the output data */
+ if (arr) {
+ attr = &(arr[i]);
+ if (attr->type != type) {
+ warning (("returned attributes in invalid order"));
+ return PARSE_ERROR;
+ }
+
+ if (validity) {
+ /* Just requesting the attribute size */
+ if (!attr->pValue) {
+ attr->ulValueLen = attrlen;
+
+ /* Wants attribute data, but too small */
+ } else if (attr->ulValueLen < attrlen) {
+ attr->ulValueLen = attrlen;
+ ret = CKR_BUFFER_TOO_SMALL;
+
+ /* Wants attribute data, value is null */
+ } else if (attrval == NULL) {
+ attr->ulValueLen = 0;
+
+ /* Wants attribute data, enough space */
+ } else {
+ attr->ulValueLen = attrlen;
+ memcpy (attr->pValue, attrval, attrlen);
+ }
+
+ /* Not a valid attribute */
+ } else {
+ attr->ulValueLen = ((CK_ULONG)-1);
+ }
+ }
+ }
+
+ if (_p11_buffer_has_error (&msg->buffer))
+ return PARSE_ERROR;
+
+ /* Read in the code that goes along with these attributes */
+ if (!_p11_rpc_message_read_ulong (msg, &ret))
+ return PARSE_ERROR;
+
+ return ret;
+}
+
+static CK_RV
+proto_read_byte_array (RpcMessage *msg, CK_BYTE_PTR arr,
+ CK_ULONG_PTR len, CK_ULONG max)
+{
+ const unsigned char *val;
+ unsigned char valid;
+ uint32_t length;
+ size_t vlen;
+
+ assert (len);
+ assert (msg);
+
+ /* Make sure this is in the right order */
+ assert (!msg->signature || _p11_rpc_message_verify_part (msg, "ay"));
+
+ /* A single byte which determines whether valid or not */
+ if (!_p11_buffer_get_byte (&msg->buffer, msg->parsed, &msg->parsed, &valid))
+ return PARSE_ERROR;
+
+ /* If not valid, then just the length is encoded, this can signify CKR_BUFFER_TOO_SMALL */
+ if (!valid) {
+ if (!_p11_buffer_get_uint32 (&msg->buffer, msg->parsed, &msg->parsed, &length))
+ return PARSE_ERROR;
+
+ *len = length;
+
+ if (arr)
+ return CKR_BUFFER_TOO_SMALL;
+ else
+ return CKR_OK;
+ }
+
+ /* Get the actual bytes */
+ if (!_p11_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 (RpcMessage *msg, CK_ULONG_PTR arr,
+ CK_ULONG_PTR len, CK_ULONG max)
+{
+ uint32_t i, num;
+ uint64_t val;
+ unsigned char valid;
+
+ assert (len);
+ assert (msg);
+
+ /* Make sure this is in the right order */
+ assert (!msg->signature || _p11_rpc_message_verify_part (msg, "au"));
+
+ /* A single byte which determines whether valid or not */
+ if (!_p11_buffer_get_byte (&msg->buffer, msg->parsed, &msg->parsed, &valid))
+ return PARSE_ERROR;
+
+ /* Get the number of items. */
+ if (!_p11_buffer_get_uint32 (&msg->buffer, msg->parsed, &msg->parsed, &num))
+ return PARSE_ERROR;
+
+ *len = num;
+
+ /* If not valid, then just the length is encoded, this can signify CKR_BUFFER_TOO_SMALL */
+ if (!valid) {
+ if (arr)
+ return CKR_BUFFER_TOO_SMALL;
+ else
+ return CKR_OK;
+ }
+
+ if (max < num)
+ return CKR_BUFFER_TOO_SMALL;
+
+ /* We need to go ahead and read everything in all cases */
+ for (i = 0; i < num; ++i) {
+ _p11_buffer_get_uint64 (&msg->buffer, msg->parsed, &msg->parsed, &val);
+ if (arr)
+ arr[i] = (CK_ULONG)val;
+ }
+
+ return _p11_buffer_has_error (&msg->buffer) ? PARSE_ERROR : CKR_OK;
+}
+
+static CK_RV
+proto_write_mechanism (RpcMessage *msg, CK_MECHANISM_PTR mech)
+{
+ assert (msg);
+ assert (mech);
+
+ /* Make sure this is in the right order */
+ assert (!msg->signature || _p11_rpc_message_verify_part (msg, "M"));
+
+ /* The mechanism type */
+ _p11_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 (_p11_rpc_mechanism_has_no_parameters (mech->mechanism))
+ _p11_buffer_add_byte_array (&msg->buffer, NULL, 0);
+ else if (_p11_rpc_mechanism_has_sane_parameters (mech->mechanism))
+ _p11_buffer_add_byte_array (&msg->buffer, mech->pParameter,
+ mech->ulParameterLen);
+ else
+ return CKR_MECHANISM_INVALID;
+
+ return _p11_buffer_has_error (&msg->buffer) ? CKR_HOST_MEMORY : CKR_OK;
+}
+
+static CK_RV
+proto_read_info (RpcMessage *msg, CK_INFO_PTR info)
+{
+ assert (msg);
+ assert (info);
+
+ if (!_p11_rpc_message_read_version (msg, &info->cryptokiVersion) ||
+ !_p11_rpc_message_read_space_string (msg, info->manufacturerID, 32) ||
+ !_p11_rpc_message_read_ulong (msg, &info->flags) ||
+ !_p11_rpc_message_read_space_string (msg, info->libraryDescription, 32) ||
+ !_p11_rpc_message_read_version (msg, &info->libraryVersion))
+ return PARSE_ERROR;
+
+ return CKR_OK;
+}
+
+static CK_RV
+proto_read_slot_info (RpcMessage *msg, CK_SLOT_INFO_PTR info)
+{
+ assert (msg);
+ assert (info);
+
+ if (!_p11_rpc_message_read_space_string (msg, info->slotDescription, 64) ||
+ !_p11_rpc_message_read_space_string (msg, info->manufacturerID, 32) ||
+ !_p11_rpc_message_read_ulong (msg, &info->flags) ||
+ !_p11_rpc_message_read_version (msg, &info->hardwareVersion) ||
+ !_p11_rpc_message_read_version (msg, &info->firmwareVersion))
+ return PARSE_ERROR;
+
+ return CKR_OK;
+}
+
+static CK_RV
+proto_read_token_info (RpcMessage *msg, CK_TOKEN_INFO_PTR info)
+{
+ assert (msg);
+ assert (info);
+
+ if (!_p11_rpc_message_read_space_string (msg, info->label, 32) ||
+ !_p11_rpc_message_read_space_string (msg, info->manufacturerID, 32) ||
+ !_p11_rpc_message_read_space_string (msg, info->model, 16) ||
+ !_p11_rpc_message_read_space_string (msg, info->serialNumber, 16) ||
+ !_p11_rpc_message_read_ulong (msg, &info->flags) ||
+ !_p11_rpc_message_read_ulong (msg, &info->ulMaxSessionCount) ||
+ !_p11_rpc_message_read_ulong (msg, &info->ulSessionCount) ||
+ !_p11_rpc_message_read_ulong (msg, &info->ulMaxRwSessionCount) ||
+ !_p11_rpc_message_read_ulong (msg, &info->ulRwSessionCount) ||
+ !_p11_rpc_message_read_ulong (msg, &info->ulMaxPinLen) ||
+ !_p11_rpc_message_read_ulong (msg, &info->ulMinPinLen) ||
+ !_p11_rpc_message_read_ulong (msg, &info->ulTotalPublicMemory) ||
+ !_p11_rpc_message_read_ulong (msg, &info->ulFreePublicMemory) ||
+ !_p11_rpc_message_read_ulong (msg, &info->ulTotalPrivateMemory) ||
+ !_p11_rpc_message_read_ulong (msg, &info->ulFreePrivateMemory) ||
+ !_p11_rpc_message_read_version (msg, &info->hardwareVersion) ||
+ !_p11_rpc_message_read_version (msg, &info->firmwareVersion) ||
+ !_p11_rpc_message_read_space_string (msg, info->utcTime, 16))
+ return PARSE_ERROR;
+
+ return CKR_OK;
+}
+
+static CK_RV
+proto_read_mechanism_info (RpcMessage *msg, CK_MECHANISM_INFO_PTR info)
+{
+ assert (msg);
+ assert (info);
+
+ if (!_p11_rpc_message_read_ulong (msg, &info->ulMinKeySize) ||
+ !_p11_rpc_message_read_ulong (msg, &info->ulMaxKeySize) ||
+ !_p11_rpc_message_read_ulong (msg, &info->flags))
+ return PARSE_ERROR;
+
+ return CKR_OK;
+}
+
+static CK_RV
+proto_read_sesssion_info (RpcMessage *msg, CK_SESSION_INFO_PTR info)
+{
+ assert (msg);
+ assert (info);
+
+ if (!_p11_rpc_message_read_ulong (msg, &info->slotID) ||
+ !_p11_rpc_message_read_ulong (msg, &info->state) ||
+ !_p11_rpc_message_read_ulong (msg, &info->flags) ||
+ !_p11_rpc_message_read_ulong (msg, &info->ulDeviceError))
+ return PARSE_ERROR;
+
+ return CKR_OK;
+}
+
+/* -------------------------------------------------------------------
+ * CALL MACROS
+ */
+
+#define BEGIN_CALL_OR(call_id, if_no_daemon) \
+ 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_DEVICE_REMOVED) return (if_no_daemon); \
+ if (_ret != CKR_OK) return _ret; \
+ _ret = call_prepare (_cs, 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 (!_p11_rpc_message_write_byte (_cs->req, val)) \
+ { _ret = CKR_HOST_MEMORY; goto _cleanup; }
+
+#define IN_ULONG(val) \
+ if (!_p11_rpc_message_write_ulong (_cs->req, val)) \
+ { _ret = CKR_HOST_MEMORY; goto _cleanup; }
+
+#define IN_STRING(val) \
+ if (!_p11_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 (!_p11_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; goto _cleanup; } \
+ if (!_p11_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 (!_p11_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; goto _cleanup; }\
+ if (!_p11_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 (!_p11_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 (!_p11_rpc_message_write_attribute_array (_cs->req, (arr), (num))) \
+ { _ret = CKR_HOST_MEMORY; goto _cleanup; }
+
+#define IN_MECHANISM_TYPE(val) \
+ if(!_p11_rpc_mechanism_is_supported (val)) \
+ { _ret = CKR_MECHANISM_INVALID; goto _cleanup; } \
+ if (!_p11_rpc_message_write_ulong (_cs->req, val)) \
+ { _ret = CKR_HOST_MEMORY; goto _cleanup; }
+
+#define IN_MECHANISM(val) \
+ if (val == NULL) \
+ { _ret = CKR_ARGUMENTS_BAD; goto _cleanup; } \
+ _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 && !_p11_rpc_message_read_ulong (_cs->resp, val)) \
+ _ret = PARSE_ERROR;
+
+#define OUT_BYTE_ARRAY(arr, len) \
+ if (len == NULL) \
+ _ret = CKR_ARGUMENTS_BAD; \
+ if (_ret == CKR_OK) \
+ _ret = proto_read_byte_array (_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) \
+ _p11_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;
+
+ debug (("C_Initialize: enter"));
+
+#ifdef _DEBUG
+ 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;
+ }
+
+ /*
+ * We support setting the socket path and other arguments from from the
+ * pReserved pointer, similar to how NSS PKCS#11 components are initialized.
+ */
+ if (args->pReserved)
+ parse_arguments ((const char*)args->pReserved);
+ }
+
+ 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;
+ }
+ }
+
+ /* Lookup the socket path, append '/pkcs11' */
+ if (pkcs11_socket_path == NULL) {
+ path = getenv ("GNOME_KEYRING_CONTROL");
+ if (path && path[0]) {
+ pkcs11_socket_path = malloc (strlen (path) + strlen ("/pkcs11") + 1);
+ if (pkcs11_socket_path == NULL) {
+ warning (("can't malloc memory"));
+ ret = CKR_HOST_MEMORY;
+ goto done;
+ }
+ sprintf (pkcs11_socket_path, "%s/pkcs11", path);
+ }
+ }
+
+ /* Call through and initialize the daemon if available */
+ if (pkcs11_socket_path != NULL) {
+ ret = call_lookup (&cs);
+ if (ret == CKR_OK) {
+ ret = call_prepare (cs, RPC_CALL_C_Initialize);
+ if (ret == CKR_OK)
+ if (!_p11_rpc_message_write_byte_array (cs->req, RPC_HANDSHAKE, RPC_HANDSHAKE_LEN))
+ ret = CKR_HOST_MEMORY;
+ if (ret == CKR_OK)
+ ret = call_run (cs);
+ 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;
+ free (pkcs11_socket_path);
+ pkcs11_socket_path = NULL;
+ }
+
+ 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 = CKR_OK;
+
+ 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);
+
+ if (pkcs11_socket_path != NULL) {
+ ret = call_lookup (&cs);
+ if (ret == CKR_OK) {
+ ret = call_prepare (cs, 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;
+ free (pkcs11_socket_path);
+ pkcs11_socket_path = NULL;
+
+ pthread_mutex_unlock (&init_mutex);
+
+ debug (("C_Finalize: %d", CKR_OK));
+ return CKR_OK;
+}
+
+static CK_RV
+fill_stand_in_info (CK_INFO_PTR info)
+{
+ static CK_INFO stand_in_info = {
+ { CRYPTOKI_VERSION_MAJOR, CRYPTOKI_VERSION_MINOR },
+ "GNOME Keyring ",
+ 0,
+ "GNOME Keyring (without daemon) ",
+ { 1, 1 },
+ };
+ memcpy (info, &stand_in_info, sizeof (CK_INFO));
+ return CKR_OK;
+
+}
+
+static CK_RV
+rpc_C_GetInfo (CK_INFO_PTR info)
+{
+ return_val_if_fail (info, CKR_ARGUMENTS_BAD);
+
+ BEGIN_CALL_OR (C_GetInfo, fill_stand_in_info (info));
+ 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 rpc_p11_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_OR (C_GetSlotList, (*count = 0, CKR_OK));
+ IN_BYTE (token_present);
+ IN_ULONG_BUFFER (slot_list, count);
+ PROCESS_CALL;
+ OUT_ULONG_ARRAY (slot_list, count);
+ END_CALL;
+}
+
+static CK_RV
+rpc_C_GetSlotInfo (CK_SLOT_ID id, CK_SLOT_INFO_PTR info)
+{
+ return_val_if_fail (info, CKR_ARGUMENTS_BAD);
+
+ BEGIN_CALL_OR (C_GetSlotInfo, CKR_SLOT_ID_INVALID);
+ 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_OR (C_GetTokenInfo, CKR_SLOT_ID_INVALID);
+ 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_OR (C_GetMechanismList, CKR_SLOT_ID_INVALID);
+ 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_OR (C_GetMechanismInfo, CKR_SLOT_ID_INVALID);
+ 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_OR (C_InitToken, CKR_SLOT_ID_INVALID);
+ 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_OR (C_WaitForSlotEvent, CKR_DEVICE_REMOVED);
+ 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_OR (C_OpenSession, CKR_SLOT_ID_INVALID);
+ 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_OR (C_CloseSession, CKR_SESSION_HANDLE_INVALID);
+ IN_ULONG (session);
+ PROCESS_CALL;
+ END_CALL;
+}
+
+static CK_RV
+rpc_C_CloseAllSessions (CK_SLOT_ID id)
+{
+ BEGIN_CALL_OR (C_CloseAllSessions, CKR_SLOT_ID_INVALID);
+ IN_ULONG (id);
+ PROCESS_CALL;
+ END_CALL;
+}
+
+static CK_RV
+rpc_C_GetFunctionStatus (CK_SESSION_HANDLE session)
+{
+ BEGIN_CALL_OR (C_GetFunctionStatus, CKR_SESSION_HANDLE_INVALID);
+ IN_ULONG (session);
+ PROCESS_CALL;
+ END_CALL;
+}
+
+static CK_RV
+rpc_C_CancelFunction (CK_SESSION_HANDLE session)
+{
+ BEGIN_CALL_OR (C_CancelFunction, CKR_SESSION_HANDLE_INVALID);
+ 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_OR (C_GetSessionInfo, CKR_SESSION_HANDLE_INVALID);
+ 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_OR (C_InitPIN, CKR_SESSION_HANDLE_INVALID);
+ 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_OR (C_SetPIN, CKR_SESSION_HANDLE_INVALID);
+ 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_OR (C_GetOperationState, CKR_SESSION_HANDLE_INVALID);
+ IN_ULONG (session);
+ IN_BYTE_BUFFER (operation_state, operation_state_len);
+ PROCESS_CALL;
+ OUT_BYTE_ARRAY (operation_state, operation_state_len);
+ END_CALL;
+}
+
+static CK_RV
+rpc_C_SetOperationState (CK_SESSION_HANDLE session, CK_BYTE_PTR operation_state,
+ CK_ULONG operation_state_len, CK_OBJECT_HANDLE encryption_key,
+ CK_OBJECT_HANDLE authentication_key)
+{
+ BEGIN_CALL_OR (C_SetOperationState, CKR_SESSION_HANDLE_INVALID);
+ IN_ULONG (session);
+ IN_BYTE_ARRAY (operation_state, operation_state_len);
+ IN_ULONG (encryption_key);
+ IN_ULONG (authentication_key);
+ PROCESS_CALL;
+ END_CALL;
+}
+
+static CK_RV
+rpc_C_Login (CK_SESSION_HANDLE session, CK_USER_TYPE user_type,
+ CK_UTF8CHAR_PTR pin, CK_ULONG pin_len)
+{
+ BEGIN_CALL_OR (C_Login, CKR_SESSION_HANDLE_INVALID);
+ IN_ULONG (session);
+ IN_ULONG (user_type);
+ IN_BYTE_ARRAY (pin, pin_len);
+ PROCESS_CALL;
+ END_CALL;
+}
+
+static CK_RV
+rpc_C_Logout (CK_SESSION_HANDLE session)
+{
+ BEGIN_CALL_OR (C_Logout, CKR_SESSION_HANDLE_INVALID);
+ 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_OR (C_CreateObject, CKR_SESSION_HANDLE_INVALID);
+ IN_ULONG (session);
+ IN_ATTRIBUTE_ARRAY (template, count);
+ PROCESS_CALL;
+ OUT_ULONG (new_object);
+ END_CALL;
+}
+
+static CK_RV
+rpc_C_CopyObject (CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object,
+ CK_ATTRIBUTE_PTR template, CK_ULONG count,
+ CK_OBJECT_HANDLE_PTR new_object)
+{
+ return_val_if_fail (new_object, CKR_ARGUMENTS_BAD);
+
+ BEGIN_CALL_OR (C_CopyObject, CKR_SESSION_HANDLE_INVALID);
+ IN_ULONG (session);
+ IN_ULONG (object);
+ IN_ATTRIBUTE_ARRAY (template, count);
+ PROCESS_CALL;
+ OUT_ULONG (new_object);
+ END_CALL;
+}
+
+
+static CK_RV
+rpc_C_DestroyObject (CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object)
+{
+ BEGIN_CALL_OR (C_DestroyObject, CKR_SESSION_HANDLE_INVALID);
+ 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_OR (C_GetObjectSize, CKR_SESSION_HANDLE_INVALID);
+ 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_OR (C_GetAttributeValue, CKR_SESSION_HANDLE_INVALID);
+ IN_ULONG (session);
+ IN_ULONG (object);
+ IN_ATTRIBUTE_BUFFER (template, count);
+ PROCESS_CALL;
+ OUT_ATTRIBUTE_ARRAY (template, count);
+ END_CALL;
+}
+
+static CK_RV
+rpc_C_SetAttributeValue (CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object,
+ CK_ATTRIBUTE_PTR template, CK_ULONG count)
+{
+ BEGIN_CALL_OR (C_SetAttributeValue, CKR_SESSION_HANDLE_INVALID);
+ IN_ULONG (session);
+ IN_ULONG (object);
+ IN_ATTRIBUTE_ARRAY (template, count);
+ PROCESS_CALL;
+ END_CALL;
+}
+
+static CK_RV
+rpc_C_FindObjectsInit (CK_SESSION_HANDLE session, CK_ATTRIBUTE_PTR template,
+ CK_ULONG count)
+{
+ BEGIN_CALL_OR (C_FindObjectsInit, CKR_SESSION_HANDLE_INVALID);
+ 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)
+{
+ /* HACK: To fix a stupid gcc warning */
+ CK_ULONG_PTR address_of_max_count = &max_count;
+
+ return_val_if_fail (count, CKR_ARGUMENTS_BAD);
+
+ BEGIN_CALL_OR (C_FindObjects, CKR_SESSION_HANDLE_INVALID);
+ IN_ULONG (session);
+ IN_ULONG_BUFFER (objects, address_of_max_count);
+ PROCESS_CALL;
+ *count = max_count;
+ OUT_ULONG_ARRAY (objects, count);
+ END_CALL;
+}
+
+static CK_RV
+rpc_C_FindObjectsFinal (CK_SESSION_HANDLE session)
+{
+ BEGIN_CALL_OR (C_FindObjectsFinal, CKR_SESSION_HANDLE_INVALID);
+ 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_OR (C_EncryptInit, CKR_SESSION_HANDLE_INVALID);
+ 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_OR (C_Encrypt, CKR_SESSION_HANDLE_INVALID);
+ IN_ULONG (session);
+ IN_BYTE_ARRAY (data, data_len);
+ IN_BYTE_BUFFER (encrypted_data, encrypted_data_len);
+ PROCESS_CALL;
+ OUT_BYTE_ARRAY (encrypted_data, encrypted_data_len);
+ END_CALL;
+}
+
+static CK_RV
+rpc_C_EncryptUpdate (CK_SESSION_HANDLE session, CK_BYTE_PTR part,
+ CK_ULONG part_len, CK_BYTE_PTR encrypted_part,
+ CK_ULONG_PTR encrypted_part_len)
+{
+ return_val_if_fail (encrypted_part_len, CKR_ARGUMENTS_BAD);
+
+ BEGIN_CALL_OR (C_EncryptUpdate, CKR_SESSION_HANDLE_INVALID);
+ IN_ULONG (session);
+ IN_BYTE_ARRAY (part, part_len);
+ IN_BYTE_BUFFER (encrypted_part, encrypted_part_len);
+ PROCESS_CALL;
+ OUT_BYTE_ARRAY (encrypted_part, encrypted_part_len);
+ END_CALL;
+}
+
+static CK_RV
+rpc_C_EncryptFinal (CK_SESSION_HANDLE session, CK_BYTE_PTR last_part,
+ CK_ULONG_PTR last_part_len)
+{
+ return_val_if_fail (last_part_len, CKR_ARGUMENTS_BAD);
+
+ BEGIN_CALL_OR (C_EncryptFinal, CKR_SESSION_HANDLE_INVALID);
+ IN_ULONG (session);
+ IN_BYTE_BUFFER (last_part, last_part_len);
+ PROCESS_CALL;
+ OUT_BYTE_ARRAY (last_part, last_part_len);
+ END_CALL;
+}
+
+static CK_RV
+rpc_C_DecryptInit (CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism,
+ CK_OBJECT_HANDLE key)
+{
+ BEGIN_CALL_OR (C_DecryptInit, CKR_SESSION_HANDLE_INVALID);
+ 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_OR (C_Decrypt, CKR_SESSION_HANDLE_INVALID);
+ IN_ULONG (session);
+ IN_BYTE_ARRAY (enc_data, enc_data_len);
+ IN_BYTE_BUFFER (data, data_len);
+ PROCESS_CALL;
+ OUT_BYTE_ARRAY (data, data_len);
+ END_CALL;
+}
+
+static CK_RV
+rpc_C_DecryptUpdate (CK_SESSION_HANDLE session, CK_BYTE_PTR enc_part,
+ CK_ULONG enc_part_len, CK_BYTE_PTR part, CK_ULONG_PTR part_len)
+{
+ return_val_if_fail (part_len, CKR_ARGUMENTS_BAD);
+
+ BEGIN_CALL_OR (C_DecryptUpdate, CKR_SESSION_HANDLE_INVALID);
+ IN_ULONG (session);
+ IN_BYTE_ARRAY (enc_part, enc_part_len);
+ IN_BYTE_BUFFER (part, part_len);
+ PROCESS_CALL;
+ OUT_BYTE_ARRAY (part, part_len);
+ END_CALL;
+}
+
+static CK_RV
+rpc_C_DecryptFinal (CK_SESSION_HANDLE session, CK_BYTE_PTR last_part,
+ CK_ULONG_PTR last_part_len)
+{
+ return_val_if_fail (last_part_len, CKR_ARGUMENTS_BAD);
+
+ BEGIN_CALL_OR (C_DecryptFinal, CKR_SESSION_HANDLE_INVALID);
+ IN_ULONG (session);
+ IN_BYTE_BUFFER (last_part, last_part_len);
+ PROCESS_CALL;
+ OUT_BYTE_ARRAY (last_part, last_part_len);
+ END_CALL;
+}
+
+static CK_RV
+rpc_C_DigestInit (CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism)
+{
+ BEGIN_CALL_OR (C_DigestInit, CKR_SESSION_HANDLE_INVALID);
+ 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_OR (C_Digest, CKR_SESSION_HANDLE_INVALID);
+ IN_ULONG (session);
+ IN_BYTE_ARRAY (data, data_len);
+ IN_BYTE_BUFFER (digest, digest_len);
+ PROCESS_CALL;
+ OUT_BYTE_ARRAY (digest, digest_len);
+ END_CALL;
+}
+
+static CK_RV
+rpc_C_DigestUpdate (CK_SESSION_HANDLE session, CK_BYTE_PTR part, CK_ULONG part_len)
+{
+ BEGIN_CALL_OR (C_DigestUpdate, CKR_SESSION_HANDLE_INVALID);
+ 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_OR (C_DigestKey, CKR_SESSION_HANDLE_INVALID);
+ 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_OR (C_DigestFinal, CKR_SESSION_HANDLE_INVALID);
+ IN_ULONG (session);
+ IN_BYTE_BUFFER (digest, digest_len);
+ PROCESS_CALL;
+ OUT_BYTE_ARRAY (digest, digest_len);
+ END_CALL;
+}
+
+static CK_RV
+rpc_C_SignInit (CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism,
+ CK_OBJECT_HANDLE key)
+{
+ BEGIN_CALL_OR (C_SignInit, CKR_SESSION_HANDLE_INVALID);
+ 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_OR (C_Sign, CKR_SESSION_HANDLE_INVALID);
+ IN_ULONG (session);
+ IN_BYTE_ARRAY (data, data_len);
+ IN_BYTE_BUFFER (signature, signature_len);
+ PROCESS_CALL;
+ OUT_BYTE_ARRAY (signature, signature_len);
+ END_CALL;
+}
+
+static CK_RV
+rpc_C_SignUpdate (CK_SESSION_HANDLE session, CK_BYTE_PTR part, CK_ULONG part_len)
+{
+ return_val_if_fail (part_len, CKR_ARGUMENTS_BAD);
+
+ BEGIN_CALL_OR (C_SignUpdate, CKR_SESSION_HANDLE_INVALID);
+ 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_OR (C_SignFinal, CKR_SESSION_HANDLE_INVALID);
+ IN_ULONG (session);
+ IN_BYTE_BUFFER (signature, signature_len);
+ PROCESS_CALL;
+ OUT_BYTE_ARRAY (signature, signature_len);
+ END_CALL;
+}
+
+static CK_RV
+rpc_C_SignRecoverInit (CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism,
+ CK_OBJECT_HANDLE key)
+{
+ BEGIN_CALL_OR (C_SignRecoverInit, CKR_SESSION_HANDLE_INVALID);
+ 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_OR (C_SignRecover, CKR_SESSION_HANDLE_INVALID);
+ IN_ULONG (session);
+ IN_BYTE_ARRAY (data, data_len);
+ IN_BYTE_BUFFER (signature, signature_len);
+ PROCESS_CALL;
+ OUT_BYTE_ARRAY (signature, signature_len);
+ END_CALL;
+}
+
+static CK_RV
+rpc_C_VerifyInit (CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism,
+ CK_OBJECT_HANDLE key)
+{
+ BEGIN_CALL_OR (C_VerifyInit, CKR_SESSION_HANDLE_INVALID);
+ 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_OR (C_Verify, CKR_SESSION_HANDLE_INVALID);
+ IN_ULONG (session);
+ IN_BYTE_ARRAY (data, data_len);
+ IN_BYTE_ARRAY (signature, signature_len);
+ PROCESS_CALL;
+ END_CALL;
+}
+
+static CK_RV
+rpc_C_VerifyUpdate (CK_SESSION_HANDLE session, CK_BYTE_PTR part, CK_ULONG part_len)
+{
+ BEGIN_CALL_OR (C_VerifyUpdate, CKR_SESSION_HANDLE_INVALID);
+ 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_OR (C_VerifyFinal, CKR_SESSION_HANDLE_INVALID);
+ 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_OR (C_VerifyRecoverInit, CKR_SESSION_HANDLE_INVALID);
+ 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_OR (C_VerifyRecover, CKR_SESSION_HANDLE_INVALID);
+ IN_ULONG (session);
+ IN_BYTE_ARRAY (signature, signature_len);
+ IN_BYTE_BUFFER (data, data_len);
+ PROCESS_CALL;
+ OUT_BYTE_ARRAY (data, data_len);
+ END_CALL;
+}
+
+static CK_RV
+rpc_C_DigestEncryptUpdate (CK_SESSION_HANDLE session, CK_BYTE_PTR part,
+ CK_ULONG part_len, CK_BYTE_PTR enc_part,
+ CK_ULONG_PTR enc_part_len)
+{
+ return_val_if_fail (enc_part_len, CKR_ARGUMENTS_BAD);
+
+ BEGIN_CALL_OR (C_DigestEncryptUpdate, CKR_SESSION_HANDLE_INVALID);
+ IN_ULONG (session);
+ IN_BYTE_ARRAY (part, part_len);
+ IN_BYTE_BUFFER (enc_part, enc_part_len);
+ PROCESS_CALL;
+ OUT_BYTE_ARRAY (enc_part, enc_part_len);
+ END_CALL;
+}
+
+static CK_RV
+rpc_C_DecryptDigestUpdate (CK_SESSION_HANDLE session, CK_BYTE_PTR enc_part,
+ CK_ULONG enc_part_len, CK_BYTE_PTR part,
+ CK_ULONG_PTR part_len)
+{
+ return_val_if_fail (part_len, CKR_ARGUMENTS_BAD);
+
+ BEGIN_CALL_OR (C_DecryptDigestUpdate, CKR_SESSION_HANDLE_INVALID);
+ IN_ULONG (session);
+ IN_BYTE_ARRAY (enc_part, enc_part_len);
+ IN_BYTE_BUFFER (part, part_len);
+ PROCESS_CALL;
+ OUT_BYTE_ARRAY (part, part_len);
+ END_CALL;
+}
+
+static CK_RV
+rpc_C_SignEncryptUpdate (CK_SESSION_HANDLE session, CK_BYTE_PTR part,
+ CK_ULONG part_len, CK_BYTE_PTR enc_part,
+ CK_ULONG_PTR enc_part_len)
+{
+ return_val_if_fail (enc_part_len, CKR_ARGUMENTS_BAD);
+
+ BEGIN_CALL_OR (C_SignEncryptUpdate, CKR_SESSION_HANDLE_INVALID);
+ IN_ULONG (session);
+ IN_BYTE_ARRAY (part, part_len);
+ IN_BYTE_BUFFER (enc_part, enc_part_len);
+ PROCESS_CALL;
+ OUT_BYTE_ARRAY (enc_part, enc_part_len);
+ END_CALL;
+}
+
+static CK_RV
+rpc_C_DecryptVerifyUpdate (CK_SESSION_HANDLE session, CK_BYTE_PTR enc_part,
+ CK_ULONG enc_part_len, CK_BYTE_PTR part,
+ CK_ULONG_PTR part_len)
+{
+ return_val_if_fail (part_len, CKR_ARGUMENTS_BAD);
+
+ BEGIN_CALL_OR (C_DecryptVerifyUpdate, CKR_SESSION_HANDLE_INVALID);
+ IN_ULONG (session);
+ IN_BYTE_ARRAY (enc_part, enc_part_len);
+ IN_BYTE_BUFFER (part, part_len);
+ PROCESS_CALL;
+ OUT_BYTE_ARRAY (part, part_len);
+ END_CALL;
+}
+
+static CK_RV
+rpc_C_GenerateKey (CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism,
+ CK_ATTRIBUTE_PTR template, CK_ULONG count,
+ CK_OBJECT_HANDLE_PTR key)
+{
+ BEGIN_CALL_OR (C_GenerateKey, CKR_SESSION_HANDLE_INVALID);
+ IN_ULONG (session);
+ IN_MECHANISM (mechanism);
+ IN_ATTRIBUTE_ARRAY (template, count);
+ PROCESS_CALL;
+ OUT_ULONG (key);
+ END_CALL;
+}
+
+static CK_RV
+rpc_C_GenerateKeyPair (CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism,
+ CK_ATTRIBUTE_PTR pub_template, CK_ULONG pub_count,
+ CK_ATTRIBUTE_PTR priv_template, CK_ULONG priv_count,
+ CK_OBJECT_HANDLE_PTR pub_key, CK_OBJECT_HANDLE_PTR priv_key)
+{
+ BEGIN_CALL_OR (C_GenerateKeyPair, CKR_SESSION_HANDLE_INVALID);
+ IN_ULONG (session);
+ IN_MECHANISM (mechanism);
+ IN_ATTRIBUTE_ARRAY (pub_template, pub_count);
+ IN_ATTRIBUTE_ARRAY (priv_template, priv_count);
+ PROCESS_CALL;
+ OUT_ULONG (pub_key);
+ OUT_ULONG (priv_key);
+ END_CALL;
+}
+
+static CK_RV
+rpc_C_WrapKey (CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism,
+ CK_OBJECT_HANDLE wrapping_key, CK_OBJECT_HANDLE key,
+ CK_BYTE_PTR wrapped_key, CK_ULONG_PTR wrapped_key_len)
+{
+ return_val_if_fail (wrapped_key_len, CKR_ARGUMENTS_BAD);
+
+ BEGIN_CALL_OR (C_WrapKey, CKR_SESSION_HANDLE_INVALID);
+ IN_ULONG (session);
+ IN_MECHANISM (mechanism);
+ IN_ULONG (wrapping_key);
+ IN_ULONG (key);
+ IN_BYTE_BUFFER (wrapped_key, wrapped_key_len);
+ PROCESS_CALL;
+ OUT_BYTE_ARRAY (wrapped_key, wrapped_key_len);
+ END_CALL;
+}
+
+static CK_RV
+rpc_C_UnwrapKey (CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism,
+ CK_OBJECT_HANDLE unwrapping_key, CK_BYTE_PTR wrapped_key,
+ CK_ULONG wrapped_key_len, CK_ATTRIBUTE_PTR template,
+ CK_ULONG count, CK_OBJECT_HANDLE_PTR key)
+{
+ BEGIN_CALL_OR (C_UnwrapKey, CKR_SESSION_HANDLE_INVALID);
+ IN_ULONG (session);
+ IN_MECHANISM (mechanism);
+ IN_ULONG (unwrapping_key);
+ IN_BYTE_ARRAY (wrapped_key, wrapped_key_len);
+ IN_ATTRIBUTE_ARRAY (template, count);
+ PROCESS_CALL;
+ OUT_ULONG (key);
+ END_CALL;
+}
+
+static CK_RV
+rpc_C_DeriveKey (CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism,
+ CK_OBJECT_HANDLE base_key, CK_ATTRIBUTE_PTR template,
+ CK_ULONG count, CK_OBJECT_HANDLE_PTR key)
+{
+ BEGIN_CALL_OR (C_DeriveKey, CKR_SESSION_HANDLE_INVALID);
+ IN_ULONG (session);
+ IN_MECHANISM (mechanism);
+ IN_ULONG (base_key);
+ IN_ATTRIBUTE_ARRAY (template, count);
+ PROCESS_CALL;
+ OUT_ULONG (key);
+ END_CALL;
+}
+
+static CK_RV
+rpc_C_SeedRandom (CK_SESSION_HANDLE session, CK_BYTE_PTR seed, CK_ULONG seed_len)
+{
+ BEGIN_CALL_OR (C_SeedRandom, CKR_SESSION_HANDLE_INVALID);
+ 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)
+{
+ CK_ULONG_PTR address = &random_len;
+ BEGIN_CALL_OR (C_GenerateRandom, CKR_SESSION_HANDLE_INVALID);
+ IN_ULONG (session);
+ IN_BYTE_BUFFER (random_data, address);
+ PROCESS_CALL;
+ OUT_BYTE_ARRAY (random_data, address);
+ 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
+rpc_p11_C_GetFunctionList (CK_FUNCTION_LIST_PTR_PTR list)
+{
+ return_val_if_fail (list, CKR_ARGUMENTS_BAD);
+
+ *list = &functionList;
+ return CKR_OK;
+}
diff --git a/p11-kit/rpc-message.c b/p11-kit/rpc-message.c
new file mode 100644
index 0000000..5655e01
--- /dev/null
+++ b/p11-kit/rpc-message.c
@@ -0,0 +1,473 @@
+/* -*- 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 "rpc-private.h"
+
+#include <string.h>
+
+#ifdef G_DISABLE_ASSERT
+#define assert(x)
+#else
+#include <assert.h>
+#endif
+
+RpcMessage*
+_p11_rpc_message_new (buffer_allocator allocator)
+{
+ RpcMessage *msg;
+
+ assert (allocator);
+
+ msg = (RpcMessage*) (allocator)(NULL, sizeof (RpcMessage));
+ if (!msg)
+ return NULL;
+ memset (msg, 0, sizeof (*msg));
+
+ if (!_p11_buffer_init_full (&msg->buffer, 64, allocator)) {
+ (allocator) (msg, 0); /* Frees allocation */
+ return NULL;
+ }
+
+ _p11_rpc_message_reset (msg);
+
+ return msg;
+}
+
+void
+_p11_rpc_message_free (RpcMessage *msg)
+{
+ buffer_allocator allocator;
+
+ if (msg) {
+ assert (msg->buffer.allocator);
+ allocator = msg->buffer.allocator;
+ _p11_buffer_uninit (&msg->buffer);
+
+ /* frees data buffer */
+ (allocator) (msg, 0);
+ }
+}
+
+void
+_p11_rpc_message_reset (RpcMessage *msg)
+{
+ assert (msg);
+
+ msg->call_id = 0;
+ msg->call_type = 0;
+ msg->signature = NULL;
+ msg->sigverify = NULL;
+ msg->parsed = 0;
+
+ _p11_buffer_reset (&msg->buffer);
+}
+
+int
+_p11_rpc_message_prep (RpcMessage *msg, int call_id, RpcMessageType type)
+{
+ int len;
+
+ assert (type);
+ assert (call_id >= RPC_CALL_ERROR);
+ assert (call_id < RPC_CALL_MAX);
+
+ _p11_rpc_message_reset (msg);
+
+ if (call_id != RPC_CALL_ERROR) {
+
+ /* The call id and signature */
+ if (type == RPC_REQUEST)
+ msg->signature = rpc_calls[call_id].request;
+ else if (type == RPC_RESPONSE)
+ msg->signature = rpc_calls[call_id].response;
+ else
+ assert (0 && "invalid message type");
+ assert (msg->signature);
+ msg->sigverify = msg->signature;
+ }
+
+ msg->call_id = call_id;
+ msg->call_type = type;
+
+ /* Encode the two of them */
+ _p11_buffer_add_uint32 (&msg->buffer, call_id);
+ if (msg->signature) {
+ len = strlen (msg->signature);
+ _p11_buffer_add_byte_array (&msg->buffer, (unsigned char*)msg->signature, len);
+ }
+
+ msg->parsed = 0;
+ return !_p11_buffer_has_error (&msg->buffer);
+}
+
+int
+_p11_rpc_message_parse (RpcMessage *msg, RpcMessageType type)
+{
+ const unsigned char *val;
+ size_t len;
+ uint32_t call_id;
+
+ msg->parsed = 0;
+
+ /* Pull out the call identifier */
+ if (!_p11_buffer_get_uint32 (&msg->buffer, msg->parsed, &(msg->parsed), &call_id)) {
+ _p11_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 == RPC_CALL_ERROR) {
+ if (type == RPC_REQUEST) {
+ _p11_rpc_warn ("invalid message: error code in request");
+ return 0;
+ }
+
+ return 1;
+ }
+
+ /* The call id and signature */
+ if (call_id <= 0 || call_id >= RPC_CALL_MAX) {
+ _p11_rpc_warn ("invalid message: bad call id: %d", call_id);
+ return 0;
+ }
+ if (type == RPC_REQUEST)
+ msg->signature = rpc_calls[call_id].request;
+ else if (type == RPC_RESPONSE)
+ msg->signature = 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 (!_p11_buffer_get_byte_array (&msg->buffer, msg->parsed, &(msg->parsed), &val, &len)) {
+ _p11_rpc_warn ("invalid message: couldn't read signature");
+ return 0;
+ }
+
+ if ((strlen (msg->signature) != len) || (memcmp (val, msg->signature, len) != 0)) {
+ _p11_rpc_warn ("invalid message: signature doesn't match");
+ return 0;
+ }
+
+ return 1;
+}
+
+int
+_p11_rpc_message_equals (RpcMessage *m1, RpcMessage *m2)
+{
+ assert (m1 && m2);
+
+ /* Any errors and messages are never equal */
+ if (_p11_buffer_has_error (&m1->buffer) ||
+ _p11_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 _p11_buffer_equal (&m1->buffer, &m2->buffer);
+}
+
+int
+_p11_rpc_message_verify_part (RpcMessage *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
+_p11_rpc_message_write_attribute_buffer (RpcMessage *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 || _p11_rpc_message_verify_part (msg, "fA"));
+
+ /* Write the number of items */
+ _p11_buffer_add_uint32 (&msg->buffer, num);
+
+ for (i = 0; i < num; ++i) {
+ attr = &(arr[i]);
+
+ /* The attribute type */
+ _p11_buffer_add_uint32 (&msg->buffer, attr->type);
+
+ /* And the attribute buffer length */
+ _p11_buffer_add_uint32 (&msg->buffer, attr->pValue ? attr->ulValueLen : 0);
+ }
+
+ return !_p11_buffer_has_error (&msg->buffer);
+}
+
+int
+_p11_rpc_message_write_attribute_array (RpcMessage *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 || _p11_rpc_message_verify_part (msg, "aA"));
+
+ /* Write the number of items */
+ _p11_buffer_add_uint32 (&msg->buffer, num);
+
+ for (i = 0; i < num; ++i) {
+ attr = &(arr[i]);
+
+ /* The attribute type */
+ _p11_buffer_add_uint32 (&msg->buffer, attr->type);
+
+ /* Write out the attribute validity */
+ validity = (((CK_LONG)attr->ulValueLen) == -1) ? 0 : 1;
+ _p11_buffer_add_byte (&msg->buffer, validity);
+
+ /* The attribute length and value */
+ if (validity) {
+ _p11_buffer_add_uint32 (&msg->buffer, attr->ulValueLen);
+ _p11_buffer_add_byte_array (&msg->buffer, attr->pValue, attr->ulValueLen);
+ }
+ }
+
+ return !_p11_buffer_has_error (&msg->buffer);
+}
+
+int
+_p11_rpc_message_read_byte (RpcMessage *msg, CK_BYTE *val)
+{
+ assert (msg);
+
+ /* Make sure this is in the right order */
+ assert (!msg->signature || _p11_rpc_message_verify_part (msg, "y"));
+ return _p11_buffer_get_byte (&msg->buffer, msg->parsed, &msg->parsed, val);
+}
+
+int
+_p11_rpc_message_write_byte (RpcMessage *msg, CK_BYTE val)
+{
+ assert (msg);
+
+ /* Make sure this is in the right order */
+ assert (!msg->signature || _p11_rpc_message_verify_part (msg, "y"));
+ return _p11_buffer_add_byte (&msg->buffer, val);
+}
+
+int
+_p11_rpc_message_read_ulong (RpcMessage *msg, CK_ULONG *val)
+{
+ uint64_t v;
+ assert (msg);
+
+ /* Make sure this is in the right order */
+ assert (!msg->signature || _p11_rpc_message_verify_part (msg, "u"));
+
+ if (!_p11_buffer_get_uint64 (&msg->buffer, msg->parsed, &msg->parsed, &v))
+ return 0;
+ if (val)
+ *val = (CK_ULONG)v;
+ return 1;
+}
+
+int
+_p11_rpc_message_write_ulong (RpcMessage *msg, CK_ULONG val)
+{
+ assert (msg);
+
+ /* Make sure this is in the rigth order */
+ assert (!msg->signature || _p11_rpc_message_verify_part (msg, "u"));
+ return _p11_buffer_add_uint64 (&msg->buffer, val);
+}
+
+int
+_p11_rpc_message_write_byte_buffer (RpcMessage *msg, CK_ULONG count)
+{
+ assert (msg);
+
+ /* Make sure this is in the right order */
+ assert (!msg->signature || _p11_rpc_message_verify_part (msg, "fy"));
+ return _p11_buffer_add_uint32 (&msg->buffer, count);
+}
+
+int
+_p11_rpc_message_write_byte_array (RpcMessage *msg, CK_BYTE_PTR arr, CK_ULONG num)
+{
+ assert (msg);
+
+ /* Make sure this is in the right order */
+ assert (!msg->signature || _p11_rpc_message_verify_part (msg, "ay"));
+
+ /* No array, no data, just length */
+ if (!arr) {
+ _p11_buffer_add_byte (&msg->buffer, 0);
+ _p11_buffer_add_uint32 (&msg->buffer, num);
+ } else {
+ _p11_buffer_add_byte (&msg->buffer, 1);
+ _p11_buffer_add_byte_array (&msg->buffer, arr, num);
+ }
+
+ return !_p11_buffer_has_error (&msg->buffer);
+}
+
+int
+_p11_rpc_message_write_ulong_buffer (RpcMessage *msg, CK_ULONG count)
+{
+ assert (msg);
+
+ /* Make sure this is in the right order */
+ assert (!msg->signature || _p11_rpc_message_verify_part (msg, "fu"));
+ return _p11_buffer_add_uint32 (&msg->buffer, count);
+}
+
+int
+_p11_rpc_message_write_ulong_array (RpcMessage *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 || _p11_rpc_message_verify_part (msg, "au"));
+
+ /* We send a byte which determines whether there's actual data present or not */
+ _p11_buffer_add_byte (&msg->buffer, array ? 1 : 0);
+ _p11_buffer_add_uint32 (&msg->buffer, n_array);
+
+ /* Now send the data if valid */
+ if (array) {
+ for (i = 0; i < n_array; ++i)
+ _p11_buffer_add_uint64 (&msg->buffer, array[i]);
+ }
+
+ return !_p11_buffer_has_error (&msg->buffer);
+}
+
+int
+_p11_rpc_message_read_version (RpcMessage *msg, CK_VERSION* version)
+{
+ assert (msg);
+ assert (version);
+
+ /* Check that we're supposed to have this at this point */
+ assert (!msg->signature || _p11_rpc_message_verify_part (msg, "v"));
+
+ return _p11_buffer_get_byte (&msg->buffer, msg->parsed, &msg->parsed, &version->major) &&
+ _p11_buffer_get_byte (&msg->buffer, msg->parsed, &msg->parsed, &version->minor);
+}
+
+int
+_p11_rpc_message_write_version (RpcMessage *msg, CK_VERSION* version)
+{
+ assert (msg);
+ assert (version);
+
+ /* Check that we're supposed to have this at this point */
+ assert (!msg->signature || _p11_rpc_message_verify_part (msg, "v"));
+
+ _p11_buffer_add_byte (&msg->buffer, version->major);
+ _p11_buffer_add_byte (&msg->buffer, version->minor);
+
+ return !_p11_buffer_has_error (&msg->buffer);
+}
+
+int
+_p11_rpc_message_read_space_string (RpcMessage *msg, CK_UTF8CHAR* buffer, CK_ULONG length)
+{
+ const unsigned char *data;
+ size_t n_data;
+
+ assert (msg);
+ assert (buffer);
+ assert (length);
+
+ assert (!msg->signature || _p11_rpc_message_verify_part (msg, "s"));
+
+ if (!_p11_buffer_get_byte_array (&msg->buffer, msg->parsed, &msg->parsed, &data, &n_data))
+ return 0;
+
+ if (n_data != length) {
+ _p11_rpc_warn ("invalid length space padded string received: %d != %d", length, n_data);
+ return 0;
+ }
+
+ memcpy (buffer, data, length);
+ return 1;
+}
+
+int
+_p11_rpc_message_write_space_string (RpcMessage *msg,
+ CK_UTF8CHAR *buffer,
+ CK_ULONG length)
+{
+ assert (msg);
+ assert (buffer);
+ assert (length);
+
+ assert (!msg->signature || _p11_rpc_message_verify_part (msg, "s"));
+
+ return _p11_buffer_add_byte_array (&msg->buffer, buffer, length);
+}
+
+int
+_p11_rpc_message_write_zero_string (RpcMessage *msg,
+ CK_UTF8CHAR *string)
+{
+ assert (msg);
+ assert (string);
+
+ assert (!msg->signature || _p11_rpc_message_verify_part (msg, "z"));
+
+ return _p11_buffer_add_string (&msg->buffer, (const char*)string);
+}
diff --git a/p11-kit/rpc-private.h b/p11-kit/rpc-private.h
new file mode 100644
index 0000000..c363c08
--- /dev/null
+++ b/p11-kit/rpc-private.h
@@ -0,0 +1,344 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* 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 _RPC_PRIVATE_H
+#define _RPC_PRIVATE_H
+
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "buffer.h"
+
+#include "pkcs11.h"
+
+
+/* Whether to print debug output or not */
+#define DEBUG_OUTPUT 0
+
+
+/* The calls, must be in sync with array below */
+enum {
+ RPC_CALL_ERROR = 0,
+
+ RPC_CALL_C_Initialize,
+ RPC_CALL_C_Finalize,
+ RPC_CALL_C_GetInfo,
+ RPC_CALL_C_GetSlotList,
+ RPC_CALL_C_GetSlotInfo,
+ RPC_CALL_C_GetTokenInfo,
+ RPC_CALL_C_GetMechanismList,
+ RPC_CALL_C_GetMechanismInfo,
+ RPC_CALL_C_InitToken,
+ RPC_CALL_C_WaitForSlotEvent,
+
+ RPC_CALL_C_OpenSession,
+
+ RPC_CALL_C_CloseSession,
+ RPC_CALL_C_CloseAllSessions,
+ RPC_CALL_C_GetFunctionStatus,
+ RPC_CALL_C_CancelFunction,
+
+ RPC_CALL_C_GetSessionInfo,
+ RPC_CALL_C_InitPIN,
+ RPC_CALL_C_SetPIN,
+ RPC_CALL_C_GetOperationState,
+ RPC_CALL_C_SetOperationState,
+ RPC_CALL_C_Login,
+ RPC_CALL_C_Logout,
+ RPC_CALL_C_CreateObject,
+ RPC_CALL_C_CopyObject,
+ RPC_CALL_C_DestroyObject,
+ RPC_CALL_C_GetObjectSize,
+ RPC_CALL_C_GetAttributeValue,
+ RPC_CALL_C_SetAttributeValue,
+ RPC_CALL_C_FindObjectsInit,
+ RPC_CALL_C_FindObjects,
+ RPC_CALL_C_FindObjectsFinal,
+ RPC_CALL_C_EncryptInit,
+ RPC_CALL_C_Encrypt,
+ RPC_CALL_C_EncryptUpdate,
+ RPC_CALL_C_EncryptFinal,
+ RPC_CALL_C_DecryptInit,
+ RPC_CALL_C_Decrypt,
+ RPC_CALL_C_DecryptUpdate,
+ RPC_CALL_C_DecryptFinal,
+ RPC_CALL_C_DigestInit,
+ RPC_CALL_C_Digest,
+ RPC_CALL_C_DigestUpdate,
+ RPC_CALL_C_DigestKey,
+ RPC_CALL_C_DigestFinal,
+ RPC_CALL_C_SignInit,
+ RPC_CALL_C_Sign,
+ RPC_CALL_C_SignUpdate,
+ RPC_CALL_C_SignFinal,
+ RPC_CALL_C_SignRecoverInit,
+ RPC_CALL_C_SignRecover,
+ RPC_CALL_C_VerifyInit,
+ RPC_CALL_C_Verify,
+ RPC_CALL_C_VerifyUpdate,
+ RPC_CALL_C_VerifyFinal,
+ RPC_CALL_C_VerifyRecoverInit,
+ RPC_CALL_C_VerifyRecover,
+ RPC_CALL_C_DigestEncryptUpdate,
+ RPC_CALL_C_DecryptDigestUpdate,
+ RPC_CALL_C_SignEncryptUpdate,
+ RPC_CALL_C_DecryptVerifyUpdate,
+ RPC_CALL_C_GenerateKey,
+ RPC_CALL_C_GenerateKeyPair,
+ RPC_CALL_C_WrapKey,
+ RPC_CALL_C_UnwrapKey,
+ RPC_CALL_C_DeriveKey,
+ RPC_CALL_C_SeedRandom,
+ RPC_CALL_C_GenerateRandom,
+
+ RPC_CALL_MAX
+};
+
+typedef struct _RpcCall {
+ int call_id;
+ const char* name;
+ const char* request;
+ const char* response;
+} RpcCall;
+
+/*
+ * a_ = prefix denotes array of _
+ * A = CK_ATTRIBUTE
+ * f_ = prefix denotes buffer for _
+ * M = CK_MECHANISM
+ * u = CK_ULONG
+ * s = space padded string
+ * v = CK_VERSION
+ * y = CK_BYTE
+ * z = null terminated string
+ */
+
+static const RpcCall rpc_calls[] = {
+ { RPC_CALL_ERROR, "ERROR", NULL, NULL },
+ { RPC_CALL_C_Initialize, "C_Initialize", "ay", "" },
+ { RPC_CALL_C_Finalize, "C_Finalize", "", "" },
+ { RPC_CALL_C_GetInfo, "C_GetInfo", "", "vsusv" },
+ { RPC_CALL_C_GetSlotList, "C_GetSlotList", "yfu", "au" },
+ { RPC_CALL_C_GetSlotInfo, "C_GetSlotInfo", "u", "ssuvv" },
+ { RPC_CALL_C_GetTokenInfo, "C_GetTokenInfo", "u", "ssssuuuuuuuuuuuvvs" },
+ { RPC_CALL_C_GetMechanismList, "C_GetMechanismList", "ufu", "au" },
+ { RPC_CALL_C_GetMechanismInfo, "C_GetMechanismInfo", "uu", "uuu" },
+ { RPC_CALL_C_InitToken, "C_InitToken", "uayz", "" },
+ { RPC_CALL_C_WaitForSlotEvent, "C_WaitForSlotEvent", "u", "u" },
+ { RPC_CALL_C_OpenSession, "C_OpenSession", "uu", "u" },
+ { RPC_CALL_C_CloseSession, "C_CloseSession", "u", "" },
+ { RPC_CALL_C_CloseAllSessions, "C_CloseAllSessions", "u", "" },
+ { RPC_CALL_C_GetFunctionStatus, "C_GetFunctionStatus", "u", "" },
+ { RPC_CALL_C_CancelFunction, "C_CancelFunction", "u", "" },
+ { RPC_CALL_C_GetSessionInfo, "C_GetSessionInfo", "u", "uuuu" },
+ { RPC_CALL_C_InitPIN, "C_InitPIN", "uay", "" },
+ { RPC_CALL_C_SetPIN, "C_SetPIN", "uayay", "" },
+ { RPC_CALL_C_GetOperationState, "C_GetOperationState", "ufy", "ay" },
+ { RPC_CALL_C_SetOperationState, "C_SetOperationState", "uayuu", "" },
+ { RPC_CALL_C_Login, "C_Login", "uuay", "" },
+ { RPC_CALL_C_Logout, "C_Logout", "u", "" },
+ { RPC_CALL_C_CreateObject, "C_CreateObject", "uaA", "u" },
+ { RPC_CALL_C_CopyObject, "C_CopyObject", "uuaA", "u" },
+ { RPC_CALL_C_DestroyObject, "C_DestroyObject", "uu", "" },
+ { RPC_CALL_C_GetObjectSize, "C_GetObjectSize", "uu", "u" },
+ { RPC_CALL_C_GetAttributeValue, "C_GetAttributeValue", "uufA", "aAu" },
+ { RPC_CALL_C_SetAttributeValue, "C_SetAttributeValue", "uuaA", "" },
+ { RPC_CALL_C_FindObjectsInit, "C_FindObjectsInit", "uaA", "" },
+ { RPC_CALL_C_FindObjects, "C_FindObjects", "ufu", "au" },
+ { RPC_CALL_C_FindObjectsFinal, "C_FindObjectsFinal", "u", "" },
+ { RPC_CALL_C_EncryptInit, "C_EncryptInit", "uMu", "" },
+ { RPC_CALL_C_Encrypt, "C_Encrypt", "uayfy", "ay" },
+ { RPC_CALL_C_EncryptUpdate, "C_EncryptUpdate", "uayfy", "ay" },
+ { RPC_CALL_C_EncryptFinal, "C_EncryptFinal", "ufy", "ay" },
+ { RPC_CALL_C_DecryptInit, "C_DecryptInit", "uMu", "" },
+ { RPC_CALL_C_Decrypt, "C_Decrypt", "uayfy", "ay" },
+ { RPC_CALL_C_DecryptUpdate, "C_DecryptUpdate", "uayfy", "ay" },
+ { RPC_CALL_C_DecryptFinal, "C_DecryptFinal", "ufy", "ay" },
+ { RPC_CALL_C_DigestInit, "C_DigestInit", "uM", "" },
+ { RPC_CALL_C_Digest, "C_Digest", "uayfy", "ay" },
+ { RPC_CALL_C_DigestUpdate, "C_DigestUpdate", "uay", "" },
+ { RPC_CALL_C_DigestKey, "C_DigestKey", "uu", "" },
+ { RPC_CALL_C_DigestFinal, "C_DigestFinal", "ufy", "ay" },
+ { RPC_CALL_C_SignInit, "C_SignInit", "uMu", "" },
+ { RPC_CALL_C_Sign, "C_Sign", "uayfy", "ay" },
+ { RPC_CALL_C_SignUpdate, "C_SignUpdate", "uay", "" },
+ { RPC_CALL_C_SignFinal, "C_SignFinal", "ufy", "ay" },
+ { RPC_CALL_C_SignRecoverInit, "C_SignRecoverInit", "uMu", "" },
+ { RPC_CALL_C_SignRecover, "C_SignRecover", "uayfy", "ay" },
+ { RPC_CALL_C_VerifyInit, "C_VerifyInit", "uMu", "" },
+ { RPC_CALL_C_Verify, "C_Verify", "uayay", "" },
+ { RPC_CALL_C_VerifyUpdate, "C_VerifyUpdate", "uay", "" },
+ { RPC_CALL_C_VerifyFinal, "C_VerifyFinal", "uay", "" },
+ { RPC_CALL_C_VerifyRecoverInit, "C_VerifyRecoverInit", "uMu", "" },
+ { RPC_CALL_C_VerifyRecover, "C_VerifyRecover", "uayfy", "ay" },
+ { RPC_CALL_C_DigestEncryptUpdate, "C_DigestEncryptUpdate", "uayfy", "ay" },
+ { RPC_CALL_C_DecryptDigestUpdate, "C_DecryptDigestUpdate", "uayfy", "ay" },
+ { RPC_CALL_C_SignEncryptUpdate, "C_SignEncryptUpdate", "uayfy", "ay" },
+ { RPC_CALL_C_DecryptVerifyUpdate, "C_DecryptVerifyUpdate", "uayfy", "ay" },
+ { RPC_CALL_C_GenerateKey, "C_GenerateKey", "uMaA", "u" },
+ { RPC_CALL_C_GenerateKeyPair, "C_GenerateKeyPair", "uMaAaA", "uu" },
+ { RPC_CALL_C_WrapKey, "C_WrapKey", "uMuufy", "ay" },
+ { RPC_CALL_C_UnwrapKey, "C_UnwrapKey", "uMuayaA", "u" },
+ { RPC_CALL_C_DeriveKey, "C_DeriveKey", "uMuaA", "u" },
+ { RPC_CALL_C_SeedRandom, "C_SeedRandom", "uay", "" },
+ { RPC_CALL_C_GenerateRandom, "C_GenerateRandom", "ufy", "ay" },
+};
+
+#ifdef _DEBUG
+#define RPC_CHECK_CALLS() \
+ { int i; for (i = 0; i < RPC_CALL_MAX; ++i) assert (rpc_calls[i].call_id == i); }
+#endif
+
+#define RPC_HANDSHAKE \
+ ((unsigned char*)"PRIVATE-GNOME-KEYRING-PKCS11-PROTOCOL-V-1")
+#define RPC_HANDSHAKE_LEN \
+ (strlen ((char *)RPC_HANDSHAKE))
+
+#define GKM_RPC_SOCKET_EXT "pkcs11"
+
+typedef enum _RpcMessageType {
+ RPC_REQUEST = 1,
+ RPC_RESPONSE
+} RpcMessageType;
+
+typedef struct _RpcMessage {
+ int call_id;
+ RpcMessageType call_type;
+ const char *signature;
+ buffer_t buffer;
+
+ size_t parsed;
+ const char *sigverify;
+} RpcMessage;
+
+RpcMessage * _p11_rpc_message_new (buffer_allocator allocator);
+
+void _p11_rpc_message_free (RpcMessage *msg);
+
+void _p11_rpc_message_reset (RpcMessage *msg);
+
+int _p11_rpc_message_equals (RpcMessage *m1,
+ RpcMessage *m2);
+
+#define _p11_rpc_message_is_verified(msg) (!(msg)->sigverify || (msg)->sigverify[0] == 0)
+
+#define _p11_rpc_message_buffer_error(msg) (_p11_buffer_has_error (&(msg)->buffer))
+
+int _p11_rpc_message_prep (RpcMessage *msg,
+ int call_id,
+ RpcMessageType type);
+
+int _p11_rpc_message_parse (RpcMessage *msg,
+ RpcMessageType type);
+
+int _p11_rpc_message_verify_part (RpcMessage *msg,
+ const char* part);
+
+int _p11_rpc_message_write_byte (RpcMessage *msg,
+ CK_BYTE val);
+
+int _p11_rpc_message_write_ulong (RpcMessage *msg,
+ CK_ULONG val);
+
+int _p11_rpc_message_write_zero_string (RpcMessage *msg,
+ CK_UTF8CHAR *string);
+
+int _p11_rpc_message_write_space_string (RpcMessage *msg,
+ CK_UTF8CHAR *buffer,
+ CK_ULONG length);
+
+int _p11_rpc_message_write_byte_buffer (RpcMessage *msg,
+ CK_ULONG count);
+
+int _p11_rpc_message_write_byte_array (RpcMessage *msg,
+ CK_BYTE_PTR arr,
+ CK_ULONG num);
+
+int _p11_rpc_message_write_ulong_buffer (RpcMessage *msg,
+ CK_ULONG count);
+
+int _p11_rpc_message_write_ulong_array (RpcMessage *msg,
+ CK_ULONG_PTR arr,
+ CK_ULONG num);
+
+int _p11_rpc_message_write_attribute_buffer (RpcMessage *msg,
+ CK_ATTRIBUTE_PTR arr,
+ CK_ULONG num);
+
+int _p11_rpc_message_write_attribute_array (RpcMessage *msg,
+ CK_ATTRIBUTE_PTR arr,
+ CK_ULONG num);
+
+int _p11_rpc_message_write_version (RpcMessage *msg,
+ CK_VERSION* version);
+
+int _p11_rpc_message_read_byte (RpcMessage *msg,
+ CK_BYTE* val);
+
+int _p11_rpc_message_read_ulong (RpcMessage *msg,
+ CK_ULONG* val);
+
+int _p11_rpc_message_read_space_string (RpcMessage *msg,
+ CK_UTF8CHAR* buffer,
+ CK_ULONG length);
+
+int _p11_rpc_message_read_version (RpcMessage *msg,
+ CK_VERSION* version);
+
+void _p11_rpc_log (const char *line);
+
+void _p11_rpc_warn (const char* msg,
+ ...);
+
+void _p11_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 _p11_rpc_mechanism_is_supported (CK_MECHANISM_TYPE mech);
+void _p11_rpc_mechanism_list_purge (CK_MECHANISM_TYPE_PTR mechs,
+ CK_ULONG_PTR n_mechs);
+int _p11_rpc_mechanism_has_sane_parameters (CK_MECHANISM_TYPE type);
+int _p11_rpc_mechanism_has_no_parameters (CK_MECHANISM_TYPE mech);
+
+/* TODO: Figure out how to initialize and call */
+
+CK_RV rpc_p11_C_GetFunctionList (CK_FUNCTION_LIST_PTR_PTR list);
+
+#endif /* _RPC_PRIVATE_H */
diff --git a/p11-kit/rpc-util.c b/p11-kit/rpc-util.c
new file mode 100644
index 0000000..2ce5527
--- /dev/null
+++ b/p11-kit/rpc-util.c
@@ -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 "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);
+ _p11_rpc_log (buffer);
+}
+
+void
+_p11_rpc_warn (const char* msg, ...)
+{
+ va_list va;
+ va_start (va, msg);
+ do_log ("WARNING: gnome-keyring:", msg, va);
+ va_end (va);
+}
+
+void
+_p11_rpc_debug (const char* msg, ...)
+{
+ va_list va;
+ va_start (va, msg);
+ do_log ("DEBUG: gnome-keyring:", msg, va);
+ va_end (va);
+}
+
+int
+_p11_rpc_mechanism_is_supported (CK_MECHANISM_TYPE mech)
+{
+ if (_p11_rpc_mechanism_has_no_parameters (mech) ||
+ _p11_rpc_mechanism_has_sane_parameters (mech))
+ return 1;
+ return 0;
+}
+void
+_p11_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)(*n_mechs); ++i) {
+ if (!_p11_rpc_mechanism_has_no_parameters (mechs[i]) &&
+ !_p11_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
+_p11_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
+_p11_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;
+ };
+}