[git] GnuPG - branch, neal/kdb, updated. gnupg-2.1.9-169-g6da0e52

by Neal H. Walfield cvs at cvs.gnupg.org
Mon Nov 23 22:39:17 CET 2015


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "The GNU Privacy Guard".

The branch, neal/kdb has been updated
  discards  68be39bdc474e77c533b3320541206084efa525d (commit)
       via  6da0e52af3e53469f50f0623f12a7d09521fdad8 (commit)
       via  19362a8dd7ee986c082a5afc5a446f939991ec0f (commit)
       via  5b84b0d660c8329e184d98682665aaea7e1703d2 (commit)
       via  b75e1b3d8b1643640d046f7f8e89adf5b1caa7a3 (commit)
       via  946faaff04f3340ed6db9e89c5036dc5f9beca6a (commit)

This update added new revisions after undoing existing revisions.  That is
to say, the old revision is not a strict subset of the new revision.  This
situation occurs when you --force push a change and generate a repository
containing something like this:

 * -- * -- B -- O -- O -- O (68be39bdc474e77c533b3320541206084efa525d)
            \
             N -- N -- N (6da0e52af3e53469f50f0623f12a7d09521fdad8)

When this happens we assume that you've already had alert emails for all
of the O revisions, and so we here report only the revisions in the N
branch from the common base, B.

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 6da0e52af3e53469f50f0623f12a7d09521fdad8
Author: Neal H. Walfield <neal at g10code.com>
Date:   Thu Oct 29 10:18:13 2015 +0100

    New key db.

diff --git a/g10/Makefile.am b/g10/Makefile.am
index 10714f6..16e9ce9 100644
--- a/g10/Makefile.am
+++ b/g10/Makefile.am
@@ -30,7 +30,7 @@ include $(top_srcdir)/am/cmacros.am
 AM_CFLAGS = $(SQLITE3_CFLAGS) $(LIBGCRYPT_CFLAGS) \
             $(LIBASSUAN_CFLAGS) $(GPG_ERROR_CFLAGS)
 
-needed_libs = ../kbx/libkeybox.a $(libcommon)
+needed_libs = ../kbx/libkeybox.a $(libcommon) $(SQLITE3_LIBS)
 
 bin_PROGRAMS = gpg2
 if !HAVE_W32CE_SYSTEM
@@ -58,7 +58,7 @@ trust_source = trustdb.c trustdb.h tdbdump.c tdbio.c tdbio.h
 endif
 
 if USE_TOFU
-tofu_source = tofu.h tofu.c sqlite.c sqlite.h
+tofu_source = tofu.h tofu.c
 else
 tofu_source =
 endif
@@ -102,7 +102,9 @@ common_source =  \
 	      sig-check.c	\
 	      keylist.c 	\
 	      pkglue.c pkglue.h \
-	      ecdh.c
+	      ecdh.c            \
+	      kdb.c kdb.h       \
+	      sqlite.c sqlite.h
 
 gpg2_SOURCES  = gpg.c		\
 	      server.c          \
@@ -148,7 +150,7 @@ gpgv2_SOURCES = gpgv.c           \
 
 LDADD =  $(needed_libs) ../common/libgpgrl.a \
          $(ZLIBS) $(LIBINTL) $(CAPLIBS) $(NETLIBS)
-gpg2_LDADD = $(LDADD) $(SQLITE3_LIBS) $(LIBGCRYPT_LIBS) $(LIBREADLINE) \
+gpg2_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) $(LIBREADLINE) \
              $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \
 	     $(LIBICONV) $(resource_objs) $(extra_sys_libs)
 gpg2_LDFLAGS = $(extra_bin_ldflags)
diff --git a/g10/import.c b/g10/import.c
index e1577b8..7555cc6 100644
--- a/g10/import.c
+++ b/g10/import.c
@@ -1154,6 +1154,9 @@ import_one (ctrl_t ctrl,
           goto leave;
         }
 
+      assert (strcmp (keystr_from_pk(keyblock_orig->pkt->pkt.public_key),
+                      keystr_from_pk(pk)) == 0);
+
       /* Make sure the original direct key sigs are all sane.  */
       n_sigs_cleaned = fix_bad_direct_key_sigs (keyblock_orig, keyid);
       if (n_sigs_cleaned)
