gpg: Fix export bug using exact search with only one key in the keybox.
[gnupg.git] / g10 / keydb.c
index 186f017..a578c7c 100644 (file)
@@ -1,6 +1,7 @@
 /* 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
  *
  * This file is part of GnuPG.
  *
@@ -66,22 +67,58 @@ 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
+   because (due to lack of a copy_keyblock function) we need to store
+   an image of the keyblock which is fortunately instantly available
+   for keyboxes.  */
+enum keyblock_cache_states {
+  KEYBLOCK_CACHE_EMPTY,
+  KEYBLOCK_CACHE_PREPARED,
+  KEYBLOCK_CACHE_FILLED
+};
+
+struct {
+  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;
+
+
 static int lock_all (KEYDB_HANDLE hd);
 static void unlock_all (KEYDB_HANDLE hd);
 
 
+static void
+keyblock_cache_clear (void)
+{
+  keyblock_cache.state = KEYBLOCK_CACHE_EMPTY;
+  xfree (keyblock_cache.sigstatus);
+  keyblock_cache.sigstatus = NULL;
+  iobuf_close (keyblock_cache.iobuf);
+  keyblock_cache.iobuf = NULL;
+}
+
+
 /* Handle the creation of a keyring or a keybox if it does not yet
-   exist.  Take into acount that other processes might have the
+   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. */
+   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.  */
 static int
-maybe_create_keyring_or_box (char *filename, int is_box, int force)
+maybe_create_keyring_or_box (char *filename, int is_box, int force_create)
 {
   dotlock_t lockhd = NULL;
   IOBUF iobuf;
@@ -96,14 +133,14 @@ maybe_create_keyring_or_box (char *filename, int is_box, int force)
 
   /* If we don't want to create a new file at all, there is no need to
      go any further - bail out right here.  */
-  if (!force)
+  if (!force_create)
     return gpg_error (GPG_ERR_ENOENT);
 
   /* First of all we try to create the home directory.  Note, that we
      don't do any locking here because any sane application of gpg
      would create the home directory by itself and not rely on gpg's
-     tricky auto-creation which is anyway only done for some home
-     directory name patterns. */
+     tricky auto-creation which is anyway only done for certain home
+     directory name pattern. */
   last_slash_in_filename = strrchr (filename, DIRSEP_C);
 #if HAVE_W32_SYSTEM
   {
@@ -151,8 +188,8 @@ maybe_create_keyring_or_box (char *filename, int is_box, int force)
         log_info ("can't allocate lock for '%s': %s\n",
                   filename, gpg_strerror (rc));
 
-      if (!force)
-        return gpg_error (GPG_ERR_ENOENT);
+      if (!force_create)
+        return gpg_error (GPG_ERR_ENOENT);  /* Won't happen.  */
       else
         return rc;
     }
@@ -180,7 +217,7 @@ maybe_create_keyring_or_box (char *filename, int is_box, int force)
       gpg_err_set_errno (EPERM);
     }
   else
-    iobuf = iobuf_create (filename);
+    iobuf = iobuf_create (filename, 0);
   umask (oldmask);
   if (!iobuf)
     {
@@ -207,7 +244,7 @@ maybe_create_keyring_or_box (char *filename, int is_box, int force)
         rc = gpg_error_from_syserror ();
       else
         {
-          rc = _keybox_write_header_blob (fp);
+          rc = _keybox_write_header_blob (fp, 1);
           fclose (fp);
         }
       if (rc)
@@ -242,6 +279,50 @@ maybe_create_keyring_or_box (char *filename, int is_box, int force)
 }
 
 
+/* 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.  */
+static KeydbResourceType
+rt_from_file (const char *filename, int *r_found, int *r_openpgp)
+{
+  u32 magic;
+  unsigned char verbuf[4];
+  FILE *fp;
+  KeydbResourceType rt = KEYDB_RESOURCE_TYPE_NONE;
+
+  *r_found = *r_openpgp = 0;
+  fp = fopen (filename, "rb");
+  if (fp)
+    {
+      *r_found = 1;
+
+      if (fread (&magic, 4, 1, fp) == 1 )
+        {
+          if (magic == 0x13579ace || magic == 0xce9a5713)
+            ; /* GDBM magic - not anymore supported. */
+          else if (fread (&verbuf, 4, 1, fp) == 1
+                   && verbuf[0] == 1
+                   && fread (&magic, 4, 1, fp) == 1
+                   && !memcmp (&magic, "KBXf", 4))
+            {
+              if ((verbuf[3] & 0x02))
+                *r_openpgp = 1;
+              rt = KEYDB_RESOURCE_TYPE_KEYBOX;
+            }
+          else
+            rt = KEYDB_RESOURCE_TYPE_KEYRING;
+        }
+      else /* Maybe empty: assume keyring. */
+        rt = KEYDB_RESOURCE_TYPE_KEYRING;
+
+      fclose (fp);
+    }
+
+  return rt;
+}
+
+
 /*
  * Register a resource (keyring or aeybox).  The first keyring or
  * keybox which is added by this function is created if it does not
@@ -256,6 +337,7 @@ keydb_add_resource (const char *url, unsigned int flags)
   char *filename = NULL;
   int create;
   int read_only = !!(flags&KEYDB_RESOURCE_FLAG_READONLY);
+  int is_default = !!(flags&KEYDB_RESOURCE_FLAG_DEFAULT);
   int rc = 0;
   KeydbResourceType rt = KEYDB_RESOURCE_TYPE_NONE;
   void *token;
@@ -301,29 +383,49 @@ keydb_add_resource (const char *url, unsigned int flags)
   /* See whether we can determine the filetype.  */
   if (rt == KEYDB_RESOURCE_TYPE_NONE)
     {
-      FILE *fp = fopen (filename, "rb");
-
-      if (fp)
+      int found, openpgp_flag;
+      int pass = 0;
+      size_t filenamelen;
+
+    check_again:
+      filenamelen = strlen (filename);
+      rt = rt_from_file (filename, &found, &openpgp_flag);
+      if (found)
         {
-          u32 magic;
-
-          if (fread (&magic, 4, 1, fp) == 1 )
+          /* The file exists and we have the resource type in RT.
+
+             Now let us check whether in addition to the "pubring.gpg"
+             a "pubring.kbx with openpgp keys exists.  This is so that
+             GPG 2.1 will use an existing "pubring.kbx" by default iff
+             that file has been created or used by 2.1.  This check is
+             needed because after creation or use of the kbx file with
+             2.1 an older version of gpg may have created a new
+             pubring.gpg for its own use.  */
+          if (!pass && is_default && rt == KEYDB_RESOURCE_TYPE_KEYRING
+              && filenamelen > 4 && !strcmp (filename+filenamelen-4, ".gpg"))
             {
-              if (magic == 0x13579ace || magic == 0xce9a5713)
-                ; /* GDBM magic - not anymore supported. */
-              else if (fread (&magic, 4, 1, fp) == 1
-                       && !memcmp (&magic, "\x01", 1)
-                       && fread (&magic, 4, 1, fp) == 1
-                       && !memcmp (&magic, "KBXf", 4))
+              strcpy (filename+filenamelen-4, ".kbx");
+              if ((rt_from_file (filename, &found, &openpgp_flag)
+                   == KEYDB_RESOURCE_TYPE_KEYBOX) && found && openpgp_flag)
                 rt = KEYDB_RESOURCE_TYPE_KEYBOX;
-              else
-                rt = KEYDB_RESOURCE_TYPE_KEYRING;
-           }
-          else /* Maybe empty: assume keyring. */
-            rt = KEYDB_RESOURCE_TYPE_KEYRING;
-
-          fclose (fp);
+              else /* Restore filename */
+                strcpy (filename+filenamelen-4, ".gpg");
+            }
        }
+      else if (!pass
+               && is_default && create
+               && filenamelen > 4 && !strcmp (filename+filenamelen-4, ".gpg"))
+        {
+          /* The file does not exist, the default resource has been
+             requested, the file shall be created, and the file has a
+             ".gpg" suffix.  Change the suffix to ".kbx" and try once
+             more.  This way we achieve that we open an existing
+             ".gpg" keyring, but create a new keybox file with an
+             ".kbx" suffix.  */
+          strcpy (filename+filenamelen-4, ".kbx");
+          pass++;
+          goto check_again;
+        }
       else /* No file yet: create keybox. */
         rt = KEYDB_RESOURCE_TYPE_KEYBOX;
     }
