g10: Simplify get_seckey_byname: it was never called with NAME not NULL.
[gnupg.git] / g10 / keydb.c
index 401478a..da18bc0 100644 (file)
@@ -1,7 +1,6 @@
 /* keydb.c - key database dispatcher
- * Copyright (C) 2001, 2002, 2003, 2004, 2005,
- *               2008, 2009, 2011, 2013 Free Software Foundation, Inc.
- * Coyrright (C) 2013 Werner Koch
+ * Copyright (C) 2001-2013 Free Software Foundation, Inc.
+ * Coyrright (C) 2001-2015 Werner Koch
  *
  * This file is part of GnuPG.
  *
@@ -63,17 +62,6 @@ static struct resource_item all_resources[MAX_KEYDB_RESOURCES];
 static int used_resources;
 static void *primary_keyring=NULL;
 
-struct keydb_handle
-{
-  int locked;
-  int found;
-  unsigned long skipped_long_blobs;
-  int no_caching;
-  int current;
-  int used;   /* Number of items in ACTIVE. */
-  struct resource_item active[MAX_KEYDB_RESOURCES];
-};
-
 
 /* This is a simple cache used to return the last result of a
    successful fingerprint search.  This works only for keybox resources
@@ -86,37 +74,188 @@ enum keyblock_cache_states {
   KEYBLOCK_CACHE_FILLED
 };
 
-struct {
+struct keyblock_cache {
   enum keyblock_cache_states state;
   byte fpr[MAX_FINGERPRINT_LEN];
   iobuf_t iobuf; /* Image of the keyblock.  */
   u32 *sigstatus;
   int pk_no;
   int uid_no;
-} keyblock_cache;
+};
+
+
+struct keydb_handle
+{
+  /* When we locked all of the resources in ACTIVE (using keyring_lock
+     / keybox_lock, as appropriate).  */
+  int locked;
+
+  /* The index into ACTIVE of the resources in which the last search
+     result was found.  Initially -1.  */
+  int found;
+
+  /* Initially -1 (invalid).  This is used to save a search result and
+     later restore it as the selected result.  */
+  int saved_found;
+
+  /* The number of skipped long blobs since the last search
+     (keydb_search_reset).  */
+  unsigned long skipped_long_blobs;
+
+  /* If set, this disables the use of the keyblock cache.  */
+  int no_caching;
+
+  /* Whether the next search will be from the beginning of the
+     database (and thus consider all records).  */
+  int is_reset;
+
+  /* The "file position."  In our case, this is index of the current
+     resource in ACTIVE.  */
+  int current;
+
+  /* The number of resources in ACTIVE.  */
+  int used;
+
+  /* Cache of the last found and parsed key block (only used for
+     keyboxes, not keyrings).  */
+  struct keyblock_cache keyblock_cache;
+
+  /* Copy of ALL_RESOURCES when keydb_new is called.  */
+  struct resource_item active[MAX_KEYDB_RESOURCES];
+};
+
+/* Looking up keys is expensive.  To hide the cost, we cache whether
+   keys exist in the key database.  Then, if we know a key does not
+   exist, we don't have to spend time looking it up.  This
+   particularly helps the --list-sigs and --check-sigs commands.
+
+   The cache stores the results in a hash using separate chaining.
+   Concretely: we use the LSB of the keyid to index the hash table and
+   each bucket consists of a linked list of entries.  An entry
+   consists of the 64-bit key id.  If a key id is not in the cache,
+   then we don't know whether it is in the DB or not.
+
+   To simplify the cache consistency protocol, we simply flush the
+   whole cache whenever a key is inserted or updated.  */
+
+#define KID_NOT_FOUND_CACHE_BUCKETS 256
+static struct kid_not_found_cache_bucket *
+  kid_not_found_cache[KID_NOT_FOUND_CACHE_BUCKETS];
+
+/* The total number of entries in the hash table.  */
+static unsigned int kid_not_found_cache_count;
+
+struct kid_not_found_cache_bucket
+{
+  struct kid_not_found_cache_bucket *next;
+  u32 kid[2];
+};
 
 
 static int lock_all (KEYDB_HANDLE hd);
 static void unlock_all (KEYDB_HANDLE hd);
 
 