diff --git a/g10/kdb.c b/g10/kdb.c
new file mode 100644
index 0000000..b1c5544
--- /dev/null
+++ b/g10/kdb.c
@@ -0,0 +1,1349 @@
+#include <config.h>
+#include <assert.h>
+#include <sqlite3.h>
+
+#include "gpg.h"
+#include "util.h"
+#include "logging.h"
+#include "i18n.h"
+#include "mbox-util.h"
+#include "sqlite.h"
+
+#include "kdb.h"
+
+#if 0
+#  define DEBUG(fmt, ...)                                        \
+  do {                                                           \
+    log_debug("%s:%d: "fmt, __func__, __LINE__, ##__VA_ARGS__);  \
+  } while (0)
+#else
+#  define DEBUG(fmt, ...) do {} while (0)
+#endif
+
+
+struct kdb_resource
+{
+  struct kdb_resource *next;
+  int read_only;
+  sqlite3 *db;
+
+  long long int key;
+  char *kb;
+  int kb_len;
+  u32 *sigstatus;
+
+  char fname[1];
+};
+typedef struct kdb_resource *KDB_RESOURCE;
+typedef struct kdb_resource const * CONST_KDB_RESOURCE;
+
+/* All registered resources.  */
+static KDB_RESOURCE kdb_resources;
+
+static void
+hdr_cache_clear (KDB_RESOURCE resource)
+{
+  xfree (resource->kb);
+  resource->kb = NULL;
+
+  xfree (resource->sigstatus);
+  resource->sigstatus = NULL;
+
+  resource->key = -1;
+}
+

+struct key
+{
+  unsigned long int key;
+  char *kb;
+  size_t kb_len;
+  u32 *sigstatus;
+  struct key *next;
+};
+
+struct keydb_handle
+{
+  KDB_RESOURCE resource;
+  /* Current key.  */
+  long long int key;
+  int eof;
+
+  struct key *full_scan;
+
+  struct {
+    long long int key;
+    int pk_no;
+    int uid_no;
+  } found, saved_found;
+};
+
+/* Perform an in-place reversal.  Returns the new head.  */
+static struct key *
+key_list_reverse (struct key *list)
+{
+  struct key *list_rev = NULL;
+
+  while (list)
+    {
+      struct key *list_next_orig = list->next;
+      list->next = list_rev;
+      list_rev = list;
+      list = list_next_orig;
+    }
+  return list_rev;
+}
+
+static struct key *
+key_free (struct key *key)
+{
+  struct key *key_next;
+
+  if (! key)
+    return NULL;
+
+  key_next = key->next;
+  xfree (key->kb);
+  xfree (key->sigstatus);
+  xfree (key);
+
+  return key_next;
+}
+
+static void
+hd_cache_clear (KDB_HANDLE hd)
+{
+  struct key *key_next;
+
+  if (! hd->full_scan)
+    return;
+
+  key_next = hd->full_scan;
+  while (key_next)
+    key_next = key_free (key_next);
+
+  hd->full_scan = NULL;
+}
+
+/* RESOURCE is a value returned by a previous call to
+   kdb_register_file in *RESOURCEP.  */
+KDB_HANDLE
+kdb_new (void *resource)
+{
+  KDB_RESOURCE r;
+  KDB_HANDLE hd;
+
+  /* Assert that the resource was indeed previously registered.  */
+  for (r = kdb_resources; r; r = r->next)
+    if (r == resource)
+      break;
+  assert (r);
+
+  hd = xmalloc_clear (sizeof (*hd));
+  hd->resource = resource;
+  hd->key = -1;
+  return hd;
+}
+
+
+/* Collect a series of integers from a query.  Aborts if the argument
+   is not a valid integer (or real of the form X.0).  COOKIE points to
+   an array of unsigned long ints, which has enough space for ARGC
+   values.  */
+static int
+get_unsigned_longs_cb (void *cookie, int argc, char **argv, char **azColName)
+{
+  unsigned long int *values = cookie;
+  int i;
+  char *tail = NULL;
+
+  (void) azColName;
+
+  for (i = 0; i < argc; i ++)
+    {
+      if (! argv[i])
+        values[i] = 0;
+      else
+        {
+          errno = 0;
+          values[i] = strtoul (argv[i], &tail, 0);
+          if (errno || ! (strcmp (tail, ".0") == 0 || *tail == '\0'))
+            /* Abort.  */
+            return 1;
+        }
+    }
+
+  return 0;
+}
+
+static int
+get_unsigned_longs_cb2 (void *cookie, int argc, char **argv, char **azColName,
+                        sqlite3_stmt *stmt)
+{
+  (void) stmt;
+  return get_unsigned_longs_cb (cookie, argc, argv, azColName);
+}
+
+/* We expect a single integer column whose name is "version".  COOKIE
+   must point to an int.  This function always aborts.  On error or a
+   if the version is bad, sets *VERSION to -1.  */
+static int
+version_check_cb (void *cookie, int argc, char **argv, char **azColName)
+{
+  int *version = cookie;
+
+  if (argc != 1 || strcmp (azColName[0], "version") != 0)
+    {
+      *version = -1;
+      return 1;
+    }
+
+  if (strcmp (argv[0], "1") == 0)
+    *version = 1;
+  else
+    {
+      log_error (_("unsupported kdb version: %s\n"), argv[0]);
+      *version = -1;
+    }
+
+  /* Don't run again.  */
+  return 1;
+}
+
+/* Register a new file.  If the file has already been registered then
+   returns NULL otherwise returns */
+gpg_error_t
+kdb_register_file (const char *fname, int read_only, void **resourcep)
+{
+  KDB_RESOURCE resource;
+  int rc;
+  sqlite3 *db = NULL;
+  char *err;
+  unsigned long int count;
+  int need_init = 1;
+
+  for (resource = kdb_resources; resource; resource = resource->next)
+    if (same_file_p (resource->fname, fname))
+      {
+        if (resourcep)
+          *resourcep = resource;
+        if (read_only)
+          resource->read_only = 1;
+        return 0;
+      }
+
+  rc = sqlite3_open_v2 (fname, &db,
+                        read_only
+                        ? SQLITE_OPEN_READONLY
+                        : (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE),
+                        NULL);
+  if (rc)
+    {
+      log_error ("Failed to open the key db '%s': %s\n",
+                 fname, sqlite3_errstr (rc));
+      return rc;
+    }
+
+  /* If the DB has no tables, then assume this is a new DB that needs
+     to be initialized.  */
+  rc = sqlite3_exec (db,
+		     "select count(*) from sqlite_master where type='table';",
+		     get_unsigned_longs_cb, &count, &err);
+  if (rc)
+    {
+      log_error (_("error querying kdb's available tables: %s\n"),
+		 err);
+      sqlite3_free (err);
+      goto out;
+    }
+  else if (count != 0)
+    /* Assume that the DB is already initialized.  Make sure the
+       version is okay.  */
+    {
+      int version = -1;
+      rc = sqlite3_exec (db, "select version from version;", version_check_cb,
+			 &version, &err);
+      if (rc == SQLITE_ABORT && version == 1)
+	/* Happy, happy, joy, joy.  */
+	{
+	  sqlite3_free (err);
+          rc = 0;
+          need_init = 0;
+	}
+      else if (rc == SQLITE_ABORT && version == -1)
+	/* Unsupported version.  */
+	{
+	  /* An error message was already displayed.  */
+	  sqlite3_free (err);
+          goto out;
+	}
+      else if (rc)
+	/* Some error.  */
+	{
+	  log_error (_("error determining kdb's version: %s\n"), err);
+	  sqlite3_free (err);
+          goto out;
+	}
+      else
+	/* Unexpected success.  This can only happen if there are no
+	   rows.  */
+	{
+	  log_error (_("error determining kdb's version: %s\n"),
+		     "select returned 0, but expected ABORT");
+          rc = 1;
+          goto out;
+	}
+    }
+
+  if (need_init)
+    {
+      /* Create the version table.  */
+      rc = sqlite3_exec (db,
+                         "create table version (version INTEGER);",
+                         NULL, NULL, &err);
+      if (rc)
+        {
+          log_error (_("error initializing kdb database (%s): %s\n"),
+                     "version", err);
+          sqlite3_free (err);
+          goto out;
+        }
+
+      /* Initialize the version table, which contains a single integer
+         value.  */
+      rc = sqlite3_exec (db,
+                         "insert into version values (1);",
+                         NULL, NULL, &err);
+      if (rc)
+        {
+          log_error (_("error initializing kdb database (%s): %s\n"),
+                     "version, init", err);
+          sqlite3_free (err);
+          goto out;
+        }
+
+      /* We have 3 tables:
+
+         primaries - the list of all primary keys and the key block.
+
+         keys - the list of all keys and subkeys.
+
+         user ids - the list of all user ids.  */
+
+      rc = sqlite3_exec
+        (db,
+         /* Enable foreign key constraints.  */
+         "pragma foreign_keys = on;\n"
+         "create table primaries\n"
+         " (oid INTEGER PRIMARY KEY AUTOINCREMENT,\n"
+         "  fingerprint_rev TEXT COLLATE NOCASE, keyblock BLOB,\n"
+         "  sigstatus TEXT);\n"
+         "create index primaries_fingerprint on primaries\n"
+         " (fingerprint_rev COLLATE NOCASE);\n"
+         "\n"
+         "create table keys\n"
+         " (primary_key INTEGER, fingerprint_rev TEXT COLLATE NOCASE,\n"
+         "  pk_no INTEGER,\n"
+         "  unique (primary_key, pk_no),\n"
+         "  foreign key (primary_key) references primaries(oid));\n"
+         "create index keys_fingerprint_primary_key_pk_no on keys\n"
+         " (fingerprint_rev COLLATE NOCASE, primary_key, pk_no);\n"
+         "create index keys_primary_key_pk_no on keys (primary_key, pk_no);\n"
+         "\n"
+         /* XXX: Is COLLATE NOCASE reasonable?  */
+         "create table uids\n"
+         " (primary_key INTEGER, uid TEXT COLLATE NOCASE,\n"
+         "  email TEXT COLLATE NOCASE, uid_no INTEGER,\n"
+         "  unique (primary_key, uid_no),\n"
+         "  foreign key (primary_key) references primaries(oid));\n"
+         "create index uids_ordered on uids (primary_key, uid_no);\n"
+         /* In most cases, we search for a substring (like
+            '%foo at bar.com%'.  This can't exploit an index so the
+            following indices mostly represent overhead.  */
+#if 0
+         "create index uids_uid_ordered on uids\n"
+         " (uid COLLATE NOCASE, primary_key, uid_no);\n"
+         "create index uids_email_ordered on uids\n"
+         " (email COLLATE NOCASE, primary_key, uid_no);\n"
+#endif
+         ,
+         NULL, NULL, &err);
+      if (rc)
+        {
+          log_error (_("error initializing kdb database: %s\n"), err);
+          sqlite3_free (err);
+          goto out;
+        }
+    }
+
+  resource = xmalloc_clear (sizeof *resource + strlen (fname));
+  strcpy (resource->fname, fname);
+  resource->read_only = read_only;
+  resource->db = db;
+  resource->next = kdb_resources;
+  kdb_resources = resource;
+
+  if (resourcep)
+    *resourcep = resource;
+
+ out:
+  if (rc)
+    {
+      if (resourcep)
+        *resourcep = NULL;
+
+      sqlite3_close (db);
+      return gpg_error (GPG_ERR_GENERAL);
+    }
+
+  return 0;
+}
+
+int
+kdb_is_writable (void *token)
+{
+  KDB_RESOURCE resource = token;
+  if (resource->read_only)
+    return 0;
+  return 1;
+}
+
+/* Release the handle.  */
+void
+kdb_release (KDB_HANDLE hd)
+{
+  KDB_RESOURCE r;
+
+  if (! hd)
+    return;
+
+  /* Check for double frees.  */
+  assert (hd->resource);
+  for (r = kdb_resources; r; r = r->next)
+    if (r == hd->resource)
+      break;
+  assert (r);
+
+  hd_cache_clear (hd);
+
+  hd->resource = NULL;
+
+  xfree (hd);
+}
+
+void
+kdb_push_found_state (KDB_HANDLE hd)
+{
+  hd->saved_found = hd->found;
+  hd->found.key = -1;
+}
+
+void
+kdb_pop_found_state (KDB_HANDLE hd)
+{
+  hd->found = hd->saved_found;
+  hd->saved_found.key = -1;
+}
+
+const char *
+kdb_get_resource_name (KDB_HANDLE hd)
+{
+  if (!hd || !hd->resource)
+    return NULL;
+  return hd->resource->fname;
+}
+
+/* If YES is 1, lock the DB.  Otherwise, unlock it.  Returns an error
+   code if locking failed.  */
+int
+kdb_lock (KDB_HANDLE hd, int yes)
+{
+  int rc;
+  char *err;
+
+  if (yes)
+    /* Lock.  */
+    {
+      rc = sqlite3_exec (hd->resource->db, "savepoint lock;",
+                         NULL, NULL, &err);
+      if (rc)
+        {
+          log_error (_("error beginning transaction on KDB database: %s\n"),
+                     err);
+          sqlite3_free (err);
+          return 1;
+        }
+
+      return 0;
+    }
+  else
+    /* Unlock.  */
+    {
+      rc = sqlite3_exec (hd->resource->db, "release lock;", NULL, NULL, &err);
+      if (rc)
+        {
+          log_error (_("error ending transaction on KDB database: %s\n"),
+                     err);
+          sqlite3_free (err);
+          return 1;
+        }
+
+      return 0;
+    }
+}
+
+static u32 *
+sigstatus_parse (const char *sigstatus_str)
+{
+  int entries;
+  int i;
+  u32 *sigstatus;
+  char *tail;
+
+  /* Count the number of values (= # of semicolons plus 1).  */
+  entries = 1;
+  for (i = 0; i < strlen (sigstatus_str); i ++)
+    if (sigstatus_str[i] == ';')
+      entries ++;
+
+  /* The first entry is the number of entries.  */
+  sigstatus = xmalloc (sizeof (sigstatus[0]) * (1 + entries));
+  sigstatus[0] = entries;
+
+  for (i = 0; i < entries; i ++)
+    {
+      errno = 0;
+      sigstatus[i + 1] = strtoul (sigstatus_str, &tail, 0);
+      if (errno || ! ((i < entries - 1 && *tail == ';')
+                      || (i == entries - 1 && *tail == '\0')))
+        /* Abort.  */
+        {
+          log_info ("%s: Failed to parse %s\n", __func__, sigstatus_str);
+          return NULL;
+        }
+
+      sigstatus_str = tail;
+      if (i < entries - 1)
+        {
+          assert (*tail == ';');
+          sigstatus_str ++;
+        }
+      else
+        assert (*tail == '\0');
+    }
+
+  return sigstatus;
+}
+
+static int keyblock_cached;
+static int keyblock_cache_hit;
+static int keyblock_cache_miss;
+
+/* The caller needs to make sure that hd->resource->key is updated!  */
+static int
+keyblock_cb (void *cookie, int cols, char **values, char **names,
+             sqlite3_stmt *stmt)
+{
+  KDB_HANDLE hd = cookie;
+
+  (void) cols;
+  (void) values;
+  (void) names;
+
+  assert (cols == 2);
+  assert (strcmp (names[0], "keyblock") == 0);
+  assert (strcmp (names[1], "sigstatus") == 0);
+
+  xfree (hd->resource->kb);
+  hd->resource->kb_len = sqlite3_column_bytes (stmt, 0);
+  hd->resource->kb = xmalloc (hd->resource->kb_len);
+  memcpy (hd->resource->kb, sqlite3_column_blob (stmt, 0),
+          hd->resource->kb_len);
+  hd->resource->sigstatus = sigstatus_parse (values[1]);
+
+  keyblock_cached ++;
+
+  /* Abort to indicate success.  */
+  return 1;
+}
+
+int
+kdb_get_keyblock (KDB_HANDLE hd, iobuf_t *iobuf,
+                  int *pk_no, int *uid_no, u32 **sigstatus)
+{
+  int rc;
+  char *err;
+  sqlite3_stmt *stmt = NULL;
+
+  if (pk_no)
+    *pk_no = 0;
+  if (uid_no)
+    *uid_no = 0;
+  if (sigstatus)
+    *sigstatus = NULL;
+
+  if (hd->found.key == -1)
+    /* Got nothing.  */
+    return gpg_error (GPG_ERR_EOF);
+
+  DEBUG ("getting keyblock for key #%lld\n", hd->found.key);
+
+  if (keyblock_cache_hit || keyblock_cache_miss)
+    DEBUG ("keyblock cache: %d fills, %d hits (%d%%), %d misses\n",
+           keyblock_cached, keyblock_cache_hit,
+           (keyblock_cache_hit * 100)
+           / (keyblock_cache_hit + keyblock_cache_miss),
+           keyblock_cache_miss);
+
+  if (hd->resource->kb && hd->resource->key == hd->found.key)
+    {
+      DEBUG("read keyblock from resource cache.\n");
+      keyblock_cache_hit ++;
+      *iobuf = iobuf_temp_with_content (hd->resource->kb, hd->resource->kb_len);
+      if (hd->resource->sigstatus)
+        {
+          size_t s = (sizeof (hd->resource->sigstatus[0])
+                      * (1 + hd->resource->sigstatus[0]));
+          *sigstatus = xmalloc (s);
+          memcpy (*sigstatus, hd->resource->sigstatus, s);
+        }
+      return 0;
+    }
+  else if (hd->full_scan && hd->full_scan->key == hd->found.key)
+    {
+      DEBUG("read keyblock from full scan cache.\n");
+      *iobuf = iobuf_temp_with_content (hd->full_scan->kb,
+                                        hd->full_scan->kb_len);
+      if (hd->full_scan->sigstatus)
+        {
+          size_t s = (sizeof (hd->full_scan->sigstatus[0])
+                      * (1 + hd->full_scan->sigstatus[0]));
+          *sigstatus = xmalloc (s);
+          memcpy (*sigstatus, hd->full_scan->sigstatus, s);
+        }
+      return 0;
+    }
+  else
+    keyblock_cache_miss ++;
+
+  rc = sqlite3_stepx
+    (hd->resource->db,
+     &stmt, keyblock_cb, hd, &err,
+     "select keyblock, sigstatus from primaries where oid = ?",
+     SQLITE_ARG_LONG_LONG, hd->found.key, SQLITE_ARG_END);
+  if (rc == SQLITE_ABORT)
+    /* Success.  */
+    {
+      assert (hd->resource->kb);
+      hd->resource->key = hd->found.key;
+      *iobuf = iobuf_temp_with_content (hd->resource->kb, hd->resource->kb_len);
+
+      rc = 0;
+      if (uid_no)
+        *uid_no = hd->found.uid_no;
+      if (pk_no)
+        *pk_no = hd->found.pk_no;
+      if (sigstatus && hd->resource->sigstatus)
+        {
+          size_t s = (sizeof (hd->resource->sigstatus[0])
+                      * (1 + hd->resource->sigstatus[0]));
+          *sigstatus = xmalloc (s);
+          memcpy (*sigstatus, hd->resource->sigstatus, s);
+        }
+    }
+  else if (! rc)
+    /* If we don't get an abort, then we didn't find the record.  */
+    rc = gpg_error (GPG_ERR_NOT_FOUND);
+  else
+    {
+      log_error (_("reading keyblock from keydb DB: %s\n"), err);
+      sqlite3_free (err);
+      rc = gpg_error (GPG_ERR_GENERAL);
+    }
+
+  sqlite3_finalize (stmt);
+  return rc;
+}
+
+int
+kdb_update_keyblock (KDB_HANDLE hd, kbnode_t kb,
+                     const void *image, size_t imagelen)
+{
+  (void) hd;
+  (void) kb;
+  (void) image;
+  (void) imagelen;
+
+  log_fatal ("Implement %s.", __func__);
+}
+
+static char *
+strrev (char *str)
+{
+  int i;
+  int l = strlen (str);
+
+  for (i = 0; i < l / 2; i ++)
+    {
+      char t = str[i];
+      str[i] = str[l - 1 - i];
+      str[l - 1 - i] = t;
+    }
+
+  return str;
+}
+
+static char *
+fingerprint_ascii_rev (char *fingerprint_bin, int len)
+{
+  char *fingerprint = xmalloc (2 * len + 1);
+  bin2hex (fingerprint_bin, len, fingerprint);
+  return strrev (fingerprint);
+}
+
+gpg_error_t
+kdb_insert_keyblock (KDB_HANDLE hd, kbnode_t root,
+                     const void *image, size_t imagelen, u32 *sigstatus)
+{
+  PKT_public_key *mainpk = root->pkt->pkt.public_key;
+  char fingerprint_bin[MAX_FINGERPRINT_LEN];
+  size_t fingerprint_bin_len = sizeof (fingerprint_bin);
+  char *fingerprint_rev = NULL;
+
+  char *sigstatus_str = NULL;
+
+  int rc;
+  char *err;
+
+  sqlite3_stmt *uid_stmt = NULL;
+  sqlite3_stmt *key_stmt = NULL;
+
+  long long oid;
+  int uid_no;
+  int pk_no;
+  kbnode_t k;
+
+  if (!hd)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  hdr_cache_clear (hd->resource);
+  hd_cache_clear (hd);
+
+  /* XXX: If we have a search result (hd->found), are we supposed to
+     replace it even if it isn't for the same key?  */
+  /* See if we are replacing or adding this record to the
+     database.  */
+  fingerprint_from_pk (mainpk, fingerprint_bin, &fingerprint_bin_len);
+  assert (fingerprint_bin_len == sizeof (fingerprint_bin));
+  fingerprint_rev =
+    fingerprint_ascii_rev (fingerprint_bin, fingerprint_bin_len);
+
+  if (sigstatus)
+    {
+      int i;
+      char *p;
+      p = sigstatus_str = xmalloc ((10 + 1) * sigstatus[0]);
+      for (i = 0; i < sigstatus[0]; i ++)
+        {
+          p += sprintf (p, "%d", sigstatus[i + 1]);
+          if (i != sigstatus[0] - 1)
+            *p ++ = ';';
+        }
+    }
+
+  oid = -1;
+  rc = sqlite3_stepx
+    (hd->resource->db, NULL, get_unsigned_longs_cb2, &oid, &err,
+     "select oid from primaries where fingerprint_rev = ?;",
+     SQLITE_ARG_STRING, fingerprint_rev, SQLITE_ARG_END);
+  if (rc)
+    {
+      log_error (_("looking up key in keydb DB: %s\n"), err);
+      sqlite3_free (err);
+      return gpg_error (GPG_ERR_GENERAL);
+    }
+
+  if (oid != -1)
+    /* This key is already in the DB.  Replace it.  */
+    {
+      DEBUG ("%s already in DB (oid = %lld), updating.\n",
+             fingerprint_rev, oid);
+
+      hdr_cache_clear (hd->resource);
+
+      rc = sqlite3_exec_printf
+        (hd->resource->db, NULL, NULL, &err,
+         "delete from primaries where oid = %lld;"
+         "delete from keys where primary_key = %lld;"
+         "delete from uids where primary_key = %lld;",
+         oid, oid, oid);
+      if (rc)
+        {
+          log_error (_("updating key in keydb DB: %s\n"), err);
+          sqlite3_free (err);
+          return gpg_error (GPG_ERR_GENERAL);
+        }
+
+      /* Reuse the oid.  So that any extant search won't return the
+         new record.  */
+      rc = sqlite3_stepx
+        (hd->resource->db, NULL, NULL, NULL, &err,
+         "insert into primaries (oid, fingerprint_rev, keyblock, sigstatus)\n"
+         " values (?, ?, ?, ?);",
+         SQLITE_ARG_LONG_LONG, oid,
+         SQLITE_ARG_STRING, fingerprint_rev,
+         SQLITE_ARG_BLOB, image, (long long) imagelen,
+         SQLITE_ARG_STRING, sigstatus_str,
+         SQLITE_ARG_END);
+    }
+  else
+    {
+      DEBUG ("New keyblock for %s.\n", fingerprint_rev);
+      rc = sqlite3_stepx
+        (hd->resource->db, NULL, NULL, NULL, &err,
+         "insert into primaries (fingerprint_rev, keyblock, sigstatus)\n"
+         " values (?, ?, ?);",
+         SQLITE_ARG_STRING, fingerprint_rev,
+         SQLITE_ARG_BLOB, image, (long long) imagelen,
+         SQLITE_ARG_STRING, sigstatus_str,
+         SQLITE_ARG_END);
+    }
+
+  xfree (sigstatus_str);
+  xfree (fingerprint_rev);
+  fingerprint_rev = NULL;
+
+  if (rc)
+    {
+      log_error (_("inserting %s record into keydb DB: %s\n"),
+                 "primary key", err);
+      sqlite3_free (err);
+      return gpg_error (GPG_ERR_GENERAL);
+    }
+
+  oid = sqlite3_last_insert_rowid (hd->resource->db);
+
+  uid_no = 0;
+  pk_no = 0;
+  for (k = root; k; k = k->next)
+    {
+      if (k->pkt->pkttype == PKT_USER_ID)
+        {
+          PKT_user_id *uid = k->pkt->pkt.user_id;
+          const char *user_id = uid->name;
+          char *email = mailbox_from_userid (user_id);
+
+          uid_no ++;
+
+          rc = sqlite3_stepx
+            (hd->resource->db, &uid_stmt, NULL, NULL, &err,
+             "insert into uids (primary_key, uid, email, uid_no)"
+             " values (?, ?, ?, ?);",
+             SQLITE_ARG_LONG_LONG, oid,
+             SQLITE_ARG_STRING, user_id, SQLITE_ARG_STRING, email,
+             SQLITE_ARG_INT, uid_no,
+             SQLITE_ARG_END);
+          xfree (email);
+          if (rc)
+            {
+              log_error (_("inserting %s record into keydb DB: %s\n"),
+                         "uid", err);
+              sqlite3_free (err);
+              return gpg_error (GPG_ERR_GENERAL);
+            }
+        }
+      else if (k->pkt->pkttype == PKT_PUBLIC_KEY
+               || k->pkt->pkttype == PKT_PUBLIC_SUBKEY)
+        {
+          PKT_public_key *pk = k->pkt->pkt.public_key;
+
+          pk_no ++;
+
+          fingerprint_from_pk (pk, fingerprint_bin, &fingerprint_bin_len);
+          assert (fingerprint_bin_len == sizeof (fingerprint_bin));
+          fingerprint_rev = fingerprint_ascii_rev (fingerprint_bin,
+                                                   fingerprint_bin_len);
+
+          rc = sqlite3_stepx
+            (hd->resource->db, &key_stmt, NULL, NULL, &err,
+             "insert into keys (primary_key, fingerprint_rev, pk_no)"
+             " values (?, ?, ?);",
+             SQLITE_ARG_LONG_LONG, oid,
+             SQLITE_ARG_STRING, fingerprint_rev,
+             SQLITE_ARG_INT, pk_no,
+             SQLITE_ARG_END);
+
+          xfree (fingerprint_rev);
+          fingerprint_rev = NULL;
+
+          if (rc)
+            {
+              log_error (_("inserting %s record into keydb DB: %s\n"),
+                         "key", err);
+              sqlite3_free (err);
+              return gpg_error (GPG_ERR_GENERAL);
+            }
+        }
+    }
+
+  sqlite3_finalize (uid_stmt);
+  sqlite3_finalize (key_stmt);
+
+  return 0;
+}
+
+int
+kdb_delete (KDB_HANDLE hd)
+{
+  int rc;
+  char *err;
+
+  if (!hd)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  if (hd->found.key == -1)
+    /* No search result.  */
+    return gpg_error (GPG_ERR_NOTHING_FOUND);
+
+  hdr_cache_clear (hd->resource);
+  hd_cache_clear (hd);
+
+  rc = sqlite3_exec_printf
+    (hd->resource->db, NULL, NULL, &err,
+     "delete from keys where primary_key = %d;\n"
+     "delete from uids where primary_key = %d;\n"
+     "delete from primaries where oid = %d;\n",
+     hd->found.key, hd->found.key, hd->found.key);
+  if (rc)
+    {
+      log_error (_("error deleting key from kdb database: %s\n"), err);
+      sqlite3_free (err);
+      rc = gpg_error (GPG_ERR_GENERAL);
+    }
+
+  return rc;
+}
+
+int
+kdb_search_reset (KDB_HANDLE hd)
+{
+  hd->key = -1;
+  hd->eof = 0;
+
+  hd->found.key = -1;
+
+  hd_cache_clear (hd);
+
+  return 0;
+}
+
+struct key_array
+{
+  long int count;
+  /* The maximum amount of memory to use before aborting.  If 0,
+     unlimited.  */
+  size_t memory;
+  struct key *keys;
+};
+
+static int
+get_keyblock_array_cb (void *cookie, int argc, char **argv,
+                       char **azColName, sqlite3_stmt *stmt)
+{
+  struct key_array *a = cookie;
+  struct key *entry;
+  size_t len = sqlite3_column_bytes (stmt, 0);
+  char *tail;
+
+  assert (argc == 3);
+
+  (void) azColName;
+
+  entry = xmalloc_clear (sizeof (*entry));
+  entry->kb_len = len;
+  entry->kb = xmalloc (len);
+  memcpy (entry->kb, sqlite3_column_blob (stmt, 0), len);
+
+  entry->sigstatus = sigstatus_parse (argv[1]);
+
+  errno = 0;
+  entry->key = strtoul (argv[2], &tail, 0);
+  if (errno || ! (strcmp (tail, ".0") == 0 || *tail == '\0'))
+    /* Abort.  */
+    {
+      xfree (entry);
+      return 1;
+    }
+
+  /* Attach it.  */
+  entry->next = a->keys;
+  a->keys = entry;
+
+  a->count ++;
+
+  if (len >= a->memory)
+    /* Don't read another record.  We're out of memory.  */
+    {
+      log_debug ("%s: Stopped prefilling read-ahead cache (%zd bytes larger than %zd bytes of available memory.\n",
+                 __func__, len, a->memory);
+      return 1;
+    }
+  else
+      log_debug ("%s: Used %zd bytes of remaining %zd.\n",
+                 __func__, len, a->memory);
+
+  a->memory -= len;
+
+  return 0;
+}
+
+static gpg_error_t
+key_list_load (struct keydb_handle *hd)
+{
+  int rc;
+  char *err = NULL;
+  struct key_array a;
+  a.count = 0;
+  /* Don't read much more than 20 MB of keyblocks into memory at a
+     time.  */
+  a.memory = 20 * 1024 * 1024;
+  a.keys = NULL;
+
+  hd_cache_clear (hd);
+
+  if (hd->key == -1)
+    /* From the beginning.  */
+    rc = sqlite3_stepx
+      (hd->resource->db, NULL,
+       get_keyblock_array_cb, &a, &err,
+       "select keyblock, sigstatus, oid from primaries"
+       " order by oid",
+       SQLITE_ARG_END);
+  else
+    rc = sqlite3_stepx
+      (hd->resource->db, NULL,
+       get_keyblock_array_cb, &a, &err,
+       "select keyblock, sigstatus, oid from primaries"
+       " where oid > ?"
+       " order by oid",
+       SQLITE_ARG_LONG_LONG, hd->key,
+       SQLITE_ARG_END);
+
+  if (! rc)
+    /* We got the whole thing.  */
+    {
+      /* Add an EOF record.  */
+      struct key *k = xmalloc_clear (sizeof (*k));
+      k->key = -1;
+      k->next = a.keys;
+      hd->full_scan = k;
+    }
+  else if (rc == SQLITE_ABORT)
+    /* Partial list.  */
+    {
+      rc = 0;
+      hd->full_scan = a.keys;
+    }
+  else
+    /* It is possible that we got a partial listing.  */
+    {
+      log_fatal ("error listing primary table: %s\n", err);
+      sqlite3_free (err);
+    }
+
+  log_debug ("%s: Got %ld records.\n", __func__, a.count);
+
+  hd->full_scan = key_list_reverse (hd->full_scan);
+
+  if (rc)
+    return gpg_error (GPG_ERR_GENERAL);
+  return 0;
+}
+
+static int
+kdb_search_cb (void *cookie, int argc, char **argv, char **azColName,
+               sqlite3_stmt *stmt)
+{
+  KDB_HANDLE hd = cookie;
+  int i = 0;
+  unsigned long int values[argc];
+  int got_keyblock = 0;
+
+  /* Get the keyblock.  */
+  if (argc >= 2
+      && strcmp (azColName[0], "keyblock") == 0
+      && strcmp (azColName[1], "sigstatus") == 0)
+    {
+      /* When we do: select keyblock, min(oid) and we don't have any
+         results, then keyblock will be NULL.  */
+      if (argv[0])
+        {
+          keyblock_cb (hd, 2, argv, azColName, stmt);
+          got_keyblock = 1;
+        }
+      i = 2;
+    }
+
+  get_unsigned_longs_cb (&values[i], argc - i, &argv[i], &azColName[i]);
+  hd->found.uid_no = hd->found.pk_no = 0;
+  for (; i < argc; i ++)
+    if (strcmp (azColName[i], "oid") == 0)
+      {
+        hd->key = hd->found.key = values[i];
+        if (got_keyblock)
+          hd->resource->key = hd->key;
+      }
+    else if (strcmp (azColName[i], "uid_no") == 0)
+      hd->found.uid_no = values[i];
+    else if (strcmp (azColName[i], "pk_no") == 0)
+      hd->found.pk_no = values[i];
+    else
+      log_bug ("%s: Bad column name: %s\n", __func__, azColName[i]);
+
+  /* Abort.  */
+  return 1;
+}
+
+int
+kdb_search (KDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
+            size_t ndesc, size_t *descindex)
+{
+  int n;
+  int have_next = 0;
+  char *where_uid = NULL;
+  char *where_key = NULL;
+  char *text;
+  int anyskip = 0;
+  int rc = 0;
+  char *err = NULL;
+  char *sql = NULL;
+
+  hd->found.key = -1;
+
+  for (n = 0; n < ndesc; n ++)
+    {
+      if (desc[n].mode == KEYDB_SEARCH_MODE_FIRST)
+        {
+          kdb_search_reset (hd);
+          desc[n].mode = KEYDB_SEARCH_MODE_NEXT;
+          have_next = 1;
+        }
+      if (desc[n].mode == KEYDB_SEARCH_MODE_NEXT)
+        have_next = 1;
+    }
+
+  if (have_next)
+    {
+      if (hd->full_scan && hd->full_scan->key == hd->key)
+        /* The head of the full_scan list is the current key.
+           Remove the current key from the full_scan list and
+           return the next one.  */
+        hd->full_scan = key_free (hd->full_scan);
+      else
+        hd_cache_clear (hd);
+
+      if (! hd->full_scan)
+        {
+          rc = key_list_load (hd);
+          if (rc)
+            goto out;
+        }
+
+      /* We always add an EOF record.  So, this can't be NULL.  */
+      assert (hd->full_scan);
+
+      if (hd->full_scan->key == -1)
+        /* EOF!  */
+        hd->eof = 1;
+      else
+        hd->key = hd->found.key = hd->full_scan->key;
+
+      goto out;
+    }
+
+  hd_cache_clear (hd);
+
+  if (hd->eof)
+    /* We're at the end of the file.  There is nothing else to get.  */
+    return gpg_error (GPG_ERR_EOF);
+
+#define ADD_TERM(thing, op, fmt, ...) do {              \
+    char *t = sqlite3_mprintf                           \
+      ("%s%s("fmt")",                                   \
+       thing ? thing : "", thing ? "\n "op" " : "",     \
+       ##__VA_ARGS__);                                  \
+    sqlite3_free (thing);                               \
+    thing = t;                                          \
+  } while (0)
+#define O(thing, fmt, ...) ADD_TERM(thing, "OR", fmt, ##__VA_ARGS__)
+#define A(thing, fmt, ...) ADD_TERM(thing, "AND", fmt, ##__VA_ARGS__)
+
+  if (descindex)
+    log_fatal ("Implement descindex\n");
+
+  for (n = 0; n < ndesc; n ++)
+    {
+      KEYDB_SEARCH_DESC *d = &desc[n];
+
+      switch (d->mode)
+        {
+        case KEYDB_SEARCH_MODE_EXACT:
+          O(where_uid, "uids.uid = %Q", desc[n].u.name);
+          break;
+
+        case KEYDB_SEARCH_MODE_SUBSTR:
+        case KEYDB_SEARCH_MODE_MAIL:
+        case KEYDB_SEARCH_MODE_MAILSUB:
+        case KEYDB_SEARCH_MODE_MAILEND:
+          {
+            char *escaped = xmalloc (1 + 2 * strlen (d->u.name) + 1 + 1);
+            int i, j = 0;
+
+            if (d->mode == KEYDB_SEARCH_MODE_SUBSTR
+                || d->mode == KEYDB_SEARCH_MODE_MAILSUB
+                || d->mode == KEYDB_SEARCH_MODE_MAILEND)
+              escaped[j ++] = '%';
+
+            for (i = 0; i < strlen (d->u.name); i ++)
+              {
+                if (d->u.name[i] == '%' || d->u.name[i] == '_'
+                    || d->u.name[i] == '\'' || d->u.name[i] == '\\')
+                  escaped[j ++] = '\\';
+                escaped[j ++] = d->u.name[i];
+              }
+
+            if (d->mode == KEYDB_SEARCH_MODE_SUBSTR
+                || d->mode == KEYDB_SEARCH_MODE_MAILSUB)
+              escaped[j ++] = '%';
+
+            escaped[j] = 0;
+
+            O(where_uid, "uids.%s like %Q",
+              d->mode == KEYDB_SEARCH_MODE_SUBSTR ? "uid" : "email",
+              escaped);
+          }
+          break;
+
+
+        case KEYDB_SEARCH_MODE_WORDS:
+          log_fatal ("Implement me!\n");
+          break;
+
+
+        case KEYDB_SEARCH_MODE_SHORT_KID:
+          text = xmalloc (8 + 1);
+          snprintf (text, 9, "%08lX", (ulong) d->u.kid[1]);
+          O(where_key, "keys.fingerprint_rev like '%s%%'", strrev (text));
+          xfree (text);
+          break;
+
+        case KEYDB_SEARCH_MODE_LONG_KID:
+          text = xmalloc (8 * 2 + 1);
+          snprintf (text, 8 * 2 + 1, "%08lX%08lX",
+                    (ulong) d->u.kid[0], (ulong) d->u.kid[1]);
+          O(where_key, "keys.fingerprint_rev like '%s%%'", strrev (text));
+          xfree (text);
+          break;
+
+        case KEYDB_SEARCH_MODE_FPR16:
+          if (d->mode == KEYDB_SEARCH_MODE_FPR16)
+            text = bin2hex (d->u.fpr, 16, NULL);
+          /* Fall through.  */
+
+        case KEYDB_SEARCH_MODE_FPR20:
+        case KEYDB_SEARCH_MODE_FPR:
+          if (d->mode == KEYDB_SEARCH_MODE_FPR20
+              || d->mode == KEYDB_SEARCH_MODE_FPR)
+            text = bin2hex (d->u.fpr, 20, NULL);
+
+          strrev (text);
+          O(where_key, "keys.fingerprint_rev = '%s'", text);
+          xfree (text);
+          break;
+
+        case KEYDB_SEARCH_MODE_FIRST:
+        case KEYDB_SEARCH_MODE_NEXT:
+          /* Already handled above.  */
+          break;
+
+        default:
+          break;
+        }
+
+      if (d->skipfnc)
+        anyskip = 1;
+    }
+
+  if (anyskip)
+    log_fatal ("Implement anyskip.");
+
+  DEBUG ("uid: %s\n", where_uid);
+  DEBUG ("key: %s\n", where_key);
+
+  assert (where_uid || where_key);
+  if (where_uid && where_key)
+    sql = sqlite3_mprintf
+      ("select keyblock, sigstatus,\n"
+       "  keys.primary_key oid, keys.pk_no, uids.uid_no\n"
+       " from primaries\n"
+       " left join keys on primaries.oid = keys.primary_key\n"
+       " left join uids on primaries.oid = uids.primary_key\n"
+       " where %s%lld and (%s and %s)\n"
+       " order by keys.primary_key, keys.pk_no, uids.uid_no\n"
+       " limit 1\n",
+       hd->key == -1 ? "" : "keys.primary_key > ", hd->key,
+       where_uid, where_key);
+  else if (where_key)
+    sql = sqlite3_mprintf
+      ("select primary_key oid, pk_no\n"
+       " from keys\n"
+       " where %s%lld and (%s)\n"
+       " order by primary_key, pk_no\n"
+       " limit 1;\n",
+       hd->key == -1 ? "" : "primary_key > ", hd->key, where_key);
+  else
+    sql = sqlite3_mprintf
+      ("select primary_key oid, uid_no\n"
+       " from uids\n"
+       " where %s%lld and (%s)\n"
+       " order by primary_key, uid_no\n"
+       " limit 1;\n",
+       hd->key == -1 ? "" : "primary_key > ", hd->key, where_uid);
+  DEBUG ("Running '%s'\n", sql);
+  rc = sqlite3_stepx (hd->resource->db, NULL, kdb_search_cb, hd, &err,
+                      sql, SQLITE_ARG_END);
+  if (rc == SQLITE_ABORT)
+    /* Success.  */
+    rc = 0;
+  else if (rc)
+    {
+      log_fatal ("error search DB: %s\n", err);
+      sqlite3_free (err);
+      goto out;
+    }
+  else
+    /* EOF.  */
+    {
+      assert (hd->found.key == -1);
+      hd->eof = 1;
+    }
+
+ out:
+  sqlite3_free (sql);
+  sqlite3_free (where_uid);
+  sqlite3_free (where_key);
+
+  if (rc)
+    {
+      DEBUG ("Search result: Error.\n");
+      return gpg_error (GPG_ERR_GENERAL);
+    }
+  if (hd->eof)
+    {
+      DEBUG ("Search result: ENOENT.\n");
+      return gpg_error (GPG_ERR_EOF);
+    }
+
+  DEBUG ("Search result: key #%lld.\n", hd->key);
+
+  return 0;
+}
diff --git a/g10/kdb.h b/g10/kdb.h
new file mode 100644
index 0000000..55e6962
--- /dev/null
+++ b/g10/kdb.h
@@ -0,0 +1,29 @@
+#ifndef GNUPG_KDB_H
+#define GNUPG_KDB_H
+
+#include "keydb.h"
+
+typedef struct keydb_handle *KDB_HANDLE;
+
+gpg_error_t kdb_register_file (const char *fname, int read_only, void **ptr);
+int kdb_is_writable (void *token);
+
+KDB_HANDLE kdb_new (void *token);
+void kdb_release (KDB_HANDLE hd);
+void kdb_push_found_state (KDB_HANDLE hd);
+void kdb_pop_found_state (KDB_HANDLE hd);
+const char *kdb_get_resource_name (KDB_HANDLE hd);
+int kdb_lock (KDB_HANDLE hd, int yes);
+int kdb_get_keyblock (KDB_HANDLE hd, iobuf_t *iobuf,
+                      int *pk_no, int *uid_no, u32 **sigstatus);
+int kdb_update_keyblock (KDB_HANDLE hd, kbnode_t kb,
+                         const void *image, size_t imagelen);
+gpg_error_t kdb_insert_keyblock (KDB_HANDLE hd, kbnode_t kb,
+                                 const void *image, size_t imagelen,
+                                 u32 *sigstatus);
+int kdb_delete (KDB_HANDLE hd);
+int kdb_search_reset (KDB_HANDLE hd);
+int kdb_search (KDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
+                size_t ndesc, size_t *descindex);
+
+#endif
diff --git a/g10/keydb.c b/g10/keydb.c
index 8a68980..2540af8 100644
--- a/g10/keydb.c
+++ b/g10/keydb.c
@@ -37,6 +37,7 @@
 #include "../kbx/keybox.h"
 #include "keydb.h"
 #include "i18n.h"
+#include "kdb.h"
 
 static int active_handles;
 
@@ -44,7 +45,8 @@ typedef enum
   {
     KEYDB_RESOURCE_TYPE_NONE = 0,
     KEYDB_RESOURCE_TYPE_KEYRING,
-    KEYDB_RESOURCE_TYPE_KEYBOX
+    KEYDB_RESOURCE_TYPE_KEYBOX,
+    KEYDB_RESOURCE_TYPE_KEYDB
   } KeydbResourceType;
 #define MAX_KEYDB_RESOURCES 40
 
@@ -54,6 +56,7 @@ struct resource_item
   union {
     KEYRING_HANDLE kr;
     KEYBOX_HANDLE kb;
+    KDB_HANDLE kdb;
   } u;
   void *token;
 };
