[Cryptech-Commits] [sw/pkcs11] 01/05: New branch for PKCS #11 without SQLite3. Checkpoint, doesn't compile yet.

git at cryptech.is git at cryptech.is
Tue Nov 22 05:27:36 UTC 2016


This is an automated email from the git hooks/post-receive script.

sra at hactrn.net pushed a commit to branch ksng
in repository sw/pkcs11.

commit 40d6b71c7e7bc9891b309747cc09e6627d4d9b0e
Author: Rob Austein <sra at hactrn.net>
AuthorDate: Sat Nov 19 18:03:20 2016 -0500

    New branch for PKCS #11 without SQLite3.  Checkpoint, doesn't compile yet.
    
    We're going to want this in a separate branch from ksng at least for a
    little while, so that we can flip back and forth easily to run the
    same tests.
    
    Current code doesn't even compile yet, but is far enough along to be
    worth backing up off-machine.
---
 pkcs11.c | 1935 ++++++++++++++++++++++++++------------------------------------
 1 file changed, 802 insertions(+), 1133 deletions(-)

diff --git a/pkcs11.c b/pkcs11.c
index 6c52383..fd8d156 100644
--- a/pkcs11.c
+++ b/pkcs11.c
@@ -42,8 +42,6 @@
 #include <stdarg.h>
 #include <assert.h>
 