+/* Check whether the keyid KID is in key id is definately not in the
+   database.
+
+   Returns:
+
+     0 - Indeterminate: the key id is not in the cache; we don't know
+         whether the key is in the database or not.  If you want a
+         definitive answer, you'll need to perform a lookup.
+
+     1 - There is definitely no key with this key id in the database.
+         We searched for a key with this key id previously, but we
+         didn't find it in the database.  */
+static int
+kid_not_found_p (u32 *kid)
+{
+  struct kid_not_found_cache_bucket *k;
+
+  for (k = kid_not_found_cache[kid[0] % KID_NOT_FOUND_CACHE_BUCKETS]; k; k = k->next)
+    if (k->kid[0] == kid[0] && k->kid[1] == kid[1])
+      {
+        if (DBG_CACHE)
+          log_debug ("keydb: kid_not_found_p (%08lx%08lx) => not in DB\n",
+                     (ulong)kid[0], (ulong)kid[1]);
+        return 1;
+      }
+
+  if (DBG_CACHE)
+    log_debug ("keydb: kid_not_found_p (%08lx%08lx) => indeterminate\n",
+               (ulong)kid[0], (ulong)kid[1]);
+  return 0;
+}
+
+
+/* Insert the keyid KID into the kid_not_found_cache.  FOUND is whether
+   the key is in the key database or not.
+
+   Note this function does not check whether the key id is already in
+   the cache.  As such, kid_not_found_p() should be called first.  */
+static void
+kid_not_found_insert (u32 *kid)
+{
+  struct kid_not_found_cache_bucket *k;
+
+  if (DBG_CACHE)
+    log_debug ("keydb: kid_not_found_insert (%08lx%08lx)\n",
+               (ulong)kid[0], (ulong)kid[1]);
+  k = xmalloc (sizeof *k);
+  k->kid[0] = kid[0];
+  k->kid[1] = kid[1];
+  k->next = kid_not_found_cache[kid[0] % KID_NOT_FOUND_CACHE_BUCKETS];
+  kid_not_found_cache[kid[0] % KID_NOT_FOUND_CACHE_BUCKETS] = k;
+  kid_not_found_cache_count++;
+}
+
+
+/* Flush the kid not found cache.  */
 static void
-keyblock_cache_clear (void)
+kid_not_found_flush (void)
 {
-  keyblock_cache.state = KEYBLOCK_CACHE_EMPTY;
-  xfree (keyblock_cache.sigstatus);
-  keyblock_cache.sigstatus = NULL;
-  iobuf_close (keyblock_cache.iobuf);
-  keyblock_cache.iobuf = NULL;
+  struct kid_not_found_cache_bucket *k, *knext;
+  int i;
+
+  if (DBG_CACHE)
+    log_debug ("keydb: kid_not_found_flush\n");
+
+  if (!kid_not_found_cache_count)
+    return;
+
+  for (i=0; i < DIM(kid_not_found_cache); i++)
+    {
+      for (k = kid_not_found_cache[i]; k; k = knext)
+        {
+          knext = k->next;
+          xfree (k);
+        }
+      kid_not_found_cache[i] = NULL;
+    }
+  kid_not_found_cache_count = 0;
+}
+
+
+static void
+keyblock_cache_clear (struct keydb_handle *hd)
+{
+  hd->keyblock_cache.state = KEYBLOCK_CACHE_EMPTY;
+  xfree (hd->keyblock_cache.sigstatus);
+  hd->keyblock_cache.sigstatus = NULL;
+  iobuf_close (hd->keyblock_cache.iobuf);
+  hd->keyblock_cache.iobuf = NULL;
 }
 
 
 /* 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 IS_BOX is true
-   the filename is expected to be a keybox.  If FORCE_CREATE is true
-   the keyring or keybox shall be created.  */
+   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.
+
+   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)
 {
@@ -279,10 +418,15 @@ maybe_create_keyring_or_box (char *filename, int is_box, int force_create)
 }
 
 