@@ -251,13 +254,14 @@ keyblock_cache_clear (struct keydb_handle *hd)
 /* Handle the creation of a keyring or a keybox if it does not yet
    exist.  Take into account that other processes might have the
    keyring/keybox already locked.  This lock check does not work if
-   the directory itself is not yet available.  If IS_BOX is true the
-   filename is expected to refer to a keybox.  If FORCE_CREATE is true
-   the keyring or keybox will be created.
+   the directory itself is not yet available.  RT is the type of
+   resource being created.  If FORCE_CREATE is true the keyring or
+   keybox will be created.
 
    Return 0 if it is okay to access the specified file.  */
 static int
-maybe_create_keyring_or_box (char *filename, int is_box, int force_create)
+maybe_create_resource (char *filename, KeydbResourceType rt,
+                             int force_create)
 {
   dotlock_t lockhd = NULL;
   IOBUF iobuf;
@@ -361,12 +365,25 @@ maybe_create_keyring_or_box (char *filename, int is_box, int force_create)
   if (!iobuf)
     {
       rc = gpg_error_from_syserror ();
-      if (is_box)
-        log_error (_("error creating keybox '%s': %s\n"),
-                   filename, gpg_strerror (rc));
-      else
-        log_error (_("error creating keyring '%s': %s\n"),
-                   filename, gpg_strerror (rc));
+      switch (rt)
+        {
+        case KEYDB_RESOURCE_TYPE_NONE:
+          log_fatal ("Bad value for resource type: %d\n", rt);
+          break;
+
+        case KEYDB_RESOURCE_TYPE_KEYRING:
+          log_error (_("error creating keyring '%s': %s\n"),
+                     filename, gpg_strerror (rc));
+          break;
+        case KEYDB_RESOURCE_TYPE_KEYBOX:
+          log_error (_("error creating keybox '%s': %s\n"),
+                     filename, gpg_strerror (rc));
+          break;
+        case KEYDB_RESOURCE_TYPE_KEYDB:
+          log_error (_("error creating keydb '%s': %s\n"),
+                     filename, gpg_strerror (rc));
+          break;
+        }
       goto leave;
     }
 
@@ -376,7 +393,7 @@ maybe_create_keyring_or_box (char *filename, int is_box, int force_create)
 
   /* Make sure that at least one record is in a new keybox file, so
      that the detection magic will work the next time it is used.  */
-  if (is_box)
+  if (rt == KEYDB_RESOURCE_TYPE_KEYBOX)
     {
       FILE *fp = fopen (filename, "w");
       if (!fp)
@@ -388,22 +405,29 @@ maybe_create_keyring_or_box (char *filename, int is_box, int force_create)
         }
       if (rc)
         {
-          if (is_box)
-            log_error (_("error creating keybox '%s': %s\n"),
-                       filename, gpg_strerror (rc));
-          else
-            log_error (_("error creating keyring '%s': %s\n"),
-                       filename, gpg_strerror (rc));
+          log_error (_("error creating keybox '%s': %s\n"),
+                     filename, gpg_strerror (rc));
           goto leave;
         }
     }
 
   if (!opt.quiet)
     {
-      if (is_box)
-        log_info (_("keybox '%s' created\n"), filename);
-      else
-        log_info (_("keyring '%s' created\n"), filename);
+      switch (rt)
+        {
+        case KEYDB_RESOURCE_TYPE_NONE:
+          log_fatal ("Bad value for resource type: %d\n", rt);
+          break;
+        case KEYDB_RESOURCE_TYPE_KEYRING:
+          log_info (_("keyring '%s' created\n"), filename);
+          break;
+        case KEYDB_RESOURCE_TYPE_KEYBOX:
+          log_info (_("keybox '%s' created\n"), filename);
+          break;
+        case KEYDB_RESOURCE_TYPE_KEYDB:
+          log_info (_("keydb '%s' created\n"), filename);
+          break;
+        }
     }
 
   rc = 0;
