aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Austein <sra@hactrn.net>2015-07-01 16:30:51 -0400
committerRob Austein <sra@hactrn.net>2015-07-01 16:30:51 -0400
commitc45562762aab7e874eac71792f9eebb5185ee47d (patch)
tree99c2943914bc095ee1a6ba4b83d8fc0c98fd8f61
parent4f7d9f0bcc0de535083bdc240f1f5c1037955990 (diff)
Add p11util program to do things like fiddling with the BPKDF2
iteration count, setting PINs, and so forth. Factor some SQL utility code out to a separate file so we can reuse it for p11util.
-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:
+ */