Fix stdint.h problem for Apple.
[gnupg.git] / g10 / keydb.c
index c67c361..c192e06 100644 (file)
@@ -1,11 +1,13 @@
 /* keydb.c - key database dispatcher
- * Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
+ * 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.
  *
  * GnuPG is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
  * GnuPG is distributed in the hope that it will be useful,
@@ -14,8 +16,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
+#include "gpg.h"
 #include "util.h"
 #include "options.h"
 #include "main.h" /*try_make_homedir ()*/
 #include "packet.h"
 #include "keyring.h"
-#include "keydb.h" 
+#include "../kbx/keybox.h"
+#include "keydb.h"
 #include "i18n.h"
 
 static int active_handles;
 
-typedef enum {
+typedef enum
+  {
     KEYDB_RESOURCE_TYPE_NONE = 0,
-    KEYDB_RESOURCE_TYPE_KEYRING
-} KeydbResourceType;
+    KEYDB_RESOURCE_TYPE_KEYRING,
+    KEYDB_RESOURCE_TYPE_KEYBOX
+  } KeydbResourceType;
 #define MAX_KEYDB_RESOURCES 40
 
-struct resource_item {
+struct resource_item
+{
   KeydbResourceType type;
   union {
     KEYRING_HANDLE kr;
+    KEYBOX_HANDLE kb;
   } u;
   void *token;
-  int secret;
 };
 
 static struct resource_item all_resources[MAX_KEYDB_RESOURCES];
 static int used_resources;
 static void *primary_keyring=NULL;
 
-struct keydb_handle {
+struct keydb_handle
+{
   int locked;
   int found;
+  unsigned long skipped_long_blobs;
+  int no_caching;
   int current;
-  int used; /* items in active */
+  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);
 
 
-/*
- * Register a resource (which currently may only be a keyring file).
- * The first keyring which is added by this function is
- * created if it does not exist.
- * Note: this function may be called before secure memory is
- * available.
- * Flag 1 == force
- * Flag 2 == default
- */
-int
-keydb_add_resource (const char *url, int flags, int secret)
+static void
+keyblock_cache_clear (void)
 {
-    static int any_secret, any_public;
-    const char *resname = url;
-    IOBUF iobuf = NULL;
-    char *filename = NULL;
-    int force=(flags&1);
-    int rc = 0;
-    KeydbResourceType rt = KEYDB_RESOURCE_TYPE_NONE;
-    void *token;
-
-    /* Do we have an URL?
-     * gnupg-ring:filename  := this is a plain keyring
-     * filename := See what is is, but create as plain keyring.
-     */
-    if (strlen (resname) > 11) {
-       if (!strncmp( resname, "gnupg-ring:", 11) ) {
-           rt = KEYDB_RESOURCE_TYPE_KEYRING;
-           resname += 11;
-       }
-#if !defined(HAVE_DRIVE_LETTERS) && !defined(__riscos__)
-       else if (strchr (resname, ':')) {
-           log_error ("invalid key resource URL `%s'\n", url );
-           rc = G10ERR_GENERAL;
-           goto leave;
-       }
-#endif /* !HAVE_DRIVE_LETTERS && !__riscos__ */
+  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 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.  */
+static int
+maybe_create_keyring_or_box (char *filename, int is_box, int force_create)
+{
+  dotlock_t lockhd = NULL;
+  IOBUF iobuf;
+  int rc;
+  mode_t oldmask;
+  char *last_slash_in_filename;
+  int save_slash;
+
+  /* A quick test whether the filename already exists. */
+  if (!access (filename, F_OK))
+    return 0;
+
+  /* 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_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 certain home
+     directory name pattern. */
+  last_slash_in_filename = strrchr (filename, DIRSEP_C);
+#if HAVE_W32_SYSTEM
+  {
+    /* Windows may either have a slash or a backslash.  Take care of it.  */
+    char *p = strrchr (filename, '/');
+    if (!last_slash_in_filename || p > last_slash_in_filename)
+      last_slash_in_filename = p;
+  }
+#endif /*HAVE_W32_SYSTEM*/
+  if (!last_slash_in_filename)
+    return gpg_error (GPG_ERR_ENOENT);  /* No slash at all - should
+                                           not happen though.  */
+  save_slash = *last_slash_in_filename;
+  *last_slash_in_filename = 0;
+  if (access(filename, F_OK))
+    {
+      static int tried;
+
+      if (!tried)
+        {
+          tried = 1;
+          try_make_homedir (filename);
+        }
+      if (access (filename, F_OK))
+        {
+          rc = gpg_error_from_syserror ();
+          *last_slash_in_filename = save_slash;
+          goto leave;
+        }
+    }
+  *last_slash_in_filename = save_slash;
+
+  /* To avoid races with other instances of gpg trying to create or
+     update the keyring (it is removed during an update for a short
+     time), we do the next stuff in a locked state. */
+  lockhd = dotlock_create (filename, 0);
+  if (!lockhd)
+    {
+      rc = gpg_error_from_syserror ();
+      /* A reason for this to fail is that the directory is not
+         writable. However, this whole locking stuff does not make
+         sense if this is the case. An empty non-writable directory
+         with no keyring is not really useful at all. */
+      if (opt.verbose)
+        log_info ("can't allocate lock for '%s': %s\n",
+                  filename, gpg_strerror (rc));
+
+      if (!force_create)
+        return gpg_error (GPG_ERR_ENOENT);  /* Won't happen.  */
+      else
+        return rc;
     }
 
-    if (*resname != DIRSEP_C ) { /* do tilde expansion etc */
-       if (strchr(resname, DIRSEP_C) )
-           filename = make_filename (resname, NULL);
-       else
-           filename = make_filename (opt.homedir, resname, NULL);
+  if ( dotlock_take (lockhd, -1) )
+    {
+      rc = gpg_error_from_syserror ();
+      /* This is something bad.  Probably a stale lockfile.  */
+      log_info ("can't lock '%s': %s\n", filename, gpg_strerror (rc));
+      goto leave;
     }
-    else
-       filename = m_strdup (resname);
 
-    if (!force)
-       force = secret? !any_secret : !any_public;
+  /* Now the real test while we are locked. */
+  if (!access (filename, F_OK))
+    {
+      rc = 0;  /* Okay, we may access the file now.  */
+      goto leave;
+    }
 
-    /* see whether we can determine the filetype */
-    if (rt == KEYDB_RESOURCE_TYPE_NONE) {
-       FILE *fp = fopen( filename, "rb" );
+  /* The file does not yet exist, create it now. */
+  oldmask = umask (077);
+  if (is_secured_filename (filename))
+    {
+      iobuf = NULL;
+      gpg_err_set_errno (EPERM);
+    }
+  else
+    iobuf = iobuf_create (filename, 0);
+  umask (oldmask);
+  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));
+      goto leave;
+    }
 
-       if (fp) {
-           u32 magic;
+  iobuf_close (iobuf);
+  /* Must invalidate that ugly cache */
+  iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, filename);
 
-           if (fread( &magic, 4, 1, fp) == 1 ) {
-               if (magic == 0x13579ace || magic == 0xce9a5713)
-                   ; /* GDBM magic - no more support */
-               else
-                   rt = KEYDB_RESOURCE_TYPE_KEYRING;
-           }
-           else /* maybe empty: assume ring */
-               rt = KEYDB_RESOURCE_TYPE_KEYRING;
-           fclose( fp );
+  /* 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)
+    {
+      FILE *fp = fopen (filename, "w");
+      if (!fp)
+        rc = gpg_error_from_syserror ();
+      else
+        {
+          rc = _keybox_write_header_blob (fp, 1);
+          fclose (fp);
+        }
+      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));
+          goto leave;
+        }
+    }
+
+  if (!opt.quiet)
+    {
+      if (is_box)
+        log_info (_("keybox '%s' created\n"), filename);
+      else
+        log_info (_("keyring '%s' created\n"), filename);
+    }
+
+  rc = 0;
+
+ leave:
+  if (lockhd)
+    {
+      dotlock_release (lockhd);
+      dotlock_destroy (lockhd);
+    }
+  return rc;
+}
+
+
+/* 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
+ * 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)
+{
+  static int any_registered;
+  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 rc = 0;
+  KeydbResourceType rt = KEYDB_RESOURCE_TYPE_NONE;
+  void *token;
+
+  /* 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;
+      resname += 11;
+    }
+  else if (strlen (resname) > 10 && !strncmp (resname, "gnupg-kbx:", 10) )
+    {
+      rt = KEYDB_RESOURCE_TYPE_KEYBOX;
+      resname += 10;
+    }
+#if !defined(HAVE_DRIVE_LETTERS) && !defined(__riscos__)
+  else if (strchr (resname, ':'))
+    {
+      log_error ("invalid key resource URL '%s'\n", url );
+      rc = gpg_error (GPG_ERR_GENERAL);
+      goto leave;
+    }
+#endif /* !HAVE_DRIVE_LETTERS && !__riscos__ */
+
+  if (*resname != DIRSEP_C )
+    {
+      /* Do tilde expansion etc. */
+      if (strchr(resname, DIRSEP_C) )
+        filename = make_filename (resname, NULL);
+      else
+        filename = make_filename (opt.homedir, resname, NULL);
+    }
+  else
+    filename = xstrdup (resname);
+
+  /* See whether we can determine the filetype.  */
+  if (rt == KEYDB_RESOURCE_TYPE_NONE)
+    {
+      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)
+        {
+          /* 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"))
+            {
+              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 /* Restore filename */
+                strcpy (filename+filenamelen-4, ".gpg");
+            }
        }
-       else /* no file yet: create ring */
-           rt = KEYDB_RESOURCE_TYPE_KEYRING;
+      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;
     }
 
-    switch (rt) {
-      case KEYDB_RESOURCE_TYPE_NONE:
-       log_error ("unknown type of key resource `%s'\n", url );
-       rc = G10ERR_GENERAL;
-       goto leave;
+  switch (rt)
+    {
+    case KEYDB_RESOURCE_TYPE_NONE:
+      log_error ("unknown type of key resource '%s'\n", url );
+      rc = gpg_error (GPG_ERR_GENERAL);
+      goto leave;
 
-      case KEYDB_RESOURCE_TYPE_KEYRING:
-        if (access(filename, F_OK))
-          { /* file does not exist */
-           mode_t oldmask;
-           char *last_slash_in_filename;
+    case KEYDB_RESOURCE_TYPE_KEYRING:
+      rc = maybe_create_keyring_or_box (filename, 0, create);
+      if (rc)
+        goto leave;
 
-            if (!force) 
+      if (keyring_register_filename (filename, read_only, &token))
+        {
+          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.kr = NULL; /* Not used here */
+              all_resources[used_resources].token = token;
+              used_resources++;
+            }
+        }
+      else
+        {
+          /* This keyring was already registered, so ignore it.
+             However, we can still mark it as primary even if it was
+             already registered.  */
+          if ((flags & KEYDB_RESOURCE_FLAG_PRIMARY))
+            primary_keyring = token;
+        }
+      break;
+
+    case KEYDB_RESOURCE_TYPE_KEYBOX:
+      {
+        rc = maybe_create_keyring_or_box (filename, 1, create);
+        if (rc)
+          goto leave;
+
+        /* FIXME: How do we register a read-only keybox?  */
+        token = keybox_register_file (filename, 0);
+        if (token)
+          {
+            if (used_resources >= MAX_KEYDB_RESOURCES)
+              rc = gpg_error (GPG_ERR_RESOURCE_LIMIT);
+            else
               {
-                rc = G10ERR_OPEN_FILE;
-                goto leave;
-              }
+                /* 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;
 
-           last_slash_in_filename = strrchr (filename, DIRSEP_C);
-           *last_slash_in_filename = 0;
-           if (access(filename, F_OK))
-              { /* On the first time we try to create the default
-                  homedir and check again. */
-                static int tried;
-                
-                if (!tried)
-                  {
-                    tried = 1;
-                    try_make_homedir (filename);
-                  }
-               if (access (filename, F_OK))
-                  {
-                    rc = G10ERR_OPEN_FILE;
-                    *last_slash_in_filename = DIRSEP_C;
-                    goto leave;
-                  }
-              }
-           *last_slash_in_filename = DIRSEP_C;
+                /* FIXME: Do a compress run if needed and no other
+                   user is currently using the keybox. */
 
-           oldmask=umask(077);
-           iobuf = iobuf_create (filename);
-           umask(oldmask);
-           if (!iobuf) 
-              {
-               log_error ( _("error creating keyring `%s': %s\n"),
-                            filename, strerror(errno));
-               rc = G10ERR_OPEN_FILE;
-               goto leave;
+                used_resources++;
               }
-
-            if (!opt.quiet)
-              log_info (_("keyring `%s' created\n"), filename);
-            iobuf_close (iobuf);
-            iobuf = NULL;
-            /* must invalidate that ugly cache */
-            iobuf_ioctl (NULL, 2, 0, (char*)filename);
-          } /* end file creation */
-
-        if(keyring_register_filename (filename, secret, &token))
-         {
-           if (used_resources >= MAX_KEYDB_RESOURCES)
-             rc = G10ERR_RESOURCE_LIMIT;
-           else 
-             {
-               if(flags&2)
-                 primary_keyring=token;
-               all_resources[used_resources].type = rt;
-               all_resources[used_resources].u.kr = NULL; /* Not used here */
-               all_resources[used_resources].token = token;
-               all_resources[used_resources].secret = secret;
-               used_resources++;
-             }
-         }
-       else
-         {
-           /* This keyring was already registered, so ignore it.
-              However, we can still mark it as primary even if it was
-              already registered. */
-           if(flags&2)
-             primary_keyring=token;
-         }
-       break;
+          }
+        else
+          {
+            /* Already registered.  We will mark it as the primary key
+               if requested.  */
+            /* FIXME: How to do that?  Change the keybox interface?  */
+            /* if ((flags & KEYDB_RESOURCE_FLAG_PRIMARY)) */
+            /*   primary_keyring = token; */
+          }
+      }
+      break;
 
       default:
-       log_error ("resource type of `%s' not supported\n", url);
-       rc = G10ERR_GENERAL;
+       log_error ("resource type of '%s' not supported\n", url);
+       rc = gpg_error (GPG_ERR_GENERAL);
        goto leave;
     }
 
-    /* fixme: check directory permissions and print a warning */
+  /* fixme: check directory permissions and print a warning */
 
-  leave:
-    if (rc)
-       log_error ("keyblock resource `%s': %s\n", filename, g10_errstr(rc));
-    else if (secret)
-       any_secret = 1;
-    else
-       any_public = 1;
-    m_free (filename);
-    return rc;
+ leave:
+  if (rc)
+    log_error (_("keyblock resource '%s': %s\n"), filename, gpg_strerror (rc));
+  else
+    any_registered = 1;
+  xfree (filename);
+  return rc;
 }
 
 
 
 
 KEYDB_HANDLE