@@ -445,6 +469,8 @@ rt_from_file (const char *filename, int *r_found, int *r_openpgp)
         {
           if (magic == 0x13579ace || magic == 0xce9a5713)
             ; /* GDBM magic - not anymore supported. */
+          else if (memcmp (&magic, "SQLi", 4) == 0)
+            rt = KEYDB_RESOURCE_TYPE_KEYDB;
           else if (fread (&verbuf, 4, 1, fp) == 1
                    && verbuf[0] == 1
                    && fread (&magic, 4, 1, fp) == 1
@@ -559,6 +585,11 @@ keydb_add_resource (const char *url, unsigned int flags)
       rt = KEYDB_RESOURCE_TYPE_KEYBOX;
       resname += 10;
     }
+  else if (strlen (resname) > 10 && !strncmp (resname, "gnupg-kdb:", 10) )
+    {
+      rt = KEYDB_RESOURCE_TYPE_KEYDB;
+      resname += 10;
+    }
 #if !defined(HAVE_DRIVE_LETTERS) && !defined(__riscos__)
   else if (strchr (resname, ':'))
     {
@@ -662,7 +693,8 @@ keydb_add_resource (const char *url, unsigned int flags)
       goto leave;
 
     case KEYDB_RESOURCE_TYPE_KEYRING:
-      rc = maybe_create_keyring_or_box (filename, 0, create);
+      rc = maybe_create_resource (filename, KEYDB_RESOURCE_TYPE_KEYRING,
+                                  create);
       if (rc)
         goto leave;
 
@@ -692,7 +724,8 @@ keydb_add_resource (const char *url, unsigned int flags)
 
     case KEYDB_RESOURCE_TYPE_KEYBOX:
       {
-        rc = maybe_create_keyring_or_box (filename, 1, create);
+        rc = maybe_create_resource (filename, KEYDB_RESOURCE_TYPE_KEYBOX,
+                                    create);
         if (rc)
           goto leave;
 
@@ -727,6 +760,36 @@ keydb_add_resource (const char *url, unsigned int flags)
       }
       break;
 
+    case KEYDB_RESOURCE_TYPE_KEYDB:
+      {
+        rc = maybe_create_resource (filename, KEYDB_RESOURCE_TYPE_KEYDB, create);
+        if (rc)
+          goto leave;
+
+        rc = kdb_register_file (filename, read_only, &token);
+        if (rc == 0)
+          {
+            if (used_resources >= MAX_KEYDB_RESOURCES)
+              rc = gpg_error (GPG_ERR_RESOURCE_LIMIT);
+            else
+              {
+                /* if ((flags & KEYDB_RESOURCE_FLAG_PRIMARY)) */
+                /*   primary_keyring = token; */
+                all_resources[used_resources].type = rt;
+                all_resources[used_resources].u.kb = NULL; /* Not used here */
+                all_resources[used_resources].token = token;
+
+                used_resources++;
+              }
+          }
+
+        /* XXX: How to mark this as a primary if it was already
+           registered.  */
+        /* if ((flags & KEYDB_RESOURCE_FLAG_PRIMARY)) */
+        /*   primary_keyring = token; */
+      }
+      break;
+
       default:
 	log_error ("resource type of '%s' not supported\n", url);
 	rc = gpg_error (GPG_ERR_GENERAL);
@@ -792,6 +855,14 @@ keydb_new (void)
 	    die = 1;
           j++;
           break;
+        case KEYDB_RESOURCE_TYPE_KEYDB:
+          hd->active[j].type   = all_resources[i].type;
+          hd->active[j].token  = all_resources[i].token;
+          hd->active[j].u.kdb  = kdb_new (all_resources[i].token);
+          if (!hd->active[j].u.kdb)
+	    die = 1;
+          j++;
+          break;
         }
     }
   hd->used = j;