@@ -336,7 +438,7 @@ keydb_add_resource (const char *url, unsigned int flags)
       goto leave;
 
     case KEYDB_RESOURCE_TYPE_KEYRING:
-      rc = maybe_create_keyring_or_box (filename, create, 0);
+      rc = maybe_create_keyring_or_box (filename, 0, create);
       if (rc)
         goto leave;
 
@@ -366,7 +468,7 @@ keydb_add_resource (const char *url, unsigned int flags)
 
     case KEYDB_RESOURCE_TYPE_KEYBOX:
       {
-        rc = maybe_create_keyring_or_box (filename, create, 1);
+        rc = maybe_create_keyring_or_box (filename, 1, create);
         if (rc)
           goto leave;
 
@@ -427,6 +529,9 @@ keydb_new (void)
   KEYDB_HANDLE hd;
   int i, j;
 
+  if (DBG_CLOCK)
+    log_clock ("keydb_new");
+
   hd = xmalloc_clear (sizeof *hd);
   hd->found = -1;
 
@@ -450,7 +555,7 @@ keydb_new (void)
         case KEYDB_RESOURCE_TYPE_KEYBOX:
           hd->active[j].type   = all_resources[i].type;
           hd->active[j].token  = all_resources[i].token;
-          hd->active[j].u.kb   = keybox_new (all_resources[i].token, 0);
+          hd->active[j].u.kb   = keybox_new_openpgp (all_resources[i].token, 0);
           if (!hd->active[j].u.kb)
             {
               xfree (hd);
@@ -466,6 +571,7 @@ keydb_new (void)
   return hd;
 }
 
+
 void
 keydb_release (KEYDB_HANDLE hd)
 {
@@ -496,6 +602,17 @@ 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)
+{
+  if (hd)
+    hd->no_caching = 1;
+}
+
+
 /*
  * Return the name of the current resource.  This is function first
  * looks for the last found found, then for the current search
@@ -787,6 +904,19 @@ 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)
+    {
+      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);
+      if (err)
+        keyblock_cache_clear ();
+      return err;
+    }
+
   if (hd->found < 0 || hd->found >= hd->used)
     return gpg_error (GPG_ERR_VALUE_NOT_FOUND);
 
@@ -810,13 +940,27 @@ keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb)
           {
             err = parse_keyblock_image (iobuf, pk_no, uid_no, sigstatus,
                                         ret_kb);
-            xfree (sigstatus);
-            iobuf_close (iobuf);
+            if (!err && 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;
+              }
+            else
+              {
+                xfree (sigstatus);
+                iobuf_close (iobuf);
+              }
           }
       }
       break;
     }
 
+  if (keyblock_cache.state != KEYBLOCK_CACHE_FILLED)
+    keyblock_cache_clear ();
+
   return err;
 }
 
@@ -834,18 +978,24 @@ build_keyblock_image (kbnode_t keyblock, iobuf_t *r_iobuf, u32 **r_sigstatus)
   u32 *sigstatus;
 
   *r_iobuf = NULL;
-  *r_sigstatus = NULL;
+  if (r_sigstatus)
+    *r_sigstatus = NULL;
 
   /* Allocate a vector for the signature cache.  This is an array of
      u32 values with the first value giving the number of elements to
      follow and each element descriping the cache status of the
      signature.  */
-  for (kbctx = NULL, n_sigs = 0; (node = walk_kbnode (keyblock, &kbctx, 0));)
-    if (node->pkt->pkttype == PKT_SIGNATURE)
-      n_sigs++;
-  sigstatus = xtrycalloc (1+n_sigs, sizeof *sigstatus);
-  if (!sigstatus)
-    return gpg_error_from_syserror ();
+  if (r_sigstatus)
+    {
+      for (kbctx=NULL, n_sigs=0; (node = walk_kbnode (keyblock, &kbctx, 0));)
+        if (node->pkt->pkttype == PKT_SIGNATURE)
+          n_sigs++;
+      sigstatus = xtrycalloc (1+n_sigs, sizeof *sigstatus);
+      if (!sigstatus)
+        return gpg_error_from_syserror ();
+    }
+  else
+    sigstatus = NULL;
 
   iobuf = iobuf_temp ();
   for (kbctx = NULL, n_sigs = 0; (node = walk_kbnode (keyblock, &kbctx, 0));)
@@ -878,8 +1028,8 @@ build_keyblock_image (kbnode_t keyblock, iobuf_t *r_iobuf, u32 **r_sigstatus)
           PKT_signature *sig = node->pkt->pkt.signature;
 
           n_sigs++;
-          /* Fixme: Detect tye "missing key" status.  */
-          if (sig->flags.checked)
+          /* Fixme: Detect the "missing key" status.  */
+          if (sig->flags.checked && sigstatus)
             {
               if (sig->flags.valid)
                 {
@@ -895,10 +1045,12 @@ build_keyblock_image (kbnode_t keyblock, iobuf_t *r_iobuf, u32 **r_sigstatus)
             }
         }
     }