-/* Helper for keydb_add_resource.  Opens FILENAME to figures out the
-   resource type.  Returns the resource type and a flag at R_NOTFOUND
-   indicating whether FILENAME could be opened at all.  If the openpgp
-   flag is set in a keybox header, R_OPENPGP will be set to true.  */
+/* Helper for keydb_add_resource.  Opens FILENAME to figure out the
+   resource type.
+
+   Returns the specified file's likely type.  If the file does not
+   exist, returns KEYDB_RESOURCE_TYPE_NONE and sets *R_FOUND to 0.
+   Otherwise, tries to figure out the file's type.  This is either
+   KEYDB_RESOURCE_TYPE_KEYBOX, KEYDB_RESOURCE_TYPE_KEYRING or
+   KEYDB_RESOURCE_TYPE_KEYNONE.  If the file is a keybox and it has
+   the OpenPGP flag set, then R_OPENPGP is also set.  */
 static KeydbResourceType
 rt_from_file (const char *filename, int *r_found, int *r_openpgp)
 {
@@ -323,21 +467,19 @@ rt_from_file (const char *filename, int *r_found, int *r_openpgp)
 }
 
 
-/*
- * Register a resource (keyring or aeybox).  The first keyring or
- * keybox which is added by this function is created if it does not
- * exist.  FLAGS are a combination of the KEYDB_RESOURCE_FLAG_
- * constants as defined in keydb.h.
- */
 gpg_error_t
 keydb_add_resource (const char *url, unsigned int flags)
 {
+  /* Whether we have successfully registered a resource.  */
   static int any_registered;
+  /* The file named by the URL (i.e., without the prototype).  */
   const char *resname = url;
+
   char *filename = NULL;
   int create;
   int read_only = !!(flags&KEYDB_RESOURCE_FLAG_READONLY);
   int is_default = !!(flags&KEYDB_RESOURCE_FLAG_DEFAULT);
+  int is_gpgvdef = !!(flags&KEYDB_RESOURCE_FLAG_GPGVDEF);
   int rc = 0;
   KeydbResourceType rt = KEYDB_RESOURCE_TYPE_NONE;
   void *token;
@@ -345,11 +487,6 @@ keydb_add_resource (const char *url, unsigned int flags)
   /* Create the resource if it is the first registered one.  */
   create = (!read_only && !any_registered);
 
-  /* Do we have an URL?
-   *   gnupg-ring:filename  := this is a plain keyring.
-   *   gnupg-kbx:filename   := this is a keybox file.
-   *   filename := See what is is, but create as plain keyring.
-   */
   if (strlen (resname) > 11 && !strncmp( resname, "gnupg-ring:", 11) )
     {
       rt = KEYDB_RESOURCE_TYPE_KEYRING;
@@ -369,10 +506,18 @@ keydb_add_resource (const char *url, unsigned int flags)
     }
 #endif /* !HAVE_DRIVE_LETTERS && !__riscos__ */
 
-  if (*resname != DIRSEP_C )
+  if (*resname != DIRSEP_C
+#ifdef HAVE_W32_SYSTEM
+      && *resname != '/'  /* Fixme: does not handle drive letters.  */
+#endif
+        )
     {
       /* Do tilde expansion etc. */
-      if (strchr(resname, DIRSEP_C) )
+      if (strchr (resname, DIRSEP_C)
+#ifdef HAVE_W32_SYSTEM
+          || strchr (resname, '/')  /* Windows also accepts this.  */
+#endif
+          )
         filename = make_filename (resname, NULL);
       else
         filename = make_filename (opt.homedir, resname, NULL);
@@ -412,6 +557,23 @@ keydb_add_resource (const char *url, unsigned int flags)
                 strcpy (filename+filenamelen-4, ".gpg");
             }
        }