@@ -831,6 +902,9 @@ keydb_release (KEYDB_HANDLE hd)
         case KEYDB_RESOURCE_TYPE_KEYBOX:
           keybox_release (hd->active[i].u.kb);
           break;
+        case KEYDB_RESOURCE_TYPE_KEYDB:
+          kdb_release (hd->active[i].u.kdb);
+          break;
         }
     }
 
@@ -873,6 +947,9 @@ keydb_get_resource_name (KEYDB_HANDLE hd)
     case KEYDB_RESOURCE_TYPE_KEYBOX:
       s = keybox_get_resource_name (hd->active[idx].u.kb);
       break;
+    case KEYDB_RESOURCE_TYPE_KEYDB:
+      s = kdb_get_resource_name (hd->active[idx].u.kdb);
+      break;
     }
 
   return s? s: "";
@@ -904,6 +981,9 @@ lock_all (KEYDB_HANDLE hd)
         case KEYDB_RESOURCE_TYPE_KEYBOX:
           rc = keybox_lock (hd->active[i].u.kb, 1);
           break;
+        case KEYDB_RESOURCE_TYPE_KEYDB:
+          rc = kdb_lock (hd->active[i].u.kdb, 1);
+          break;
         }
     }
 
@@ -922,6 +1002,9 @@ lock_all (KEYDB_HANDLE hd)
             case KEYDB_RESOURCE_TYPE_KEYBOX:
               rc = keybox_lock (hd->active[i].u.kb, 0);
               break;