-#include <sqlite3.h>
-
 #include <hal.h>
 
 /*
@@ -74,6 +72,21 @@
 #define P11_ONE_AND_ONLY_SLOT   0
 
 /*
+ * How many sessions and object handles to allow.  We could do this
+ * with dynamic memory, but static arrays are simpler and faster.  We
+ * don't expect all that many sessions, and slots in the object table
+ * are cheap.
+ */
+
+#ifndef P11_MAX_SESSION_HANDLES
+#define P11_MAX_SESSION_HANDLES (64)
+#endif
+
+#ifndef P11_MAX_OBJECT_HANDLES
+#define P11_MAX_OBJECT_HANDLES  (4096)
+#endif
+
+/*
  * Version numbers.  Placeholders for now.
  *
  * Software version number is just the version of this PKCS #11
@@ -130,29 +143,19 @@
 

 
 /*
- * PKCS #11 session.
- */
-
-/*
- * At present we have no concept of encryption or signature algorithms
- * in libhal, as we only support RSA and AES.  For PKCS #11 purposes
- * we can figure out what kind of key we're looking at from attributes
- * like CKA_KEY_TYPE, so it's just something we look up given the key
- * object handle.
- *
- * General idea is that we have separate descriptors/handles/state for
- * each operation that we're allowed to do in parallel, so sign,
- * verify, digest, encrypt, decrypt, wrapkey, and unwrapkey all need
- * separate slots in the session structure.  Add these as we go.
+ * PKCS #11 sessions.  General idea is that we have separate
+ * descriptors/handles/state for each operation that we're allowed to
+ * do in parallel, so sign, verify, digest, encrypt, decrypt, wrapkey,
+ * and unwrapkey all need separate slots in the session structure.
+ * Add these as we go.
  */
 
 typedef struct p11_session {
   CK_SESSION_HANDLE handle;             /* Session handle */
-  struct p11_session *link;             /* Next session in list */
   CK_STATE state;                       /* State (CKS_*) of this session */
   CK_NOTIFY notify;                     /* Notification callback */
   CK_VOID_PTR application;              /* Application data */
-  sqlite3_stmt *find_query;             /* FindObject*() query state */
+  hal_rpc_pkey_attribute *find_query;   /* FindObject*() query state */
   int find_query_done;                  /* find_query has terminated */
   hal_digest_algorithm_t
     digest_algorithm,                   /* Hash algorithm for C_Digest*() */
@@ -168,34 +171,40 @@ typedef struct p11_session {
 } p11_session_t;
 
 /*
+ * PKCS #11 objects.  These are pretty simple, as they're really just
+ * mappings from PKCS #11's naming scheme to libhal UUIDs, with a little
+ * extra fun for PKCS #11 "session" objects.
+ */
+
+typedef struct p11_object {
+  CK_OBJECT_HANDLE  handle;             /* Object handle */
+  CK_SESSION_HANDLE session;            /* Associated session (if any) */
+  hal_uuid_t        uuid;               /* libhal key UUID */
+} p11_object_t;
+
+/*
  * PKCS #11 handle management.  PKCS #11 has two kinds of handles:
  * session handles and object handles.  We subdivide object handles
- * into token object handles (handles for objects that live on the
- * token) and session object handles (handles for objects that live
- * only as long as the session does), and we steal a bit of the object
- * handle as a flag to distinguish between our two kinds of object
- * handles, considerably simplifing the objected-related SQL code.
+ * into token object handles (handles for objects which live on the
+ * token, ie, in non-volatile storage) and session object handles
+ * (handles for objects which live only as long as the session does),
+ * and we steal two bits of the handle as as flags to distinguish
+ * between these three kinds handles.  We sub-divide the rest of a
+ * handle into a nonce (well, a lame one -- for now this is just a
+ * counter, if this becomes an issue we could do better) and an array
+ * index into the relevant table.
  */
 
 typedef enum {
-  handle_flavor_session,
-  handle_flavor_token_object,
-  handle_flavor_session_object
+  handle_flavor_none            = 0, /* Matches CK_INVALID_HANDLE */
+  handle_flavor_session         = 1,
+  handle_flavor_token_object    = 2,
+  handle_flavor_session_object  = 3
 } handle_flavor_t;
 
-#define FLAG_HANDLE_TOKEN               0x80000000
-
-#define is_token_handle(_handle_)       (((_handle_) & FLAG_HANDLE_TOKEN) != 0)
-
-/*
- * Digest algorithm to use when computing a key hashes.  This doesn't
- * need to be particularly secure, we're just using it to generate
- * reasonably unique identifier strings from public keys.  We use
- * SHA-1 for this because that's what most X.509 implementations use
- * for this purpose.
- */
-
-#define P11_KEY_HASH_ALGORITHM  HAL_DIGEST_ALGORITHM_SHA1
+#define HANDLE_MASK_FLAVOR      (0xc0000000)
+#define HANDLE_MASK_NONCE       (0x3fff0000)
+#define HANDLE_MASK_INDEX       (0x0000ffff)
 
 

 
@@ -210,19 +219,14 @@ static enum {
 } logged_in_as = not_logged_in;
 
 /*
- * PKCS #11 sessions for this application.
+ * PKCS #11 sessions and object handles for this application.
  */
 
-static p11_session_t *p11_sessions;
-
-/*
- * Next PKCS #11 handle to allocate.  We use a single handle space for
- * both session and object handles, and we just keep incrementing
- * until it wraps, to reduce the amount of time we have to spend
- * on SQL probes to avoid handle conflicts.
- */
+static p11_session_t p11_sessions[P11_MAX_SESSION_HANDLES];
+static p11_object_t  p11_objects [P11_MAX_OBJECT_HANDLES];
 
-static CK_ULONG next_handle;
+static unsigned p11_sessions_in_use, p11_session_next;
+static unsigned p11_objects_in_use,  p11_object_next;
 
 /*
  * Mutex callbacks.
@@ -410,202 +414,6 @@ static CK_RV _p11_error_from_hal(const hal_error_t err, const char * const file,
 

 
 /*
- * SQL utilities.
- */
-
-/*
- * Debugging control.
- */
-
-#ifndef DEBUG_SQL
-#define DEBUG_SQL       1
-#endif
-
-/*
- * Default filename for SQL database lives.  Can be overriden at
- * runtime by setting PKCS11_DATABASE environment variable.
- */
-
-#ifndef SQL_DATABASE
-#define SQL_DATABASE ".cryptech-pkcs11.db"
-#endif
-
-/*
- * SQL database.
- */
-
-static sqlite3 *sqldb = NULL;
-
-/*
- * Error checking for SQLite calls.
- */
-
-#if DEBUG_SQL
-
-#define sql_whine(_expr_)                                               \
-  (fprintf(stderr, "\n%s:%u: %s returned %s\n",                         \
-           __FILE__, __LINE__, #_expr_, sqlite3_errmsg(sqldb)),         \
-   sql_breakpoint())
-
-#else  /* DEBUG_SQL */
-
-#define sql_whine(_expr_)                                               \
-  ((void) 0)
-
-#endif  /* DEBUG_SQL */
-
-#define sql_check(_good_, _expr_)                                       \
-  ((_expr_) == (_good_) ? 1 : (sql_whine(_expr_), 0))
-
-#define sql_check_ok(_expr_)    sql_check(SQLITE_OK, _expr_)
-#define sql_check_row(_expr_)   sql_check(SQLITE_ROW, _expr_)
-#define sql_check_done(_expr_)  sql_check(SQLITE_DONE, _expr_)
-#define sql_whine_step()        sql_whine(sqlite3_step())
-
-/*
- * Hook on which to hang a debugger breakpoint on SQL errors.
- */
-
-#if DEBUG_SQL
-static void sql_breakpoint(void)
-{
-  fprintf(stderr, "\n[sql_breakpoint]\n");
-}
-#endif
-
-/*
- * Execute SQL code that doesn't require a prepared query.
- */
-
-static int sql_exec(const char *cmd)
-{
-  char *msg = NULL;
-
-  if (sql_check_ok(sqlite3_exec(sqldb, cmd, NULL, NULL, &msg)))
-    return 1;
-
-#if DEBUG_SQL
-  if (msg != NULL)
-    fprintf(stderr, "\n[%s]\n", msg);
-#endif
-
-  return 0;
-}
-
-/*
- * Initialize SQL.  This includes loading our schema, portions of
- * which live in the temp (memory) database thus always need to be
- * created on startup.
- */
-
-static int sql_init(void)
-{
-  static const char schema[] =
-#include "schema.h"
-    ;
-
-  assert(sqldb == NULL);
-
-  const char * const env  = getenv("CRYPTECH_PKCS11_DATABASE");
-  const char * const home = getenv("HOME");
-  const char * const base = SQL_DATABASE;
-  int ok;
-
-  if (env != NULL) {
-    ok = sql_check_ok(sqlite3_open(env, &sqldb));
-  }
-
-  else if (home == NULL) {
-    ok = sql_check_ok(sqlite3_open(base, &sqldb));
-  }
-
-  else {
-    char fn[strlen(home) + strlen(base) + 2];
-    snprintf(fn, sizeof(fn), "%s/%s", home, base);
-    ok = sql_check_ok(sqlite3_open(fn, &sqldb));
-  }
-
-  return ok && sql_exec(schema);
-}
-
-/*
- * Shut down SQL.
- *
- * Yes, this can return failure, although it's not clear what we're
- * meant to do about that if the application is going to shut down
- * regardless of what we do.  I guess we could loop retrying a few
- * times for errors like SQLITE_BUSY, but that's about it.
- */
-
-static int sql_fini(void)
-{
-  if (!sql_check_ok(sqlite3_close(sqldb)))
-    return 0;
-
-  sqldb = NULL;
-  return 1;
-}
-
-/*
- * GCC attribute declaration to help catch format string errors,
- * ignored by other compilers.
- */
-
-#ifdef __GNUC__
-static int sql_prepare(sqlite3_stmt **q,
-                       const char *format, ...)
-  __attribute__ ((format (printf, 2, 3)));
-#endif
-
-/*
- * Prepare an SQLite3 query, using vsnprintf() to format the query.
- *
- *             WARNING WARNING WARNING WARNING
- *
- * Do not use this formatting mechanism for anything involving
- * user-supplied data.  It's only intended to handle things like
- * selecting between two parallel table structures or queries using
- * manifest constants that are only available in C header files.
- */
-
-static int sql_prepare(sqlite3_stmt **q, const char *format, ...)
-{
-  char buffer[2048];
-  va_list ap;
-  size_t n;
-
-  va_start(ap, format);
-  n = vsnprintf(buffer, sizeof(buffer), format, ap);
-  va_end(ap);
-
-  if (n >= sizeof(buffer))
-    return SQLITE_TOOBIG;
-
-  return sqlite3_prepare_v2(sqldb, buffer, -1, q, NULL);
-}
-
-/*
- * This idiom occurs frequently, bundle it so we have the option of
- * doing it along with the normal conditional control flow that SQL
- * queries seem to follow.
- */
-
-static int sql_finalize_and_clear(sqlite3_stmt **q)
-{
-  assert(q != NULL);
-
-  int err = sqlite3_finalize(*q);
-
-  if (err != SQLITE_OK)
-    return err;
-
-  *q = NULL;
-  return SQLITE_OK;
-}
-
-

-
-/*
  * Thread mutex utilities.  We need to handle three separate cases:
  *
  * 1) User doesn't care about mutexes;
@@ -778,6 +586,20 @@ static CK_RV posix_mutex_unlock(CK_VOID_PTR pMutex)
 

 
 /*
+ * Bit mask twiddling utilities.
+ */
+
+static inline CK_ULONG mask_ldb(const CK_ULONG mask, const CK_ULONG value)
+{
+  return (value & mask) / (mask & ~(mask - 1));
+}
+
+static inline CK_ULONG mask_dpb(const CK_ULONG mask, const CK_ULONG value)
+{
+  return (value * (mask & ~(mask - 1))) & mask;
+}
+
+/*
  * Translate between libhal EC curve names and OIDs.
  */
 #warning Perhaps this should be  a utility routine in libhal instead of here
@@ -828,82 +650,32 @@ static inline hal_session_handle_t p11_session_hal_session(const p11_session_t *
   return handle;
 }
 
-

-
 /*
- * Find an unused handle.
- *
- * Note that zero is an excluded value (CK_INVALID_HANDLE), hence the
- * slightly odd arithmetic.
- *
- * For object handles, we steal the high-order bit to flag whether the
- * handle represents a session object or token object.
+ * Handle utilities.
  */
 
-static CK_ULONG p11_allocate_unused_handle(const handle_flavor_t flavor)
+static inline CK_ULONG handle_compose(const handle_flavor_t flavor,
+                                      const unsigned nonce,
+                                      const unsigned index)
 {
-  static const char select_format[] =
-    " SELECT %s_id FROM %s WHERE %s_handle = ?";
-
-  const char *table = flavor == handle_flavor_session ? "session" : "object";
-  sqlite3_stmt *q = NULL;
-  CK_ULONG handle;
-  int ret;
-
-  if (!sql_check_ok(sql_prepare(&q, select_format, table, table, table)))
-    goto fail;
-
-  for (;;) {
-
-    handle = ++next_handle;
-    next_handle %= 0xFFFFFFFF;
-
-    switch (flavor) {
-    case handle_flavor_session:
-      break;
-    case handle_flavor_token_object:
-      handle |= FLAG_HANDLE_TOKEN;
-      break;
-    case handle_flavor_session_object:
-      handle &= ~FLAG_HANDLE_TOKEN;
-      break;
-    }
-
-    assert(handle != CK_INVALID_HANDLE);
-
-    if (!sql_check_ok(sqlite3_reset(q)) ||
-        !sql_check_ok(sqlite3_bind_int64(q, 1, handle)))
-      goto fail;
-
-    if ((ret = sqlite3_step(q)) == SQLITE_ROW)
-      continue;
-
-    if (ret == SQLITE_DONE)
-      break;
-
-    sql_whine_step();
-    goto fail;
-
-  }
-
-  sqlite3_finalize(q);
-  return handle;
-
- fail:
-  sqlite3_finalize(q);
-  return CK_INVALID_HANDLE;
+  return (mask_dpb(HANDLE_MASK_FLAVOR, flavor) |
+          mask_dpb(HANDLE_MASK_NONCE,  nonce)  |
+          mask_dbp(HANDLE_MASK_INDEX,  index));
 }
 
-/*
- * Translate CKA_TOKEN value to handle flavor.
- */
+static inline handle_flavor_t handle_flavor(const CK_ULONG handle)
+{
+  return (handle_flavor_t) mask_ldb(HANDLE_MASK_FLAVOR, handle);
+}
 
-static handle_flavor_t p11_handle_flavor_from_cka_token(const CK_BBOOL *bbool)
+static inline unsigned handle_index(const CK_ULONG handle)
 {
-  assert(bbool != NULL);
-  return  *bbool ? handle_flavor_token_object : handle_flavor_session_object;
+  return mask_ldb(HANDLE_INDEX, handle);
 }
 
+// Backwards compatability, probably phase this out
+#define is_token_handle(_handle_) (handle_flavor(_handle_) == handle_flavor_token_object)
+
 

 
 /*
@@ -918,32 +690,13 @@ static handle_flavor_t p11_handle_flavor_from_cka_token(const CK_BBOOL *bbool)
  * const specifiers (CK_ATTRIBUTE_PTR has an internal non-const void*).
  */
 
-static int p11_attribute_set(const CK_OBJECT_HANDLE object_handle,
+static int p11_attribute_set(const hal_pkey_handle_t pkey,
                              const CK_ATTRIBUTE_TYPE type,
                              const void * const value,
                              const CK_ULONG length)
 {
-  static const char insert_format[] =
-    " INSERT OR REPLACE INTO %s_attribute (%s_object_id, type, value)"
-    " VALUES ((SELECT %s_object_id FROM object WHERE object_handle = ?1), ?2, ?3)";
-
-  const char *flavor = is_token_handle(object_handle) ? "token" : "session";
-
-  sqlite3_stmt *q = NULL;
-  int ok = 0;
-
-  if (!sql_check_ok(sql_prepare(&q, insert_format, flavor, flavor, flavor))     ||
-      !sql_check_ok(sqlite3_bind_int64(q, 1, object_handle))                    ||
-      !sql_check_ok(sqlite3_bind_int64(q, 2, type))                             ||
-      !sql_check_ok(sqlite3_bind_blob( q, 3, value, length, NULL))              ||
-      !sql_check_done(sqlite3_step(q)))
-    goto fail;
-
-  ok = 1;
-
- fail:
-  sqlite3_finalize(q);
-  return ok;
+  const hal_rpc_pkey_attribute_t attribute = {.type = type, .length = length, .value = value};
+  return hal_check(hal_rpc_pkey_set_attributes(pkey, &attribute, 1));
 }
 
 /*
@@ -953,83 +706,51 @@ static int p11_attribute_set(const CK_OBJECT_HANDLE object_handle,
  * cost of more complicated error semantics.
  */
 
-static int p11_attribute_get(const CK_OBJECT_HANDLE object_handle,
+static int p11_attribute_get(const hal_pkey_handle_t pkey,
                              const CK_ATTRIBUTE_TYPE type,
                              void *value,
                              CK_ULONG *length,
                              const CK_ULONG maxlength)
 {
-  static const char select_format[] =
-    " SELECT value FROM %s_attribute NATURAL JOIN object"
-    " WHERE object_handle = ?1 AND type = ?2";
-
-  const char *flavor = is_token_handle(object_handle) ? "token" : "session";
-
-  sqlite3_stmt *q = NULL;
-  int ret, ok = 0;
-  CK_ULONG len;
-
-  if (!sql_check_ok(sql_prepare(&q, select_format, flavor))     ||
-      !sql_check_ok(sqlite3_bind_int64(q, 1, object_handle))    ||
-      !sql_check_ok(sqlite3_bind_int64(q, 2, type)))
-    goto fail;
+  hal_rpc_pkey_attribute_t attribute = {.type = type};
 
-  ret = sqlite3_step(q);
-
-  if (ret == SQLITE_DONE)
-    goto fail;
-
-  if (ret != SQLITE_ROW) {
-    sql_whine_step();
-    goto fail;
-  }
-
-  len = sqlite3_column_bytes(q, 0);
+  if (!hal_check(hal_rpc_pkey_get_attributes(pkey, &attribute, 1, value, maxlength)))
+    return 0;
 
   if (length != NULL)
-    *length = len;
-
-  if (value != NULL && maxlength < len)
-    goto fail;
+    *length = attribute.length;
 
-  if (value != NULL)
-    memcpy(value, sqlite3_column_blob(q, 0), len);
-
-  ok = 1;
-
- fail:
-  sqlite3_finalize(q);
-  return ok;
+  return 1;
 }
 
 /*
  * Wrappers to set and get CK_BBOOL and CK_ULONG values.
  */
 
-static int p11_attribute_set_bbool(const CK_OBJECT_HANDLE object_handle, const CK_ATTRIBUTE_TYPE type, const CK_BBOOL value)
+static int p11_attribute_set_bbool(const hal_pkey_handle_t pkey, const CK_ATTRIBUTE_TYPE type, const CK_BBOOL value)
 {
-  return p11_attribute_set(object_handle, type, &value, sizeof(value));
+  return p11_attribute_set(pkey, type, &value, sizeof(value));
 }
 
 #if 0
 
-static int p11_attribute_set_ulong(const CK_OBJECT_HANDLE object_handle, const CK_ATTRIBUTE_TYPE type, const CK_ULONG value)
+static int p11_attribute_set_ulong(const hal_pkey_handle_t pkey, const CK_ATTRIBUTE_TYPE type, const CK_ULONG value)
 {
-  return p11_attribute_set(object_handle, type, &value, sizeof(value));
+  return p11_attribute_set(pkey, type, &value, sizeof(value));
 }
 
 #endif
 
-static int p11_attribute_get_bbool(const CK_OBJECT_HANDLE object_handle, const CK_ATTRIBUTE_TYPE type, CK_BBOOL *value)
+static int p11_attribute_get_bbool(const hal_pkey_handle_t pkey, const CK_ATTRIBUTE_TYPE type, CK_BBOOL *value)
 {
   CK_ULONG length;
-  return p11_attribute_get(object_handle, type, value, &length, sizeof(*value)) && length == sizeof(*value);
+  return p11_attribute_get(pkey, type, value, &length, sizeof(*value)) && length == sizeof(*value);
 }
 
-static int p11_attribute_get_ulong(const CK_OBJECT_HANDLE object_handle, const CK_ATTRIBUTE_TYPE type, CK_ULONG *value)
+static int p11_attribute_get_ulong(const hal_pkey_handle_t pkey, const CK_ATTRIBUTE_TYPE type, CK_ULONG *value)
 {
   CK_ULONG length;
-  return p11_attribute_get(object_handle, type, value, &length, sizeof(*value)) && length == sizeof(*value);
+  return p11_attribute_get(pkey, type, value, &length, sizeof(*value)) && length == sizeof(*value);
 }
 
 /*
@@ -1171,6 +892,139 @@ static int p11_attribute_is_sensitive(const p11_descriptor_t *descriptor,
  */
 
 /*
+ * Allocate a new object.
+ */
+
+static CK_OBJECT_HANDLE p11_object_allocate(const handle_flavor_t flavor,
+                                            const hal_uuid_t *uuid,
+                                            const p11_session_t *session)
+{
+  if (uuid == NULL)
+    return CK_INVALID_HANDLE;
+
+  if (flavor != handle_flavor_token_object && flavor != handle_flavor_session_object)
+    return CK_INVALID_HANDLE;
+
+  static unsigned next_index, nonce;
+  const unsigned  last_index = next_index;
+  p11_object_t *object = NULL;
+
+  do {
+
+    next_index = (next_index + 1) % (sizeof(p11_objects) / sizeof(*p11_objects));
+
+    if (next_index == last_index)
+      return CK_INVALID_HANDLE;
+
+    if (next_index == 0)
+      ++nonce;
+
+    object = p11_objects[next_index];
+
+  } while (object.handle == CK_INVALID_HANDLE);
+
+  object->handle = handle_compose(flavor, nonce, next_handle);
+  object->uuid = *uuid;
+  object->session = flavor == handle_flavor_session_object ? session->handle : CK_INVALID_HANDLE;
+  return object->handle;
+}
+
+/*
+ * Free an object.
+ */
+
+static void p11_object_free(p11_object_t *object)
+{
+  if (object == NULL)
+    return;
+
+#warning Should we delete session objects here?
+
+  memset(object, 0, sizeof(*object));
+}
+
+/*
+ * Find an object.
+ */
+
+static p11_object_t *p11_object_find(const CK_OBJECT_HANDLE object_handle)
+{
+  if (handle_flavor(object_handle) != handle_flavor_session_object &&
+      handle_flavor(object_handle) != handle_flavor_token_object)
+    return NULL;
+
+  const unsigned index = handle_index(object_handle);
+
+  if (index >= sizeof(p11_objects) / sizeof(*p11_objects))
+    return NULL;
+
+  p11_object_t *object = &p11_objects[index];
+
+  if (object->handle != object_handle)
+    return NULL;
+
+  return object;
+}
+
+/*
+ * Iterate over object handles.  Start with CK_INVALID_HANDLE,
+ * returns CK_INVALID_HANDLE when done.
+ */
+
+static CK_BJECT_HANDLE p11_object_handle_iterate(const CK_OBJECT_HANDLE object_handle)
+{
+  if (handle_flavor(object_handle) != handle_flavor_session_object &&
+      handle_flavor(object_handle) != handle_flavor_token_object)
+    return CK_HANDLE_INVALID;
+
+  for (unsigned index = session_handle == CK_INVALID_HANDLE ? 0 : handle_index(session_handle) + 1;
+       index < sizeof(p11_sessions) / sizeof(*p11_sessions);
+       index++)
+    if (handle_flavor(p11_sessions[index].handle) == handle_flavor_session_object ||
+        handle_flavor(p11_sessions[index].handle) == handle_flavor_token_object)
+      return p11_sessions[index].handle;
+
+  return CK_HANDLE_INVALID;
+}
+
+/*
+ * Syntactic sugar to iterate over objects instead of object handles.
+ */
+
+static p11_object_t *p11_object_iterate(p11_object_t *object)
+{
+  if (object == NULL)
+    return NULL;
+
+  return p11_object_find(p11_object_handle_iterate(object->handle));
+}
+
+/*
+ * Translate CKA_TOKEN value to handle flavor.
+ */
+
+static inline handle_flavor_t p11_object_flavor_from_cka_token(const CK_BBOOL *bbool)
+{
+  return (bbool != NULL && *bbool) ? handle_flavor_token_object : handle_flavor_session_object;
+}
+
+/*
+ * Open the HSM pkey object (if any) corresponding to the PKCS #11 handle.
+ */
+
+static int p11_object_pkey_open(const p11_session_t *session,
+                                const CK_OBJECT_HANDLE object_handle,
+                                hal_pkey_handle_t *pkey)
+{
+  return (session != NULL && pkey != NULL &&
+          hal_check(hal_rpc_pkey_open(p11_session_hal_client(session),
+                                      p11_session_hal_session(session),
+                                      pkey,
+                                      p11_object_get_uuid(object_handle),
+                                      p11_object_get_hal_flags(object_handle))));
+}
+
+/*
  * Check access rights for an object.
  */
 
@@ -1180,13 +1034,7 @@ static CK_RV p11_object_check_rights(const p11_session_t *session,
                                      const CK_OBJECT_HANDLE object_handle,
                                      const p11_object_access_t rights)
 {
-  static const char object_exists_query[] =
-    " SELECT count(*) FROM object WHERE object_handle = ?1";
-
-  static const char session_object_query[] =
-    " SELECT session_handle FROM session NATURAL JOIN object WHERE object_handle = ?1";
-
-  sqlite3_stmt *q = NULL;
+  hal_pkey_handle_t pkey = {HAL_HANDLE_NONE};
   CK_BBOOL cka_private;
   CK_BBOOL cka_token;
   CK_RV rv;
@@ -1194,10 +1042,13 @@ static CK_RV p11_object_check_rights(const p11_session_t *session,
   if (session == NULL)
     lose(CKR_SESSION_HANDLE_INVALID);
 
-  if (!p11_attribute_get_bbool(object_handle, CKA_PRIVATE, &cka_private))
+  if (!p11_object_pkey_open(session, object_handle, &pkey))
+    lose(CKR_OBJECT_HANDLE_INVALID);
+
+  if (!p11_attribute_get_bbool(pkey, CKA_PRIVATE, &cka_private))
     cka_private = CK_TRUE;
 
-  if (!p11_attribute_get_bbool(object_handle, CKA_TOKEN, &cka_token))
+  if (!p11_attribute_get_bbool(pkey, CKA_TOKEN, &cka_token))
     cka_token = CK_FALSE;
 
   /*
@@ -1230,186 +1081,59 @@ static CK_RV p11_object_check_rights(const p11_session_t *session,
       lose(CKR_OBJECT_HANDLE_INVALID);
   }
 
+#warning Want session-object visibility sanity check here?
   /*
-   * Does the object even exist?
-   */
-
-  if (!sql_check_ok(sql_prepare(&q, object_exists_query))       ||
-      !sql_check_ok(sqlite3_bind_int64(q, 1, object_handle))    ||
-      !sql_check_row(sqlite3_step(q))                           ||
-      !sqlite3_column_int(q, 0))
-    lose(CKR_OBJECT_HANDLE_INVALID);
-
-  /*
-   * Session objects are only visible to the session which created them.
-   */
-
-  if (!is_token_handle(object_handle)                           &&
-      (!sql_check_ok(sql_finalize_and_clear(&q))                ||
-       !sql_check_ok(sql_prepare(&q, session_object_query))     ||
-       !sql_check_ok(sqlite3_bind_int64(q, 1, object_handle))   ||
-       !sql_check_row(sqlite3_step(q))                          ||
-       sqlite3_column_int64(q, 0) != session->handle))
-    lose(CKR_OBJECT_HANDLE_INVALID);
-
-  /*
-   * Ran out of reasons to reject, guess we should allow it.
+   * At this point the old code checked for objects which are not
+   * supposed to be visible to this session.  In theory, the keystore
+   * now handles that, but we might want a sanity check here too.
    */
 
   rv = CKR_OK;
 
  fail:
-  sqlite3_finalize(q);
+  (void) hal_check(hal_rpc_pkey_close(pkey));
   return rv;
 }
 
 /*
- * Delete all private objects, probably because user logged out.
- *
- * In the case of token objects, the object itself remains in the
- * token, we're just deleting our handle for the object.
- *
- * In the case of session objects, the object itself goes away.
- */
-
-static int p11_object_delete_all_private(void)
-{
-  static const char delete_format[] =
-    " WITH"
-    "  s AS (SELECT session_object_id FROM session_attribute WHERE type = %u AND value <> X'00'),"
-    "  t AS (SELECT token_object_id   FROM token_attribute   WHERE type = %u AND value <> X'00')"
-    " DELETE FROM object WHERE token_object_id IN t OR session_object_id IN s";
-
-  sqlite3_stmt *q = NULL;
-  int ok = 0;
-
-  if (!sql_check_ok(sql_prepare(&q, delete_format, CKA_PRIVATE, CKA_PRIVATE))   ||
-      !sql_check_done(sqlite3_step(q)))
-    goto fail;
-
-  ok = 1;
-
- fail:
-  sqlite3_finalize(q);
-  return ok;
-}
-
-/*
- * Create a new object.
- *
- * This is a bit nasty due to the SQL foreign key constraints and the
- * different handling required for session and token objects.
+ * Given a pkey handle and UUID for a newly created HSM key, attach
+ * attributes and create an object handle.
  */
 
 static CK_OBJECT_HANDLE p11_object_create(const p11_session_t *session,
+                                          const hal_pkey_handle_t pkey,
+                                          const hal_uuid_t *uuid,
                                           const handle_flavor_t flavor,
                                           const CK_ATTRIBUTE_PTR template,
                                           const CK_ULONG template_length,
                                           const p11_descriptor_t * const descriptor,
-                                          const CK_MECHANISM_PTR mechanism)
+                                          const hal_rpc_pkey_attribute_t *extra,
+                                          const unsigned extra_length)
 {
-  static const char insert_object[] =
-    " INSERT INTO object (object_handle)"
-    " VALUES (?)";
-
-  static const char insert_token_object[] =
-    " INSERT INTO token_object DEFAULT VALUES";
-
-  static const char insert_session_object[] =
-    " INSERT INTO session_object (object_id) VALUES (?)";
-
-  static const char update_object_session_object[] =
-    " UPDATE object SET"
-    "   session_id = (SELECT session_id FROM session WHERE session_handle = ?1),"
-    "   session_object_id = ?2"
-    " WHERE object_id = ?3";
-
-  static const char update_object_token_object[] =
-    " UPDATE object SET token_object_id = ?1 WHERE object_id = ?2";
-
-  static const char insert_token_attribute[] =
-    " INSERT OR REPLACE INTO token_attribute (token_object_id, type, value)"
-    " VALUES (?1, ?2, ?3)";
-
-  static const char insert_session_attribute[] =
-    " INSERT OR REPLACE INTO session_attribute (session_object_id, type, value)"
-    " VALUES (?1, ?2, ?3)";
-
-  CK_OBJECT_HANDLE object_handle = p11_allocate_unused_handle(flavor);;
-  sqlite3_int64 object_id, session_object_id, token_object_id;
-  sqlite3_stmt *q = NULL;
-  int i, ok = 0;
-
-  assert(session != NULL && template != NULL && descriptor != NULL &&
-         (flavor == handle_flavor_token_object ||
-          flavor == handle_flavor_session_object));
-
-  if (!sql_check_ok(sql_prepare(&q, insert_object))             ||
-      !sql_check_ok(sqlite3_bind_int64(q, 1, object_handle))    ||
-      !sql_check_done(sqlite3_step(q)))
-    goto fail;
-
-  object_id = sqlite3_last_insert_rowid(sqldb);
-
-  if (!sql_check_ok(sql_finalize_and_clear(&q)))
-    goto fail;
-
-  switch (flavor) {
-
-  case handle_flavor_token_object:
-    if (!sql_check_ok(sql_prepare(&q, insert_token_object))             ||
-        !sql_check_done(sqlite3_step(q)))
-      goto fail;
-    token_object_id = sqlite3_last_insert_rowid(sqldb);
-    if (!sql_check_ok(sql_finalize_and_clear(&q))                       ||
-        !sql_check_ok(sql_prepare(&q, update_object_token_object))      ||
-        !sql_check_ok(sqlite3_bind_int64(q, 1, token_object_id))        ||
-        !sql_check_ok(sqlite3_bind_int64(q, 2, object_id))              ||
-        !sql_check_done(sqlite3_step(q))                                ||
-        !sql_check_ok(sql_finalize_and_clear(&q))                       ||
-        !sql_check_ok(sql_prepare(&q, insert_token_attribute))          ||
-        !sql_check_ok(sqlite3_bind_int64(q, 1, token_object_id)))
-      goto fail;
-    break;
-
-  case handle_flavor_session_object:
-    if (!sql_check_ok(sql_prepare(&q, insert_session_object))           ||
-        !sql_check_ok(sqlite3_bind_int64(q, 1, object_id))              ||
-        !sql_check_done(sqlite3_step(q)))
-      goto fail;
-    session_object_id = sqlite3_last_insert_rowid(sqldb);
-    if (!sql_check_ok(sql_finalize_and_clear(&q))                       ||
-        !sql_check_ok(sql_prepare(&q, update_object_session_object))    ||
-        !sql_check_ok(sqlite3_bind_int64(q, 1, session->handle))        ||
-        !sql_check_ok(sqlite3_bind_int64(q, 2, session_object_id))      ||
-        !sql_check_ok(sqlite3_bind_int64(q, 3, object_id))              ||
-        !sql_check_done(sqlite3_step(q))                                ||
-        !sql_check_ok(sql_finalize_and_clear(&q))                       ||
-        !sql_check_ok(sql_prepare(&q, insert_session_attribute))        ||
-        !sql_check_ok(sqlite3_bind_int64(q, 1, session_object_id)))
-      goto fail;
-    break;
-
-  default:                      /* Suppress GCC warning */
-    goto fail;
-  }
+  assert(session != NULL && template != NULL && descriptor != NULL && uuid != NULL &&
+         (extra_length == 0 || extra != NULL) &&
+         (flavor == handle_flavor_token_object || flavor == handle_flavor_session_object));
 
   /*
-   * Now populate attributes, starting with the application's
-   * template, which we assume has already been blessed by the API
-   * function that called this method.
+   * Populate attributes, starting with the application's template,
+   * which we assume has already been blessed by the API function that
+   * called this method.
    *
    * If the attribute is flagged as sensitive in the descriptor, we
-   * don't store it in SQL.  Generally, this only arises for private
-   * key components of objects created with C_CreateObject(), but in
-   * theory there are some corner cases in which a user could choose
-   * to mark a private key as extractable and not sensitive, so we
-   * might have to back-fill missing values in those cases if anyone
-   * ever thinks up a sane reason for supporting them.  For now, assume
-   * that private keys are bloody well supposed to be private.
+   * don't store it as an attribute.  Generally, this only arises for
+   * private key components of objects created with C_CreateObject(),
+   * but in theory there are some corner cases in which a user could
+   * choose to mark a private key as extractable and not sensitive, so
+   * we might have to back-fill missing values in those cases if
+   * anyone ever thinks up a sane reason for supporting them.  For
+   * now, assume that private keys are bloody well supposed to be
+   * private.
    */
 
-  for (i = 0; i < template_length; i++) {
+  hal_rpc_pkey_attribute_t attributes[template_length + descriptor->n_attributes + extra_length];
+  unsigned n = 0;
+
+  for (int i = 0; i < template_length; i++) {
     const CK_ATTRIBUTE_TYPE type = template[i].type;
     const void *             val = template[i].pValue;
     const int                len = template[i].ulValueLen;
@@ -1417,18 +1141,20 @@ static CK_OBJECT_HANDLE p11_object_create(const p11_session_t *session,
     if (p11_attribute_is_sensitive(descriptor, type))
       continue;
 
-    if (!sql_check_ok(sqlite3_reset(q))                         ||
-        !sql_check_ok(sqlite3_bind_int64(q, 2, type))           ||
-        !sql_check_ok(sqlite3_bind_blob( q, 3, val, len, NULL)) ||
-        !sql_check_done(sqlite3_step(q)))
-      goto fail;
+    if (n >= sizeof(attributes) / sizeof(*attributes))
+      lose(CKR_FUNCTION_FAILED);
+
+    attributes[n].type   = type;
+    attributes[n].value  = val;
+    attributes[n].length = len;
+    n++;
   }
 
   /*
    * Next, add defaults from the descriptor.
    */
 
-  for (i = 0; i < descriptor->n_attributes; i++) {
+  for (int i = 0; i < descriptor->n_attributes; i++) {
     const CK_ATTRIBUTE_TYPE type = descriptor->attributes[i].type;
     const void *             val = descriptor->attributes[i].value;
     const int                len = descriptor->attributes[i].length;
@@ -1440,107 +1166,98 @@ static CK_OBJECT_HANDLE p11_object_create(const p11_session_t *session,
     if (val == NULL || p11_attribute_find_in_template(type, template, template_length) >= 0)
       continue;
 
-    if (!sql_check_ok(sqlite3_reset(q))                         ||
-        !sql_check_ok(sqlite3_bind_int64(q, 2, type))           ||
-        !sql_check_ok(sqlite3_bind_blob( q, 3, val, len, NULL)) ||
-        !sql_check_done(sqlite3_step(q)))
-      goto fail;
+    if (n >= sizeof(attributes) / sizeof(*attributes))
+      lose(CKR_FUNCTION_FAILED);
+
+    attributes[n].type   = type;
+    attributes[n].value  = val;
+    attributes[n].length = len;
+    n++;
   }
 
   /*
-   * Finally, add generation mechanism attributes as needed.
+   * Finally, add any attributes provided by the calling function itself.
    */
 
-  if (mechanism != NULL &&
-      (!sql_check_ok(sqlite3_reset(q))                                                                          ||
-       !sql_check_ok(sqlite3_bind_int64(q, 2, CKA_LOCAL))                                                       ||
-       !sql_check_ok(sqlite3_bind_blob( q, 3, &const_CK_TRUE, sizeof(const_CK_TRUE), NULL))                     ||
-       !sql_check_done(sqlite3_step(q))                                                                         ||
-       !sql_check_ok(sqlite3_reset(q))                                                                          ||
-       !sql_check_ok(sqlite3_bind_int64(q, 2, CKA_KEY_GEN_MECHANISM))                                           ||
-       !sql_check_ok(sqlite3_bind_blob( q, 3, &mechanism->mechanism, sizeof(mechanism->mechanism), NULL))       ||
-       !sql_check_done(sqlite3_step(q))))
-    goto fail;
+  for (int i = 0; i < extra_length; i++) {
+    if (n >= sizeof(attributes) / sizeof(*attributes))
+      lose(CKR_FUNCTION_FAILED);
+
+    attributes[n].type   = extra[i].type;
+    attributes[n].value  = extra[i].value;
+    attributes[n].length = extra[i].length;
+    n++;
+  }
 
   /*
-   * If we made it past all that, we're happy.
+   * Set all those attributes.
    */
 
-  ok = 1;
-
- fail:
-  sqlite3_finalize(q);
-  return ok ? object_handle : CK_INVALID_HANDLE;
-}
-
-/*
- * Bind PKCS #11 objects to keystore objects, via the key's pkey type
- * and UUID.
- */
-
-static int p11_object_bind_pkey(const p11_session_t * const session,
-                                const CK_OBJECT_HANDLE object_handle,
-                                const hal_uuid_t * const pkey_uuid)
-{
-  assert(session != NULL && pkey_uuid != NULL);
-
-  static const char update_format[] =
-    " UPDATE %s_object SET hal_pkey_uuid = ?1"
-    " WHERE %s_object_id = (SELECT %s_object_id FROM object WHERE object_handle = ?2)";
+  if (!hal_check(hal_rpc_pkey_set_attributes(pkey, attributes, n)))
+    lose(CKR_FUNCTION_FAILED);
 
-  const char *flavor = is_token_handle(object_handle) ? "token" : "session";
-  sqlite3_stmt *q = NULL;
+  /*
+   * Create the object handle, then we're done.
+   */
 
-  int ok = (sql_check_ok(sql_prepare(&q, update_format, flavor, flavor, flavor))        &&
-            sql_check_ok(sqlite3_bind_blob( q, 1, pkey_uuid, sizeof(*pkey_uuid), NULL)) &&
-            sql_check_ok(sqlite3_bind_int64(q, 2, object_handle))                       &&
-            sql_check_done(sqlite3_step(q)));
+  return p11_object_allocate(flavor, uuid, session);
 
-  sqlite3_finalize(q);
-  return ok;
+ fail:
+  return CK_INVALID_HANDLE;
 }
 
 /*
  * Create pkeys to go with PKCS #11 key objects loaded by C_CreateObject().
  */
 
-static inline int p11_object_create_rsa_public_key(const p11_session_t * const session,
-                                                   const CK_OBJECT_HANDLE object_handle,
-                                                   const hal_key_flags_t flags)
+static int p11_object_create_rsa_public_key(const p11_session_t * const session,
+                                            const handle_flavor_t flavor,
+                                            const CK_ATTRIBUTE_PTR template,
+                                            const CK_ULONG template_len,
+                                            const p11_descriptor_t * const descriptor,
+                                            CK_OBJECT_HANDLE_PTR phObject,
+                                            const hal_key_flags_t flags)
 {
-  static const char select_format[] =
-    " WITH a (type, value) "
-    "   AS (SELECT type, value FROM %s_attribute NATURAL JOIN object WHERE object_handle = ?1)"
-    " SELECT a1.value, a2.value FROM a AS a1, a AS a2 WHERE a1.type = %u AND a2.type = %u";
-
-  const char *flavor = is_token_handle(object_handle) ? "token" : "session";
+  const hal_rpc_pkey_attributes extra[] = {
+    {.type = CKA_LOCAL, .value = &const_CKA_FALSE, .length = sizeof(const_CKA_FALSE)}
+  };
 
   hal_pkey_handle_t pkey = {HAL_HANDLE_NONE};
   uint8_t keybuf[hal_rsa_key_t_size];
   hal_rsa_key_t *key = NULL;
-  sqlite3_stmt *q = NULL;
 
-  int ok
-    = (sql_check_ok(sql_prepare(&q, select_format, flavor, CKA_MODULUS, CKA_PUBLIC_EXPONENT))   &&
-       sql_check_ok(sqlite3_bind_int64(q, 1, object_handle))                                    &&
-       sql_check_row(sqlite3_step(q))                                                           &&
-       sqlite3_column_type(q, 0) == SQLITE_BLOB                                                 &&
-       sqlite3_column_type(q, 1) == SQLITE_BLOB                                                 &&
-       hal_check(hal_rsa_key_load_public(&key, keybuf, sizeof(keybuf),
-                                         sqlite3_column_blob( q, 0),
-                                         sqlite3_column_bytes(q, 0),
-                                         sqlite3_column_blob( q, 1),
-                                         sqlite3_column_bytes(q, 1))));
+  const uint8_t *cka_modulus = NULL;
+  size_t cka_modulus_len = 0;
+  const uint8_t *cka_public_exponent = const_0x010001;
+  size_t cka_public_exponent_len = sizeof(const_0x010001);
+
+  for (int i = 0; i < template_len; i++) {
+    const void * const val = template[i].pValue;
+    const size_t       len = template[i].ulValueLen;
+    switch (template[i].type) {
+    case CKA_MODULUS:          cka_modulus          = val; cka_modulus_len          = len; break;
+    case CKA_PUBLIC_EXPONENT:  cka_public_exponent  = val; cka_public_exponent_len  = len; break;
+    }
+  }
+
+  int ok = hal_check(hal_rsa_key_load_public(&key, keybuf, sizeof(keybuf),
+                                             cka_modulus, cka_modulus_len,
+                                             cka_public_exponent, cka_public_exponent_len));
 
   if (ok) {
     uint8_t der[hal_rsa_public_key_to_der_len(key)];
     hal_uuid_t uuid;
-    ok = (hal_check(hal_rsa_public_key_to_der(key, der, NULL, sizeof(der)))                     &&
+    ok = (hal_check(hal_rsa_public_key_to_der(key, der, NULL, sizeof(der))) &&
           hal_check(hal_rpc_pkey_load(p11_session_hal_client(session),
                                       p11_session_hal_session(session),
                                       &pkey, HAL_KEY_TYPE_RSA_PUBLIC, HAL_CURVE_NONE,
-                                      &uuid, der, sizeof(der), flags))                          &&
-          p11_object_bind_pkey(session, object_handle, &uuid));
+                                      &uuid, der, sizeof(der), flags)));
+  }
+
+  if (ok) {
+    *phObject = p11_object_create(session, pkey, &uuid, flavor, template, template_len,
+                                  descriptor, extra, sizeof(extra)/sizeof(*extra));
+    ok = *phObject != CK_INVALID_HANDLE;
   }
 
   if (!ok && pkey.handle != HAL_HANDLE_NONE)
@@ -1548,48 +1265,58 @@ static inline int p11_object_create_rsa_public_key(const p11_session_t * const s
   else
     (void) hal_rpc_pkey_close(pkey);
 
-  sqlite3_finalize(q);
   return ok;
 }
 
-static inline int p11_object_create_ec_public_key(const p11_session_t * const session,
-                                                  const CK_OBJECT_HANDLE object_handle,
-                                                  const hal_key_flags_t flags)
+static int p11_object_create_ec_public_key(const p11_session_t * const session,
+                                           const handle_flavor_t flavor,
+                                           const CK_ATTRIBUTE_PTR template,
+                                           const CK_ULONG template_len,
+                                           const p11_descriptor_t * const descriptor,
+                                           CK_OBJECT_HANDLE_PTR phObject,
+                                           const hal_key_flags_t flags)
 {
-  static const char select_format[] =
-    " WITH a (type, value) "
-    "   AS (SELECT type, value FROM %s_attribute NATURAL JOIN object WHERE object_handle = ?1)"
-    " SELECT a1.value, a2.value FROM a AS a1, a AS a2 WHERE a1.type = %u AND a2.type = %u";
-
-  const char *flavor = is_token_handle(object_handle) ? "token" : "session";
+  const hal_rpc_pkey_attributes extra[] = {
+    {.type = CKA_LOCAL, .value = &const_CKA_FALSE, .length = sizeof(const_CKA_FALSE)}
+  };
 
   hal_pkey_handle_t pkey = {HAL_HANDLE_NONE};
   uint8_t keybuf[hal_ecdsa_key_t_size];
   hal_ecdsa_key_t *key = NULL;
   hal_curve_name_t curve;
-  sqlite3_stmt *q = NULL;
+
+  const uint8_t *cka_ec_point  = NULL;  size_t cka_ec_point_len  = 0;
+  const uint8_t *cka_ec_params = NULL;  size_t cka_ec_params_len = 0;
+
+  for (int i = 0; i < template_len; i++) {
+    const void * const val = template[i].pValue;
+    const size_t       len = template[i].ulValueLen;
+    switch (template[i].type) {
+    case CKA_EC_POINT:  cka_ec_point  = val; cka_ec_point_len  = len; break;
+    case CKA_EC_PARAMS: cka_ec_params = val; cka_ec_params_len = len; break;
+    }
+  }
 
   int ok
-    = (sql_check_ok(sql_prepare(&q, select_format, flavor, CKA_EC_PARAMS, CKA_EC_POINT))        &&
-       sql_check_ok(sqlite3_bind_int64(q, 1, object_handle))                                    &&
-       sql_check_row(sqlite3_step(q))                                                           &&
-       sqlite3_column_type(q, 0) == SQLITE_BLOB                                                 &&
-       sqlite3_column_type(q, 1) == SQLITE_BLOB                                                 &&
-       ec_curve_oid_to_name(sqlite3_column_blob( q, 0), sqlite3_column_bytes(q, 0), &curve)     &&
+    = (ec_curve_oid_to_name(cka_ec_params, cka_ec_params_len, &curve) &&
        hal_check(hal_ecdsa_key_from_ecpoint(&key, keybuf, sizeof(keybuf),
-                                            sqlite3_column_blob( q, 1),
-                                            sqlite3_column_bytes(q, 1),
+                                            cka_ec_point, cka_ec_point_len,
                                             curve)));
 
   if (ok) {
     uint8_t der[hal_ecdsa_public_key_to_der_len(key)];
     hal_uuid_t uuid;
-    ok = (hal_check(hal_ecdsa_public_key_to_der(key, der, NULL, sizeof(der)))                   &&
+    ok = (hal_check(hal_ecdsa_public_key_to_der(key, der, NULL, sizeof(der))) &&
           hal_check(hal_rpc_pkey_load(p11_session_hal_client(session),
                                       p11_session_hal_session(session),
                                       &pkey, HAL_KEY_TYPE_EC_PUBLIC, curve,
-                                      &uuid, der, sizeof(der), flags))                          &&
-          p11_object_bind_pkey(session, object_handle, &uuid));
+                                      &uuid, der, sizeof(der), flags)));
+  }
+
+  if (ok) {
+    *phObject = p11_object_create(session, pkey, &uuid, flavor, template, template_len,
+                                  descriptor, extra, sizeof(extra)/sizeof(*extra));
+    ok = *phObject != CK_INVALID_HANDLE;
   }
 
   if (!ok && pkey.handle != HAL_HANDLE_NONE)
@@ -1597,193 +1324,157 @@ static inline int p11_object_create_ec_public_key(const p11_session_t * const se
   else
     (void) hal_rpc_pkey_close(pkey);
 
-  sqlite3_finalize(q);
   return ok;
 }
 
-static inline int p11_object_create_rsa_private_key(const p11_session_t * const session,
-                                                    const CK_OBJECT_HANDLE object_handle,
-                                                    const hal_key_flags_t flags,
-                                                    const CK_ATTRIBUTE_PTR template,
-                                                    const CK_ULONG template_len)
+static int p11_object_create_rsa_private_key(const p11_session_t * const session,
+                                             const handle_flavor_t flavor,
+                                             const CK_ATTRIBUTE_PTR template,
+                                             const CK_ULONG template_len,
+                                             const p11_descriptor_t * const descriptor,
+                                             CK_OBJECT_HANDLE_PTR phObject,
+                                             const hal_key_flags_t flags)
 {
-  static const char select_format[] =
-    " WITH a (type, value) "
-    "   AS (SELECT type, value FROM %s_attribute NATURAL JOIN object WHERE object_handle = ?1)"
-    " SELECT a1.value, a2.value FROM a AS a1, a AS a2 WHERE a1.type = %u AND a2.type = %u";
-
-  const char *flavor = is_token_handle(object_handle) ? "token" : "session";
+  const hal_rpc_pkey_attributes extra[] = {
+    {.type = CKA_LOCAL,             .value = &const_CKA_FALSE, .length = sizeof(const_CKA_FALSE)},
+    {.type = CKA_ALWAYS_SENSITIVE,  .value = &const_CKA_FALSE, .length = sizeof(const_CKA_FALSE)},
+    {.type = CKA_NEVER_EXTRACTABLE, .value = &const_CKA_FALSE, .length = sizeof(const_CKA_FALSE)}
+  };
 
   hal_pkey_handle_t pkey = {HAL_HANDLE_NONE};
   uint8_t keybuf[hal_rsa_key_t_size];
   hal_rsa_key_t *key = NULL;
-  sqlite3_stmt *q = NULL;
+  hal_uuid_t uuid;
 
+  const uint8_t *cka_modulus          = NULL;   size_t cka_modulus_len          = 0;
   const uint8_t *cka_private_exponent = NULL;   size_t cka_private_exponent_len = 0;
-  const uint8_t *cka_prime_1 = NULL;            size_t cka_prime_1_len = 0;
-  const uint8_t *cka_prime_2 = NULL;            size_t cka_prime_2_len = 0;
-  const uint8_t *cka_exponent_1 = NULL;         size_t cka_exponent_1_len = 0;
-  const uint8_t *cka_exponent_2 = NULL;         size_t cka_exponent_2_len = 0;
-  const uint8_t *cka_coefficient = NULL;        size_t cka_coefficient_len = 0;
+  const uint8_t *cka_prime_1          = NULL;   size_t cka_prime_1_len          = 0;
+  const uint8_t *cka_prime_2          = NULL;   size_t cka_prime_2_len          = 0;
+  const uint8_t *cka_exponent_1       = NULL;   size_t cka_exponent_1_len       = 0;
+  const uint8_t *cka_exponent_2       = NULL;   size_t cka_exponent_2_len       = 0;
+  const uint8_t *cka_coefficient      = NULL;   size_t cka_coefficient_len      = 0;
+
+  const uint8_t *cka_public_exponent = const_0x010001;
+  size_t cka_public_exponent_len = sizeof(const_0x010001);
 
   for (int i = 0; i < template_len; i++) {
+    const void * const val = template[i].pValue;
+    const size_t       len = template[i].ulValueLen;
     switch (template[i].type) {
-    case CKA_PRIVATE_EXPONENT:
-      cka_private_exponent = template[i].pValue;      	cka_private_exponent_len = template[i].ulValueLen;
-      break;
-    case CKA_PRIME_1:
-      cka_prime_1 = template[i].pValue;                	cka_prime_1_len = template[i].ulValueLen;
-      break;
-    case CKA_PRIME_2:
-      cka_prime_2 = template[i].pValue;                	cka_prime_2_len = template[i].ulValueLen;
-      break;
-    case CKA_EXPONENT_1:
-      cka_exponent_1 = template[i].pValue;             	cka_exponent_1_len = template[i].ulValueLen;
-      break;
-    case CKA_EXPONENT_2:
-      cka_exponent_2 = template[i].pValue;             	cka_exponent_2_len = template[i].ulValueLen;
-      break;
-    case CKA_COEFFICIENT:
-      cka_coefficient = template[i].pValue;            	cka_coefficient_len = template[i].ulValueLen;
-      break;
+    case CKA_MODULUS:          cka_modulus          = val; cka_modulus_len          = len; break;
+    case CKA_PUBLIC_EXPONENT:  cka_public_exponent  = val; cka_public_exponent_len  = len; break;
+    case CKA_PRIVATE_EXPONENT: cka_private_exponent = val; cka_private_exponent_len = len; break;
+    case CKA_PRIME_1:          cka_prime_1          = val; cka_prime_1_len          = len; break;
+    case CKA_PRIME_2:          cka_prime_2          = val; cka_prime_2_len          = len; break;
+    case CKA_EXPONENT_1:       cka_exponent_1       = val; cka_exponent_1_len       = len; break;
+    case CKA_EXPONENT_2:       cka_exponent_2       = val; cka_exponent_2_len       = len; break;
+    case CKA_COEFFICIENT:      cka_coefficient      = val; cka_coefficient_len      = len; break;
     }
   }
 
-  int ok
-    = (sql_check_ok(sql_prepare(&q, select_format, flavor, CKA_MODULUS, CKA_PUBLIC_EXPONENT))   &&
-       sql_check_ok(sqlite3_bind_int64(q, 1, object_handle))                                    &&
-       sql_check_row(sqlite3_step(q))                                                           &&
-       sqlite3_column_type(q, 0) == SQLITE_BLOB                                                 &&
-       sqlite3_column_type(q, 1) == SQLITE_BLOB                                                 &&
-       hal_check(hal_rsa_key_load_private(&key, keybuf, sizeof(keybuf),
-                                          sqlite3_column_blob( q, 0),   sqlite3_column_bytes(q, 0),
-                                          sqlite3_column_blob( q, 1),   sqlite3_column_bytes(q, 1),
-                                          cka_private_exponent,         cka_private_exponent_len,
-                                          cka_prime_1,                  cka_prime_1_len,
-                                          cka_prime_2,                  cka_prime_2_len,
-                                          cka_coefficient,              cka_coefficient_len,
-                                          cka_exponent_1,               cka_exponent_1_len,
-                                          cka_exponent_2,               cka_exponent_2_len)));
-
+  int ok = hal_check(hal_rsa_key_load_private(&key, keybuf, sizeof(keybuf),
+                                              cka_modulus,                  cka_modulus_len,
+                                              cka_public_exponent,          cka_public_exponent_len,
+                                              cka_private_exponent,         cka_private_exponent_len,
+                                              cka_prime_1,                  cka_prime_1_len,
+                                              cka_prime_2,                  cka_prime_2_len,
+                                              cka_coefficient,              cka_coefficient_len,
+                                              cka_exponent_1,               cka_exponent_1_len,
+                                              cka_exponent_2,               cka_exponent_2_len));
   if (ok) {
     uint8_t der[hal_rsa_private_key_to_der_len(key)];
-    hal_uuid_t uuid;
-    ok = (hal_check(hal_rsa_private_key_to_der(key, der, NULL, sizeof(der)))                    &&
+    ok = (hal_check(hal_rsa_private_key_to_der(key, der, NULL, sizeof(der))) &&
           hal_check(hal_rpc_pkey_load(p11_session_hal_client(session),
                                       p11_session_hal_session(session),
-                                      &pkey, HAL_KEY_TYPE_RSA_PRIVATE, HAL_CURVE_NONE,
-                                      &uuid, der, sizeof(der), flags))                          &&
-          p11_object_bind_pkey(session, object_handle, &uuid));
+                                      &pkey, HAL_KEY_TYPE_RSA_PRIVATE,
+                                      HAL_CURVE_NONE, &uuid,
+                                      der, sizeof(der), flags)));
     memset(der, 0, sizeof(der));
   }
 
   memset(keybuf, 0, sizeof(keybuf));
 
+  if (ok) {
+    *phObject = p11_object_create(session, pkey, &uuid, flavor, template, template_len,
+                                  descriptor, extra, sizeof(extra)/sizeof(*extra));
+    ok = *phObject != CK_INVALID_HANDLE;
+  }
+
   if (!ok && pkey.handle != HAL_HANDLE_NONE)
     (void) hal_rpc_pkey_delete(pkey);
   else
     (void) hal_rpc_pkey_close(pkey);
 
-  sqlite3_finalize(q);
   return ok;
 }
 
-static inline int p11_object_create_ec_private_key(const p11_session_t * const session,
-                                                   const CK_OBJECT_HANDLE object_handle,
-                                                   const hal_key_flags_t flags,
-                                                   const CK_ATTRIBUTE_PTR template,
-                                                   const CK_ULONG template_len)
+static int p11_object_create_ec_private_key(const p11_session_t * const session,
+                                            const handle_flavor_t flavor,
+                                            const CK_ATTRIBUTE_PTR template,
+                                            const CK_ULONG template_len,
+                                            const p11_descriptor_t * const descriptor,
+                                            CK_OBJECT_HANDLE_PTR phObject,
+                                            const hal_key_flags_t flags)
 {
-  static const char select_format[] =
-    " WITH a (type, value) "
-    "   AS (SELECT type, value FROM %s_attribute NATURAL JOIN object WHERE object_handle = ?1)"
-    " SELECT a1.value, a2.value FROM a AS a1, a AS a2 WHERE a1.type = %u AND a2.type = %u";
-
-  const char *flavor = is_token_handle(object_handle) ? "token" : "session";
+  const hal_rpc_pkey_attributes extra[] = {
+    {.type = CKA_LOCAL,             .value = &const_CKA_FALSE, .length = sizeof(const_CKA_FALSE)},
+    {.type = CKA_ALWAYS_SENSITIVE,  .value = &const_CKA_FALSE, .length = sizeof(const_CKA_FALSE)},
+    {.type = CKA_NEVER_EXTRACTABLE, .value = &const_CKA_FALSE, .length = sizeof(const_CKA_FALSE)}
+  };
 
   hal_pkey_handle_t pkey = {HAL_HANDLE_NONE};
   uint8_t keybuf[hal_ecdsa_key_t_size];
   hal_ecdsa_key_t *key = NULL;
   hal_curve_name_t curve;
-  sqlite3_stmt *q = NULL;
-  const uint8_t *ecpoint = NULL;
-  size_t ecpoint_len = 0;
 
-  const uint8_t *cka_value = NULL; size_t cka_value_len = 0;
+  const uint8_t *cka_value     = NULL;  size_t cka_value_len     = 0;
+  const uint8_t *cka_ec_point  = NULL;  size_t cka_ec_point_len  = 0;
+  const uint8_t *cka_ec_params = NULL;  size_t cka_ec_params_len = 0;
 
-  for (int i = 0; i < template_len; i++)
-    if (template[i].type == CKA_VALUE)
-      cka_value = template[i].pValue, cka_value_len = template[i].ulValueLen;
+  for (int i = 0; i < template_len; i++) {
+    const void * const val = template[i].pValue;
+    const size_t       len = template[i].ulValueLen;
+    switch (template[i].type) {
+    case CKA_VALUE:     cka_value     = val; cka_value_len     = len; break;
+    case CKA_EC_POINT:  cka_ec_point  = val; cka_ec_point_len  = len; break;
+    case CKA_EC_PARAMS: cka_ec_params = val; cka_ec_params_len = len; break;
+    }
+  }
 
   int ok
-    = (sql_check_ok(sql_prepare(&q, select_format, flavor, CKA_EC_PARAMS, CKA_EC_POINT))        &&
-       sql_check_ok(sqlite3_bind_int64(q, 1, object_handle))                                    &&
-       sql_check_row(sqlite3_step(q))                                                           &&
-       sqlite3_column_type(q, 0) == SQLITE_BLOB                                                 &&
-       sqlite3_column_type(q, 1) == SQLITE_BLOB                                                 &&
-       ec_curve_oid_to_name(sqlite3_column_blob( q, 0), sqlite3_column_bytes(q, 0), &curve)	&&
-       ((ecpoint_len = sqlite3_column_bytes(q, 1)) & 1) != 0                                    &&
-       *(ecpoint = sqlite3_column_blob( q, 1)) == 0x04                                          &&
+    = (ec_curve_oid_to_name(cka_ec_params, cka_ec_params_len, &curve) &&
        hal_check(hal_ecdsa_key_load_private(&key, keybuf, sizeof(keybuf), curve,
-                                            ecpoint + 1 + 0 * ecpoint_len / 2,  ecpoint_len / 2,
-                                            ecpoint + 1 + 0 * ecpoint_len / 2,  ecpoint_len / 2,
-                                            cka_value,                          cka_value_len)));
+                                            cka_ec_point + 1 + 0 * cka_ec_point_len / 2,
+                                            cka_ec_point_len / 2,
+                                            cka_ec_point + 1 + 1 * cka_ec_point_len / 2,
+                                            cka_ec_point_len / 2,
+                                            cka_value,
+                                            cka_value_len)));
 
   if (ok) {
     uint8_t der[hal_ecdsa_private_key_to_der_len(key)];
     hal_uuid_t uuid;
-    ok = (hal_check(hal_ecdsa_private_key_to_der(key, der, NULL, sizeof(der)))                  &&
+    ok = (hal_check(hal_ecdsa_private_key_to_der(key, der, NULL, sizeof(der))) &&
           hal_check(hal_rpc_pkey_load(p11_session_hal_client(session),
                                       p11_session_hal_session(session),
                                       &pkey, HAL_KEY_TYPE_EC_PRIVATE, curve,
-                                      &uuid, der, sizeof(der), flags))                          &&
-          p11_object_bind_pkey(session, object_handle, &uuid));
+                                      &uuid, der, sizeof(der), flags)));
     memset(der, 0, sizeof(der));
   }
 
   memset(keybuf, 0, sizeof(keybuf));
 
+  if (ok) {
+    *phObject = p11_object_create(session, pkey, &uuid, flavor, template, template_len,
+                                  descriptor, extra, sizeof(extra)/sizeof(*extra));
+    ok = *phObject != CK_INVALID_HANDLE;
+  }
+
   if (!ok && pkey.handle != HAL_HANDLE_NONE)
     (void) hal_rpc_pkey_delete(pkey);
   else
     (void) hal_rpc_pkey_close(pkey);
 
-  sqlite3_finalize(q);
-  return ok;
-}
-
-/*
- * Given a PKCS #11 object, obtain a libhal pkey handle.
- */
-
-static int p11_object_get_pkey_handle(const p11_session_t * const session,
-                                      const CK_OBJECT_HANDLE object_handle,
-                                      hal_pkey_handle_t *pkey_handle)
-{
-  static const char select_format[] =
-    " SELECT hal_pkey_uuid FROM %s_object NATURAL JOIN object WHERE object_handle = ?1";
-
-  hal_key_flags_t flags = is_token_handle(object_handle) ? HAL_KEY_FLAG_TOKEN : 0;
-  const char *flavor = is_token_handle(object_handle) ? "token" : "session";
-  sqlite3_stmt *q = NULL;
-  int ok = 0;
-
-  assert(pkey_handle != NULL);
-
-  if (!sql_check_ok(sql_prepare(&q, select_format, flavor))     ||
-      !sql_check_ok(sqlite3_bind_int64(q, 1, object_handle))    ||
-      !sql_check_row(sqlite3_step(q))                           ||
-      sqlite3_column_type(q, 0) != SQLITE_BLOB                  ||
-      sqlite3_column_bytes(q, 0) != sizeof(hal_uuid_t))
-    goto fail;
-
-  const hal_uuid_t * const pkey_uuid = sqlite3_column_blob(q, 0);
-
-  ok = hal_check(hal_rpc_pkey_open(p11_session_hal_client(session), p11_session_hal_session(session),
-                                   pkey_handle, pkey_uuid, flags));
-
- fail:
-  sqlite3_finalize(q);
   return ok;
 }
 
@@ -1797,12 +1488,32 @@ static int p11_object_get_pkey_handle(const p11_session_t * const session,
  * Create a new session.
  */
 
-static p11_session_t *p11_session_new(void)
+static p11_session_t *p11_session_allocate(void)
 {
-  p11_session_t *session = malloc(sizeof(*session));
-  if (session == NULL)
+  static unsigned next_index, nonce;
+  const unsigned  last_index = next_index;
+  p11_session_t *session = NULL;
+
+  if (p11_sessions_in_use >= sizeof(p11_sessions) / sizeof(*p11_sessions))
     return NULL;
+
+  do {
+
+    next_index = (next_index + 1) % (sizeof(p11_sessions) / sizeof(*p11_sessions));
+
+    if (next_index == last_index)
+      return NULL;
+
+    if (next_index == 0)
+      ++nonce;
+
+    session = p11_sessions[next_index];
+
+  } while (session.handle == CK_INVALID_HANDLE);
+
   memset(session, 0, sizeof(*session));
+  session.handle = handle_compose(handle_flavor_session, nonce, next_handle);
+  p11_sessions_in_use++;
   return session;
 }
 
@@ -1815,137 +1526,89 @@ static void p11_session_free(p11_session_t *session)
   if (session == NULL)
     return;
 
-  sql_finalize_and_clear(&session->find_query);
+  assert(p11_sessions_in_use > 0);
+
+  if (session->find_query)
+    free(session->find_query);
+
   (void) hal_rpc_hash_finalize(session->digest_handle, NULL, 0);
   (void) hal_rpc_hash_finalize(session->sign_digest_handle, NULL, 0);
   (void) hal_rpc_hash_finalize(session->verify_digest_handle, NULL, 0);
-  free(session);
+
+  memset(session, 0, sizeof(*session));
+
+  if (--p11_sessions_in_use == 0)
+    logged_in_as = not_logged_in;
 }
 
 /*
- * Assign a handle to a session and add the session to SQL.
+ * Find a session.
  */
 
-static int p11_session_add(p11_session_t *session)
+static p11_session_t *p11_session_find(const CK_SESSION_HANDLE session_handle)
 {
-  static const char insert_session[] =
-    " INSERT INTO session (session_handle) VALUES (?)";
-
-  sqlite3_stmt *q = NULL;
-  int ok = 0;
+  if (handle_flavor(session_handle) != handle_flavor_session)
+    return NULL;
 
-  assert(session != NULL);
+  const unsigned index = handle_index(session_handle);
 
-  session->handle = p11_allocate_unused_handle(handle_flavor_session);
+  if (index >= sizeof(p11_sessions) / sizeof(*p11_sessions))
+    return NULL;
 
-  if (!sql_check_ok(sql_prepare(&q, insert_session))            ||
-      !sql_check_ok(sqlite3_bind_int64(q, 1, session->handle))  ||
-      !sql_check_done(sqlite3_step(q)))
-    goto fail;
+  p11_session_t *session = &p11_sessions[index];
 
-  session->link = p11_sessions;
-  p11_sessions = session;
-  ok = 1;
+  if (session->handle != session_handle)
+    return NULL;
 
- fail:
-  sqlite3_finalize(q);
-  return ok;
+  return session;
 }
 
 /*
- * Find a session.
+ * Iterate over session handles.  Start with CK_INVALID_HANDLE,
+ * returns CK_INVALID_HANDLE when done.
  *
- * Since we don't expect the total number of sessions to be all that
- * high, we use a linked list with a move-to-the-front search.  Some
- * of the other session methods assume this behavior, so be careful if
- * you decide to change it.
+ * This does not verify the provided session handle, because we want
+ * to be able to modify the sessions this finds, including deleting
+ * them (which invalidates the session handle).  Don't trust the
+ * returned handle until it has been blessed by p11_session_find().
  */
 
-static p11_session_t *p11_session_find(const CK_SESSION_HANDLE session_handle)
+static CK_SESSION_HANDLE p11_session_handle_iterate(const CK_SESSION_HANDLE session_handle)
 {
-  p11_session_t **link, *session;
-
-  for (link = &p11_sessions;
-       (session = *link) != NULL && session->handle != session_handle;
-       link = &session->link)
-    ;
+  if (handle_flavor(session_handle) != handle_flavor_session)
+    return CK_HANDLE_INVALID;
 
-  if (session != NULL && link != &p11_sessions) {
-    *link = session->link;
-    session->link = p11_sessions;
-    p11_sessions = session;
-  }
+  for (unsigned index = session_handle == CK_INVALID_HANDLE ? 0 : handle_index(session_handle) + 1;
+       index < sizeof(p11_sessions) / sizeof(*p11_sessions);
+       index++)
+    if (handle_flavor(p11_sessions[index].handle) == handle_flavor_session)
+      return p11_sessions[index].handle;
 
-  return session;
+  return CK_HANDLE_INVALID;
 }
 
 /*
- * Delete a session: remove it from SQL and free the session data
- * structure.
- *
- * This method assumes the move-to-the-front behavior of
- * p11_session_find().
+ * Same thing, but return session objects instead of session handles.
+ * This is just syntactic sugar around a common idiom.
  */
 
-static CK_RV p11_session_delete(const CK_SESSION_HANDLE session_handle)
+static p11_session_t *p11_session_iterate(p11_session_t *session)
 {
-  static const char delete_session[] =
-    " DELETE FROM session WHERE session_handle = ?";
-
-  p11_session_t *session = p11_session_find(session_handle);
-  sqlite3_stmt *q = NULL;
-  CK_RV rv = CKR_OK;
-
   if (session == NULL)
-    return CKR_SESSION_HANDLE_INVALID;
-
-  if (!sql_check_ok(sql_prepare(&q, delete_session))            ||
-      !sql_check_ok(sqlite3_bind_int64(q, 1, session_handle))   ||
-      !sql_check_done(sqlite3_step(q)))
-    lose(CKR_FUNCTION_FAILED);
-
-  /* Check that move-to-the-front behaved as expected */
-  assert(p11_sessions == session);
-
-  p11_sessions = session->link;
-  p11_session_free(session);
-
-  /* Deleting last session also logs us out */
-  if (p11_sessions == NULL)
-    logged_in_as = not_logged_in;
+    return NULL;
 
- fail:
-  sqlite3_finalize(q);
-  return rv;
+  return p11_session_find(p11_session_handle_iterate(session->handle));
 }
 
 /*
- * Delete all sessions.
+ * Delete all sessions.  Have to use p11_session_handle_iterate() here.
  */
 
-#warning Should this also clear the object table?
-
-static CK_RV p11_session_delete_all(void)
+static void p11_session_free_all(void)
 {
-  static const char delete_all_sessions[] =
-    " DELETE FROM session";
-
-  p11_session_t *session;
-  CK_RV rv = CKR_OK;
-
-  if (!sql_exec(delete_all_sessions))
-    lose(CKR_FUNCTION_FAILED);
-
-  while (p11_sessions != NULL) {
-    session = p11_sessions;
-    p11_sessions = session->link;
-    p11_session_free(session);
-  }
-
-  logged_in_as = not_logged_in;
-
- fail:
-  return rv;
+  for (CK_SESSION_HANDLE handle = p11_session_handle_iterate(CK_INVALID_HANDLE);
+       handle != CK_INVALID_HANDLE; handle = p11_session_handle_iterate(handle))
+    p11_session_free(p11_session_find(handle));
 }
 
 /*
@@ -1956,24 +1619,25 @@ static CK_RV p11_session_delete_all(void)
 
 static int p11_session_consistent_login(void)
 {
-  p11_session_t *session;
-
   switch (logged_in_as) {
 
   case not_logged_in:
-    for (session = p11_sessions; session != NULL; session = session->link)
+    for (p11_session_t *session = p11_session_iterate(NULL);
+         session != NULL; session = p11_session_iterate(session))
       if (session->state != CKS_RO_PUBLIC_SESSION && session->state != CKS_RW_PUBLIC_SESSION)
         return 0;
     return 1;
 
   case logged_in_as_user:
-    for (session = p11_sessions; session != NULL; session = session->link)
+    for (p11_session_t *session = p11_session_iterate(NULL);
+         session != NULL; session = p11_session_iterate(session))
       if (session->state != CKS_RO_USER_FUNCTIONS && session->state != CKS_RW_USER_FUNCTIONS)
         return 0;
     return 1;
 
   case logged_in_as_so:
-    for (session = p11_sessions; session != NULL; session = session->link)
+    for (p11_session_t *session = p11_session_iterate(NULL);
+         session != NULL; session = p11_session_iterate(session))
       if (session->state != CKS_RW_SO_FUNCTIONS)
         return 0;
     return 1;
@@ -2255,27 +1919,33 @@ static CK_RV p11_check_keypair_attributes(const p11_session_t *session,
  */
 
 static CK_RV generate_keypair_rsa_pkcs(p11_session_t *session,
+                                       const handle_flavor_t public_handle_flavor,
                                        const CK_ATTRIBUTE_PTR pPublicKeyTemplate,
                                        const CK_ULONG ulPublicKeyAttributeCount,
-                                       const CK_OBJECT_HANDLE public_handle,
+                                       const p11_descriptor_t *public_descriptor,
+                                       CK_OBJECT_HANDLE_PTR phPublicKey,
                                        const hal_key_flags_t public_flags,
+                                       const handle_flavor_t private_handle_flavor,
                                        const CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
                                        const CK_ULONG ulPrivateKeyAttributeCount,
-                                       const CK_OBJECT_HANDLE private_handle,
-                                       const hal_key_flags_t private_flags)
+                                       const p11_descriptor_t *private_descriptor,
+                                       CK_OBJECT_HANDLE_PTR phPrivateKey,
+                                       const hal_key_flags_t private_flags,
+                                       const CK_MECHANISM_PTR pMechanism)
 {
-  const int same_keystore = is_token_handle(public_handle) == is_token_handle(private_handle);
+  hal_pkey_handle_t public_pkey = {HAL_HANDLE_NONE}, private_pkey = {HAL_HANDLE_NONE};
   const uint8_t *public_exponent = const_0x010001;
   size_t public_exponent_len = sizeof(const_0x010001);
-  hal_pkey_handle_t pkey1 = {HAL_HANDLE_NONE}, pkey2 = {HAL_HANDLE_NONE};
+  hal_uuid_t public_uuid, private_uuid;
   CK_ULONG keysize = 0;
-  hal_uuid_t uuid;
   CK_RV rv;
-  int i;
 
-  assert(session != NULL && pPublicKeyTemplate != NULL && pPrivateKeyTemplate != NULL);
+  assert(pPublicKeyTemplate != NULL && pPrivateKeyTemplate != NULL &&
+         public_descriptor  != NULL && private_descriptor  != NULL &&
+         phPublicKey        != NULL && phPrivateKey        != NULL &&
+         session            != NULL && pMechanism          != NULL);
 
-  for (i = 0; i < ulPublicKeyAttributeCount; i++) {
+  for (int i = 0; i < ulPublicKeyAttributeCount; i++) {
     const CK_ATTRIBUTE_TYPE type = pPublicKeyTemplate[i].type;
     const void * const       val = pPublicKeyTemplate[i].pValue;
     const size_t             len = pPublicKeyTemplate[i].ulValueLen;
@@ -2298,47 +1968,58 @@ static CK_RV generate_keypair_rsa_pkcs(p11_session_t *session,
   if (keysize == 0)
     return CKR_TEMPLATE_INCOMPLETE;
 
-  if (!hal_check(hal_rpc_pkey_generate_rsa(p11_session_hal_client(session),
-                                           p11_session_hal_session(session),
-                                           &pkey1, &uuid, keysize,
-                                           public_exponent, public_exponent_len,
-                                           private_flags))                                      ||
-      !p11_object_bind_pkey(session, private_handle, &uuid))
-    lose(CKR_FUNCTION_FAILED);
-
   {
-    uint8_t der[hal_rpc_pkey_get_public_key_len(pkey1)], keybuf[hal_rsa_key_t_size];
+    if (!hal_check(hal_rpc_pkey_generate_rsa(p11_session_hal_client(session),
+                                             p11_session_hal_session(session),
+                                             &private_pkey, &private_uuid, keysize,
+                                             public_exponent, public_exponent_len,
+                                             private_flags)))
+      lose(CKR_FUNCTION_FAILED);
+
+    uint8_t der[hal_rpc_pkey_get_public_key_len(private_pkey)], keybuf[hal_rsa_key_t_size];
     size_t der_len, modulus_len;
     hal_rsa_key_t *key = NULL;
 
-    if (!hal_check(hal_rpc_pkey_get_public_key(pkey1, der, &der_len, sizeof(der)))              ||
+    if (!hal_check(hal_rpc_pkey_get_public_key(private_pkey, der, &der_len, sizeof(der)))       ||
         !hal_check(hal_rsa_public_key_from_der(&key, keybuf, sizeof(keybuf), der, der_len))     ||
+        !hal_check(hal_rpc_pkey_load(p11_session_hal_client(session),
+                                     p11_session_hal_session(session),
+                                     &public_pkey, HAL_KEY_TYPE_RSA_PUBLIC, HAL_CURVE_NONE,
+                                     &public_uuid, der, der_len, public_flags))                 ||
         !hal_check(hal_rsa_key_get_modulus(key, NULL, &modulus_len, 0)))
       lose(CKR_FUNCTION_FAILED);
 
     uint8_t modulus[modulus_len];
 
-    if (!hal_check(hal_rsa_key_get_modulus(key, modulus, NULL, sizeof(modulus)))                ||
-        !p11_attribute_set(public_handle,  CKA_MODULUS, modulus, modulus_len)                   ||
-        !p11_attribute_set(private_handle, CKA_MODULUS, modulus, modulus_len))
+    if (!hal_check(hal_rsa_key_get_modulus(key, modulus, NULL, sizeof(modulus))))
       lose(CKR_FUNCTION_FAILED);
 
-    if (!same_keystore                                                                          &&
-        !hal_check(hal_rpc_pkey_load(p11_session_hal_client(session),
-                                     p11_session_hal_session(session),
-                                     &pkey2, HAL_KEY_TYPE_RSA_PUBLIC, HAL_CURVE_NONE,
-                                     &uuid, der, der_len, public_flags)))
+    const hal_rpc_pkey_attribute_t extra[] = {
+      {.type   = CKA_LOCAL,
+       .value  = &const_CK_TRUE,        .length = sizeof(const_CK_TRUE)},
+      {.type   = CKA_KEY_GEN_MECHANISM,
+       .value  = &mechanism->mechanism, .length = sizeof(mechanism->mechanism)},
+      {.type   = CKA_MODULUS,
+       value   = modulus,               .length = modulus_len}
+    };
+
+    *phPrivateKey = p11_object_create(session, private_pkey, &private_uuid, private_handle_flavor,
+                                      pPrivateKeyTemplate, ulPrivateKeyAttributeCount,
+                                      private_descriptor, extra, sizeof(extra)/sizeof(*extra));
+
+    *phPublicKey = p11_object_create(session, public_handle_flavor,
+                                     pPublicKeyTemplate, ulPublicKeyAttributeCount,
+                                     public_descriptor, extra, sizeof(extra)/sizeof(*extra));
+
+    if (*phPrivateKey == CK_INVALID_HANDLE || *phPublicKey == CK_INVALID_HANDLE)
       lose(CKR_FUNCTION_FAILED);
   }
 
-  if (!p11_object_bind_pkey(session, public_handle,  &uuid))
-    lose(CKR_FUNCTION_FAILED);
-
   rv = CKR_OK;
 
  fail:
-  hal_rpc_pkey_close(pkey1);
-  hal_rpc_pkey_close(pkey2);
+  hal_rpc_pkey_close(private_pkey);
+  hal_rpc_pkey_close(public_pkey);
   return rv;
 }
 
@@ -2347,27 +2028,30 @@ static CK_RV generate_keypair_rsa_pkcs(p11_session_t *session,
  */
 
 static CK_RV generate_keypair_ec(p11_session_t *session,
+                                 const handle_flavor_t public_handle_flavor,
                                  const CK_ATTRIBUTE_PTR pPublicKeyTemplate,
                                  const CK_ULONG ulPublicKeyAttributeCount,
-                                 const CK_OBJECT_HANDLE public_handle,
+                                 const p11_descriptor_t *public_descriptor,
+                                 CK_OBJECT_HANDLE_PTR phPublicKey,
                                  const hal_key_flags_t public_flags,
+                                 const handle_flavor_t private_handle_flavor,
                                  const CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
                                  const CK_ULONG ulPrivateKeyAttributeCount,
-                                 const CK_OBJECT_HANDLE private_handle,
-                                 const hal_key_flags_t private_flags)
+                                 const p11_descriptor_t *private_descriptor,
+                                 CK_OBJECT_HANDLE_PTR phPrivateKey,
+                                 const hal_key_flags_t private_flags,
+                                 const CK_MECHANISM_PTR pMechanism)
 {
-  const int same_keystore = is_token_handle(public_handle) == is_token_handle(private_handle);
-  hal_pkey_handle_t pkey1 = {HAL_HANDLE_NONE}, pkey2 = {HAL_HANDLE_NONE};
+  hal_pkey_handle_t public_pkey = {HAL_HANDLE_NONE}, private_pkey = {HAL_HANDLE_NONE};
   const CK_BYTE *params = NULL;
   hal_curve_name_t curve;
   size_t params_len;
-  hal_uuid_t uuid;
+  hal_uuid_t public_uuid, private_uuid;
   CK_RV rv;
-  int i;
 
   assert(session != NULL && pPublicKeyTemplate != NULL && pPrivateKeyTemplate != NULL);
 
-  for (i = 0; i < ulPublicKeyAttributeCount; i++) {
+  for (int i = 0; i < ulPublicKeyAttributeCount; i++) {
     const CK_ATTRIBUTE_TYPE type = pPublicKeyTemplate[i].type;
     const void * const       val = pPublicKeyTemplate[i].pValue;
     const size_t             len = pPublicKeyTemplate[i].ulValueLen;
@@ -2384,45 +2068,59 @@ static CK_RV generate_keypair_ec(p11_session_t *session,
   if (!ec_curve_oid_to_name(params, params_len, &curve))
     return CKR_TEMPLATE_INCOMPLETE;
 
-  if (!hal_check(hal_rpc_pkey_generate_ec(p11_session_hal_client(session),
-                                          p11_session_hal_session(session),
-                                          &pkey1, &uuid, curve, private_flags))                 ||
-      !p11_object_bind_pkey(session, private_handle, &uuid)                                     ||
-      !p11_attribute_set(public_handle,  CKA_EC_PARAMS, params, params_len)                     ||
-      !p11_attribute_set(private_handle, CKA_EC_PARAMS, params, params_len))
-    lose(CKR_FUNCTION_FAILED);
-
   {
-    uint8_t der[hal_rpc_pkey_get_public_key_len(pkey1)], keybuf[hal_ecdsa_key_t_size];
+
+    if (!hal_check(hal_rpc_pkey_generate_ec(p11_session_hal_client(session),
+                                            p11_session_hal_session(session),
+                                            &private_pkey, &private_uuid,
+                                            curve, private_flags)))
+      lose(CKR_FUNCTION_FAILED);
+
+    uint8_t der[hal_rpc_pkey_get_public_key_len(private_pkey)], keybuf[hal_ecdsa_key_t_size];
     hal_ecdsa_key_t *key = NULL;
     size_t der_len;
 
-    if (!hal_check(hal_rpc_pkey_get_public_key(pkey1, der, &der_len, sizeof(der)))              ||
-        !hal_check(hal_ecdsa_public_key_from_der(&key, keybuf, sizeof(keybuf), der, der_len)))
+    if (!hal_check(hal_rpc_pkey_get_public_key(private_pkey, der, &der_len, sizeof(der)))       ||
+        !hal_check(hal_ecdsa_public_key_from_der(&key, keybuf, sizeof(keybuf), der, der_len))   ||
+        !hal_check(hal_rpc_pkey_load(p11_session_hal_client(session),
+                                     p11_session_hal_session(session),
+                                     &public_pkey, HAL_KEY_TYPE_EC_PUBLIC, curve,
+                                     &public_uuid, der, der_len, public_flags)))
       lose(CKR_FUNCTION_FAILED);
 
     uint8_t point[hal_ecdsa_key_to_ecpoint_len(key)];
 
-    if (!hal_check(hal_ecdsa_key_to_ecpoint(key, point, NULL, sizeof(point)))                   ||
-        !p11_attribute_set(public_handle,  CKA_EC_POINT, point, sizeof(point)))
+    if (!hal_check(hal_ecdsa_key_to_ecpoint(key, point, NULL, sizeof(point))))
       lose(CKR_FUNCTION_FAILED);
 
-    if (!same_keystore                                                                          &&
-        !hal_check(hal_rpc_pkey_load(p11_session_hal_client(session),
-                                     p11_session_hal_session(session),
-                                     &pkey2, HAL_KEY_TYPE_EC_PUBLIC, curve,
-                                     &uuid, der, der_len, public_flags)))
+    const hal_rpc_pkey_attribute_t extra[] = {
+      {.type   = CKA_LOCAL,
+       .value  = &const_CK_TRUE,        .length = sizeof(const_CK_TRUE)},
+      {.type   = CKA_KEY_GEN_MECHANISM,
+       .value  = &mechanism->mechanism, .length = sizeof(mechanism->mechanism)},
+      {.type   = CKA_EC_PARAMS,
+       value   = params,                .length = params_len},
+      {.type   = CKA_EC_POINT,
+       value   = point,                 .length = sizeof(point)}
+    };
+
+    *phPrivateKey = p11_object_create(session, private_pkey, &private_uuid, private_handle_flavor,
+                                      pPrivateKeyTemplate, ulPrivateKeyAttributeCount,
+                                      private_descriptor, extra, sizeof(extra)/sizeof(*extra) - 1);
+
+    *phPublicKey = p11_object_create(session, public_handle_flavor,
+                                     pPublicKeyTemplate, ulPublicKeyAttributeCount,
+                                     public_descriptor, extra, sizeof(extra)/sizeof(*extra));
+
+    if (*phPrivateKey == CK_INVALID_HANDLE || *phPublicKey == CK_INVALID_HANDLE)
       lose(CKR_FUNCTION_FAILED);
   }
 
-  if (!p11_object_bind_pkey(session, public_handle, &uuid))
-    lose(CKR_FUNCTION_FAILED);
-
   rv = CKR_OK;
 
  fail:
-  hal_rpc_pkey_close(pkey1);
-  hal_rpc_pkey_close(pkey2);
+  hal_rpc_pkey_close(private_pkey);
+  hal_rpc_pkey_close(public_pkey);
   return rv;
 }
 
@@ -2434,16 +2132,19 @@ static CK_RV generate_keypair_ec(p11_session_t *session,
 static CK_RV generate_keypair(p11_session_t *session,
                               const CK_MECHANISM_PTR pMechanism,
                               CK_RV (*mechanism_handler)(p11_session_t *session,
-
+                                                         const handle_flavor_t public_handle_flavor,
                                                          const CK_ATTRIBUTE_PTR pPublicKeyTemplate,
                                                          const CK_ULONG ulPublicKeyAttributeCount,
-                                                         const CK_OBJECT_HANDLE public_handle,
+                                                         const p11_descriptor_t *public_descriptor,
+                                                         CK_OBJECT_HANDLE_PTR phPublicKey,
                                                          const hal_key_flags_t public_flags,
-
+                                                         const handle_flavor_t private_handle_flavor,
                                                          const CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
                                                          const CK_ULONG ulPrivateKeyAttributeCount,
-                                                         const CK_OBJECT_HANDLE private_handle,
-                                                         const hal_key_flags_t private_flags),
+                                                         const p11_descriptor_t *private_descriptor,
+                                                         CK_OBJECT_HANDLE_PTR phPrivateKey,
+                                                         const hal_key_flags_t private_flags,
+                                                         const CK_MECHANISM_PTR pMechanism),
                               const CK_ATTRIBUTE_PTR pPublicKeyTemplate,
                               const CK_ULONG ulPublicKeyAttributeCount,
                               const p11_descriptor_t * const public_descriptor,
@@ -2453,19 +2154,17 @@ static CK_RV generate_keypair(p11_session_t *session,
                               const p11_descriptor_t * const private_descriptor,
                               CK_OBJECT_HANDLE_PTR phPrivateKey)
 {
-  CK_OBJECT_HANDLE public_handle = CK_INVALID_HANDLE;
-  CK_OBJECT_HANDLE private_handle = CK_INVALID_HANDLE;
-  handle_flavor_t public_handle_flavor = handle_flavor_session_object;
+  handle_flavor_t public_handle_flavor  = handle_flavor_session_object;
   handle_flavor_t private_handle_flavor = handle_flavor_session_object;
-  hal_key_flags_t public_flags = 0;
+  hal_key_flags_t public_flags  = 0;
   hal_key_flags_t private_flags = 0;
-  int in_transaction = 0;
   CK_RV rv;
-  int i;
 
   rv = p11_check_keypair_attributes(session,
-                                    pPublicKeyTemplate,  ulPublicKeyAttributeCount,  public_descriptor,  &public_flags,
-                                    pPrivateKeyTemplate, ulPrivateKeyAttributeCount, private_descriptor, &private_flags);
+                                    pPublicKeyTemplate,  ulPublicKeyAttributeCount,
+                                    public_descriptor,   &public_flags,
+                                    pPrivateKeyTemplate, ulPrivateKeyAttributeCount,
+                                    private_descriptor,  &private_flags);
   if (rv != CKR_OK)
     return rv;
 
@@ -2473,52 +2172,26 @@ static CK_RV generate_keypair(p11_session_t *session,
          pPublicKeyTemplate  != NULL && phPublicKey  != NULL &&
          pPrivateKeyTemplate != NULL && phPrivateKey != NULL);
 
-  for (i = 0; i < ulPublicKeyAttributeCount; i++)
+  for (int i = 0; i < ulPublicKeyAttributeCount; i++)
     if (pPublicKeyTemplate[i].type == CKA_TOKEN)
-      public_handle_flavor = p11_handle_flavor_from_cka_token(pPublicKeyTemplate[i].pValue);
+      public_handle_flavor = p11_object_flavor_from_cka_token(pPublicKeyTemplate[i].pValue);
 
-  for (i = 0; i < ulPrivateKeyAttributeCount; i++)
+  for (int i = 0; i < ulPrivateKeyAttributeCount; i++)
     if (pPrivateKeyTemplate[i].type == CKA_TOKEN)
-      private_handle_flavor = p11_handle_flavor_from_cka_token(pPrivateKeyTemplate[i].pValue);
+      private_handle_flavor = p11_object_flavor_from_cka_token(pPrivateKeyTemplate[i].pValue);
 
   if (public_handle_flavor == handle_flavor_token_object)
-    public_flags |= HAL_KEY_FLAG_TOKEN;
+    public_flags  |= HAL_KEY_FLAG_TOKEN;
 
   if (private_handle_flavor == handle_flavor_token_object)
     private_flags |= HAL_KEY_FLAG_TOKEN;
 
-  if (!sql_exec("BEGIN"))
-    lose(CKR_FUNCTION_FAILED);
-
-  in_transaction = 1;
-
-  if ((public_handle = p11_object_create(session, public_handle_flavor,
-                                         pPublicKeyTemplate, ulPublicKeyAttributeCount,
-                                         public_descriptor, pMechanism)) == CK_INVALID_HANDLE)
-    lose(CKR_FUNCTION_FAILED);
-
-  if ((private_handle = p11_object_create(session, private_handle_flavor,
-                                          pPrivateKeyTemplate,  ulPrivateKeyAttributeCount,
-                                          private_descriptor, pMechanism)) == CK_INVALID_HANDLE)
-    lose(CKR_FUNCTION_FAILED);
-
-  rv = mechanism_handler(session,
-                         pPublicKeyTemplate,  ulPublicKeyAttributeCount,  public_handle,  public_flags,
-                         pPrivateKeyTemplate, ulPrivateKeyAttributeCount, private_handle, private_flags);
-  if (rv != CKR_OK)
-    goto fail;
-
-  if (!sql_exec("COMMIT"))
-    lose(CKR_FUNCTION_FAILED);
-
-  *phPublicKey  = public_handle;
-  *phPrivateKey = private_handle;
-  return CKR_OK;
-
- fail:
-  if (in_transaction && !sql_exec("ROLLBACK"))
-    rv = CKR_GENERAL_ERROR;
-  return rv;
+  return mechanism_handler(session,
+                           public_handle_flavor,  pPublicKeyTemplate,  ulPublicKeyAttributeCount,
+                           public_descriptor,     phPublicKey,         public_flags,
+                           private_handle_flavor, pPrivateKeyTemplate, ulPrivateKeyAttributeCount,
+                           private_descriptor,    phPrivateKey,        private_flags,
+                           pMechanism);
 }
 
 /*
@@ -2661,19 +2334,19 @@ static int get_signature_len(const CK_OBJECT_HANDLE object_handle,
   CK_BYTE oid[20];
   CK_ULONG len;
 
-  if (!p11_attribute_get_ulong(object_handle, CKA_KEY_TYPE, &cka_key_type))
+  if (!p11_attribute_get_ulong(session, object_handle, CKA_KEY_TYPE, &cka_key_type))
     return 0;
 
   switch (cka_key_type) {
 
   case CKK_RSA:
-    if (!p11_attribute_get(object_handle, CKA_MODULUS, NULL, &len, 0))
+    if (!p11_attribute_get(session, object_handle, CKA_MODULUS, NULL, &len, 0))
       return 0;
     *signature_len = len;
     return 1;
 
   case CKK_EC:
-    if (!p11_attribute_get(object_handle, CKA_EC_PARAMS, oid, &len, sizeof(oid)) ||
+    if (!p11_attribute_get(session, object_handle, CKA_EC_PARAMS, oid, &len, sizeof(oid)) ||
         !ec_curve_oid_to_name(oid, len, &curve))
       return 0;
     switch (curve) {
@@ -2703,7 +2376,7 @@ static CK_RV sign_hal_rpc(p11_session_t *session,
 
   assert(session != NULL && pulSignatureLen != NULL);
 
-  if (!p11_object_get_pkey_handle(session, session->sign_key_handle, &pkey))
+  if (!p11_object_pkey_open(session, session->sign_key_handle, &pkey))
     lose(CKR_FUNCTION_FAILED);
 
   if (!get_signature_len(session->sign_key_handle, pkey, &signature_len))
@@ -2738,7 +2411,7 @@ static CK_RV verify_hal_rpc(p11_session_t *session,
 
   assert(session != NULL);
 
-  if (!p11_object_get_pkey_handle(session, session->verify_key_handle, &pkey))
+  if (!p11_object_pkey_open(session, session->verify_key_handle, &pkey))
     lose(CKR_FUNCTION_FAILED);
 
   rv = p11_whine_from_hal(hal_rpc_pkey_verify(pkey, session->verify_digest_handle, pData, ulDataLen,
@@ -2763,8 +2436,6 @@ CK_RV C_Initialize(CK_VOID_PTR pInitArgs)
   ENTER_PUBLIC_FUNCTION(C_Initialize);
 
   CK_C_INITIALIZE_ARGS_PTR a = pInitArgs;
-  int initialized_sql = 0;
-  int initialized_rpc = 0;
   CK_RV rv;
 
   /*
@@ -2864,18 +2535,6 @@ CK_RV C_Initialize(CK_VOID_PTR pInitArgs)
   if (!hal_check(hal_rpc_client_init()))
     lose(CKR_GENERAL_ERROR);
 
-  initialized_rpc = 1;
-
-  /*
-   * Initialize SQLite3, opening the database(s) and loading the
-   * schema and views.
-   */
-
-  if (!sql_init())
-    lose(CKR_GENERAL_ERROR);
-
-  initialized_sql = 1;
-
 #if USE_POSIX
   initialized_pid = getpid();
 #endif
@@ -2883,13 +2542,6 @@ CK_RV C_Initialize(CK_VOID_PTR pInitArgs)
   return CKR_OK;
 
  fail:
-
-  if (initialized_sql)
-    sql_fini();
-
-  if (initialized_rpc)
-    hal_rpc_client_close();
-
   return rv;
 }
 
@@ -2911,14 +2563,7 @@ CK_RV C_Finalize(CK_VOID_PTR pReserved)
   p11_session_delete_all();
 
   /*
-   * Shut down SQLite3.
-   */
-
-  if (!sql_fini())
-    lose(CKR_GENERAL_ERROR);
-
-  /*
-   * By this point we're pretty well committed to shutting down, so
+   * At this point we're pretty well committed to shutting down, so
    * there's not much to be done if any of the rest of this fails.
    */
 
@@ -2933,10 +2578,6 @@ CK_RV C_Finalize(CK_VOID_PTR pReserved)
 #endif
 
   return rv;
-
- fail:
-  (void) mutex_unlock(p11_global_mutex);
-  return rv;
 }
 
 CK_RV C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList)
@@ -3113,7 +2754,7 @@ CK_RV C_OpenSession(CK_SLOT_ID slotID,
   if (parallel_session)
     lose(CKR_SESSION_PARALLEL_NOT_SUPPORTED);
 
-  if ((session = p11_session_new()) == NULL)
+  if ((session = p11_session_allocate()) == NULL)
     lose(CKR_HOST_MEMORY);
 
   switch (logged_in_as) {
@@ -3136,9 +2777,6 @@ CK_RV C_OpenSession(CK_SLOT_ID slotID,
   session->notify = Notify;
   session->application = pApplication;
 
-  if (!p11_session_add(session))
-    lose(CKR_FUNCTION_FAILED);
-
   assert(p11_session_consistent_login());
 
   if ((rv = mutex_unlock(p11_global_mutex)) != CKR_OK)
@@ -3188,7 +2826,6 @@ CK_RV C_Login(CK_SESSION_HANDLE hSession,
   ENTER_PUBLIC_FUNCTION(C_Login);
 
   const hal_client_handle_t client = {HAL_HANDLE_NONE};
-  p11_session_t *session;
   hal_user_t user = HAL_USER_NONE;
   CK_RV rv = CKR_OK;
 
@@ -3232,7 +2869,8 @@ CK_RV C_Login(CK_SESSION_HANDLE hSession,
     case logged_in_as_so:       lose(CKR_USER_ALREADY_LOGGED_IN);
     case logged_in_as_user:     lose(CKR_USER_ANOTHER_ALREADY_LOGGED_IN);
     }
-    for (session = p11_sessions; session != NULL; session = session->link)
+    for (p11_session_t *session = p11_session_iterate(NULL);
+         session != NULL; session = p11_session_iterate(session))
       if (session->state == CKS_RO_PUBLIC_SESSION)
         lose(CKR_SESSION_READ_ONLY_EXISTS);
     user = HAL_USER_SO;
@@ -3259,7 +2897,8 @@ CK_RV C_Login(CK_SESSION_HANDLE hSession,
 
   logged_in_as = userType == CKU_SO ? logged_in_as_so : logged_in_as_user;
 
-  for (session = p11_sessions; session != NULL; session = session->link) {
+  for (p11_session_t *session = p11_session_iterate(NULL);
+       session != NULL; session = p11_session_iterate(session)) {
     switch (session->state) {
 
     case CKS_RO_PUBLIC_SESSION:
@@ -3285,7 +2924,6 @@ CK_RV C_Logout(CK_SESSION_HANDLE hSession)
   ENTER_PUBLIC_FUNCTION(C_Logout);
 
   const hal_client_handle_t client = {HAL_HANDLE_NONE};
-  p11_session_t *session;
   CK_RV rv = CKR_OK;
 
   mutex_lock_or_return_failure(p11_global_mutex);
@@ -3303,36 +2941,108 @@ CK_RV C_Logout(CK_SESSION_HANDLE hSession)
     lose(CKR_USER_NOT_LOGGED_IN);
 
   /*
-   * Update global login state, then delete any private objects and
-   * whack every existing session into the right state.
+   * Delete any private objects and whack every existing session into
+   * the right state.
    */
 
-  assert(p11_session_consistent_login());
-
-  if ((rv = p11_whine_from_hal(hal_rpc_logout(client))) != CKR_OK)
-    goto fail;
-
-  logged_in_as = not_logged_in;
-
-  p11_object_delete_all_private();
-
-  for (session = p11_sessions; session != NULL; session = session->link) {
-    switch (session->state) {
+  {
+    assert(p11_session_consistent_login());
+
+    const hal_session_handle_t session_handle_none = {HAL_HANDLE_NONE};
+
+    const hal_rpc_pkey_attribute attrs[] = {
+      {.type = CKA_PRIVATE, .value = &const_CK_TRUE, .length = sizeof(const_CK_TRUE)}
+    };
+
+    hal_uuid_t uuids[64];
+    unsigned n;
+
+    memset(uuids, 0, sizeof(uuids));
+    do {
+
+      rv = p11_whine_from_hal(hal_rpc_pkey_match(p11_session_hal_client(session),
+                                                 session_handle_none,
+                                                 HAL_KEY_TYPE_NONE, HAL_CURVE_NONE,
+                                                 0,
+                                                 attrs, sizeof(attrs)/sizeof(*attrs),
+                                                 uuids, &n, sizeof(uuids)/sizeof(*uuuids),
+                                                 &uuids[sizeof(uuids)/sizeof(*uuuids) - 1]));
+      if (rv != CKR_OK)
+        goto fail;
+
+      for (int i = 0; i < n; i++) {
+        hal_pkey_handle_t pkey;
+        rv = p11_whine_from_hal(hal_rpc_pkey_open(p11_session_hal_client(session),
+                                                  session_handle_none, &pkey, &uuids[i], 0));
+        if (rv != CKR_OK)
+          goto fail;
+        if ((rv = p11_whine_from_hal(hal_rpc_pkey_delete(pkey))) != CKR_OK) {
+          (void) hal_rpc_pkey_close(pkey);
+          goto fail;
+        }
+      }
+
+      if (n > 0) {
+        for (CK_OBJECT_HANDLE handle = p11_object_handle_iterate(CK_INVALID_HANDLE);
+             handle != CK_INVALID_HANDLE; handle = p11_object_handle_iterate(handle)) {
+          p11_object_t *object = p11_object_find(handle);
+          assert(object != NULL);
+          if (bsearch(&object->uuid, uuids, n, sizeof(*uuids), hal_uuid_cmp) != NULL)
+            p11_object_free(object);
+        }
+      }
+
+    } while (n == sizeof(uuids)/sizeof(*uuuids));
+
+    memset(uuids, 0, sizeof(uuids));
+    do {
+
+      rv = p11_whine_from_hal(hal_rpc_pkey_match(p11_session_hal_client(session),
+                                                 session_handle_none,
+                                                 HAL_KEY_TYPE_NONE, HAL_CURVE_NONE,
+                                                 HAL_KEY_FLAG_TOKEN,
+                                                 attrs, sizeof(attrs)/sizeof(*attrs),
+                                                 uuids, &n, sizeof(uuids)/sizeof(*uuuids),
+                                                 &uuids[sizeof(uuids)/sizeof(*uuuids) - 1]));
+      if (rv != CKR_OK)
+        goto fail;
+
+      if (n > 0) {
+        for (CK_OBJECT_HANDLE handle = p11_object_handle_iterate(CK_INVALID_HANDLE);
+             handle != CK_INVALID_HANDLE; handle = p11_object_handle_iterate(handle)) {
+          p11_object_t *object = p11_object_find(handle);
+          assert(object != NULL);
+          if (bsearch(&object->uuid, uuids, n, sizeof(*uuids), hal_uuid_cmp) != NULL)
+            p11_object_free(object);
+        }
+      }
+
+    } while (n == sizeof(uuids)/sizeof(*uuuids));
+
+    for (p11_session_t *session = p11_session_iterate(NULL);
+         session != NULL; session = p11_session_iterate(session)) {
+      switch (session->state) {
+
+      case CKS_RO_USER_FUNCTIONS:
+        session->state = CKS_RO_PUBLIC_SESSION;
+        continue;
+
+      case CKS_RW_USER_FUNCTIONS:
+      case CKS_RW_SO_FUNCTIONS:
+        session->state = CKS_RW_PUBLIC_SESSION;
+        continue;
+
+      }
+    }
 
-    case CKS_RO_USER_FUNCTIONS:
-      session->state = CKS_RO_PUBLIC_SESSION;
-      continue;
+    if ((rv = p11_whine_from_hal(hal_rpc_logout(client))) != CKR_OK)
+      goto fail;
 
-    case CKS_RW_USER_FUNCTIONS:
-    case CKS_RW_SO_FUNCTIONS:
-      session->state = CKS_RW_PUBLIC_SESSION;
-      continue;
+    logged_in_as = not_logged_in;
 
-    }
+    assert(p11_session_consistent_login());
   }
 
-  assert(p11_session_consistent_login());
-
  fail:
   mutex_unlock_return_with_rv(rv, p11_global_mutex);
 }
@@ -3345,7 +3055,6 @@ CK_RV C_CreateObject(CK_SESSION_HANDLE hSession,
   ENTER_PUBLIC_FUNCTION(C_CreateObject);
 
   CK_OBJECT_HANDLE handle = CK_INVALID_HANDLE;
-  int in_transaction = 0;
   p11_session_t *session;
   CK_RV rv;
 
@@ -3385,8 +3094,7 @@ CK_RV C_CreateObject(CK_SESSION_HANDLE hSession,
   if ((rv = p11_check_create_attributes(session, pTemplate, ulCount, descriptor)) != CKR_OK)
     goto fail;
 
-  const handle_flavor_t flavor
-    = cka_token == NULL ? handle_flavor_session_object : p11_handle_flavor_from_cka_token(cka_token);
+  const handle_flavor_t flavor = p11_object_flavor_from_cka_token(cka_token);
 
   switch (session->state) {
   case CKS_RO_PUBLIC_SESSION:
@@ -3395,56 +3103,40 @@ CK_RV C_CreateObject(CK_SESSION_HANDLE hSession,
       lose(CKR_SESSION_READ_ONLY);
   }
 
-  if (!sql_exec("BEGIN"))
-    lose(CKR_FUNCTION_FAILED);
-
-  in_transaction = 1;
-
-  if ((handle = p11_object_create(session, flavor, pTemplate, ulCount, descriptor, NULL)) == CK_INVALID_HANDLE)
-    lose(CKR_FUNCTION_FAILED);
-
-  if (!p11_attribute_set_bbool(handle, CKA_LOCAL, CK_FALSE))
-    lose(CKR_FUNCTION_FAILED);
-
-  switch (*cka_class) {
-  case CKO_PRIVATE_KEY:
-  case CKO_SECRET_KEY:
-    if (!p11_attribute_set_bbool(handle, CKA_ALWAYS_SENSITIVE,  CK_FALSE) ||
-        !p11_attribute_set_bbool(handle, CKA_NEVER_EXTRACTABLE, CK_FALSE))
-      lose(CKR_FUNCTION_FAILED);
-  }
-
   hal_key_flags_t flags = flavor == handle_flavor_token_object ? HAL_KEY_FLAG_TOKEN : 0;
 
   for (int i = 0; i < ulCount; i++)
     p11_attribute_apply_keyusage(&flags, pTemplate[i].type, pTemplate[i].pValue);
 
-  if (*cka_class == CKO_PUBLIC_KEY && *cka_key_type == CKK_RSA &&
-      !p11_object_create_rsa_public_key(session, handle, flags))
-    lose(CKR_FUNCTION_FAILED);
+  int (*handler)(const p11_session_t *session,
+                 const handle_flavor_t flavor,
+                 const CK_ATTRIBUTE_PTR pTemplate,
+                 const CK_ULONG ulCount,
+                 const p11_descriptor_t * const descriptor,
+                 CK_OBJECT_HANDLE_PTR phObject,
+                 const hal_key_flags_t flags) = NULL;
 
-  if (*cka_class == CKO_PUBLIC_KEY && *cka_key_type == CKK_EC &&
-      !p11_object_create_ec_public_key(session, handle, flags))
-    lose(CKR_FUNCTION_FAILED);
+  if (*cka_class == CKO_PUBLIC_KEY && *cka_key_type == CKK_RSA)
+    handler = p11_object_create_rsa_public_key;
 
-  if (*cka_class == CKO_PRIVATE_KEY && *cka_key_type == CKK_RSA &&
-      !p11_object_create_rsa_private_key(session, handle, flags, pTemplate, ulCount))
-    lose(CKR_FUNCTION_FAILED);
+  if (*cka_class == CKO_PUBLIC_KEY && *cka_key_type == CKK_EC)
+    handler = p11_object_create_ec_public_key;
 
-  if (*cka_class == CKO_PRIVATE_KEY && *cka_key_type == CKK_EC &&
-      !p11_object_create_ec_private_key(session, handle, flags, pTemplate, ulCount))
-    lose(CKR_FUNCTION_FAILED);
+  if (*cka_class == CKO_PRIVATE_KEY && *cka_key_type == CKK_RSA)
+    handler = p11_object_create_rsa_private_key;
 
-  if (!sql_exec("COMMIT"))
+  if (*cka_class == CKO_PRIVATE_KEY && *cka_key_type == CKK_EC)
+    handler = p11_object_create_ec_private_key;
+
+  if (handler == NULL)
     lose(CKR_FUNCTION_FAILED);
 
-  *phObject = handle;
+  if (!handler(session, flavor, pTemplate, ulCount, descriptor, phObject, flags))
+    lose(CKR_FUNCTION_FAILED);
 
   return mutex_unlock(p11_global_mutex);
 
  fail:
-  if (in_transaction && !sql_exec("ROLLBACK"))
-    rv = CKR_GENERAL_ERROR;
   mutex_unlock_return_with_rv(rv, p11_global_mutex);
 }
 
@@ -3453,17 +3145,8 @@ CK_RV C_DestroyObject(CK_SESSION_HANDLE hSession,
 {
   ENTER_PUBLIC_FUNCTION(C_DestroyObject);
 
-  static const char delete_object[] =
-    " DELETE FROM object WHERE object_handle = ?";
-
-  static const char delete_token_object[] =
-    " DELETE FROM token_object"
-    " WHERE token_object_id = (SELECT token_object_id FROM object WHERE object_handle = ?)";
-
-
   hal_pkey_handle_t pkey = {HAL_HANDLE_NONE};
   p11_session_t *session;
-  sqlite3_stmt *q = NULL;
   CK_RV rv = CKR_OK;
 
   mutex_lock_or_return_failure(p11_global_mutex);
@@ -3473,23 +3156,17 @@ CK_RV C_DestroyObject(CK_SESSION_HANDLE hSession,
   if ((rv = p11_object_check_rights(session, hObject, p11_object_access_write)) != CKR_OK)
     goto fail;
 
-  if (p11_object_get_pkey_handle(session, hObject, &pkey) && !hal_check(hal_rpc_pkey_delete(pkey)))
+  if (!p11_object_pkey_open(session, hObject, &pkey))
     lose(CKR_FUNCTION_FAILED);
 
-  if (is_token_handle(hObject)                                  &&
-      (!sql_check_ok(sql_prepare(&q, delete_token_object))      ||
-       !sql_check_ok(sqlite3_bind_int64(q, 1, hObject))         ||
-       !sql_check_done(sqlite3_step(q))                         ||
-       !sql_check_ok(sql_finalize_and_clear(&q))))
+  if (!hal_check(hal_rpc_pkey_delete(pkey))) {
+    (void) hal_rpc_pkey_close(pkey);
     lose(CKR_FUNCTION_FAILED);
+  }
 
-  if (!sql_check_ok(sql_prepare(&q, delete_object))             ||
-      !sql_check_ok(sqlite3_bind_int64(q, 1, hObject))          ||
-      !sql_check_done(sqlite3_step(q)))
-    lose(CKR_FUNCTION_FAILED);
+  p11_object_free(p11_object_find(hObject));
 
  fail:
-  sqlite3_finalize(q);
   mutex_unlock_return_with_rv(rv, p11_global_mutex);
 }
 
@@ -3500,21 +3177,13 @@ CK_RV C_GetAttributeValue(CK_SESSION_HANDLE hSession,
 {
   ENTER_PUBLIC_FUNCTION(C_GetAttributeValue);
 
-  static const char select_format[] =
-    " SELECT value FROM %s_attribute NATURAL JOIN object"
-    " WHERE object_handle = ?1 AND type = ?2";
-
-  const char *flavor = is_token_handle(hObject) ? "token" : "session";
-
   p11_session_t *session;
   const p11_descriptor_t *descriptor = NULL;
   CK_BBOOL cka_sensitive, cka_extractable;
   CK_OBJECT_CLASS cka_class;
   CK_KEY_TYPE cka_key_type;
   int sensitive_object = 0;
-  sqlite3_stmt *q = NULL;
   CK_RV rv;
-  int ret, i;
 
   mutex_lock_or_return_failure(p11_global_mutex);
 
@@ -3526,15 +3195,15 @@ CK_RV C_GetAttributeValue(CK_SESSION_HANDLE hSession,
   if ((rv = p11_object_check_rights(session, hObject, p11_object_access_read)) != CKR_OK)
     goto fail;
 
-  if (!p11_attribute_get_ulong(hObject, CKA_CLASS, &cka_class))
+  if (!p11_attribute_get_ulong(session, hObject, CKA_CLASS, &cka_class))
     lose(CKR_OBJECT_HANDLE_INVALID);
 
   switch (cka_class) {
 
   case CKO_PRIVATE_KEY:
   case CKO_SECRET_KEY:
-    if (!p11_attribute_get_bbool(hObject, CKA_EXTRACTABLE, &cka_extractable) ||
-        !p11_attribute_get_bbool(hObject, CKA_SENSITIVE,   &cka_sensitive))
+    if (!p11_attribute_get_bbool(session, hObject, CKA_EXTRACTABLE, &cka_extractable) ||
+        !p11_attribute_get_bbool(session, hObject, CKA_SENSITIVE,   &cka_sensitive))
       lose(CKR_OBJECT_HANDLE_INVALID);
 
     sensitive_object = cka_sensitive || !cka_extractable;
@@ -3542,7 +3211,7 @@ CK_RV C_GetAttributeValue(CK_SESSION_HANDLE hSession,
     /* Fall through */
 
   case CKO_PUBLIC_KEY:
-    if (!p11_attribute_get_ulong(hObject, CKA_KEY_TYPE, &cka_key_type))
+    if (!p11_attribute_get_ulong(session, hObject, CKA_KEY_TYPE, &cka_key_type))
       lose(CKR_OBJECT_HANDLE_INVALID);
     descriptor = p11_descriptor_from_key_type(cka_class, cka_key_type);
   }
@@ -3553,7 +3222,7 @@ CK_RV C_GetAttributeValue(CK_SESSION_HANDLE hSession,
 
   rv = CKR_OK;
 
-  for (i = 0; i < ulCount; i++) {
+  for (int i = 0; i < ulCount; i++) {
 
     if (sensitive_object && p11_attribute_is_sensitive(descriptor, pTemplate[i].type)) {
       pTemplate[i].ulValueLen = -1;
@@ -4013,9 +3682,9 @@ CK_RV C_SignInit(CK_SESSION_HANDLE hSession,
   if ((rv = p11_object_check_rights(session, hKey, p11_object_access_read)) != CKR_OK)
     goto fail;
 
-  if (!p11_attribute_get_ulong(hKey, CKA_CLASS,    &key_class)  ||
-      !p11_attribute_get_ulong(hKey, CKA_KEY_TYPE, &key_type)   ||
-      !p11_attribute_get_bbool(hKey, CKA_SIGN,     &key_sign)   ||
+  if (!p11_attribute_get_ulong(session, hKey, CKA_CLASS,    &key_class)  ||
+      !p11_attribute_get_ulong(session, hKey, CKA_KEY_TYPE, &key_type)   ||
+      !p11_attribute_get_bbool(session, hKey, CKA_SIGN,     &key_sign)   ||
       key_class != CKO_PRIVATE_KEY)
     lose(CKR_KEY_HANDLE_INVALID);
 
@@ -4110,7 +3779,7 @@ CK_RV C_Sign(CK_SESSION_HANDLE hSession,
   if (session->sign_digest_handle.handle != HAL_HANDLE_NONE)
     lose(CKR_OPERATION_ACTIVE);
 
-  if (!p11_attribute_get_ulong(session->sign_key_handle, CKA_KEY_TYPE, &key_type))
+  if (!p11_attribute_get_ulong(session, session->sign_key_handle, CKA_KEY_TYPE, &key_type))
     lose(CKR_FUNCTION_FAILED);
 
   if (session->sign_digest_algorithm != HAL_DIGEST_ALGORITHM_NONE && pSignature != NULL) {
@@ -4202,7 +3871,7 @@ CK_RV C_SignFinal(CK_SESSION_HANDLE hSession,
   if (session->sign_key_handle == CK_INVALID_HANDLE || session->sign_digest_handle.handle == HAL_HANDLE_NONE)
     lose(CKR_OPERATION_NOT_INITIALIZED);
 
-  if (!p11_attribute_get_ulong(session->sign_key_handle, CKA_KEY_TYPE, &key_type))
+  if (!p11_attribute_get_ulong(session, session->sign_key_handle, CKA_KEY_TYPE, &key_type))
     lose(CKR_FUNCTION_FAILED);
 
   switch (key_type) {
@@ -4252,9 +3921,9 @@ CK_RV C_VerifyInit(CK_SESSION_HANDLE hSession,
   if ((rv = p11_object_check_rights(session, hKey, p11_object_access_read)) != CKR_OK)
     goto fail;
 
-  if (!p11_attribute_get_ulong(hKey, CKA_CLASS,    &key_class)  ||
-      !p11_attribute_get_ulong(hKey, CKA_KEY_TYPE, &key_type)   ||
-      !p11_attribute_get_bbool(hKey, CKA_VERIFY,   &key_verify) ||
+  if (!p11_attribute_get_ulong(session, hKey, CKA_CLASS,    &key_class)  ||
+      !p11_attribute_get_ulong(session, hKey, CKA_KEY_TYPE, &key_type)   ||
+      !p11_attribute_get_bbool(session, hKey, CKA_VERIFY,   &key_verify) ||
       key_class != CKO_PUBLIC_KEY)
     lose(CKR_KEY_HANDLE_INVALID);
 
@@ -4354,7 +4023,7 @@ CK_RV C_Verify(CK_SESSION_HANDLE hSession,
     ulDataLen = 0;
   }
 
-  if (!p11_attribute_get_ulong(session->verify_key_handle, CKA_KEY_TYPE, &key_type))
+  if (!p11_attribute_get_ulong(session, session->verify_key_handle, CKA_KEY_TYPE, &key_type))
     lose(CKR_FUNCTION_FAILED);
 
   switch (key_type) {
@@ -4439,7 +4108,7 @@ CK_RV C_VerifyFinal(CK_SESSION_HANDLE hSession,
   if (session->verify_key_handle == CK_INVALID_HANDLE || session->verify_digest_handle.handle == HAL_HANDLE_NONE)
     lose(CKR_OPERATION_NOT_INITIALIZED);
 
-  if (!p11_attribute_get_ulong(session->verify_key_handle, CKA_KEY_TYPE, &key_type))
+  if (!p11_attribute_get_ulong(session, session->verify_key_handle, CKA_KEY_TYPE, &key_type))
     lose(CKR_FUNCTION_FAILED);
 
   switch (key_type) {



More information about the Commits mailing list