From f4da1455c7ab858ea9007d0813774c6d04cd4576 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 2 Jan 2020 14:21:12 +0100 Subject: [PATCH] kbx: Initial support for an SQLite backend * kbx/backend-sqlite.c: New. * kbx/Makefile.am (keyboxd_SOURCES): Add it. (keyboxd_CFLAGS, keyboxd_LDADD): Add SQLite flags. * kbx/backend.h (enum database_types): Add DB_TYPE_SQLITE. (be_sqlite_local_t): New typedef. (struct db_request_part_s): Add field besqlite. * kbx/backend-support.c (strdbtype): Add string for DB_TYPE_SQLITE. (be_generic_release_backend): Support SQLite. (be_release_request): Ditto. (be_find_request_part): Ditto. (is_x509_blob): Rename to ... (be_is_x509_blob): this and make global. * kbx/frontend.c (kbxd_set_database): Detect ".db" suffix and use that for SQLite. (kbxd_search): Support SQLite (kbxd_store): Ditto. (kbxd_delete): Ditto. * kbx/frontend.h (kbxd_store_modes): Move to ... * kbx/keyboxd.h (enum kbxd_store_modes): here. * kbx/keyboxd.c (main): USe pubring.db for now. This is a temporary hack. * kbx/backend-kbx.c (be_kbx_delete): Remove unused var cert. -- Take care: This is not finished and in particular filling the database takes quite long. Signed-off-by: Werner Koch --- kbx/Makefile.am | 7 +- kbx/backend-kbx.c | 2 - kbx/backend-sqlite.c | 1288 +++++++++++++++++++++++++++++++++++++++++ kbx/backend-support.c | 20 +- kbx/backend.h | 34 +- kbx/frontend.c | 138 ++--- kbx/frontend.h | 8 - kbx/keyboxd.c | 6 +- kbx/keyboxd.h | 8 + 9 files changed, 1424 insertions(+), 87 deletions(-) create mode 100644 kbx/backend-sqlite.c diff --git a/kbx/Makefile.am b/kbx/Makefile.am index 9f8f7953d..436733fc5 100644 --- a/kbx/Makefile.am +++ b/kbx/Makefile.am @@ -79,13 +79,16 @@ keyboxd_SOURCES = \ backend.h backend-support.c \ backend-cache.c \ backend-kbx.c \ + backend-sqlite.c \ $(common_sources) keyboxd_CFLAGS = $(AM_CFLAGS) -DKEYBOX_WITH_X509=1 \ - $(LIBASSUAN_CFLAGS) $(NPTH_CFLAGS) $(INCICONV) + $(LIBASSUAN_CFLAGS) $(NPTH_CFLAGS) $(SQLITE3_CFLAGS) \ + $(INCICONV) keyboxd_LDADD = $(commonpth_libs) \ $(KSBA_LIBS) $(LIBGCRYPT_LIBS) $(LIBASSUAN_LIBS) $(NPTH_LIBS) \ - $(GPG_ERROR_LIBS) $(LIBINTL) $(NETLIBS) $(LIBICONV) \ + $(SQLITE3_LIBS) $(GPG_ERROR_LIBS) \ + $(LIBINTL) $(NETLIBS) $(LIBICONV) \ $(resource_objs) keyboxd_LDFLAGS = $(extra_bin_ldflags) keyboxd_DEPENDENCIES = $(resource_objs) diff --git a/kbx/backend-kbx.c b/kbx/backend-kbx.c index 25d4596af..1c4b04226 100644 --- a/kbx/backend-kbx.c +++ b/kbx/backend-kbx.c @@ -434,7 +434,6 @@ be_kbx_delete (ctrl_t ctrl, backend_handle_t backend_hd, db_request_t request) { gpg_error_t err; db_request_part_t part; - ksba_cert_t cert = NULL; (void)ctrl; @@ -452,6 +451,5 @@ be_kbx_delete (ctrl_t ctrl, backend_handle_t backend_hd, db_request_t request) err = keybox_delete (part->kbx_hd); leave: - ksba_cert_release (cert); return err; } diff --git a/kbx/backend-sqlite.c b/kbx/backend-sqlite.c new file mode 100644 index 000000000..e5fda9458 --- /dev/null +++ b/kbx/backend-sqlite.c @@ -0,0 +1,1288 @@ +/* backend-sqlite.c - SQLite based backend for keyboxd + * Copyright (C) 2019 g10 Code GmbH + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "keyboxd.h" +#include "../common/i18n.h" +#include "../common/mbox-util.h" +#include "backend.h" +#include "keybox-search-desc.h" +#include "keybox-defs.h" /* (for the openpgp parser) */ + + +/* Add replacement error codes; GPGRT provides SQL error codes from + * version 1.37 on. */ +#if GPGRT_VERSION_NUMBER < 0x012500 /* 1.37 */ + +static GPGRT_INLINE gpg_error_t +gpg_err_code_from_sqlite (int sqlres) +{ + return sqlres? 1500 + (sqlres & 0xff) : 0; +} + +#define GPG_ERR_SQL_OK 1500 +#define GPG_ERR_SQL_ROW 1600 +#define GPG_ERR_SQL_DONE 1601 + +#endif /*GPGRT_VERSION_NUMBER*/ + + +/* Our definition of the backend handle. */ +struct backend_handle_s +{ + enum database_types db_type; /* Always DB_TYPE_SQLITE. */ + unsigned int backend_id; /* Always the id of the backend. */ + + char filename[1]; +}; + + +/* Definition of local request data. */ +struct be_sqlite_local_s +{ + /* The statement object of the current select command. */ + sqlite3_stmt *select_stmt; + + /* The search mode represented by the current select command. */ + KeydbSearchMode select_mode; + + /* The select statement has been executed with success. */ + int select_done; + + /* The last row has already been reached. */ + int select_eof; +}; + + +/* The Mutex we use to protect all our SQLite calls. */ +static npth_mutex_t database_mutex = NPTH_MUTEX_INITIALIZER; +/* The one and only database handle. */ +static sqlite3 *database_hd; +/* A lockfile used make sure only we are accessing the database. */ +static dotlock_t database_lock; + + +static struct +{ + const char *sql; + int special; +} table_definitions[] = + { + { "PRAGMA foreign_keys = ON" }, + + /* Table to store config values: + * Standard name value pairs: + * dbversion = 1 + * created = + */ + { "CREATE TABLE IF NOT EXISTS config (" + "name TEXT NOT NULL," + "value TEXT NOT NULL " + ")", 1 }, + + /* The actual data; either X.509 certificates or OpenPGP + * keyblocks. */ + { "CREATE TABLE IF NOT EXISTS pubkey (" + /* The 20 octet truncted primary-fpr */ + "ubid BLOB NOT NULL PRIMARY KEY," + /* The type of the public key: 1 = openpgp, 2 = X.509. */ + "type INTEGER NOT NULL," + /* The OpenPGP keyblock or X.509 certificate. */ + "keyblob BLOB NOT NULL" + ")" }, + + /* Table with fingerprints and keyids of OpenPGP and X.509 keys. + * It is also used for the primary key and the X.509 fingerprint + * because we want to be able to use the keyid and keygrip. */ + { "CREATE TABLE IF NOT EXISTS fingerprint (" + "fpr BLOB NOT NULL PRIMARY KEY," + /* The long keyid as 64 bit integer. */ + "kid INTEGER NOT NULL," + /* The keygrip for this key. */ + "keygrip BLOB NOT NULL," + /* 0 = primary, > 0 = subkey. */ + "subkey INTEGER NOT NULL," + /* The Unique Blob ID (possibly truncated fingerprint). */ + "ubid BLOB NOT NULL REFERENCES pubkey" + ")" }, + + /* Indices for the fingerprint table. */ + { "CREATE INDEX IF NOT EXISTS fingerprintidx0 on fingerprint (ubid)" }, + { "CREATE INDEX IF NOT EXISTS fingerprintidx1 on fingerprint (fpr)" }, + { "CREATE INDEX IF NOT EXISTS fingerprintidx2 on fingerprint (keygrip)" }, + + + /* Table to allow fast access via user ids or mail addresses. */ + { "CREATE TABLE IF NOT EXISTS userid (" + /* The full user id. */ + "uid TEXT NOT NULL," + /* The mail address if available or NULL. */ + "addrspec TEXT," + /* The type of the public key: 1 = openpgp, 2 = X.509. */ + "type INTEGER NOT NULL," + /* The Unique Blob ID (possibly truncated fingerprint). */ + "ubid BLOB NOT NULL REFERENCES pubkey" + ")" }, + + /* Indices for the userid table. */ + { "CREATE INDEX IF NOT EXISTS userididx0 on userid (ubid)" }, + { "CREATE INDEX IF NOT EXISTS userididx1 on userid (uid)" }, + { "CREATE INDEX IF NOT EXISTS userididx3 on userid (addrspec)" } + + }; + + + + +/* Take a lock for accessing SQLite. */ +static void +acquire_mutex (void) +{ + int res = npth_mutex_lock (&database_mutex); + if (res) + log_fatal ("failed to acquire database lock: %s\n", + gpg_strerror (gpg_error_from_errno (res))); +} + + + +/* Release a lock. */ +static void +release_mutex (void) +{ + int res = npth_mutex_unlock (&database_mutex); + if (res) + log_fatal ("failed to release database db lock: %s\n", + gpg_strerror (gpg_error_from_errno (res))); +} + + +static void +show_sqlstr (const char *sqlstr) +{ + if (!opt.verbose) + return; + + log_info ("(SQL: %s)\n", sqlstr); +} + + +static void +show_sqlstmt (sqlite3_stmt *stmt) +{ + char *p; + + if (!opt.verbose) + return; + + p = sqlite3_expanded_sql (stmt); + if (p) + log_info ("(SQL: %s)\n", p); + sqlite3_free (p); +} + + +static gpg_error_t +diag_prepare_err (int res, const char *sqlstr) +{ + gpg_error_t err; + + err = gpg_error (gpg_err_code_from_sqlite (res)); + show_sqlstr (sqlstr); + log_error ("error preparing SQL statement: %s\n", sqlite3_errstr (res)); + return err; +} + +static gpg_error_t +diag_bind_err (int res, sqlite3_stmt *stmt) +{ + gpg_error_t err; + + err = gpg_error (gpg_err_code_from_sqlite (res)); + show_sqlstmt (stmt); + log_error ("error binding a value to an SQL statement: %s\n", + sqlite3_errstr (res)); + return err; +} + + +static gpg_error_t +diag_step_err (int res, sqlite3_stmt *stmt) +{ + gpg_error_t err; + + err = gpg_error (gpg_err_code_from_sqlite (res)); + show_sqlstmt (stmt); + log_error ("error executing SQL statement: %s\n", sqlite3_errstr (res)); + return err; +} + + +/* We store the keyid in the database as an integer - this function + * converts it from a memory buffer. */ +static GPGRT_INLINE sqlite3_int64 +kid_from_mem (const unsigned char *keyid) +{ + return ( ((uint64_t)keyid[0] << 56) + | ((uint64_t)keyid[1] << 48) + | ((uint64_t)keyid[2] << 40) + | ((uint64_t)keyid[3] << 32) + | ((uint64_t)keyid[4] << 24) + | ((uint64_t)keyid[5] << 16) + | ((uint64_t)keyid[6] << 8) + | ((uint64_t)keyid[7]) + ); +} + + +/* We store the keyid in the database as an integer - this function + * converts it from the usual u32[2] array. */ +static GPGRT_INLINE sqlite3_int64 +kid_from_u32 (u32 *keyid) +{ + return (((uint64_t)keyid[0] << 32) | ((uint64_t)keyid[1]) ); +} + + +/* Run an SQL reset on STMT. */ +static gpg_error_t +run_sql_reset (sqlite3_stmt *stmt) +{ + gpg_error_t err; + int res; + + res = sqlite3_reset (stmt); + if (res) + { + err = gpg_error (gpg_err_code_from_sqlite (res)); + show_sqlstmt (stmt); + log_error ("error executing SQL reset: %s\n", sqlite3_errstr (res)); + } + else + err = 0; + return err; +} + + +/* Run an SQL prepare for SQLSTR and return a statement at R_STMT. */ +static gpg_error_t +run_sql_prepare (const char *sqlstr, sqlite3_stmt **r_stmt) +{ + gpg_error_t err; + int res; + + res = sqlite3_prepare_v2 (database_hd, sqlstr, -1, r_stmt, NULL); + if (res) + err = diag_prepare_err (res, sqlstr); + else + err = 0; + return err; +} + + +/* Helper to bind a BLOB parameter to a statement. */ +static gpg_error_t +run_sql_bind_blob (sqlite3_stmt *stmt, int no, + const void *blob, size_t bloblen) +{ + gpg_error_t err; + int res; + + res = sqlite3_bind_blob (stmt, no, blob, bloblen, SQLITE_TRANSIENT); + if (res) + err = diag_bind_err (res, stmt); + else + err = 0; + return err; +} + + +/* Helper to bind an INTEGER parameter to a statement. */ +static gpg_error_t +run_sql_bind_int (sqlite3_stmt *stmt, int no, int value) +{ + gpg_error_t err; + int res; + + res = sqlite3_bind_int (stmt, no, value); + if (res) + err = diag_bind_err (res, stmt); + else + err = 0; + return err; +} + + +/* Helper to bind an INTEGER64 parameter to a statement. */ +static gpg_error_t +run_sql_bind_int64 (sqlite3_stmt *stmt, int no, sqlite3_int64 value) +{ + gpg_error_t err; + int res; + + res = sqlite3_bind_int64 (stmt, no, value); + if (res) + err = diag_bind_err (res, stmt); + else + err = 0; + return err; +} + + +/* Helper to bind a string parameter to a statement. VALUE is allowed + * to be NULL to bind NULL. */ +static gpg_error_t +run_sql_bind_text (sqlite3_stmt *stmt, int no, const char *value) +{ + gpg_error_t err; + int res; + + res = sqlite3_bind_text (stmt, no, value, value? strlen (value):0, + SQLITE_TRANSIENT); + if (res) + err = diag_bind_err (res, stmt); + else + err = 0; + return err; +} + + +/* Helper to bind a string parameter to a statement. VALUE is allowed + * to be NULL to bind NULL. A non-NULL VALUE is clamped with percent + * signs. */ +static gpg_error_t +run_sql_bind_text_like (sqlite3_stmt *stmt, int no, const char *value) +{ + gpg_error_t err; + int res; + char *buf; + + if (!value) + { + res = sqlite3_bind_null (stmt, no); + buf = NULL; + } + else + { + buf = xtrymalloc (strlen (value) + 2 + 1); + if (!buf) + return gpg_error_from_syserror (); + *buf = '%'; + strcpy (buf+1, value); + strcat (buf+1, "%"); + res = sqlite3_bind_text (stmt, no, buf, strlen (buf), SQLITE_TRANSIENT); + } + if (res) + err = diag_bind_err (res, stmt); + else + err = 0; + xfree (buf); + return err; +} + + +/* Wrapper around sqlite3_step for use with simple functions. */ +static gpg_error_t +run_sql_step (sqlite3_stmt *stmt) +{ + gpg_error_t err; + int res; + + res = sqlite3_step (stmt); + if (res != SQLITE_DONE) + err = diag_step_err (res, stmt); + else + err = 0; + return err; +} + + +/* Wrapper around sqlite3_step for use with select. This version does + * not print diags for SQLITE_DONE or SQLITE_ROW but returns them as + * gpg error codes. */ +static gpg_error_t +run_sql_step_for_select (sqlite3_stmt *stmt) +{ + gpg_error_t err; + int res; + + res = sqlite3_step (stmt); + if (res == SQLITE_DONE || res == SQLITE_ROW) + err = gpg_error (gpg_err_code_from_sqlite (res)); + else + { + /* SQL_OK is unexpected for a select so in this case we return + * the OK error code by bypassing the special mapping. */ + if (!res) + err = gpg_error (GPG_ERR_SQL_OK); + else + err = gpg_error (gpg_err_code_from_sqlite (res)); + show_sqlstmt (stmt); + log_error ("error running SQL step: %s\n", sqlite3_errstr (res)); + } + return err; +} + + +/* Run the simple SQL statement in SQLSTR. If UBID is not NULL this + * will be bound to :1 in SQLSTR. This command may not be used for + * select or other command which return rows. */ +static gpg_error_t +run_sql_statement_bind_ubid (const char *sqlstr, const unsigned char *ubid) +{ + gpg_error_t err; + sqlite3_stmt *stmt; + + err = run_sql_prepare (sqlstr, &stmt); + if (err) + goto leave; + if (ubid) + { + err = run_sql_bind_blob (stmt, 1, ubid, UBID_LEN); + if (err) + goto leave; + } + + err = run_sql_step (stmt); + sqlite3_finalize (stmt); + if (err) + goto leave; + + leave: + return err; +} + + +/* Run the simple SQL statement in SQLSTR. This command may not be used + * for select or other command which return rows. */ +static gpg_error_t +run_sql_statement (const char *sqlstr) +{ + return run_sql_statement_bind_ubid (sqlstr, NULL); +} + + +/* Create and intitialize a new SQL database file if it does not + * exists; else open it and check that all required objects are + * available. */ +static gpg_error_t +create_or_open_database (const char *filename) +{ + gpg_error_t err; + int res; + int idx; + + if (database_hd) + return 0; /* Already initialized. */ + + acquire_mutex (); + + /* To avoid races with other temporary instances of keyboxd trying + * to create or update the database, we run the database with a lock + * file held. */ + database_lock = dotlock_create (filename, 0); + if (!database_lock) + { + err = gpg_error_from_syserror (); + /* A reason for this to fail is that the directory is not + * writable. However, this whole locking stuff does not make + * sense if this is the case. An empty non-writable directory + * with no database is not really useful at all. */ + if (opt.verbose) + log_info ("can't allocate lock for '%s': %s\n", + filename, gpg_strerror (err)); + goto leave; + } + + if (dotlock_take (database_lock, -1)) + { + err = gpg_error_from_syserror (); + /* This is something bad. Probably a stale lockfile. */ + log_info ("can't lock '%s': %s\n", filename, gpg_strerror (err)); + goto leave; + } + + /* Database has not yet been opened. Open or create it, make sure + * the tables exist, and prepare the required statements. We use + * our own locking instead of the more complex serialization sqlite + * would have to do and it avoid that we call + * npth_unprotect/protect. */ + res = sqlite3_open_v2 (filename, + &database_hd, + (SQLITE_OPEN_READWRITE + | SQLITE_OPEN_CREATE + | SQLITE_OPEN_NOMUTEX), + NULL); + if (res) + { + err = gpg_error (gpg_err_code_from_sqlite (res)); + log_error ("error opening '%s': %s\n", filename, sqlite3_errstr (res)); + goto leave; + } + /* Enable extended error codes. */ + sqlite3_extended_result_codes (database_hd, 1); + + /* Create the tables if needed. */ + for (idx=0; idx < DIM(table_definitions); idx++) + { + err = run_sql_statement (table_definitions[idx].sql); + if (err) + goto leave; + if (table_definitions[idx].special == 1) + { + /* Check and create dbversion etc entries. */ + // FIXME + } + } + + if (!opt.quiet) + log_info (_("database '%s' created\n"), filename); + err = 0; + + leave: + if (err) + { + log_error (_("error creating database '%s': %s\n"), + filename, gpg_strerror (err)); + dotlock_release (database_lock); + dotlock_destroy (database_lock); + database_lock = NULL; + } + release_mutex (); + return err; +} + + +/* Install a new resource and return a handle for that backend. */ +gpg_error_t +be_sqlite_add_resource (ctrl_t ctrl, backend_handle_t *r_hd, + const char *filename, int readonly) +{ + gpg_error_t err; + backend_handle_t hd; + + (void)ctrl; + (void)readonly; /* FIXME: implement read-only mode. */ + + *r_hd = NULL; + hd = xtrycalloc (1, sizeof *hd + strlen (filename)); + if (!hd) + return gpg_error_from_syserror (); + hd->db_type = DB_TYPE_SQLITE; + strcpy (hd->filename, filename); + + err = create_or_open_database (filename); + if (err) + goto leave; + + hd->backend_id = be_new_backend_id (); + + *r_hd = hd; + hd = NULL; + + leave: + xfree (hd); + return err; +} + + +/* Release the backend handle HD and all its resources. HD is not + * valid after a call to this function. */ +void +be_sqlite_release_resource (ctrl_t ctrl, backend_handle_t hd) +{ + (void)ctrl; + + if (!hd) + return; + hd->db_type = DB_TYPE_NONE; + + xfree (hd); +} + + +/* Helper for be_find_request_part to initialize a sqlite request part. */ +gpg_error_t +be_sqlite_init_local (backend_handle_t backend_hd, db_request_part_t part) +{ + (void)backend_hd; + + part->besqlite = xtrycalloc (1, sizeof *part->besqlite); + if (!part->besqlite) + return gpg_error_from_syserror (); + return 0; +} + + +/* Release local data of a sqlite request part. */ +void +be_sqlite_release_local (be_sqlite_local_t ctx) +{ + if (ctx->select_stmt) + sqlite3_finalize (ctx->select_stmt); + xfree (ctx); +} + + +/* Run a select for the search given by (DESC,NDESC). The data is not + * returned but stored in the request item. */ +static gpg_error_t +run_select_statement (be_sqlite_local_t ctx, + KEYDB_SEARCH_DESC *desc, unsigned int ndesc) +{ + gpg_error_t err = 0; + unsigned int descidx; + + descidx = 0; /* Fixme: take from context. */ + if (descidx >= ndesc) + { + err = gpg_error (GPG_ERR_EOF); + goto leave; + } + + /* Check whether we can re-use the current select statement. */ + if (!ctx->select_stmt) + ; + else if (ctx->select_mode != desc[descidx].mode) + { + sqlite3_finalize (ctx->select_stmt); + ctx->select_stmt = NULL; + } + + ctx->select_mode = desc[descidx].mode; + + /* Prepare the select and bind the parameters. */ + if (ctx->select_stmt) + { + err = run_sql_reset (ctx->select_stmt); + if (err) + goto leave; + } + else + err = 0; + + switch (desc[descidx].mode) + { + case KEYDB_SEARCH_MODE_NONE: + never_reached (); + err = gpg_error (GPG_ERR_INTERNAL); + break; + + case KEYDB_SEARCH_MODE_EXACT: + if (!ctx->select_stmt) + err = run_sql_prepare ("SELECT p.ubid, p.type, p.keyblob" + " FROM pubkey as p, userid as u" + " WHERE u.uid = ?1", + &ctx->select_stmt); + if (!err) + err = run_sql_bind_text (ctx->select_stmt, 1, desc[descidx].u.name); + break; + + case KEYDB_SEARCH_MODE_MAIL: + if (!ctx->select_stmt) + err = run_sql_prepare ("SELECT p.ubid, p.type, p.keyblob" + " FROM pubkey as p, userid as u" + " WHERE u.addrspec = ?1", + &ctx->select_stmt); + if (!err) + err = run_sql_bind_text (ctx->select_stmt, 1, desc[descidx].u.name); + break; + + case KEYDB_SEARCH_MODE_MAILSUB: + if (!ctx->select_stmt) + err = run_sql_prepare ("SELECT p.ubid, p.type, p.keyblob" + " FROM pubkey as p, userid as u" + " WHERE u.addrspec LIKE ?1", + &ctx->select_stmt); + if (!err) + err = run_sql_bind_text_like (ctx->select_stmt, 1, + desc[descidx].u.name); + break; + + case KEYDB_SEARCH_MODE_SUBSTR: + if (!ctx->select_stmt) + err = run_sql_prepare ("SELECT p.ubid, p.type, p.keyblob" + " FROM pubkey as p, userid as u" + " WHERE u.uid LIKE ?1", + &ctx->select_stmt); + if (!err) + err = run_sql_bind_text_like (ctx->select_stmt, 1, + desc[descidx].u.name); + break; + + case KEYDB_SEARCH_MODE_MAILEND: + case KEYDB_SEARCH_MODE_WORDS: + err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); + break; + + case KEYDB_SEARCH_MODE_ISSUER: + err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); /* FIXME */ + /* if (has_issuer (blob, desc[n].u.name)) */ + /* goto found; */ + break; + + case KEYDB_SEARCH_MODE_ISSUER_SN: + err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); /* FIXME */ + /* if (has_issuer_sn (blob, desc[n].u.name, */ + /* sn_array? sn_array[n].sn : desc[n].sn, */ + /* sn_array? sn_array[n].snlen : desc[n].snlen)) */ + /* goto found; */ + break; + case KEYDB_SEARCH_MODE_SN: + err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); /* FIXME */ + /* if (has_sn (blob, sn_array? sn_array[n].sn : desc[n].sn, */ + /* sn_array? sn_array[n].snlen : desc[n].snlen)) */ + /* goto found; */ + break; + case KEYDB_SEARCH_MODE_SUBJECT: + err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); /* FIXME */ + /* if (has_subject (blob, desc[n].u.name)) */ + /* goto found; */ + break; + + case KEYDB_SEARCH_MODE_SHORT_KID: + err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); /* FIXME */ + /* pk_no = has_short_kid (blob, desc[n].u.kid[1]); */ + /* if (pk_no) */ + /* goto found; */ + break; + + case KEYDB_SEARCH_MODE_LONG_KID: + if (!ctx->select_stmt) + err = run_sql_prepare ("SELECT p.ubid, p.type, p.keyblob" + " FROM pubkey as p, fingerprint as f" + " WHERE f.kid = ?1", + &ctx->select_stmt); + if (!err) + err = run_sql_bind_int64 (ctx->select_stmt, 1, + kid_from_u32 (desc[descidx].u.kid)); + break; + + case KEYDB_SEARCH_MODE_FPR: + if (!ctx->select_stmt) + err = run_sql_prepare ("SELECT p.ubid, p.type, p.keyblob" + " FROM pubkey as p, fingerprint as f" + " WHERE f.fpr = ?1", + &ctx->select_stmt); + if (!err) + err = run_sql_bind_blob (ctx->select_stmt, 1, + desc[descidx].u.fpr, desc[descidx].fprlen); + break; + + case KEYDB_SEARCH_MODE_KEYGRIP: + if (!ctx->select_stmt) + err = run_sql_prepare ("SELECT p.ubid, p.type, p.keyblob" + " FROM pubkey as p, fingerprint as f" + " WHERE f.keygrip = ?1", + &ctx->select_stmt); + if (!err) + err = run_sql_bind_blob (ctx->select_stmt, 1, + desc[descidx].u.grip, KEYGRIP_LEN); + break; + + case KEYDB_SEARCH_MODE_UBID: + if (!ctx->select_stmt) + err = run_sql_prepare ("SELECT ubid, type, keyblob" + " FROM pubkey" + " WHERE ubid = ?1", + &ctx->select_stmt); + if (!err) + err = run_sql_bind_blob (ctx->select_stmt, 1, + desc[descidx].u.ubid, UBID_LEN); + break; + + case KEYDB_SEARCH_MODE_FIRST: + if (!ctx->select_stmt) + err = run_sql_prepare ("SELECT ubid, type, keyblob" + " FROM pubkey ORDER by ubid", + &ctx->select_stmt); + break; + + case KEYDB_SEARCH_MODE_NEXT: + err = gpg_error (GPG_ERR_INTERNAL); + break; + + default: + err = gpg_error (GPG_ERR_INV_VALUE); + break; + } + + leave: + return err; +} + + +/* Search for the keys described by (DESC,NDESC) and return them to + * the caller. BACKEND_HD is the handle for this backend and REQUEST + * is the current database request object. */ +gpg_error_t +be_sqlite_search (ctrl_t ctrl, + backend_handle_t backend_hd, db_request_t request, + KEYDB_SEARCH_DESC *desc, unsigned int ndesc) +{ + gpg_error_t err; + db_request_part_t part; + be_sqlite_local_t ctx; + + log_assert (backend_hd && backend_hd->db_type == DB_TYPE_SQLITE); + log_assert (request); + + acquire_mutex (); + + /* Find the specific request part or allocate it. */ + err = be_find_request_part (backend_hd, request, &part); + if (err) + goto leave; + ctx = part->besqlite; + + if (!desc) + { + /* Reset */ + ctx->select_done = 0; + ctx->select_eof = 0; + err = 0; + goto leave; + } + + if (ctx->select_eof) + { + /* Still in EOF state. */ + err = gpg_error (GPG_ERR_EOF); + goto leave; + } + + if (!ctx->select_done) + { + /* Initial search - run the select. */ + err = run_select_statement (ctx, desc, ndesc); + if (err) + goto leave; + ctx->select_done = 1; + } + + show_sqlstmt (ctx->select_stmt); + + /* SQL select succeeded - get the first or next row. */ + err = run_sql_step_for_select (ctx->select_stmt); + if (gpg_err_code (err) == GPG_ERR_SQL_ROW) + { + int n; + const void *ubid, *keyblob; + size_t keybloblen; + enum pubkey_types pubkey_type; + + ubid = sqlite3_column_blob (ctx->select_stmt, 0); + n = sqlite3_column_bytes (ctx->select_stmt, 0); + if (!ubid || n < 0) + { + if (!ubid && sqlite3_errcode (database_hd) == SQLITE_NOMEM) + err = gpg_error (gpg_err_code_from_sqlite (SQLITE_NOMEM)); + else + err = gpg_error (GPG_ERR_DB_CORRUPTED); + show_sqlstmt (ctx->select_stmt); + log_error ("error in returned SQL column UBID: No column (n=%d)\n",n); + goto leave; + } + if (n != UBID_LEN) + { + show_sqlstmt (ctx->select_stmt); + log_error ("error in returned SQL column UBID: Bad value (n=%d)\n",n); + err = gpg_error (GPG_ERR_INV_VALUE); + goto leave; + } + + n = sqlite3_column_int (ctx->select_stmt, 1); + if (!n && sqlite3_errcode (database_hd) == SQLITE_NOMEM) + { + err = gpg_error (gpg_err_code_from_sqlite (SQLITE_NOMEM)); + show_sqlstmt (ctx->select_stmt); + log_error ("error in returned SQL column TYPE: %s)\n", + gpg_strerror (err)); + goto leave; + } + pubkey_type = n; + + keyblob = sqlite3_column_blob (ctx->select_stmt, 2); + n = sqlite3_column_bytes (ctx->select_stmt, 2); + if (!keyblob || n < 0) + { + if (!keyblob && sqlite3_errcode (database_hd) == SQLITE_NOMEM) + err = gpg_error (gpg_err_code_from_sqlite (SQLITE_NOMEM)); + else + err = gpg_error (GPG_ERR_DB_CORRUPTED); + show_sqlstmt (ctx->select_stmt); + log_error ("error in returned SQL column KEYBLOB: %s\n", + gpg_strerror (err)); + goto leave; + } + keybloblen = n; + + err = be_return_pubkey (ctrl, keyblob, keybloblen, pubkey_type, ubid); + if (!err) + be_cache_pubkey (ctrl, ubid, keyblob, keybloblen, pubkey_type); + } + else if (gpg_err_code (err) == GPG_ERR_SQL_DONE) + { + /* FIXME: Move on to the next description index. */ + err = gpg_error (GPG_ERR_EOF); + ctx->select_eof = 1; + } + else + { + log_assert (err); + } + + leave: + release_mutex (); + return err; +} + + + +/* Helper for be_sqlite_store to update or insert a row in the pubkey + * table. */ +static gpg_error_t +store_into_pubkey (enum kbxd_store_modes mode, + enum pubkey_types pktype, const unsigned char *ubid, + const void *blob, size_t bloblen) +{ + gpg_error_t err; + const char *sqlstr; + sqlite3_stmt *stmt = NULL; + + if (mode == KBXD_STORE_UPDATE) + sqlstr = ("UPDATE pubkey set keyblob = :3, type = :2 WHERE ubid = :1"); + else if (mode == KBXD_STORE_INSERT) + sqlstr = ("INSERT INTO pubkey(ubid,type,keyblob) VALUES(:1,:2,:3)"); + else /* Auto */ + sqlstr = ("INSERT OR REPLACE INTO pubkey(ubid,type,keyblob)" + " VALUES(:1,:2,:3)"); + err = run_sql_prepare (sqlstr, &stmt); + if (err) + goto leave; + err = run_sql_bind_blob (stmt, 1, ubid, UBID_LEN); + if (err) + goto leave; + err = run_sql_bind_int (stmt, 2, (int)pktype); + if (err) + goto leave; + err = run_sql_bind_blob (stmt, 3, blob, bloblen); + if (err) + goto leave; + + err = run_sql_step (stmt); + + leave: + if (stmt) + sqlite3_finalize (stmt); + return err; +} + + +/* Helper for be_sqlite_store to update or insert a row in the + * fingerprint table. */ +static gpg_error_t +store_into_fingerprint (const unsigned char *ubid, int subkey, + const unsigned char *keygrip, sqlite3_int64 kid, + const unsigned char *fpr, int fprlen) +{ + gpg_error_t err; + const char *sqlstr; + sqlite3_stmt *stmt = NULL; + + sqlstr = ("INSERT OR REPLACE INTO fingerprint(fpr,kid,keygrip,subkey,ubid)" + " VALUES(:1,:2,:3,:4,:5)"); + err = run_sql_prepare (sqlstr, &stmt); + if (err) + goto leave; + err = run_sql_bind_blob (stmt, 1, fpr, fprlen); + if (err) + goto leave; + err = run_sql_bind_int64 (stmt, 2, kid); + if (err) + goto leave; + err = run_sql_bind_blob (stmt, 3, keygrip, KEYGRIP_LEN); + if (err) + goto leave; + err = run_sql_bind_int (stmt, 4, subkey); + if (err) + goto leave; + err = run_sql_bind_blob (stmt, 5, ubid, UBID_LEN); + if (err) + goto leave; + + err = run_sql_step (stmt); + + leave: + if (stmt) + sqlite3_finalize (stmt); + return err; +} + + +/* Helper for be_sqlite_store to update or insert a row in the + * userid table. */ +static gpg_error_t +store_into_userid (const unsigned char *ubid, enum pubkey_types pktype, + const char *uid) +{ + gpg_error_t err; + const char *sqlstr; + sqlite3_stmt *stmt = NULL; + char *addrspec = NULL; + + sqlstr = ("INSERT OR REPLACE INTO userid(uid,addrspec,type,ubid)" + " VALUES(:1,:2,:3,:4)"); + err = run_sql_prepare (sqlstr, &stmt); + if (err) + goto leave; + + err = run_sql_bind_text (stmt, 1, uid); + if (err) + goto leave; + addrspec = mailbox_from_userid (uid, 0); + err = run_sql_bind_text (stmt, 2, addrspec); + if (err) + goto leave; + err = run_sql_bind_int (stmt, 3, pktype); + if (err) + goto leave; + err = run_sql_bind_blob (stmt, 4, ubid, UBID_LEN); + if (err) + goto leave; + + err = run_sql_step (stmt); + + leave: + if (stmt) + sqlite3_finalize (stmt); + xfree (addrspec); + return err; +} + + +/* Store (BLOB,BLOBLEN) into the database. UBID is the UBID macthing + * that blob. BACKEND_HD is the handle for this backend and REQUEST + * is the current database request object. MODE is the store + * mode. */ +gpg_error_t +be_sqlite_store (ctrl_t ctrl, backend_handle_t backend_hd, + db_request_t request, enum kbxd_store_modes mode, + enum pubkey_types pktype, const unsigned char *ubid, + const void *blob, size_t bloblen) +{ + gpg_error_t err; + db_request_part_t part; + /* be_sqlite_local_t ctx; */ + int got_mutex = 0; + int in_transaction = 0; + int info_valid = 0; + struct _keybox_openpgp_info info; + struct _keybox_openpgp_key_info *kinfo; + + (void)ctrl; + + log_assert (backend_hd && backend_hd->db_type == DB_TYPE_SQLITE); + log_assert (request); + + /* Fixme: The code below is duplicated in be_ubid_from_blob - we + * should have only one function and pass the passed info around + * with the BLOB. */ + + if (be_is_x509_blob (blob, bloblen)) + { + /* The UBID is also our fingerprint. */ + /* FIXME: Extract keygrip and KID. */ + err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); + goto leave; + } + else + { + err = _keybox_parse_openpgp (blob, bloblen, NULL, &info); + if (err) + { + log_info ("error parsing OpenPGP blob: %s\n", gpg_strerror (err)); + err = gpg_error (GPG_ERR_WRONG_BLOB_TYPE); + goto leave; + } + info_valid = 1; + log_assert (pktype == PUBKEY_TYPE_OPGP); + log_assert (info.primary.fprlen >= 20); + log_assert (!memcmp (ubid, info.primary.fpr, UBID_LEN)); + } + + + acquire_mutex (); + got_mutex = 1; + + /* Find the specific request part or allocate it. */ + err = be_find_request_part (backend_hd, request, &part); + if (err) + goto leave; + /* ctx = part->besqlite; */ + + err = run_sql_statement ("begin transaction"); + if (err) + goto leave; + in_transaction = 1; + + err = store_into_pubkey (mode, pktype, ubid, blob, bloblen); + if (err) + goto leave; + + /* Delete all related rows so that we can freshly add possibly added + * or changed user ids and subkeys. */ + err = run_sql_statement_bind_ubid + ("DELETE FROM fingerprint WHERE ubid = :1", ubid); + if (err) + goto leave; + err = run_sql_statement_bind_ubid + ("DELETE FROM userid WHERE ubid = :1", ubid); + if (err) + goto leave; + + kinfo = &info.primary; + err = store_into_fingerprint (ubid, 0, kinfo->grip, + kid_from_mem (kinfo->keyid), + kinfo->fpr, kinfo->fprlen); + if (err) + goto leave; + + if (info.nsubkeys) + { + int subkey = 1; + for (kinfo = &info.subkeys; kinfo; kinfo = kinfo->next, subkey++) + { + err = store_into_fingerprint (ubid, subkey, kinfo->grip, + kid_from_mem (kinfo->keyid), + kinfo->fpr, kinfo->fprlen); + if (err) + goto leave; + } + } + + if (info.nuids) + { + struct _keybox_openpgp_uid_info *u; + + u = &info.uids; + do + { + log_assert (u->off <= bloblen); + log_assert (u->off + u->len <= bloblen); + { + char *uid = xtrymalloc (u->len + 1); + if (!uid) + { + err = gpg_error_from_syserror (); + goto leave; + } + memcpy (uid, (const unsigned char *)blob + u->off, u->len); + uid[u->len] = 0; + /* Note that we ignore embedded zeros in the user id; this + * is what we do all over the place. */ + err = store_into_userid (ubid, pktype, uid); + xfree (uid); + } + if (err) + goto leave; + + u = u->next; + } + while (u); + } + + leave: + if (in_transaction && !err) + err = run_sql_statement ("commit"); + else if (in_transaction) + { + if (run_sql_statement ("rollback")) + log_error ("Warning: database rollback failed - should not happen!\n"); + } + if (got_mutex) + release_mutex (); + if (info_valid) + _keybox_destroy_openpgp_info (&info); + return err; +} + + +/* Delete the blob specified by UBID from the database. BACKEND_HD is + * the handle for this backend and REQUEST is the current database + * request object. */ +gpg_error_t +be_sqlite_delete (ctrl_t ctrl, backend_handle_t backend_hd, + db_request_t request, const unsigned char *ubid) +{ + gpg_error_t err; + db_request_part_t part; + /* be_sqlite_local_t ctx; */ + sqlite3_stmt *stmt = NULL; + int in_transaction = 0; + + (void)ctrl; + + log_assert (backend_hd && backend_hd->db_type == DB_TYPE_SQLITE); + log_assert (request); + + acquire_mutex (); + + /* Find the specific request part or allocate it. */ + err = be_find_request_part (backend_hd, request, &part); + if (err) + goto leave; + /* ctx = part->besqlite; */ + + err = run_sql_statement ("begin transaction"); + if (err) + goto leave; + in_transaction = 1; + + err = run_sql_statement_bind_ubid + ("DELETE from userid WHERE ubid = :1", ubid); + if (!err) + err = run_sql_statement_bind_ubid + ("DELETE from fingerprint WHERE ubid = :1", ubid); + if (!err) + err = run_sql_statement_bind_ubid + ("DELETE from pubkey WHERE ubid = :1", ubid); + + + leave: + if (stmt) + sqlite3_finalize (stmt); + if (in_transaction && !err) + err = run_sql_statement ("commit"); + else if (in_transaction) + { + if (run_sql_statement ("rollback")) + log_error ("Warning: database rollback failed - should not happen!\n"); + } + release_mutex (); + return err; +} diff --git a/kbx/backend-support.c b/kbx/backend-support.c index f1e80b0c3..c8965da9a 100644 --- a/kbx/backend-support.c +++ b/kbx/backend-support.c @@ -51,6 +51,7 @@ strdbtype (enum database_types t) case DB_TYPE_NONE: return "none"; case DB_TYPE_CACHE:return "cache"; case DB_TYPE_KBX: return "keybox"; + case DB_TYPE_SQLITE: return "sqlite"; } return "?"; } @@ -87,6 +88,9 @@ be_generic_release_backend (ctrl_t ctrl, backend_handle_t hd) case DB_TYPE_KBX: be_kbx_release_resource (ctrl, hd); break; + case DB_TYPE_SQLITE: + be_sqlite_release_resource (ctrl, hd); + break; default: log_error ("%s: faulty backend handle of type %d given\n", __func__, hd->db_type); @@ -107,6 +111,7 @@ be_release_request (db_request_t req) { partn = part->next; be_kbx_release_kbx_hd (part->kbx_hd); + be_sqlite_release_local (part->besqlite); xfree (part); } } @@ -140,6 +145,15 @@ be_find_request_part (backend_handle_t backend_hd, db_request_t request, return err; } } + else if (backend_hd->db_type == DB_TYPE_SQLITE) + { + err = be_sqlite_init_local (backend_hd, part); + if (err) + { + xfree (part); + return err; + } + } part->next = request->part; request->part = part; } @@ -174,8 +188,8 @@ be_return_pubkey (ctrl_t ctrl, const void *buffer, size_t buflen, /* Return true if (BLOB/BLOBLEN) seems to be an X509 certificate. */ -static int -is_x509_blob (const unsigned char *blob, size_t bloblen) +int +be_is_x509_blob (const unsigned char *blob, size_t bloblen) { const unsigned char *p; size_t n, objlen, hdrlen; @@ -237,7 +251,7 @@ be_ubid_from_blob (const void *blob, size_t bloblen, { gpg_error_t err; - if (is_x509_blob (blob, bloblen)) + if (be_is_x509_blob (blob, bloblen)) { /* Although libksba has a dedicated function to compute the * fingerprint we compute it here directly because we know that diff --git a/kbx/backend.h b/kbx/backend.h index 3aa848714..092f490bc 100644 --- a/kbx/backend.h +++ b/kbx/backend.h @@ -32,7 +32,8 @@ enum database_types { DB_TYPE_NONE, /* No database at all (unitialized etc.). */ DB_TYPE_CACHE, /* The cache backend (backend-cache.c). */ - DB_TYPE_KBX /* Keybox type database (backend-kbx.c). */ + DB_TYPE_KBX, /* Keybox type database (backend-kbx.c). */ + DB_TYPE_SQLITE /* SQLite type database (backend-sqlite.c).*/ }; @@ -43,6 +44,11 @@ struct backend_handle_s; typedef struct backend_handle_s *backend_handle_t; +/* Private data for sqlite requests. */ +struct be_sqlite_local_s; +typedef struct be_sqlite_local_s *be_sqlite_local_t; + + /* Object to store backend specific database information per database * handle. */ struct db_request_part_s @@ -52,9 +58,12 @@ struct db_request_part_s /* Id of the backend instance this object pertains to. */ unsigned int backend_id; - /* The handle used for a KBX backend or NULL. */ + /* Local data for a KBX backend or NULL. */ KEYBOX_HANDLE kbx_hd; + /* Local data for a sqlite backend. */ + be_sqlite_local_t besqlite; + /* For the CACHE backend the indices into the bloblist for each * index type. */ struct { @@ -106,6 +115,7 @@ gpg_error_t be_find_request_part (backend_handle_t backend_hd, gpg_error_t be_return_pubkey (ctrl_t ctrl, const void *buffer, size_t buflen, enum pubkey_types pubkey_type, const unsigned char *ubid); +int be_is_x509_blob (const unsigned char *blob, size_t bloblen); gpg_error_t be_ubid_from_blob (const void *blob, size_t bloblen, enum pubkey_types *r_pktype, char *r_ubid); @@ -148,4 +158,24 @@ gpg_error_t be_kbx_delete (ctrl_t ctrl, backend_handle_t backend_hd, db_request_t request); +/*-- backend-sqlite.c --*/ +gpg_error_t be_sqlite_add_resource (ctrl_t ctrl, backend_handle_t *r_hd, + const char *filename, int readonly); +void be_sqlite_release_resource (ctrl_t ctrl, backend_handle_t hd); + +gpg_error_t be_sqlite_init_local (backend_handle_t backend_hd, + db_request_part_t part); +void be_sqlite_release_local (be_sqlite_local_t ctx); +gpg_error_t be_sqlite_search (ctrl_t ctrl, backend_handle_t hd, + db_request_t request, + KEYDB_SEARCH_DESC *desc, unsigned int ndesc); +gpg_error_t be_sqlite_store (ctrl_t ctrl, backend_handle_t backend_hd, + db_request_t request, enum kbxd_store_modes mode, + enum pubkey_types pktype, + const unsigned char *ubid, + const void *blob, size_t bloblen); +gpg_error_t be_sqlite_delete (ctrl_t ctrl, backend_handle_t backend_hd, + db_request_t request, const unsigned char *ubid); + + #endif /*KBX_BACKEND_H*/ diff --git a/kbx/frontend.c b/kbx/frontend.c index 1793a05a0..508bbc072 100644 --- a/kbx/frontend.c +++ b/kbx/frontend.c @@ -115,6 +115,8 @@ kbxd_set_database (ctrl_t ctrl, const char *filename_arg, int readonly) ; /* We already know it. */ else if (n > 4 && !strcmp (filename + n - 4, ".kbx")) db_type = DB_TYPE_KBX; + else if (n > 3 && !strcmp (filename + n - 3, ".db")) + db_type = DB_TYPE_SQLITE; else { log_error (_("can't use file '%s': %s\n"), filename, _("unknown suffix")); @@ -135,7 +137,11 @@ kbxd_set_database (ctrl_t ctrl, const char *filename_arg, int readonly) case DB_TYPE_KBX: err = be_kbx_add_resource (ctrl, &handle, filename, readonly); break; - } + + case DB_TYPE_SQLITE: + err = be_sqlite_add_resource (ctrl, &handle, filename, readonly); + break; + } if (err) goto leave; @@ -170,7 +176,9 @@ kbxd_release_session_info (ctrl_t ctrl) /* Search for the keys described by (DESC,NDESC) and return them to - * the caller. If RESET is set, the search state is first reset. */ + * the caller. If RESET is set, the search state is first reset. + * Only a reset guarantees that changed search description in DESC are + * considered. */ gpg_error_t kbxd_search (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, unsigned int ndesc, int reset) @@ -178,7 +186,6 @@ kbxd_search (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, unsigned int ndesc, gpg_error_t err; int i; db_request_t request; - int start_at_ubid = 0; if (DBG_CLOCK) log_clock ("%s: enter", __func__); @@ -230,6 +237,11 @@ kbxd_search (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, unsigned int ndesc, request, NULL, 0); break; + case DB_TYPE_SQLITE: + err = be_sqlite_search (ctrl, the_database.backend_handle, + request, NULL, 0); + break; + default: err = gpg_error (GPG_ERR_INTERNAL); break; @@ -263,22 +275,13 @@ kbxd_search (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, unsigned int ndesc, break; case DB_TYPE_KBX: - if (start_at_ubid) - { - /* We need to set the startpoint for the search. */ - err = be_kbx_seek (ctrl, the_database.backend_handle, request, - request->last_cached_ubid); - if (err) - { - log_debug ("%s: seeking %s to an UBID failed: %s\n", __func__, - strdbtype (the_database.db_type), gpg_strerror (err)); - break; - } - } err = be_kbx_search (ctrl, the_database.backend_handle, request, desc, ndesc); - if (start_at_ubid && gpg_err_code (err) == GPG_ERR_EOF) - be_cache_mark_final (ctrl, request); + break; + + case DB_TYPE_SQLITE: + err = be_sqlite_search (ctrl, the_database.backend_handle, request, + desc, ndesc); break; default: @@ -292,7 +295,6 @@ kbxd_search (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, unsigned int ndesc, log_debug ("%s: searched %s => %s\n", __func__, strdbtype (the_database.db_type), gpg_strerror (err)); request->any_search = 1; - start_at_ubid = 0; if (!err) { request->any_found = 1; @@ -303,7 +305,6 @@ kbxd_search (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, unsigned int ndesc, { if (request->last_cached_final) goto leave; - start_at_ubid = 1; } request->next_dbidx++; /* FIXME: We need to see which pubkey type we need to insert. */ @@ -366,6 +367,38 @@ kbxd_store (ctrl_t ctrl, const void *blob, size_t bloblen, if (the_database.db_type == DB_TYPE_KBX) { err = be_kbx_seek (ctrl, the_database.backend_handle, request, ubid); + if (!err) + ; /* Found - need to update. */ + else if (gpg_err_code (err) == GPG_ERR_EOF) + insert = 1; /* Not found - need to insert. */ + else + { + log_debug ("%s: searching fingerprint failed: %s\n", + __func__, gpg_strerror (err)); + goto leave; + } + + if (insert) + { + if (mode == KBXD_STORE_UPDATE) + err = gpg_error (GPG_ERR_CONFLICT); + else + err = be_kbx_insert (ctrl, the_database.backend_handle, request, + pktype, blob, bloblen); + } + else /* Update. */ + { + if (mode == KBXD_STORE_INSERT) + err = gpg_error (GPG_ERR_CONFLICT); + else + err = be_kbx_update (ctrl, the_database.backend_handle, request, + pktype, blob, bloblen); + } + } + else if (the_database.db_type == DB_TYPE_SQLITE) + { + err = be_sqlite_store (ctrl, the_database.backend_handle, request, + mode, pktype, ubid, blob, bloblen); } else { @@ -374,37 +407,6 @@ kbxd_store (ctrl_t ctrl, const void *blob, size_t bloblen, err = gpg_error (GPG_ERR_INTERNAL); } - if (!err) - ; /* Found - need to update. */ - else if (gpg_err_code (err) == GPG_ERR_EOF) - insert = 1; /* Not found - need to insert. */ - else - { - log_debug ("%s: searching fingerprint failed: %s\n", - __func__, gpg_strerror (err)); - goto leave; - } - - if (insert) - { - if (mode == KBXD_STORE_UPDATE) - err = gpg_error (GPG_ERR_CONFLICT); - else if (the_database.db_type == DB_TYPE_KBX) - err = be_kbx_insert (ctrl, the_database.backend_handle, request, - pktype, blob, bloblen); - else - err = gpg_error (GPG_ERR_INTERNAL); - } - else /* Update. */ - { - if (mode == KBXD_STORE_INSERT) - err = gpg_error (GPG_ERR_CONFLICT); - else if (the_database.db_type == DB_TYPE_KBX) - err = be_kbx_update (ctrl, the_database.backend_handle, request, - pktype, blob, bloblen); - else - err = gpg_error (GPG_ERR_INTERNAL); - } leave: release_lock (ctrl); @@ -450,6 +452,24 @@ kbxd_delete (ctrl_t ctrl, const unsigned char *ubid) if (the_database.db_type == DB_TYPE_KBX) { err = be_kbx_seek (ctrl, the_database.backend_handle, request, ubid); + if (!err) + ; /* Found - we can delete. */ + else if (gpg_err_code (err) == GPG_ERR_EOF) + { + err = gpg_error (GPG_ERR_NOT_FOUND); + goto leave; + } + else + { + log_debug ("%s: searching primary fingerprint failed: %s\n", + __func__, gpg_strerror (err)); + goto leave; + } + err = be_kbx_delete (ctrl, the_database.backend_handle, request); + } + else if (the_database.db_type == DB_TYPE_SQLITE) + { + err = be_sqlite_delete (ctrl, the_database.backend_handle, request, ubid); } else { @@ -458,24 +478,6 @@ kbxd_delete (ctrl_t ctrl, const unsigned char *ubid) err = gpg_error (GPG_ERR_INTERNAL); } - if (!err) - ; /* Found - we can delete. */ - else if (gpg_err_code (err) == GPG_ERR_EOF) - { - err = gpg_error (GPG_ERR_NOT_FOUND); - goto leave; - } - else - { - log_debug ("%s: searching primary fingerprint failed: %s\n", - __func__, gpg_strerror (err)); - goto leave; - } - - if (the_database.db_type == DB_TYPE_KBX) - err = be_kbx_delete (ctrl, the_database.backend_handle, request); - else - err = gpg_error (GPG_ERR_INTERNAL); leave: release_lock (ctrl); diff --git a/kbx/frontend.h b/kbx/frontend.h index 20565215a..d38d442f0 100644 --- a/kbx/frontend.h +++ b/kbx/frontend.h @@ -23,14 +23,6 @@ #include "keybox-search-desc.h" -enum kbxd_store_modes - { - KBXD_STORE_AUTO = 0, /* Update or insert. */ - KBXD_STORE_INSERT, /* Allow only inserts. */ - KBXD_STORE_UPDATE /* Allow only updates. */ - }; - - gpg_error_t kbxd_set_database (ctrl_t ctrl, const char *filename_arg, int readonly); diff --git a/kbx/keyboxd.c b/kbx/keyboxd.c index 25a97e9a3..59e320467 100644 --- a/kbx/keyboxd.c +++ b/kbx/keyboxd.c @@ -736,7 +736,8 @@ main (int argc, char **argv ) } kbxd_init_default_ctrl (ctrl); - kbxd_set_database (ctrl, "pubring.kbx", 0); + /* kbxd_set_database (ctrl, "pubring.kbx", 0); */ + kbxd_set_database (ctrl, "pubring.db", 0); kbxd_start_command_handler (ctrl, GNUPG_INVALID_FD, 0); kbxd_deinit_default_ctrl (ctrl); @@ -870,7 +871,8 @@ main (int argc, char **argv ) kbxd_exit (1); } kbxd_init_default_ctrl (ctrl); - kbxd_set_database (ctrl, "pubring.kbx", 0); + /* kbxd_set_database (ctrl, "pubring.kbx", 0); */ + kbxd_set_database (ctrl, "pubring.db", 0); kbxd_deinit_default_ctrl (ctrl); xfree (ctrl); } diff --git a/kbx/keyboxd.h b/kbx/keyboxd.h index edef8975c..d7bb97ab9 100644 --- a/kbx/keyboxd.h +++ b/kbx/keyboxd.h @@ -137,6 +137,14 @@ struct server_control_s #define L_(a) (a) #endif +enum kbxd_store_modes + { + KBXD_STORE_AUTO = 0, /* Update or insert. */ + KBXD_STORE_INSERT, /* Allow only inserts. */ + KBXD_STORE_UPDATE /* Allow only updates. */ + }; + + /*-- keyboxd.c --*/ void kbxd_exit (int rc) GPGRT_ATTR_NORETURN;