+            case KEYDB_RESOURCE_TYPE_KEYDB:
+              rc = kdb_lock (hd->active[i].u.kdb, 0);
+              break;
             }
         }
     }
@@ -952,6 +1035,9 @@ unlock_all (KEYDB_HANDLE hd)
         case KEYDB_RESOURCE_TYPE_KEYBOX:
           keybox_lock (hd->active[i].u.kb, 0);
           break;
+        case KEYDB_RESOURCE_TYPE_KEYDB:
+          kdb_lock (hd->active[i].u.kdb, 0);
+          break;
         }
     }
   hd->locked = 0;
@@ -981,6 +1067,9 @@ keydb_push_found_state (KEYDB_HANDLE hd)
     case KEYDB_RESOURCE_TYPE_KEYBOX:
       keybox_push_found_state (hd->active[hd->found].u.kb);
       break;
+    case KEYDB_RESOURCE_TYPE_KEYDB:
+      kdb_push_found_state (hd->active[hd->found].u.kdb);
+      break;
     }
 
   hd->saved_found = hd->found;
@@ -1009,6 +1098,9 @@ keydb_pop_found_state (KEYDB_HANDLE hd)
     case KEYDB_RESOURCE_TYPE_KEYBOX:
       keybox_pop_found_state (hd->active[hd->found].u.kb);
       break;