+      else if (!pass && is_gpgvdef
+               && filenamelen > 4 && !strcmp (filename+filenamelen-4, ".kbx"))
+        {
+          /* Not found but gpgv's default "trustedkeys.kbx" file has
+             been requested.  We did not found it so now check whether
+             a "trustedkeys.gpg" file exists and use that instead.  */
+          KeydbResourceType rttmp;
+
+          strcpy (filename+filenamelen-4, ".gpg");
+          rttmp = rt_from_file (filename, &found, &openpgp_flag);
+          if (found
+              && ((rttmp == KEYDB_RESOURCE_TYPE_KEYBOX && openpgp_flag)
+                  || (rttmp == KEYDB_RESOURCE_TYPE_KEYRING)))
+            rt = rttmp;
+          else /* Restore filename */
+            strcpy (filename+filenamelen-4, ".kbx");
+        }
       else if (!pass
                && is_default && create
                && filenamelen > 4 && !strcmp (filename+filenamelen-4, ".gpg"))
@@ -521,6 +683,13 @@ keydb_add_resource (const char *url, unsigned int flags)
 }
 
 
+void
+keydb_dump_stats (void)
+{
+  if (kid_not_found_cache_count)
+    log_info ("keydb: kid_not_found_cache: total: %u\n",
+             kid_not_found_cache_count);
+}
 
 
 KEYDB_HANDLE
@@ -528,15 +697,18 @@ keydb_new (void)
 {
   KEYDB_HANDLE hd;
   int i, j;
+  int die = 0;
 
   if (DBG_CLOCK)
     log_clock ("keydb_new");
 
   hd = xmalloc_clear (sizeof *hd);
   hd->found = -1;
+  hd->saved_found = -1;
+  hd->is_reset = 1;
 
   assert (used_resources <= MAX_KEYDB_RESOURCES);
-  for (i=j=0; i < used_resources; i++)
+  for (i=j=0; ! die && i < used_resources; i++)
     {
       switch (all_resources[i].type)
         {
@@ -546,10 +718,8 @@ keydb_new (void)
           hd->active[j].type   = all_resources[i].type;
           hd->active[j].token  = all_resources[i].token;
           hd->active[j].u.kr = keyring_new (all_resources[i].token);
-          if (!hd->active[j].u.kr) {
-            xfree (hd);
-            return NULL; /* fixme: release all previously allocated handles*/
-          }
+          if (!hd->active[j].u.kr)
+           die = 1;
           j++;
           break;
         case KEYDB_RESOURCE_TYPE_KEYBOX:
@@ -557,10 +727,7 @@ keydb_new (void)
           hd->active[j].token  = all_resources[i].token;
           hd->active[j].u.kb   = keybox_new_openpgp (all_resources[i].token, 0);
           if (!hd->active[j].u.kb)
-            {
-              xfree (hd);
-              return NULL; /* fixme: release all previously allocated handles*/
-            }
+           die = 1;
           j++;
           break;
         }
@@ -568,6 +735,13 @@ keydb_new (void)
   hd->used = j;
 
   active_handles++;
+
+  if (die)
+    {
+      keydb_release (hd);
+      hd = NULL;
+    }
+
   return hd;
 }
 
@@ -602,9 +776,6 @@ keydb_release (KEYDB_HANDLE hd)
 }
 
 
-/* Set a flag on handle to not use cached results.  This is required
-   for updating a keyring and for key listins.  Fixme: Using a new
-   parameter for keydb_new might be a better solution.  */
 void
 keydb_disable_caching (KEYDB_HANDLE hd)
 {
@@ -613,14 +784,6 @@ keydb_disable_caching (KEYDB_HANDLE hd)
 }
 
 
-/*
- * Return the name of the current resource.  This is function first
- * looks for the last found found, then for the current search
- * position, and last returns the first available resource.  The
- * returned string is only valid as long as the handle exists.  This
- * function does only return NULL if no handle is specified, in all
- * other error cases an empty string is returned.
- */
 const char *
 keydb_get_resource_name (KEYDB_HANDLE hd)
 {
@@ -733,6 +896,62 @@ unlock_all (KEYDB_HANDLE hd)
 }
 
 