-  sigstatus[0] = n_sigs;
+  if (sigstatus)
+    sigstatus[0] = n_sigs;
 
   *r_iobuf = iobuf;
-  *r_sigstatus = sigstatus;
+  if (r_sigstatus)
+    *r_sigstatus = sigstatus;
   return 0;
 }
 
@@ -909,39 +1061,49 @@ build_keyblock_image (kbnode_t keyblock, iobuf_t *r_iobuf, u32 **r_sigstatus)
 gpg_error_t
 keydb_update_keyblock (KEYDB_HANDLE hd, kbnode_t kb)
 {
-  gpg_error_t rc;
+  gpg_error_t err;
 
   if (!hd)
     return gpg_error (GPG_ERR_INV_ARG);
 
+  keyblock_cache_clear ();
+
   if (hd->found < 0 || hd->found >= hd->used)
     return gpg_error (GPG_ERR_VALUE_NOT_FOUND);
 
   if (opt.dry_run)
     return 0;
 
-  rc = lock_all (hd);
-  if (rc)
-    return rc;
+  err = lock_all (hd);
+  if (err)
+    return err;
 
   switch (hd->active[hd->found].type)
     {
     case KEYDB_RESOURCE_TYPE_NONE:
-      rc = gpg_error (GPG_ERR_GENERAL); /* oops */
+      err = gpg_error (GPG_ERR_GENERAL); /* oops */
       break;
     case KEYDB_RESOURCE_TYPE_KEYRING:
-      rc = keyring_update_keyblock (hd->active[hd->found].u.kr, kb);
+      err = keyring_update_keyblock (hd->active[hd->found].u.kr, kb);
+      break;
+    case KEYDB_RESOURCE_TYPE_KEYBOX:
+      {
+        iobuf_t iobuf;
+
+        err = build_keyblock_image (kb, &iobuf, NULL);
+        if (!err)
+          {
+            err = keybox_update_keyblock (hd->active[hd->found].u.kb,
+                                          iobuf_get_temp_buffer (iobuf),
+                                          iobuf_get_temp_length (iobuf));
+            iobuf_close (iobuf);
+          }
+      }
       break;
-    /* case KEYDB_RESOURCE_TYPE_KEYRING: */
-    /*   rc = build_keyblock (kb, &image, &imagelen); */
-    /*   if (!rc) */
-    /*     rc = keybox_update_keyblock (hd->active[hd->found].u.kb, */
-    /*                                  image, imagelen); */
-    /*   break; */
     }
 
   unlock_all (hd);
-  return rc;
+  return err;
 }
 
 