+    case KEYDB_RESOURCE_TYPE_KEYDB:
+      kdb_pop_found_state (hd->active[hd->found].u.kdb);
+      break;
     }
 }
 
@@ -1254,6 +1346,23 @@ keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb)
           }
       }
       break;
+    case KEYDB_RESOURCE_TYPE_KEYDB:
+      {
+        iobuf_t iobuf;
+        u32 *sigstatus;
+        int pk_no, uid_no;
+
+        err = kdb_get_keyblock (hd->active[hd->found].u.kdb, &iobuf,
+                                &pk_no, &uid_no, &sigstatus);
+        if (!err)
+          {
+            err = parse_keyblock_image (iobuf, pk_no, uid_no, sigstatus,
+                                        ret_kb);
+            xfree (sigstatus);
+            iobuf_close (iobuf);
+          }
+        break;
+      }
     }
 
   if (hd->keyblock_cache.state != KEYBLOCK_CACHE_FILLED)
@@ -1399,6 +1508,20 @@ keydb_update_keyblock (KEYDB_HANDLE hd, kbnode_t kb)
           }
       }
       break;
+    case KEYDB_RESOURCE_TYPE_KEYDB:
+      {
+        iobuf_t iobuf;
+
+        err = build_keyblock_image (kb, &iobuf, NULL);
+        if (!err)
+          {
+            err = kdb_update_keyblock (hd->active[hd->found].u.kdb, kb,
+                                       iobuf_get_temp_buffer (iobuf),
+                                       iobuf_get_temp_length (iobuf));
+            iobuf_close (iobuf);
+          }
+      }
+      break;
     }
 
   unlock_all (hd);