-keydb_new (int secret)
+keydb_new (void)
 {
   KEYDB_HANDLE hd;
   int i, j;
-  
-  hd = m_alloc_clear (sizeof *hd);
+
+  if (DBG_CLOCK)
+    log_clock ("keydb_new");
+
+  hd = xmalloc_clear (sizeof *hd);
   hd->found = -1;
-  
+
   assert (used_resources <= MAX_KEYDB_RESOURCES);
   for (i=j=0; i < used_resources; i++)
     {
-      if (!all_resources[i].secret != !secret)
-        continue;
       switch (all_resources[i].type)
         {
         case KEYDB_RESOURCE_TYPE_NONE: /* ignore */
@@ -268,44 +545,71 @@ keydb_new (int secret)
         case KEYDB_RESOURCE_TYPE_KEYRING:
           hd->active[j].type   = all_resources[i].type;
           hd->active[j].token  = all_resources[i].token;
-          hd->active[j].secret = all_resources[i].secret;
-          hd->active[j].u.kr = keyring_new (all_resources[i].token, secret);
+          hd->active[j].u.kr = keyring_new (all_resources[i].token);
           if (!hd->active[j].u.kr) {
-            m_free (hd);
+            xfree (hd);
             return NULL; /* fixme: release all previously allocated handles*/
           }
           j++;
           break;
+        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_openpgp (all_resources[i].token, 0);
+          if (!hd->active[j].u.kb)
+            {
+              xfree (hd);
+              return NULL; /* fixme: release all previously allocated handles*/
+            }
+          j++;
+          break;
         }
     }
   hd->used = j;
-  
+
   active_handles++;
   return hd;
 }
 