+\f
+void
+keydb_push_found_state (KEYDB_HANDLE hd)
+{
+  if (!hd)
+    return;
+
+  if (hd->found < 0 || hd->found >= hd->used)
+    {
+      hd->saved_found = -1;
+      return;
+    }
+
+  switch (hd->active[hd->found].type)
+    {
+    case KEYDB_RESOURCE_TYPE_NONE:
+      break;
+    case KEYDB_RESOURCE_TYPE_KEYRING:
+      keyring_push_found_state (hd->active[hd->found].u.kr);
+      break;
+    case KEYDB_RESOURCE_TYPE_KEYBOX:
+      keybox_push_found_state (hd->active[hd->found].u.kb);
+      break;
+    }
+
+  hd->saved_found = hd->found;
+  hd->found = -1;
+}
+
+
+void
+keydb_pop_found_state (KEYDB_HANDLE hd)
+{
+  if (!hd)
+    return;
+
+  hd->found = hd->saved_found;
+  hd->saved_found = -1;
+  if (hd->found < 0 || hd->found >= hd->used)
+    return;
+
+  switch (hd->active[hd->found].type)
+    {
+    case KEYDB_RESOURCE_TYPE_NONE:
+      break;
+    case KEYDB_RESOURCE_TYPE_KEYRING:
+      keyring_pop_found_state (hd->active[hd->found].u.kr);
+      break;
+    case KEYDB_RESOURCE_TYPE_KEYBOX:
+      keybox_pop_found_state (hd->active[hd->found].u.kb);
+      break;
+    }
+}
+
+
+\f
 static gpg_error_t
 parse_keyblock_image (iobuf_t iobuf, int pk_no, int uid_no,
                       const u32 *sigstatus, kbnode_t *r_keyblock)
@@ -771,21 +990,30 @@ parse_keyblock_image (iobuf_t iobuf, int pk_no, int uid_no,
           err = gpg_error (GPG_ERR_INV_KEYRING);
           break;
         }
-      if (pkt->pkttype == PKT_COMPRESSED)
-        {
-          log_error ("skipped compressed packet in keybox blob\n");
-          free_packet(pkt);
-          init_packet(pkt);
-          continue;
-        }
-      if (pkt->pkttype == PKT_RING_TRUST)
+
+      /* Filter allowed packets.  */
+      switch (pkt->pkttype)
         {
-          log_info ("skipped ring trust packet in keybox blob\n");
+        case PKT_PUBLIC_KEY:
+        case PKT_PUBLIC_SUBKEY:
+        case PKT_SECRET_KEY:
+        case PKT_SECRET_SUBKEY:
+        case PKT_USER_ID:
+        case PKT_ATTRIBUTE:
+        case PKT_SIGNATURE:
+          break; /* Allowed per RFC.  */
+
+        default:
+          /* Note that can't allow ring trust packets here and some of
+             the other GPG specific packets don't make sense either.  */
+          log_error ("skipped packet of type %d in keybox\n",
+                     (int)pkt->pkttype);
           free_packet(pkt);
           init_packet(pkt);
           continue;
         }
 