@@ -1460,6 +1583,23 @@ keydb_insert_keyblock (KEYDB_HANDLE hd, kbnode_t kb)
           }
       }
       break;
+    case KEYDB_RESOURCE_TYPE_KEYDB:
+      {
+        iobuf_t iobuf;
+        u32 *sigstatus;
+
+        err = build_keyblock_image (kb, &iobuf, &sigstatus);
+        if (!err)
+          {
+            err = kdb_insert_keyblock (hd->active[idx].u.kdb, kb,
+                                       iobuf_get_temp_buffer (iobuf),
+                                       iobuf_get_temp_length (iobuf),
+                                       sigstatus);
+            xfree (sigstatus);
+            iobuf_close (iobuf);
+          }
+      }
+      break;
     }
 
   unlock_all (hd);
@@ -1499,6 +1639,9 @@ keydb_delete_keyblock (KEYDB_HANDLE hd)
     case KEYDB_RESOURCE_TYPE_KEYBOX:
       rc = keybox_delete (hd->active[hd->found].u.kb);
       break;
+    case KEYDB_RESOURCE_TYPE_KEYDB:
+      rc = kdb_delete (hd->active[hd->found].u.kdb);
+      break;
     }
 
   unlock_all (hd);
@@ -1553,6 +1696,10 @@ keydb_locate_writable (KEYDB_HANDLE hd)
           if (keybox_is_writable (hd->active[hd->current].token))
             return 0; /* found (hd->current is set to it) */
           break;
+        case KEYDB_RESOURCE_TYPE_KEYDB:
+          if (kdb_is_writable (hd->active[hd->current].token))
+            return 0; /* found (hd->current is set to it) */
+          break;
         }
     }
 
@@ -1581,6 +1728,9 @@ keydb_rebuild_caches (int noisy)
         case KEYDB_RESOURCE_TYPE_KEYBOX:
           /* N/A.  */
           break;
+        case KEYDB_RESOURCE_TYPE_KEYDB:
+          /* N/A.  */
+          break;
         }
     }
 }
@@ -1626,6 +1776,9 @@ keydb_search_reset (KEYDB_HANDLE hd)
         case KEYDB_RESOURCE_TYPE_KEYBOX:
           rc = keybox_search_reset (hd->active[i].u.kb);
           break;
+        case KEYDB_RESOURCE_TYPE_KEYDB:
+          rc = kdb_search_reset (hd->active[i].u.kdb);
+          break;
         }
     }
   hd->is_reset = 1;
@@ -1717,6 +1870,10 @@ keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
                                 descindex, &hd->skipped_long_blobs);
           while (rc == GPG_ERR_LEGACY_KEY);
           break;
+        case KEYDB_RESOURCE_TYPE_KEYDB:
+          rc = kdb_search (hd->active[hd->current].u.kdb, desc,
+                           ndesc, descindex);
+          break;
         }
 
       if (DBG_LOOKUP)
diff --git a/g10/trustdb.c b/g10/trustdb.c
index 386796c..f59fa1a 100644
--- a/g10/trustdb.c
+++ b/g10/trustdb.c
@@ -1716,7 +1716,8 @@ validate_key_list (KEYDB_HANDLE hd, KeyHashTable full_trust,
   desc.skipfnc = search_skipfnc;
   desc.skipfncvalue = full_trust;
   rc = keydb_search (hd, &desc, 1, NULL);
-  if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND)
+  if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND
+      || gpg_err_code (rc) == GPG_ERR_EOF)
     {
       keys[nkeys].keyblock = NULL;
       return keys;

-----------------------------------------------------------------------

Summary of changes:
 agent/genkey.c        |   2 +-
 common/stringhelp.c   | 153 ++++++++++++++++++++++++++++++++++++++++++++++++--
 common/stringhelp.h   |   7 ++-
 common/t-stringhelp.c | 137 ++++++++++++++++++++++++++++++++++++++++++++
 dirmngr/dns-stuff.c   |   6 +-
 g10/gpg.c             |   2 +-
 g10/kdb.c             |   4 +-
 g10/tofu.c            |  16 ++++--
 8 files changed, 310 insertions(+), 17 deletions(-)


hooks/post-receive
-- 
The GNU Privacy Guard
http://git.gnupg.org




More information about the Gnupg-commits mailing list