aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--GNUmakefile10
-rw-r--r--p11util.c301
-rw-r--r--pkcs11.c229
-rw-r--r--sql_common.h255
4 files changed, 578 insertions, 217 deletions
diff --git a/GNUmakefile b/GNUmakefile
index 10299f7..c7e69c3 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -48,10 +48,10 @@ else
CFLAGS += -DUSE_PTHREADS=0
endif
-all: libpkcs11.so
+all: libpkcs11.so p11util
clean:
- rm -rf pkcs11.o pkcs11.so libpkcs11.so* schema.h attributes.h
+ rm -rf pkcs11.o pkcs11.so libpkcs11.so* p11util p11util.o schema.h attributes.h
distclean: clean
rm -f TAGS
@@ -71,6 +71,12 @@ pkcs11.so: pkcs11.o ${LIBS}
libpkcs11.so: pkcs11.so
objcopy -w -G 'C_*' $< $@
+p11util.o: p11util.c sql_common.h schema.h
+ ${CC} ${CFLAGS} -c $<
+
+p11util: p11util.o ${LIBS}
+ ${CC} ${CFLAGS} -o $@ -g $^
+
tags: TAGS
TAGS: *.[ch]
diff --git a/p11util.c b/p11util.c
new file mode 100644
index 0000000..514422d
--- /dev/null
+++ b/p11util.c
@@ -0,0 +1,301 @@
+/*
+ * Command line tool for setting up PKCS #11. Mostly this means
+ * things like setting PINs.
+ */
+
+/*
+ * Apparently getopt_long() works everywhere we're likely to care
+ * about. At least, we've been getting away with it for years in
+ * rcynic. rcynic.c has code to wrap option and usage stuff using
+ * getopt_long(), proably just reuse that.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <termios.h>
+#include <getopt.h>
+
+#include <hal.h>
+
+#include "sql_common.h"
+
+/*
+ * Apparently the cool kids don't use getpassword() anymore, and there
+ * is no fully portable replacement, so the advice is just to roll
+ * one's own using termios. Yuck, but gives us the opportunity to
+ * provide a bit of visual feedback, what the heck.
+ *
+ * This version requires /dev/tty. Use something else if you're
+ * reading from a pipe.
+ */
+
+static int getpin_tty(const char *prompt,
+ char *pinbuf,
+ const size_t pinbuf_len)
+{
+ struct termios oflags, nflags;
+ int c = '\0', ok = 0, fixtty = 0;
+ char *p = pinbuf;
+ FILE *f = NULL;
+
+ assert(prompt != NULL && pinbuf != NULL);
+
+ if ((f = fopen("/dev/tty", "r+")) == NULL ||
+ setvbuf(f, NULL, _IONBF, 0) == EOF)
+ goto fail;
+
+ tcgetattr(fileno(f), &oflags);
+ nflags = oflags;
+ nflags.c_lflag &= ~ (ECHO | ICANON);
+
+ if (tcsetattr(fileno(f), TCSANOW, &nflags) < 0)
+ goto fail;
+
+ fixtty = 1;
+ fputs(prompt, f);
+
+ while (p < pinbuf + pinbuf_len - 1 && (c = fgetc(f)) != EOF && c != '\n') {
+ fputc('*', f);
+ *p++ = (char) c;
+ }
+
+ assert(p <= pinbuf + pinbuf_len - 1);
+ *p = '\0';
+ fputc('\n', f);
+ ok = (c == '\n');
+
+ fail:
+
+ if (!ok)
+ perror("getpin_tty() failed");
+
+ if (fixtty && tcsetattr(fileno(f), TCSANOW, &oflags) < 0)
+ perror("getpin_tty() couldn't restore termios settings");
+
+ if (f != NULL)
+ fclose(f);
+
+ return ok;
+}
+
+
+
+#define OPTIONS \
+ OPT_FLG('s', "set-so-pin", "set Security Officer PIN") \
+ OPT_FLG('u', "set-user-pin", "set \"user\" PIN") \
+ OPT_ARG('i', "set-iterations", "set PBKDF2 iteration count") \
+ OPT_ARG('p', "pin-from-stdin", "read PIN from stdin instead of /dev/tty") \
+ OPT_END
+
+#define OPT_END
+
+static void usage (const int code, const char *jane)
+{
+ assert(jane != NULL);
+ FILE *f = code ? stderr : stdout;
+
+ fprintf(f, "usage: %s [options]\noptions:\n", jane);
+
+#define OPT_FLG(_short_, _long_, _help_) fprintf(f, " -%c --%-32s%s", _short_, _long_, _help_);
+#define OPT_ARG(_short_, _long_, _help_) fprintf(f, " -%c ARG --%-32s%s", _short_, _long_ " ARG", _help_);
+ OPTIONS;
+#undef OPT_ARG
+#undef OPT_FLG
+
+ exit(code);
+}
+
+static void parse_args (int argc, char *argv[],
+ int *do_set_so_pin,
+ int *do_set_user_pin,
+ int *do_set_iterations,
+ int *read_from_stdin,
+ unsigned long *iterations)
+{
+ char *endptr;
+ int c;
+
+#define OPT_FLG(_short_, _long_, _help_) _short_,
+#define OPT_ARG(_short_, _long_, _help_) _short_, ':',
+ static const char short_opts[] = { OPTIONS '\0' };
+#undef OPT_ARG
+#undef OPT_FLG
+
+#define OPT_FLG(_short_, _long_, _help_) { _long_, no_argument, NULL, _short_ },
+#define OPT_ARG(_short_, _long_, _help_) { _long_, required_argument, NULL, _short_ },
+ struct option long_opts[] = { OPTIONS { NULL } };
+#undef OPT_ARG
+#undef OPT_FLG
+
+ assert(argv != 0 &&
+ do_set_so_pin != 0 && do_set_user_pin != 0 && do_set_iterations != NULL &&
+ read_from_stdin != NULL && iterations != NULL);
+ opterr = 0;
+
+ while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL)) > 0) {
+ switch (c) {
+
+ case 'h':
+ usage(0, argv[0]);
+
+ case 'i':
+ *do_set_iterations = 1;
+ *iterations = strtoul(optarg, &endptr, 0);
+ if (*optarg == '\0' || *endptr != '\0')
+ usage(1, argv[0]);
+ continue;
+
+ case 'p':
+ *read_from_stdin = 1;
+ continue;
+
+ case 's':
+ *do_set_so_pin = 1;
+ continue;
+
+ case 'u':
+ *do_set_user_pin = 1;
+ continue;
+
+ default:
+ usage(1, argv[0]);
+ }
+ }
+
+ if (optind != argc)
+ usage(1, argv[0]);
+}
+
+
+
+#define lose(_msg_) \
+ do { \
+ fprintf(stderr, "%s\n", _msg_); \
+ goto fail; \
+ } while (0)
+
+static int set_iterations(unsigned long iterations)
+{
+ static const char update_query[] =
+ " UPDATE global SET pbkdf2_iterations = ?";
+
+ sqlite3_stmt *q = NULL;
+ int ok = 0;
+
+ if (!sql_check_ok(sql_prepare(&q, update_query)) ||
+ !sql_check_ok(sqlite3_bind_int64(q, 1, iterations)) ||
+ !sql_check_done(sqlite3_step(q)))
+ lose("Couldn't update database");
+
+ ok = 1;
+
+ fail:
+ sqlite3_finalize(q);
+ return ok;
+}
+
+static int set_pin(const char * const pin_type, const int read_from_stdin)
+{
+ static const char iterations_query[] =
+ " SELECT pbkdf2_iterations FROM global";
+
+ static const char update_format[] =
+ " UPDATE global SET %s_pin = ?1, %s_pin_salt = ?2";
+
+ /* Allow user to change these lengths? */
+ uint8_t pinbuf[32], salt[16];
+
+ char pin[P11_MAX_PIN_LENGTH + 1], *p;
+ sqlite3_stmt *q = NULL;
+ hal_error_t err;
+ int ok = 0;
+
+ if (read_from_stdin) {
+ if (fgets(pin, sizeof(pin), stdin) == NULL) {
+ perror("Couldn't read PIN");
+ return 0;
+ }
+ if ((p = strchr(pin, '\n')) != NULL)
+ *p = '\0';
+ }
+
+ else {
+ char prompt[sizeof("Enter user PIN: ")];
+ snprintf(prompt, sizeof(prompt), "Enter %s PIN: ", pin_type);
+ if (!getpin_tty(prompt, pin, sizeof(pin)))
+ return 0;
+ }
+
+ const size_t len = strlen(pin);
+
+ if (len < P11_MIN_PIN_LENGTH || len > P11_MAX_PIN_LENGTH) {
+ fprintf(stderr, "Unacceptable length %lu for %s PIN, allowd range [%lu, %lu]\n",
+ (unsigned long) len, pin_type,
+ (unsigned long) P11_MIN_PIN_LENGTH, (unsigned long) P11_MAX_PIN_LENGTH);
+ return 0;
+ }
+
+ if (!sql_check_ok(sql_prepare(&q, iterations_query)) ||
+ !sql_check_row(sqlite3_step(q)) ||
+ sqlite3_column_type(q, 0) == SQLITE_NULL)
+ lose("Couldn't retrieve PBKDF2 iteration count from SQL");
+
+ if ((err = hal_get_random(salt, sizeof(salt))) != HAL_OK) {
+ fprintf(stderr, "Couldn't generate salt: %s\n", hal_error_string(err));
+ goto fail;
+ }
+
+ if ((err = hal_pbkdf2(hal_hash_sha256, (uint8_t *) pin, len, salt, sizeof(salt),
+ pinbuf, sizeof(pinbuf), sqlite3_column_int(q, 0))) != HAL_OK) {
+ fprintf(stderr, "Couldn't process new PIN: %s\n", hal_error_string(err));
+ goto fail;
+ }
+
+ if (!sql_check_ok(sql_finalize_and_clear(&q)) ||
+ !sql_check_ok(sql_prepare(&q, update_format, pin_type, pin_type)) ||
+ !sql_check_ok(sqlite3_bind_blob(q, 1, pinbuf, sizeof(pinbuf), NULL)) ||
+ !sql_check_ok(sqlite3_bind_blob(q, 2, salt, sizeof(salt), NULL)) ||
+ !sql_check_done(sqlite3_step(q)))
+ lose("Couldn't update database");
+
+ ok = 1;
+
+ fail:
+ sqlite3_finalize(q);
+ memset(pin, 0, sizeof(pin));
+ memset(pinbuf, 0, sizeof(pinbuf));
+ memset(salt, 0, sizeof(salt));
+ return ok;
+}
+
+int main (int argc, char *argv[])
+{
+ int do_set_so_pin = 0, do_set_user_pin = 0, do_set_iterations = 0, read_from_stdin = 0;
+ unsigned long iterations;
+ int ok = 0;
+
+ parse_args(argc, argv, &do_set_so_pin, &do_set_user_pin, &do_set_iterations, &read_from_stdin, &iterations);
+
+ if (!sql_init() || !sql_exec("BEGIN"))
+ lose("Couldn't initialize SQL, giving up");
+
+ if (do_set_iterations && !set_iterations(iterations))
+ lose("Couldn't set PBKDF2 iteration count");
+
+ if (do_set_so_pin && !set_pin("so", read_from_stdin))
+ lose("Couldn't set SO PIN");
+
+ if (do_set_user_pin && !set_pin("user", read_from_stdin))
+ lose("Couldn't set user PIN");
+
+ if (!sql_exec("COMMIT"))
+ lose("Couldn't commit SQL transaction");
+
+ ok = 1;
+
+ fail:
+ sql_fini();
+ return !ok;
+}
diff --git a/pkcs11.c b/pkcs11.c
index 74886b8..a653ecf 100644
--- a/pkcs11.c
+++ b/pkcs11.c
@@ -2,12 +2,8 @@
* pkcs11.c
* --------
*
- * This is a partial implementation of PKCS #11 on top of Cryptlib on
- * top of a HAL connecting to the Cryptech FPGA cores.
- *
- * This is still at a very early stage and should not (yet?) be used
- * for any serious purpose. Among other things, it's not yet entirely
- * clear whether this approach really is workable.
+ * This is a partial implementation of PKCS #11 on top of the Cryptech
+ * libhal library connecting to the Cryptech FPGA cores.
*
* Author: Rob Austein
* Copyright (c) 2015, SUNET
@@ -44,8 +40,6 @@
#include <stdarg.h>
#include <assert.h>
-#include <sqlite3.h>
-
#include <hal.h>
/*
@@ -64,7 +58,9 @@
#endif
#include "pkcs11.h"
+
#include "attributes.h"
+#include "sql_common.h"
/*
* This PKCS #11 implementation is hardwired with one slot, the token
@@ -75,19 +71,7 @@
#define P11_ONE_AND_ONLY_SLOT 0
/*
- * Placeholders for PIN length limits. Figure out real values later.
- */
-
-#warning Figure out PIN length limits
-#define P11_MIN_PIN_LENGTH 16
-#define P11_MAX_PIN_LENGTH 4096
-
-/*
- * Version numbers. Placeholders for now. Cryptlib has a version
- * number, but from PKCS #11's point of view, Cryptlib is part of the
- * "hardware", and we're probably going to need something other than
- * Cryptlib's version number for the hardware, because we have to
- * represent the version number of the attached Cryptech FPGA cores.
+ * Version numbers. Placeholders for now.
*
* Software version number is just the version of this PKCS #11
* implementation. Probably.
@@ -103,10 +87,6 @@
* Debugging control.
*/
-#ifndef DEBUG_SQL
-#define DEBUG_SQL 1
-#endif
-
#ifndef DEBUG_HAL
#define DEBUG_HAL 1
#endif
@@ -116,15 +96,6 @@
#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
-
-/*
* Whether to include POSIX-specific features.
*/
@@ -235,12 +206,6 @@ static enum {
static p11_session_t *p11_sessions;
/*
- * SQL database.
- */
-
-static sqlite3 *sqldb = NULL;
-
-/*
* 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
@@ -323,32 +288,6 @@ static pid_t initialized_pid;
#endif /* DEBUG_PKCS11 > 1 */
/*
- * Error checking for SQLite calls.
- */
-
-#if DEBUG_SQL
-
-#define sql_whine(_expr_) \
- (fprintf(stderr, "%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())
-
-/*
* Error checking for libhal calls.
*/
@@ -539,153 +478,6 @@ static CK_RV posix_mutex_unlock(CK_VOID_PTR pMutex)
/*
- * SQL utilities.
- */
-
-/*
- * Hook on which to hang a debugger breakpoint on SQL errors.
- */
-
-#if DEBUG_SQL
-static void sql_breakpoint(void)
-{
- fprintf(stderr, "[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, "[%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("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;
-}
-
-
-
-/*
* Initialize KEK. If we had proper hardware support the KEK would be
* living in special RAM where we could wipe it if anything triggered
* our tamper circuitry. But we have no such at the moment, so we
@@ -906,6 +698,8 @@ static int p11_attribute_get(const CK_OBJECT_HANDLE object_handle,
* Wrappers to set and get CK_BBOOL and CK_ULONG values.
*/
+#if 0
+
static int p11_attribute_set_bbool(const CK_OBJECT_HANDLE object_handle, const CK_ATTRIBUTE_TYPE type, const CK_BBOOL value)
{
return p11_attribute_set(object_handle, type, &value, sizeof(value));
@@ -916,6 +710,8 @@ static int p11_attribute_set_ulong(const CK_OBJECT_HANDLE object_handle, const C
return p11_attribute_set(object_handle, 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)
{
CK_ULONG length;
@@ -1960,7 +1756,9 @@ static CK_RV generate_keypair_rsa_pkcs(p11_session_t *session,
for (i = 0; i < ulPublicKeyAttributeCount; i++) {
const CK_ATTRIBUTE_TYPE type = pPublicKeyTemplate[i].type;
const void * const val = pPublicKeyTemplate[i].pValue;
+#if 0
const size_t len = pPublicKeyTemplate[i].ulValueLen;
+#endif
assert(val != NULL);
@@ -1982,7 +1780,9 @@ static CK_RV generate_keypair_rsa_pkcs(p11_session_t *session,
for (i = 0; i < ulPrivateKeyAttributeCount; i++) {
const CK_ATTRIBUTE_TYPE type = pPrivateKeyTemplate[i].type;
const void * const val = pPrivateKeyTemplate[i].pValue;
+#if 0
const size_t len = pPrivateKeyTemplate[i].ulValueLen;
+#endif
assert (val != NULL);
@@ -2453,7 +2253,6 @@ CK_RV C_GetTokenInfo(CK_SLOT_ID slotID,
/*
* No real idea (yet) how we get many of the following parameters.
- * See cryptlib's CRYPT_DEVINFO_* attributes for some hints.
*
* pInfo->label is supposed to be set when the token is initialized.
* Not yet sure what that means in our context, but need something
@@ -2627,7 +2426,7 @@ CK_RV C_Login(CK_SESSION_HANDLE hSession,
ENTER_PUBLIC_FUNCTION(C_Login);
static const char pin_query_format[] =
- " SELECT pbkdf2_iterations, %s_pin, %s_pin_salt from global";
+ " SELECT pbkdf2_iterations, %s_pin, %s_pin_salt FROM global";
p11_session_t *session;
sqlite3_stmt *q = NULL;
diff --git a/sql_common.h b/sql_common.h
new file mode 100644
index 0000000..8f1844b
--- /dev/null
+++ b/sql_common.h
@@ -0,0 +1,255 @@
+/*
+ * sql_common.h
+ * ------------
+ *
+ * Common definitions and SQL support code for Cryptech PKCS #11 engine.
+ *
+ * We could split the functions out of this into a separate .c file,
+ * but there's no real point in doing so, and it's simpler to keep it
+ * all in one file, the build process is complex enough already.
+ *
+ * Author: Rob Austein
+ * Copyright (c) 2015, SUNET
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SQL_COMMON_H_
+#define _SQL_COMMON_H_
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <assert.h>
+
+#include <sqlite3.h>
+
+/*
+ * Placeholders for PIN length limits. Figure out real values later.
+ */
+
+#warning Figure out PIN length limits
+#define P11_MIN_PIN_LENGTH 16
+#define P11_MAX_PIN_LENGTH 4096
+
+/*
+ * 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, "%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, "[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, "[%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("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;
+}
+
+#endif /* _SQL_COMMON_H_ */
+
+/*
+ * Local variables:
+ * indent-tabs-mode: nil
+ * End:
+ */