@@ -957,6 +1119,8 @@ keydb_insert_keyblock (KEYDB_HANDLE hd, kbnode_t kb)
   if (!hd)
     return gpg_error (GPG_ERR_INV_ARG);
 
+  keyblock_cache_clear ();
+
   if (opt.dry_run)
     return 0;
 
@@ -1017,6 +1181,8 @@ keydb_delete_keyblock (KEYDB_HANDLE hd)
   if (!hd)
     return gpg_error (GPG_ERR_INV_ARG);
 
+  keyblock_cache_clear ();
+
   if (hd->found < 0 || hd->found >= hd->used)
     return gpg_error (GPG_ERR_VALUE_NOT_FOUND);
 
@@ -1113,6 +1279,8 @@ 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))
@@ -1127,11 +1295,21 @@ keydb_rebuild_caches (int noisy)
             log_error (_("failed to rebuild keyring cache: %s\n"),
                        g10_errstr (rc));
           break;
+        case KEYDB_RESOURCE_TYPE_KEYBOX:
+          /* N/A.  */
+          break;
         }
     }
 }
 
 
+/* Return the number of skipped blocks since the last search reset.  */
+unsigned long
+keydb_get_skipped_counter (KEYDB_HANDLE hd)
+{
+  return hd ? hd->skipped_long_blobs : 0;
+}
+
 
 /*
  * Start the next search on this handle right at the beginning
@@ -1145,6 +1323,15 @@ keydb_search_reset (KEYDB_HANDLE hd)
   if (!hd)
     return gpg_error (GPG_ERR_INV_ARG);
 
+  keyblock_cache_clear ();
+
+  if (DBG_CLOCK)
+    log_clock ("keydb_search_reset");
+
+  if (DBG_CACHE)
+    log_debug ("keydb_search: reset  (hd=%p)", hd);
+
+  hd->skipped_long_blobs = 0;
   hd->current = 0;
   hd->found = -1;
   /* Now reset all resources.  */
