[Cryptech-Commits] [sw/pkcs11] 14/20: 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.

git at cryptech.is git at cryptech.is
Tue Jul 7 18:26:44 UTC 2015


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

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

commit c45562762aab7e874eac71792f9eebb5185ee47d
Author: Rob Austein <sra at hactrn.net>
Date:   Wed Jul 1 16:30:51 2015 -0400

    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.
---
 GNUmakefile  |  10 +-
 p11util.c    | 301 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 pkcs11.c     | 229 +++------------------------------------------
 sql_common.h | 255 ++++++++++++++++++++++++++++++++++++++++++++++++++
 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:
+ */



More information about the Commits mailing list