diff options
author | Rob Austein <sra@hactrn.net> | 2016-05-12 16:13:00 -0400 |
---|---|---|
committer | Rob Austein <sra@hactrn.net> | 2016-05-12 16:13:00 -0400 |
commit | 63d3f7f29aecf00468025c05bc01723360d86e24 (patch) | |
tree | 101a993f35b099cd92e2285c18c1ceb7cd772ffe | |
parent | fcacebf82d87c9dedf45aa130d383102aafc3e16 (diff) |
p11util now uses libhal and doesn't need to touch SQL.
"p11util" is now something of a misnomer, since there's no longer
anything about it that's specific to PKCS #11. Probably should become
a libhal utility program, eventually.
-rw-r--r-- | GNUmakefile | 4 | ||||
-rw-r--r-- | p11_common.h | 61 | ||||
-rw-r--r-- | p11util.c | 160 | ||||
-rw-r--r-- | pkcs11.c | 200 | ||||
-rw-r--r-- | sql_common.h | 259 |
5 files changed, 319 insertions, 365 deletions
diff --git a/GNUmakefile b/GNUmakefile index 90440a7..71ca871 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -96,7 +96,7 @@ attributes.h: attributes.yaml scripts/build-attributes GNUmakefile py11/attribute_map.py: attributes.yaml scripts/build-py11-attributes GNUmakefile python scripts/build-py11-attributes attributes.yaml py11/attribute_map.py -pkcs11.o: pkcs11.c sql_common.h schema.h attributes.h +pkcs11.o: pkcs11.c p11_common.h schema.h attributes.h ${CC} ${CFLAGS} -c $< pkcs11.so: pkcs11.o ${LIBS} @@ -105,7 +105,7 @@ pkcs11.so: pkcs11.o ${LIBS} libpkcs11.so: pkcs11.so ${OBJCOPY} -w -G 'C_*' $< $@ -p11util.o: p11util.c sql_common.h schema.h +p11util.o: p11util.c p11_common.h schema.h ${CC} ${CFLAGS} -c $< p11util: p11util.o ${LIBS} diff --git a/p11_common.h b/p11_common.h new file mode 100644 index 0000000..898e087 --- /dev/null +++ b/p11_common.h @@ -0,0 +1,61 @@ +/* + * p11_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, NORDUnet A/S + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the NORDUnet nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER 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 _P11_COMMON_H_ +#define _P11_COMMON_H_ + +/* + * Placeholders for PIN length limits. Figure out real values later. + * Minimum length here is much too short, we allow it for now because + * some test programs fail if we insist on a reasonable length. + */ + +#warning Figure out PIN length limits +#define P11_MIN_PIN_LENGTH 4 +#define P11_MAX_PIN_LENGTH 4096 + +#endif /* _P11_COMMON_H_ */ + +/* + * Local variables: + * indent-tabs-mode: nil + * End: + */ @@ -47,7 +47,7 @@ #include <hal.h> -#include "sql_common.h" +#include "p11_common.h" /* * Apparently the cool kids don't use getpassword() anymore, and there @@ -114,7 +114,7 @@ static int getpin_tty(const char *prompt, OPT_FLG('h', "help", "show help") \ 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_FLG('w', "set-wheel-pin", "set \"wheel\" PIN") \ OPT_FLG('p', "pin-from-stdin", "read PIN from stdin instead of /dev/tty") \ OPT_END @@ -139,11 +139,9 @@ static void usage(const int code, const char *jane) 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) + int *do_set_wheel_pin, + int *read_from_stdin) { - char *endptr; int c; #define OPT_FLG(_short_, _long_, _help_) _short_, @@ -158,9 +156,7 @@ static void parse_args(int argc, char *argv[], #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); + assert(argv && do_set_so_pin && do_set_user_pin && do_set_wheel_pin && read_from_stdin); opterr = 0; if (argc == 1) @@ -172,13 +168,6 @@ static void parse_args(int argc, char *argv[], 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; @@ -191,6 +180,10 @@ static void parse_args(int argc, char *argv[], *do_set_user_pin = 1; continue; + case 'w': + *do_set_wheel_pin = 1; + continue; + default: usage(1, argv[0]); } @@ -202,47 +195,31 @@ static void parse_args(int argc, char *argv[], -#define lose(_msg_) \ - do { \ - fprintf(stderr, "%s\n", _msg_); \ - goto fail; \ - } while (0) - -static int set_iterations(unsigned long iterations) +static int set_pin(const hal_user_t user, const int read_from_stdin) { - static const char update_query[] = - " UPDATE global SET pbkdf2_iterations = ?"; + const char *prompt = NULL, *label = NULL; + char pin[P11_MAX_PIN_LENGTH + 1], *p; - sqlite3_stmt *q = NULL; - int ok = 0; + switch (user) { - 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"); + case HAL_USER_NORMAL: + prompt = "Enter user PIN: "; + label = "user"; + break; - ok = 1; + case HAL_USER_SO: + prompt = "Enter SO PIN: "; + label = "SO"; + break; - fail: - sqlite3_finalize(q); - return ok; -} + case HAL_USER_WHEEL: + prompt = "Enter wheel PIN: "; + label = "wheel"; + break; -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; + default: + return 0; + } if (read_from_stdin) { if (fgets(pin, sizeof(pin), stdin) == NULL) { @@ -254,8 +231,6 @@ static int set_pin(const char * const pin_type, const int read_from_stdin) } 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; } @@ -263,73 +238,52 @@ static int set_pin(const char * const pin_type, const int read_from_stdin) 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); + fprintf(stderr, "Unacceptable length %lu for %s PIN, allowed range [%lu, %lu]\n", + (unsigned long) len, label, + (unsigned long) P11_MIN_PIN_LENGTH, + (unsigned long) P11_MAX_PIN_LENGTH); + memset(pin, 0, sizeof(pin)); 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"); + const hal_client_handle_t client = {HAL_HANDLE_NONE}; - if ((err = hal_get_random(NULL, salt, sizeof(salt))) != HAL_OK) { - fprintf(stderr, "Couldn't generate salt: %s\n", hal_error_string(err)); - goto fail; - } + const hal_error_t err = hal_rpc_set_pin(client, user, pin, len); - if ((err = hal_pbkdf2(NULL, 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 (err != HAL_OK) { + fprintf(stderr, "Couldn't set %s PIN: %s\n", label, hal_error_string(err)); + memset(pin, 0, sizeof(pin)); + return 0; } - 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; + return 1; } + + 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"); + int do_set_so_pin = 0, do_set_user_pin = 0, do_set_wheel_pin = 0, read_from_stdin = 0; - if (do_set_so_pin && !set_pin("so", read_from_stdin)) - lose("Couldn't set SO PIN"); + parse_args(argc, argv, &do_set_so_pin, &do_set_user_pin, &do_set_wheel_pin, &read_from_stdin); - if (do_set_user_pin && !set_pin("user", read_from_stdin)) - lose("Couldn't set user PIN"); + if (do_set_wheel_pin && !set_pin(HAL_USER_WHEEL, read_from_stdin)) { + fprintf(stderr, "Couldn't set wheel PIN\n"); + return 1; + } - if (!sql_exec("COMMIT")) - lose("Couldn't commit SQL transaction"); + if (do_set_so_pin && !set_pin(HAL_USER_SO, read_from_stdin)) { + fprintf(stderr, "Couldn't set SO PIN\n"); + return 2; + } - ok = 1; + if (do_set_user_pin && !set_pin(HAL_USER_NORMAL, read_from_stdin)) { + fprintf(stderr, "Couldn't set user PIN\n"); + return 3; + } - fail: - sql_fini(); - return !ok; + return 0; } /* @@ -42,6 +42,8 @@ #include <stdarg.h> #include <assert.h> +#include <sqlite3.h> + #include <hal.h> /* @@ -62,7 +64,7 @@ #include "pkcs11.h" #include "attributes.h" -#include "sql_common.h" +#include "p11_common.h" /* * This PKCS #11 implementation is hardwired with one slot, the token @@ -311,6 +313,202 @@ static int _hal_check(const hal_error_t err, const char * const expr, const char /* + * 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, "%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; +} + + + +/* * Thread mutex utilities. We need to handle three separate cases: * * 1) User doesn't care about mutexes; diff --git a/sql_common.h b/sql_common.h deleted file mode 100644 index b8c7740..0000000 --- a/sql_common.h +++ /dev/null @@ -1,259 +0,0 @@ -/* - * 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, NORDUnet A/S - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * - Neither the name of the NORDUnet nor the names of its contributors may - * be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER 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. - * Minimum length here is much too short, we allow it for now because - * some test programs fail if we insist on a reasonable length. - */ - -#warning Figure out PIN length limits -#define P11_MIN_PIN_LENGTH 4 -#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: - */ |