+      /* Other sanity checks.  */
       if (!in_cert && pkt->pkttype != PKT_PUBLIC_KEY)
         {
           log_error ("parse_keyblock_image: first packet in a keybox blob "
@@ -888,12 +1116,6 @@ parse_keyblock_image (iobuf_t iobuf, int pk_no, int uid_no,
 }
 
 
-/*
- * Return the last found keyring.  Caller must free it.
- * The returned keyblock has the kbode flag bit 0 set for the node with
- * the public key used to locate the keyblock or flag bit 1 set for
- * the user ID node.
- */
 gpg_error_t
 keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb)
 {
@@ -904,17 +1126,31 @@ keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb)
   if (!hd)
     return gpg_error (GPG_ERR_INV_ARG);
 
-  if (keyblock_cache.state == KEYBLOCK_CACHE_FILLED)
+  if (DBG_CLOCK)
+    log_clock ("keydb_get_keybock enter");
+
+  if (hd->keyblock_cache.state == KEYBLOCK_CACHE_FILLED)
     {
-      iobuf_seek (keyblock_cache.iobuf, 0);
-      err = parse_keyblock_image (keyblock_cache.iobuf,
-                                  keyblock_cache.pk_no,
-                                  keyblock_cache.uid_no,
-                                  keyblock_cache.sigstatus,
-                                  ret_kb);
+      err = iobuf_seek (hd->keyblock_cache.iobuf, 0);
       if (err)
-        keyblock_cache_clear ();
-      return err;
+       {
+         log_error ("keydb_get_keyblock: failed to rewind iobuf for cache\n");
+         keyblock_cache_clear (hd);
+       }
+      else
+       {
+         err = parse_keyblock_image (hd->keyblock_cache.iobuf,
+                                     hd->keyblock_cache.pk_no,
+                                     hd->keyblock_cache.uid_no,
+                                     hd->keyblock_cache.sigstatus,
+                                     ret_kb);
+         if (err)
+           keyblock_cache_clear (hd);
+         if (DBG_CLOCK)
+           log_clock (err? "keydb_get_keyblock leave (cached, failed)"
+                      : "keydb_get_keyblock leave (cached)");
+         return err;
+       }
     }
 
   if (hd->found < 0 || hd->found >= hd->used)
@@ -940,13 +1176,13 @@ keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb)
           {
             err = parse_keyblock_image (iobuf, pk_no, uid_no, sigstatus,
                                         ret_kb);
-            if (!err && keyblock_cache.state == KEYBLOCK_CACHE_PREPARED)
+            if (!err && hd->keyblock_cache.state == KEYBLOCK_CACHE_PREPARED)
               {
-                keyblock_cache.state     = KEYBLOCK_CACHE_FILLED;
-                keyblock_cache.sigstatus = sigstatus;
-                keyblock_cache.iobuf     = iobuf;
-                keyblock_cache.pk_no     = pk_no;
-                keyblock_cache.uid_no    = uid_no;
+                hd->keyblock_cache.state     = KEYBLOCK_CACHE_FILLED;
+                hd->keyblock_cache.sigstatus = sigstatus;
+                hd->keyblock_cache.iobuf     = iobuf;
+                hd->keyblock_cache.pk_no     = pk_no;
+                hd->keyblock_cache.uid_no    = uid_no;
               }
             else
               {
@@ -958,9 +1194,12 @@ keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb)
       break;
     }
 
-  if (keyblock_cache.state != KEYBLOCK_CACHE_FILLED)
-    keyblock_cache_clear ();
+  if (hd->keyblock_cache.state != KEYBLOCK_CACHE_FILLED)
+    keyblock_cache_clear (hd);
 
+  if (DBG_CLOCK)
+    log_clock (err? "keydb_get_keyblock leave (failed)"
+               : "keydb_get_keyblock leave");
   return err;
 }
 
@@ -1055,9 +1294,6 @@ build_keyblock_image (kbnode_t keyblock, iobuf_t *r_iobuf, u32 **r_sigstatus)
 }
 
 
-/*
- * Update the current keyblock with the keyblock KB
- */
 gpg_error_t
 keydb_update_keyblock (KEYDB_HANDLE hd, kbnode_t kb)
 {
@@ -1066,7 +1302,8 @@ keydb_update_keyblock (KEYDB_HANDLE hd, kbnode_t kb)
   if (!hd)
     return gpg_error (GPG_ERR_INV_ARG);
 
-  keyblock_cache_clear ();
+  kid_not_found_flush ();
+  keyblock_cache_clear (hd);
 
   if (hd->found < 0 || hd->found >= hd->used)
     return gpg_error (GPG_ERR_VALUE_NOT_FOUND);
@@ -1107,9 +1344,6 @@ keydb_update_keyblock (KEYDB_HANDLE hd, kbnode_t kb)
 }
 
 
-/*
- * Insert a new KB into one of the resources.
- */
 gpg_error_t
 keydb_insert_keyblock (KEYDB_HANDLE hd, kbnode_t kb)
 {
@@ -1119,7 +1353,8 @@ keydb_insert_keyblock (KEYDB_HANDLE hd, kbnode_t kb)
   if (!hd)
     return gpg_error (GPG_ERR_INV_ARG);
 
-  keyblock_cache_clear ();
+  kid_not_found_flush ();
+  keyblock_cache_clear (hd);
 
   if (opt.dry_run)
     return 0;
@@ -1170,9 +1405,6 @@ keydb_insert_keyblock (KEYDB_HANDLE hd, kbnode_t kb)
 }
 
 