@@ -1166,6 +1353,53 @@ keydb_search_reset (KEYDB_HANDLE hd)
 }
 
 
+static void
+dump_search_desc (KEYDB_HANDLE hd, const char *text,
+                  KEYDB_SEARCH_DESC *desc, size_t ndesc)
+{
+  int n;
+  const char *s;
+
+  for (n=0; n < ndesc; n++)
+    {
+      switch (desc[n].mode)
+        {
+        case KEYDB_SEARCH_MODE_NONE:      s = "none";      break;
+        case KEYDB_SEARCH_MODE_EXACT:     s = "exact";     break;
+        case KEYDB_SEARCH_MODE_SUBSTR:    s = "substr";    break;
+        case KEYDB_SEARCH_MODE_MAIL:      s = "mail";      break;
+        case KEYDB_SEARCH_MODE_MAILSUB:   s = "mailsub";   break;
+        case KEYDB_SEARCH_MODE_MAILEND:   s = "mailend";   break;
+        case KEYDB_SEARCH_MODE_WORDS:     s = "words";     break;
+        case KEYDB_SEARCH_MODE_SHORT_KID: s = "short_kid"; break;
+        case KEYDB_SEARCH_MODE_LONG_KID:  s = "long_kid";  break;
+        case KEYDB_SEARCH_MODE_FPR16:     s = "fpr16";     break;
+        case KEYDB_SEARCH_MODE_FPR20:     s = "fpr20";     break;
+        case KEYDB_SEARCH_MODE_FPR:       s = "fpr";       break;
+        case KEYDB_SEARCH_MODE_ISSUER:    s = "issuer";    break;
+        case KEYDB_SEARCH_MODE_ISSUER_SN: s = "issuer_sn"; break;
+        case KEYDB_SEARCH_MODE_SN:        s = "sn";        break;
+        case KEYDB_SEARCH_MODE_SUBJECT:   s = "subject";   break;
+        case KEYDB_SEARCH_MODE_KEYGRIP:   s = "keygrip";   break;
+        case KEYDB_SEARCH_MODE_FIRST:     s = "first";     break;
+        case KEYDB_SEARCH_MODE_NEXT:      s = "next";      break;
+        default:                          s = "?";         break;
+        }
+      if (!n)
+        log_debug ("%s: mode=%s  (hd=%p)", text, s, hd);
+      else
+        log_debug ("%*s  mode=%s", (int)strlen (text), "", s);
+      if (desc[n].mode == KEYDB_SEARCH_MODE_LONG_KID)
+        log_printf (" %08lX%08lX", (unsigned long)desc[n].u.kid[0],
+                    (unsigned long)desc[n].u.kid[1]);
+      else if (desc[n].mode == KEYDB_SEARCH_MODE_SHORT_KID)
+        log_printf (" %08lX", (unsigned long)desc[n].u.kid[1]);
+      else if (desc[n].mode == KEYDB_SEARCH_MODE_SUBSTR)
+        log_printf (" '%s'", desc[n].u.name);
+    }
+}
+
+
 /*
  * Search through all keydb resources, starting at the current
  * position, for a keyblock which contains one of the keys described
@@ -1173,17 +1407,39 @@ keydb_search_reset (KEYDB_HANDLE hd)
  * keyring was found.
  */
 gpg_error_t