-void 
+
+void
 keydb_release (KEYDB_HANDLE hd)
 {
-    int i;
-
-    if (!hd)
-        return;
-    assert (active_handles > 0);
-    active_handles--;
-
-    unlock_all (hd);
-    for (i=0; i < hd->used; i++) {
-        switch (hd->active[i].type) {
-          case KEYDB_RESOURCE_TYPE_NONE:
-            break;
-          case KEYDB_RESOURCE_TYPE_KEYRING:
-            keyring_release (hd->active[i].u.kr);
-            break;
+  int i;
+
+  if (!hd)
+    return;
+  assert (active_handles > 0);
+  active_handles--;
+
+  unlock_all (hd);
+  for (i=0; i < hd->used; i++)
+    {
+      switch (hd->active[i].type)
+        {
+        case KEYDB_RESOURCE_TYPE_NONE:
+          break;
+        case KEYDB_RESOURCE_TYPE_KEYRING:
+          keyring_release (hd->active[i].u.kr);
+          break;
+        case KEYDB_RESOURCE_TYPE_KEYBOX:
+          keybox_release (hd->active[i].u.kb);
+          break;
         }
     }
 
-    m_free (hd);
+  xfree (hd);
+}
+
+
+/* Set a flag on handle to not use cached results.  This is required
+   for updating a keyring.  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;
 }
 
 
@@ -320,245 +624,615 @@ keydb_release (KEYDB_HANDLE hd)
 const char *
 keydb_get_resource_name (KEYDB_HANDLE hd)
 {
-    int idx;
-    const char *s = NULL;
+  int idx;
+  const char *s = NULL;
 
-    if (!hd) 
-        return NULL;
+  if (!hd)
+    return NULL;
 
-    if ( hd->found >= 0 && hd->found < hd->used) 
-        idx = hd->found;
-    else if ( hd->current >= 0 && hd->current < hd->used) 
-        idx = hd->current;
-    else
-        idx = 0;
+  if ( hd->found >= 0 && hd->found < hd->used)
+    idx = hd->found;
+  else if ( hd->current >= 0 && hd->current < hd->used)
+    idx = hd->current;
+  else
+    idx = 0;
 
-    switch (hd->active[idx].type) {
-      case KEYDB_RESOURCE_TYPE_NONE:
-        s = NULL; 
-        break;
-      case KEYDB_RESOURCE_TYPE_KEYRING:
-        s = keyring_get_resource_name (hd->active[idx].u.kr);
-        break;
+  switch (hd->active[idx].type)
+    {
+    case KEYDB_RESOURCE_TYPE_NONE:
+      s = NULL;
+      break;
+    case KEYDB_RESOURCE_TYPE_KEYRING:
+      s = keyring_get_resource_name (hd->active[idx].u.kr);
+      break;
+    case KEYDB_RESOURCE_TYPE_KEYBOX:
+      s = keybox_get_resource_name (hd->active[idx].u.kb);
+      break;
     }
 
-    return s? s: "";
+  return s? s: "";
 }
 
 
 
-static int 
+static int
 lock_all (KEYDB_HANDLE hd)
 {
-    int i, rc = 0;
-
-    for (i=0; !rc && i < hd->used; i++) {
-        switch (hd->active[i].type) {
-          case KEYDB_RESOURCE_TYPE_NONE:
-            break;
-          case KEYDB_RESOURCE_TYPE_KEYRING:
-            rc = keyring_lock (hd->active[i].u.kr, 1);
-            break;
+  int i, rc = 0;
+
+  /* Fixme: This locking scheme may lead to a deadlock if the resources
+     are not added in the same order by all processes.  We are
+     currently only allowing one resource so it is not a problem.
+     [Oops: Who claimed the latter]
+
+     To fix this we need to use a lock file to protect lock_all.  */
+
+  for (i=0; !rc && i < hd->used; i++)
+    {
+      switch (hd->active[i].type)
+        {
+        case KEYDB_RESOURCE_TYPE_NONE:
+          break;
+        case KEYDB_RESOURCE_TYPE_KEYRING:
+          rc = keyring_lock (hd->active[i].u.kr, 1);
+          break;
+        case KEYDB_RESOURCE_TYPE_KEYBOX:
+          rc = keybox_lock (hd->active[i].u.kb, 1);
+          break;
         }
     }
 
-    if (rc) {
-        /* revert the already set locks */
-        for (i--; i >= 0; i--) {
-            switch (hd->active[i].type) {
-              case KEYDB_RESOURCE_TYPE_NONE:
-                break;
-              case KEYDB_RESOURCE_TYPE_KEYRING:
-                keyring_lock (hd->active[i].u.kr, 0);
-                break;
+  if (rc)
+    {
+      /* Revert the already taken locks.  */
+      for (i--; i >= 0; i--)
+        {
+          switch (hd->active[i].type)
+            {
+            case KEYDB_RESOURCE_TYPE_NONE:
+              break;
+            case KEYDB_RESOURCE_TYPE_KEYRING:
+              keyring_lock (hd->active[i].u.kr, 0);
+              break;
+            case KEYDB_RESOURCE_TYPE_KEYBOX:
+              rc = keybox_lock (hd->active[i].u.kb, 0);
+              break;
             }
         }
     }
-    else
-        hd->locked = 1;
+  else
+    hd->locked = 1;
 
-    return rc;
+  return rc;
 }
 
+
 static void
 unlock_all (KEYDB_HANDLE hd)
 {
-    int i;
-
-    if (!hd->locked)
-        return;
-
-    for (i=hd->used-1; i >= 0; i--) {
-        switch (hd->active[i].type) {
-          case KEYDB_RESOURCE_TYPE_NONE:
-            break;
-          case KEYDB_RESOURCE_TYPE_KEYRING:
-            keyring_lock (hd->active[i].u.kr, 0);
-            break;
+  int i;
+
+  if (!hd->locked)
+    return;
+
+  for (i=hd->used-1; i >= 0; i--)
+    {
+      switch (hd->active[i].type)
+        {
+        case KEYDB_RESOURCE_TYPE_NONE:
+          break;
+        case KEYDB_RESOURCE_TYPE_KEYRING:
+          keyring_lock (hd->active[i].u.kr, 0);
+          break;
+        case KEYDB_RESOURCE_TYPE_KEYBOX:
+          keybox_lock (hd->active[i].u.kb, 0);
+          break;
         }
     }
-    hd->locked = 0;
+  hd->locked = 0;
+}
+
+
+static gpg_error_t
+parse_keyblock_image (iobuf_t iobuf, int pk_no, int uid_no,
+                      const u32 *sigstatus, kbnode_t *r_keyblock)
+{
+  gpg_error_t err;
+  PACKET *pkt;
+  kbnode_t keyblock = NULL;
+  kbnode_t node, *tail;
+  int in_cert, save_mode;
+  u32 n_sigs;
+  int pk_count, uid_count;
+
+  *r_keyblock = NULL;
+
+  pkt = xtrymalloc (sizeof *pkt);
+  if (!pkt)
+    return gpg_error_from_syserror ();
+  init_packet (pkt);
+  save_mode = set_packet_list_mode (0);
+  in_cert = 0;
+  n_sigs = 0;
+  tail = NULL;
+  pk_count = uid_count = 0;
+  while ((err = parse_packet (iobuf, pkt)) != -1)
+    {
+      if (gpg_err_code (err) == GPG_ERR_UNKNOWN_PACKET)
+        {
+          free_packet (pkt);
+          init_packet (pkt);
+          continue;
+       }
+      if (err)
+        {
+          log_error ("parse_keyblock_image: read error: %s\n",
+                     gpg_strerror (err));
+          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)
+        {
+          log_info ("skipped ring trust packet in keybox blob\n");
+          free_packet(pkt);
+          init_packet(pkt);
+          continue;
+        }
+
+      if (!in_cert && pkt->pkttype != PKT_PUBLIC_KEY)
+        {
+          log_error ("parse_keyblock_image: first packet in a keybox blob "
+                     "is not a public key packet\n");
+          err = gpg_error (GPG_ERR_INV_KEYRING);
+          break;
+        }
+      if (in_cert && (pkt->pkttype == PKT_PUBLIC_KEY
+                      || pkt->pkttype == PKT_SECRET_KEY))
+        {
+          log_error ("parse_keyblock_image: "
+                     "multiple keyblocks in a keybox blob\n");
+          err = gpg_error (GPG_ERR_INV_KEYRING);
+          break;
+        }
+      in_cert = 1;
+
+      if (pkt->pkttype == PKT_SIGNATURE && sigstatus)
+        {
+          PKT_signature *sig = pkt->pkt.signature;
+
+          n_sigs++;
+          if (n_sigs > sigstatus[0])
+            {
+              log_error ("parse_keyblock_image: "
+                         "more signatures than found in the meta data\n");
+              err = gpg_error (GPG_ERR_INV_KEYRING);
+              break;
+
+            }
+          if (sigstatus[n_sigs])
+            {
+              sig->flags.checked = 1;
+              if (sigstatus[n_sigs] == 1 )
+                ; /* missing key */
+              else if (sigstatus[n_sigs] == 2 )
+                ; /* bad signature */
+              else if (sigstatus[n_sigs] < 0x10000000)
+                ; /* bad flag */
+              else
+                {
+                  sig->flags.valid = 1;
+                  /* Fixme: Shall we set the expired flag here?  */
+                }
+            }
+        }
+
+      node = new_kbnode (pkt);
+
+      switch (pkt->pkttype)
+        {
+        case PKT_PUBLIC_KEY:
+        case PKT_PUBLIC_SUBKEY:
+        case PKT_SECRET_KEY:
+        case PKT_SECRET_SUBKEY:
+          if (++pk_count == pk_no)
+            node->flag |= 1;
+          break;
+
+        case PKT_USER_ID:
+          if (++uid_count == uid_no)
+            node->flag |= 2;
+          break;
+
+        default:
+          break;
+        }
+
+      if (!keyblock)
+        keyblock = node;
+      else
+        *tail = node;
+      tail = &node->next;
+      pkt = xtrymalloc (sizeof *pkt);
+      if (!pkt)
+        {
+          err = gpg_error_from_syserror ();
+          break;
+        }
+      init_packet (pkt);
+    }
+  set_packet_list_mode (save_mode);
+
+  if (err == -1 && keyblock)
+    err = 0; /* Got the entire keyblock.  */
+
+  if (!err && sigstatus && n_sigs != sigstatus[0])
+    {
+      log_error ("parse_keyblock_image: signature count does not match\n");
+      err = gpg_error (GPG_ERR_INV_KEYRING);
+    }
+
+  if (err)
+    release_kbnode (keyblock);
+  else
+    *r_keyblock = keyblock;
+  free_packet (pkt);
+  xfree (pkt);
+  return err;
 }
 
 
 /*
  * 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 public key used to locate the keyblock or flag bit 1 set for
  * the user ID node.
  */
-int
+gpg_error_t
 keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb)
 {
-    int rc = 0;
+  gpg_error_t err = 0;
 
-    if (!hd)
-        return G10ERR_INV_ARG;
+  *ret_kb = NULL;
 
-    if ( hd->found < 0 || hd->found >= hd->used) 
-        return -1; /* nothing found */
+  if (!hd)
+    return gpg_error (GPG_ERR_INV_ARG);
 
-    switch (hd->active[hd->found].type) {
-      case KEYDB_RESOURCE_TYPE_NONE:
-        rc = G10ERR_GENERAL; /* oops */
-        break;
-      case KEYDB_RESOURCE_TYPE_KEYRING:
-        rc = keyring_get_keyblock (hd->active[hd->found].u.kr, ret_kb);
-        break;
+  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;
     }
 
-    return rc;
+  if (hd->found < 0 || hd->found >= hd->used)
+    return gpg_error (GPG_ERR_VALUE_NOT_FOUND);
+
+  switch (hd->active[hd->found].type)
+    {
+    case KEYDB_RESOURCE_TYPE_NONE:
+      err = gpg_error (GPG_ERR_GENERAL); /* oops */
+      break;
+    case KEYDB_RESOURCE_TYPE_KEYRING:
+      err = keyring_get_keyblock (hd->active[hd->found].u.kr, ret_kb);
+      break;
+    case KEYDB_RESOURCE_TYPE_KEYBOX:
+      {
+        iobuf_t iobuf;
+        u32 *sigstatus;
+        int pk_no, uid_no;
+
+        err = keybox_get_keyblock (hd->active[hd->found].u.kb,
+                                   &iobuf, &pk_no, &uid_no, &sigstatus);
+        if (!err)
+          {
+            err = parse_keyblock_image (iobuf, pk_no, uid_no, sigstatus,
+                                        ret_kb);
+            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;
+}
+
+
+/* Build a keyblock image from KEYBLOCK.  Returns 0 on success and
+   only then stores a new iobuf object at R_IOBUF and a signature
+   status vecotor at R_SIGSTATUS.  */
+static gpg_error_t
+build_keyblock_image (kbnode_t keyblock, iobuf_t *r_iobuf, u32 **r_sigstatus)
+{
+  gpg_error_t err;
+  iobuf_t iobuf;
+  kbnode_t kbctx, node;
+  u32 n_sigs;
+  u32 *sigstatus;
+
+  *r_iobuf = 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.  */
+  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));)
+    {
+      /* Make sure to use only packets valid on a keyblock.  */
+      switch (node->pkt->pkttype)
+        {
+        case PKT_PUBLIC_KEY:
+        case PKT_PUBLIC_SUBKEY:
+        case PKT_SIGNATURE:
+        case PKT_USER_ID:
+        case PKT_ATTRIBUTE:
+          /* Note that we don't want the ring trust packets.  They are
+             not useful. */
+          break;
+        default:
+          continue;
+        }
+
+      err = build_packet (iobuf, node->pkt);
+      if (err)
+        {
+          iobuf_close (iobuf);
+          return err;
+        }
+
+      /* Build signature status vector.  */
+      if (node->pkt->pkttype == PKT_SIGNATURE)
+        {
+          PKT_signature *sig = node->pkt->pkt.signature;
+
+          n_sigs++;
+          /* Fixme: Detect the "missing key" status.  */
+          if (sig->flags.checked && sigstatus)
+            {
+              if (sig->flags.valid)
+                {
+                  if (!sig->expiredate)
+                    sigstatus[n_sigs] = 0xffffffff;
+                  else if (sig->expiredate < 0x1000000)
+                    sigstatus[n_sigs] = 0x10000000;
+                  else
+                    sigstatus[n_sigs] = sig->expiredate;
+                }
+              else
+                sigstatus[n_sigs] = 0x00000002; /* Bad signature.  */
+            }
+        }
+    }
+  if (sigstatus)
+    sigstatus[0] = n_sigs;
+
+  *r_iobuf = iobuf;
+  if (r_sigstatus)
+    *r_sigstatus = sigstatus;
+  return 0;
 }
 
-/* 
- * update the current keyblock with KB
+
+/*
+ * Update the current keyblock with the keyblock KB
  */
-int
-keydb_update_keyblock (KEYDB_HANDLE hd, KBNODE kb)
+gpg_error_t
+keydb_update_keyblock (KEYDB_HANDLE hd, kbnode_t kb)
 {
-    int rc = 0;
+  gpg_error_t err;
 
-    if (!hd)
-        return G10ERR_INV_ARG;
+  if (!hd)
+    return gpg_error (GPG_ERR_INV_ARG);
 
-    if ( hd->found < 0 || hd->found >= hd->used) 
-        return -1; /* nothing found */
+  keyblock_cache_clear ();
 
-    if( opt.dry_run )
-       return 0;
+  if (hd->found < 0 || hd->found >= hd->used)
+    return gpg_error (GPG_ERR_VALUE_NOT_FOUND);
 
-    rc = lock_all (hd);
-    if (rc)
-        return rc;
+  if (opt.dry_run)
+    return 0;
 
-    switch (hd->active[hd->found].type) {
-      case KEYDB_RESOURCE_TYPE_NONE:
-        rc = G10ERR_GENERAL; /* oops */
-        break;
-      case KEYDB_RESOURCE_TYPE_KEYRING:
-        rc = keyring_update_keyblock (hd->active[hd->found].u.kr, kb);
-        break;
+  err = lock_all (hd);
+  if (err)
+    return err;
+
+  switch (hd->active[hd->found].type)
+    {
+    case KEYDB_RESOURCE_TYPE_NONE:
+      err = gpg_error (GPG_ERR_GENERAL); /* oops */
+      break;
+    case KEYDB_RESOURCE_TYPE_KEYRING:
+      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;
     }
 
-    unlock_all (hd);
-    return rc;
+  unlock_all (hd);
+  return err;
 }
 
 
-/* 
- * Insert a new KB into one of the resources. 
+/*
+ * Insert a new KB into one of the resources.
  */
-int
-keydb_insert_keyblock (KEYDB_HANDLE hd, KBNODE kb)
+gpg_error_t
+keydb_insert_keyblock (KEYDB_HANDLE hd, kbnode_t kb)
 {
-    int rc = -1;
-    int idx;
+  gpg_error_t err;
+  int idx;
 
-    if (!hd) 
-        return G10ERR_INV_ARG;
+  if (!hd)
+    return gpg_error (GPG_ERR_INV_ARG);
 
-    if( opt.dry_run )
-       return 0;
+  keyblock_cache_clear ();
 
-    if ( hd->found >= 0 && hd->found < hd->used) 
-        idx = hd->found;
-    else if ( hd->current >= 0 && hd->current < hd->used) 
-        idx = hd->current;
-    else
-        return G10ERR_GENERAL;
+  if (opt.dry_run)
+    return 0;
 
-    rc = lock_all (hd);
-    if (rc)
-        return rc;
+  if (hd->found >= 0 && hd->found < hd->used)
+    idx = hd->found;
+  else if (hd->current >= 0 && hd->current < hd->used)
+    idx = hd->current;
+  else
+    return gpg_error (GPG_ERR_GENERAL);
 
-    switch (hd->active[idx].type) {
-      case KEYDB_RESOURCE_TYPE_NONE:
-        rc = G10ERR_GENERAL; /* oops */
-        break;
-      case KEYDB_RESOURCE_TYPE_KEYRING:
-        rc = keyring_insert_keyblock (hd->active[idx].u.kr, kb);
-        break;
+  err = lock_all (hd);
+  if (err)
+    return err;
+
+  switch (hd->active[idx].type)
+    {
+    case KEYDB_RESOURCE_TYPE_NONE:
+      err = gpg_error (GPG_ERR_GENERAL); /* oops */
+      break;
+    case KEYDB_RESOURCE_TYPE_KEYRING:
+      err = keyring_insert_keyblock (hd->active[idx].u.kr, kb);
+      break;
+    case KEYDB_RESOURCE_TYPE_KEYBOX:
+      { /* We need to turn our kbnode_t list of packets into a proper
+           keyblock first.  This is required by the OpenPGP key parser
+           included in the keybox code.  Eventually we can change this
+           kludge to have the caller pass the image.  */
+        iobuf_t iobuf;
+        u32 *sigstatus;
+
+        err = build_keyblock_image (kb, &iobuf, &sigstatus);
+        if (!err)
+          {
+            err = keybox_insert_keyblock (hd->active[idx].u.kb,
+                                          iobuf_get_temp_buffer (iobuf),
+                                          iobuf_get_temp_length (iobuf),
+                                          sigstatus);
+            xfree (sigstatus);
+            iobuf_close (iobuf);
+          }
+      }
+      break;
     }
 
-    unlock_all (hd);
-    return rc;
+  unlock_all (hd);
+  return err;
 }
 
 
-/* 
- * The current keyblock will be deleted.
+/*
+ * Delete the current keyblock.
  */
-int
+gpg_error_t
 keydb_delete_keyblock (KEYDB_HANDLE hd)
 {
-    int rc = -1;
+  gpg_error_t rc;
 
-    if (!hd)
-        return G10ERR_INV_ARG;
+  if (!hd)
+    return gpg_error (GPG_ERR_INV_ARG);
 
-    if ( hd->found < 0 || hd->found >= hd->used) 
-        return -1; /* nothing found */
+  keyblock_cache_clear ();
 
-    if( opt.dry_run )
-       return 0;
+  if (hd->found < 0 || hd->found >= hd->used)
+    return gpg_error (GPG_ERR_VALUE_NOT_FOUND);
 
-    rc = lock_all (hd);
-    if (rc)
-        return rc;
+  if (opt.dry_run)
+    return 0;
+
+  rc = lock_all (hd);
+  if (rc)
+    return rc;
 
-    switch (hd->active[hd->found].type) {
-      case KEYDB_RESOURCE_TYPE_NONE:
-        rc = G10ERR_GENERAL; /* oops */
-        break;
-      case KEYDB_RESOURCE_TYPE_KEYRING:
-        rc = keyring_delete_keyblock (hd->active[hd->found].u.kr);
-        break;
+  switch (hd->active[hd->found].type)
+    {
+    case KEYDB_RESOURCE_TYPE_NONE:
+      rc = gpg_error (GPG_ERR_GENERAL);
+      break;
+    case KEYDB_RESOURCE_TYPE_KEYRING:
+      rc = keyring_delete_keyblock (hd->active[hd->found].u.kr);
+      break;
+    case KEYDB_RESOURCE_TYPE_KEYBOX:
+      rc = keybox_delete (hd->active[hd->found].u.kb);
+      break;
     }
 
-    unlock_all (hd);
-    return rc;
+  unlock_all (hd);
+  return rc;
 }
 
+
 \f
 /*
  * Locate the default writable key resource, so that the next
  * operation (which is only relevant for inserts) will be done on this
- * resource.  
+ * resource.
  */
-int
+gpg_error_t
 keydb_locate_writable (KEYDB_HANDLE hd, const char *reserved)
 {
-  int rc;
-  
+  gpg_error_t rc;
+
+  (void)reserved;
+
   if (!hd)
     return G10ERR_INV_ARG;
-  
+
   rc = keydb_search_reset (hd); /* this does reset hd->current */
   if (rc)
     return rc;
 
   /* If we have a primary set, try that one first */
-  if(primary_keyring)
+  if (primary_keyring)
     {
       for ( ; hd->current >= 0 && hd->current < hd->used; hd->current++)
        {
@@ -576,9 +1250,9 @@ keydb_locate_writable (KEYDB_HANDLE hd, const char *reserved)
        return rc;
     }
 
-  for ( ; hd->current >= 0 && hd->current < hd->used; hd->current++) 
+  for ( ; hd->current >= 0 && hd->current < hd->used; hd->current++)
     {
-      switch (hd->active[hd->current].type) 
+      switch (hd->active[hd->current].type)
         {
         case KEYDB_RESOURCE_TYPE_NONE:
           BUG();
@@ -587,138 +1261,265 @@ keydb_locate_writable (KEYDB_HANDLE hd, const char *reserved)
           if (keyring_is_writable (hd->active[hd->current].token))
             return 0; /* found (hd->current is set to it) */
           break;
+        case KEYDB_RESOURCE_TYPE_KEYBOX:
+          if (keybox_is_writable (hd->active[hd->current].token))
+            return 0; /* found (hd->current is set to it) */
+          break;
         }
     }
-  
-  return -1;
+
+  return gpg_error (GPG_ERR_NOT_FOUND);
 }
 
 /*
  * Rebuild the caches of all key resources.
  */
 void
-keydb_rebuild_caches (void)
+keydb_rebuild_caches (int noisy)
 {
   int i, rc;
-  
+
+  keyblock_cache_clear ();
+
   for (i=0; i < used_resources; i++)
     {
-      if (all_resources[i].secret)
+      if (!keyring_is_writable (all_resources[i].token))
         continue;
       switch (all_resources[i].type)
         {
         case KEYDB_RESOURCE_TYPE_NONE: /* ignore */
           break;
         case KEYDB_RESOURCE_TYPE_KEYRING:
-          rc = keyring_rebuild_cache (all_resources[i].token);
+          rc = keyring_rebuild_cache (all_resources[i].token,noisy);
           if (rc)
             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
  */
-int 
+gpg_error_t
 keydb_search_reset (KEYDB_HANDLE hd)
 {
-    int i, rc = 0;
-
-    if (!hd)
-        return G10ERR_INV_ARG;
-
-    hd->current = 0; 
-    hd->found = -1;
-    /* and reset all resources */
-    for (i=0; !rc && i < hd->used; i++) {
-        switch (hd->active[i].type) {
-          case KEYDB_RESOURCE_TYPE_NONE:
-            break;
-          case KEYDB_RESOURCE_TYPE_KEYRING:
-            rc = keyring_search_reset (hd->active[i].u.kr);
-            break;
+  gpg_error_t rc = 0;
+  int i;
+
+  if (!hd)
+    return gpg_error (GPG_ERR_INV_ARG);
+
+  keyblock_cache_clear ();
+
+  if (DBG_CLOCK)
+    log_clock ("keydb_search_reset");
+
+  hd->skipped_long_blobs = 0;
+  hd->current = 0;
+  hd->found = -1;
+  /* Now reset all resources.  */
+  for (i=0; !rc && i < hd->used; i++)
+    {
+      switch (hd->active[i].type)
+        {
+        case KEYDB_RESOURCE_TYPE_NONE:
+          break;
+        case KEYDB_RESOURCE_TYPE_KEYRING:
+          rc = keyring_search_reset (hd->active[i].u.kr);
+          break;
+        case KEYDB_RESOURCE_TYPE_KEYBOX:
+          rc = keybox_search_reset (hd->active[i].u.kb);
+          break;
+        }
+    }
+  return rc;
+}
+
+
+static void
+dump_search_desc (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", text, s);
+      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);
     }
-    return rc; 
 }
 
 
-/* 
- * Search through all keydb resources, starting at the current position,
- * for a keyblock which contains one of the keys described in the DESC array.
+/*
+ * 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.
  */
-int 
-keydb_search2 (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
-              size_t ndesc, size_t *descindex)
+gpg_error_t
+keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
+              size_t ndesc, size_t *descindex)
 {
-    int rc = -1;
-
-    if (!hd)
-        return G10ERR_INV_ARG;
-
-    while (rc == -1 && hd->current >= 0 && hd->current < hd->used) {
-        switch (hd->active[hd->current].type) {
-          case KEYDB_RESOURCE_TYPE_NONE:
-            BUG(); /* we should never see it here */
-            break;
-          case KEYDB_RESOURCE_TYPE_KEYRING:
-            rc = keyring_search (hd->active[hd->current].u.kr, desc,
-                                ndesc, descindex);
-            break;
+  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 ("keydb_search", desc, ndesc);
+
+  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)
+    {
+      switch (hd->active[hd->current].type)
+        {
+        case KEYDB_RESOURCE_TYPE_NONE:
+          BUG(); /* we should never see it here */
+          break;
+        case KEYDB_RESOURCE_TYPE_KEYRING:
+          rc = keyring_search (hd->active[hd->current].u.kr, desc,
+                               ndesc, descindex);
+          break;
+        case KEYDB_RESOURCE_TYPE_KEYBOX:
+          rc = keybox_search (hd->active[hd->current].u.kb, desc,
+                              ndesc, descindex, &hd->skipped_long_blobs);
+          break;
+        }
+      if (rc == -1 || gpg_err_code (rc) == GPG_ERR_EOF)
+        {
+          /* EOF -> switch to next resource */
+          hd->current++;
         }
-        if (rc == -1) /* EOF -> switch to next resource */
-            hd->current++; 
-        else if (!rc)
-            hd->found = hd->current;
+      else if (!rc)
+        hd->found = hd->current;
     }
 
-    return rc; 
+  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 (rc? "keydb_search leave (not found)"
+                 : "keydb_search leave (found)");
+  return rc;
 }
 
-int
+
+gpg_error_t
 keydb_search_first (KEYDB_HANDLE hd)
 {
-    KEYDB_SEARCH_DESC desc;
+  KEYDB_SEARCH_DESC desc;
 
-    memset (&desc, 0, sizeof desc);
-    desc.mode = KEYDB_SEARCH_MODE_FIRST;
-    return keydb_search (hd, &desc, 1);
+  memset (&desc, 0, sizeof desc);
+  desc.mode = KEYDB_SEARCH_MODE_FIRST;
+  return keydb_search (hd, &desc, 1, NULL);
 }
 
-int
+gpg_error_t
 keydb_search_next (KEYDB_HANDLE hd)
 {
-    KEYDB_SEARCH_DESC desc;
+  KEYDB_SEARCH_DESC desc;
 
-    memset (&desc, 0, sizeof desc);
-    desc.mode = KEYDB_SEARCH_MODE_NEXT;
-    return keydb_search (hd, &desc, 1);
+  memset (&desc, 0, sizeof desc);
+  desc.mode = KEYDB_SEARCH_MODE_NEXT;
+  return keydb_search (hd, &desc, 1, NULL);
 }
 
-int
+gpg_error_t
 keydb_search_kid (KEYDB_HANDLE hd, u32 *kid)
 {
-    KEYDB_SEARCH_DESC desc;
+  KEYDB_SEARCH_DESC desc;
 
-    memset (&desc, 0, sizeof desc);
-    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);
+  memset (&desc, 0, sizeof desc);
+  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, NULL);
 }
 
-int
+gpg_error_t
 keydb_search_fpr (KEYDB_HANDLE hd, const byte *fpr)
 {
-    KEYDB_SEARCH_DESC desc;
+  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);
+  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);
 }