-/*
- * Delete the current keyblock.
- */
 gpg_error_t
 keydb_delete_keyblock (KEYDB_HANDLE hd)
 {
@@ -1181,7 +1413,8 @@ keydb_delete_keyblock (KEYDB_HANDLE hd)
   if (!hd)
     return gpg_error (GPG_ERR_INV_ARG);
 
-  keyblock_cache_clear ();
+  kid_not_found_flush ();
+  keyblock_cache_clear (hd);
 
   if (hd->found < 0 || hd->found >= hd->used)
     return gpg_error (GPG_ERR_VALUE_NOT_FOUND);
@@ -1212,18 +1445,11 @@ keydb_delete_keyblock (KEYDB_HANDLE hd)
 
 
 \f
-/*
- * Locate the default writable key resource, so that the next
- * operation (which is only relevant for inserts) will be done on this
- * resource.
- */
 gpg_error_t
-keydb_locate_writable (KEYDB_HANDLE hd, const char *reserved)
+keydb_locate_writable (KEYDB_HANDLE hd)
 {
   gpg_error_t rc;
 
-  (void)reserved;
-
   if (!hd)
     return GPG_ERR_INV_ARG;
 
@@ -1271,16 +1497,11 @@ keydb_locate_writable (KEYDB_HANDLE hd, const char *reserved)
   return gpg_error (GPG_ERR_NOT_FOUND);
 }
 
-/*
- * Rebuild the caches of all key resources.
- */
 void
 keydb_rebuild_caches (int noisy)
 {
   int i, rc;
 
-  keyblock_cache_clear ();
-
   for (i=0; i < used_resources; i++)
     {
       if (!keyring_is_writable (all_resources[i].token))
@@ -1303,7 +1524,6 @@ keydb_rebuild_caches (int noisy)
 }
 
 
-/* Return the number of skipped blocks since the last search reset.  */
 unsigned long
 keydb_get_skipped_counter (KEYDB_HANDLE hd)
 {
@@ -1311,9 +1531,6 @@ keydb_get_skipped_counter (KEYDB_HANDLE hd)
 }
 
 
-/*
- * Start the next search on this handle right at the beginning
- */
 gpg_error_t
 keydb_search_reset (KEYDB_HANDLE hd)
 {
@@ -1323,7 +1540,7 @@ keydb_search_reset (KEYDB_HANDLE hd)
   if (!hd)
     return gpg_error (GPG_ERR_INV_ARG);
 
-  keyblock_cache_clear ();
+  keyblock_cache_clear (hd);
 
   if (DBG_CLOCK)
     log_clock ("keydb_search_reset");
@@ -1349,6 +1566,7 @@ keydb_search_reset (KEYDB_HANDLE hd)
           break;
         }
     }
+  hd->is_reset = 1;
   return rc;
 }
 
@@ -1400,17 +1618,14 @@ dump_search_desc (KEYDB_HANDLE hd, const char *text,
 }
 
 