-keydb_search2 (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
-              size_t ndesc, size_t *descindex)
+keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
+              size_t ndesc, size_t *descindex)
 {
   gpg_error_t rc;
 
+  if (descindex)
+    *descindex = 0; /* Make sure it is always set on return.  */
+
   if (!hd)
     return gpg_error (GPG_ERR_INV_ARG);
 
   if (DBG_CLOCK)
     log_clock ("keydb_search enter");
 
+  if (DBG_CACHE)
+    dump_search_desc (hd, "keydb_search", desc, ndesc);
+
+  /* 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.  */
+  if (!hd->no_caching
+      && 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))
+    {
+      /* (DESCINDEX is already set).  */
+      if (DBG_CLOCK)
+        log_clock ("keydb_search leave (cached)");
+      return 0;
+    }
+
   rc = -1;
   while ((rc == -1 || gpg_err_code (rc) == GPG_ERR_EOF)
          && hd->current >= 0 && hd->current < hd->used)
@@ -1198,7 +1454,9 @@ keydb_search2 (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
                                ndesc, descindex);
           break;
         case KEYDB_RESOURCE_TYPE_KEYBOX:
-          rc = keybox_search (hd->active[hd->current].u.kb, desc, ndesc);
+          rc = keybox_search (hd->active[hd->current].u.kb, desc,
+                              ndesc, KEYBOX_BLOBTYPE_PGP,
+                              descindex, &hd->skipped_long_blobs);
           break;
         }
       if (rc == -1 || gpg_err_code (rc) == GPG_ERR_EOF)
@@ -1210,11 +1468,24 @@ keydb_search2 (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
         hd->found = hd->current;
     }
 
+  rc = ((rc == -1 || gpg_err_code (rc) == GPG_ERR_EOF)
+        ? gpg_error (GPG_ERR_NOT_FOUND)
+        : rc);
+
+  keyblock_cache_clear ();
+  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);
+    }
+
   if (DBG_CLOCK)
-    log_clock ("keydb_search leave");
-  return ((rc == -1 || gpg_err_code (rc) == GPG_ERR_EOF)
-          ? gpg_error (GPG_ERR_NOT_FOUND)
-          : rc);
+    log_clock (rc? "keydb_search leave (not found)"
+                 : "keydb_search leave (found)");
+  return rc;
 }
 
 
@@ -1225,7 +1496,7 @@ keydb_search_first (KEYDB_HANDLE hd)
 
   memset (&desc, 0, sizeof desc);
   desc.mode = KEYDB_SEARCH_MODE_FIRST;
-  return keydb_search (hd, &desc, 1);
+  return keydb_search (hd, &desc, 1, NULL);
 }
 
 gpg_error_t
@@ -1235,7 +1506,7 @@ keydb_search_next (KEYDB_HANDLE hd)
 
   memset (&desc, 0, sizeof desc);
   desc.mode = KEYDB_SEARCH_MODE_NEXT;
-  return keydb_search (hd, &desc, 1);
+  return keydb_search (hd, &desc, 1, NULL);
 }
 
 gpg_error_t
@@ -1247,7 +1518,7 @@ keydb_search_kid (KEYDB_HANDLE hd, u32 *kid)
   desc.mode = KEYDB_SEARCH_MODE_LONG_KID;
   desc.u.kid[0] = kid[0];
   desc.u.kid[1] = kid[1];
-  return keydb_search (hd, &desc, 1);
+  return keydb_search (hd, &desc, 1, NULL);
 }
 
 gpg_error_t
@@ -1258,5 +1529,5 @@ keydb_search_fpr (KEYDB_HANDLE hd, const byte *fpr)
   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);
+  return keydb_search (hd, &desc, 1, NULL);
 }