-/*
- * Search through all keydb resources, starting at the current
- * position, for a keyblock which contains one of the keys described
- * in the DESC array.  Returns GPG_ERR_NOT_FOUND if no matching
- * keyring was found.
- */
 gpg_error_t
 keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
               size_t ndesc, size_t *descindex)
 {
   gpg_error_t rc;
+  int was_reset = hd->is_reset;
+  /* If an entry is already in the cache, then don't add it again.  */
+  int already_in_cache = 0;
 
   if (descindex)
     *descindex = 0; /* Make sure it is always set on return.  */
@@ -1424,6 +1639,15 @@ keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
   if (DBG_CACHE)
     dump_search_desc (hd, "keydb_search", desc, ndesc);
 
+
+  if (ndesc == 1 && desc[0].mode == KEYDB_SEARCH_MODE_LONG_KID
+      && (already_in_cache = kid_not_found_p (desc[0].u.kid)) == 1 )
+    {
+      if (DBG_CLOCK)
+        log_clock ("keydb_search leave (not found, cached)");
+      return gpg_error (GPG_ERR_NOT_FOUND);
+    }
+
   /* NB: If one of the exact search modes below is used in a loop to
      walk over all keys (with the same fingerprint) the caching must
      have been disabled for the handle.  */
@@ -1431,8 +1655,8 @@ keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
       && ndesc == 1
       && (desc[0].mode == KEYDB_SEARCH_MODE_FPR20
           || desc[0].mode == KEYDB_SEARCH_MODE_FPR)
-      && keyblock_cache.state  == KEYBLOCK_CACHE_FILLED
-      && !memcmp (keyblock_cache.fpr, desc[0].u.fpr, 20))
+      && hd->keyblock_cache.state  == KEYBLOCK_CACHE_FILLED
+      && !memcmp (hd->keyblock_cache.fpr, desc[0].u.fpr, 20))
     {
       /* (DESCINDEX is already set).  */
       if (DBG_CLOCK)
@@ -1467,21 +1691,27 @@ keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
       else if (!rc)
         hd->found = hd->current;
     }
+  hd->is_reset = 0;
 
   rc = ((rc == -1 || gpg_err_code (rc) == GPG_ERR_EOF)
         ? gpg_error (GPG_ERR_NOT_FOUND)
         : rc);
 
-  keyblock_cache_clear ();
+  keyblock_cache_clear (hd);
   if (!hd->no_caching
       && !rc
       && ndesc == 1 && (desc[0].mode == KEYDB_SEARCH_MODE_FPR20
                         || desc[0].mode == KEYDB_SEARCH_MODE_FPR))
     {
-      keyblock_cache.state = KEYBLOCK_CACHE_PREPARED;
-      memcpy (keyblock_cache.fpr, desc[0].u.fpr, 20);
+      hd->keyblock_cache.state = KEYBLOCK_CACHE_PREPARED;
+      memcpy (hd->keyblock_cache.fpr, desc[0].u.fpr, 20);
     }
 
+  if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND
+      && ndesc == 1 && desc[0].mode == KEYDB_SEARCH_MODE_LONG_KID && was_reset
+      && !already_in_cache)
+    kid_not_found_insert (desc[0].u.kid);
+
   if (DBG_CLOCK)
     log_clock (rc? "keydb_search leave (not found)"
                  : "keydb_search leave (found)");
@@ -1489,14 +1719,16 @@ keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
 }
 
 
-/* Note that in contrast to using keydb_search in search first mode,
-   this function skips legacy keys.  */
 gpg_error_t
 keydb_search_first (KEYDB_HANDLE hd)
 {
   gpg_error_t err;
   KEYDB_SEARCH_DESC desc;
 
+  err = keydb_search_reset (hd);
+  if (err)
+    return err;
+
   memset (&desc, 0, sizeof desc);
   desc.mode = KEYDB_SEARCH_MODE_FIRST;
   err = keydb_search (hd, &desc, 1, NULL);
@@ -1506,8 +1738,6 @@ keydb_search_first (KEYDB_HANDLE hd)
 }
 
 
-/* Note that in contrast to using keydb_search in search next mode,
-   this fucntion skips legacy keys.  */
 gpg_error_t
 keydb_search_next (KEYDB_HANDLE hd)
 {
@@ -1540,10 +1770,17 @@ keydb_search_kid (KEYDB_HANDLE hd, u32 *kid)
 gpg_error_t
 keydb_search_fpr (KEYDB_HANDLE hd, const byte *fpr)
 {
+  gpg_error_t err;
   KEYDB_SEARCH_DESC desc;
 
   memset (&desc, 0, sizeof desc);
   desc.mode = KEYDB_SEARCH_MODE_FPR;
   memcpy (desc.u.fpr, fpr, MAX_FINGERPRINT_LEN);
-  return keydb_search (hd, &desc, 1, NULL);
+  do
+    {
+      err = keydb_search (hd, &desc, 1, NULL);
+    }
+  while (gpg_err_code (err) == GPG_ERR_LEGACY_KEY);
+
+  return err;
 }