gpg: Fix export bug using exact search with only one key in the keybox.
[gnupg.git] / g10 / keydb.c
index 178456a..a578c7c 100644 (file)
@@ -67,6 +67,8 @@ 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];
@@ -74,7 +76,7 @@ struct keydb_handle
 
 
 /* This is a simple cache used to return the last result of a
-   successful long kid search.  This works only for keybox resources
+   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.  */
@@ -86,7 +88,7 @@ enum keyblock_cache_states {
 
 struct {
   enum keyblock_cache_states state;
-  u32 kid[2];
+  byte fpr[MAX_FINGERPRINT_LEN];
   iobuf_t iobuf; /* Image of the keyblock.  */
   u32 *sigstatus;
   int pk_no;
@@ -242,7 +244,7 @@ maybe_create_keyring_or_box (char *filename, int is_box, int force_create)
         rc = gpg_error_from_syserror ();
       else
         {
-          rc = _keybox_write_header_blob (fp);
+          rc = _keybox_write_header_blob (fp, 1);
           fclose (fp);
         }
       if (rc)
@@ -277,6 +279,50 @@ 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.  */
+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
@@ -337,33 +383,34 @@ keydb_add_resource (const char *url, unsigned int flags)
   /* See whether we can determine the filetype.  */
   if (rt == KEYDB_RESOURCE_TYPE_NONE)
     {
-      FILE *fp;
+      int found, openpgp_flag;
       int pass = 0;
       size_t filenamelen;
 
     check_again:
       filenamelen = strlen (filename);
-      fp = fopen (filename, "rb");
-      if (fp)
+      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
@@ -508,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);
@@ -524,6 +571,7 @@ keydb_new (void)
   return hd;
 }
 
+
 void
 keydb_release (KEYDB_HANDLE hd)
 {
@@ -554,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
@@ -1244,6 +1303,13 @@ 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)
+{
+  return hd ? hd->skipped_long_blobs : 0;
+}
+
 
 /*
  * Start the next search on this handle right at the beginning
@@ -1262,6 +1328,10 @@ keydb_search_reset (KEYDB_HANDLE hd)
   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.  */
@@ -1284,7 +1354,8 @@ keydb_search_reset (KEYDB_HANDLE hd)
 
 
 static void
-dump_search_desc (const char *text, KEYDB_SEARCH_DESC *desc, size_t ndesc)
+dump_search_desc (KEYDB_HANDLE hd, const char *text,
+                  KEYDB_SEARCH_DESC *desc, size_t ndesc)
 {
   int n;
   const char *s;
@@ -1315,7 +1386,7 @@ dump_search_desc (const char *text, KEYDB_SEARCH_DESC *desc, size_t ndesc)
         default:                          s = "?";         break;
         }
       if (!n)
-        log_debug ("%s: mode=%s", text, s);
+        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)
@@ -1351,12 +1422,17 @@ keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
     log_clock ("keydb_search enter");
 
   if (DBG_CACHE)
-    dump_search_desc ("keydb_search", desc, ndesc);
-
-  if (ndesc == 1 && desc[0].mode == KEYDB_SEARCH_MODE_LONG_KID
+    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
-      && keyblock_cache.kid[0] == desc[0].u.kid[0]
-      && keyblock_cache.kid[1] == desc[0].u.kid[1])
+      && !memcmp (keyblock_cache.fpr, desc[0].u.fpr, 20))
     {
       /* (DESCINDEX is already set).  */
       if (DBG_CLOCK)
@@ -1379,7 +1455,8 @@ keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
           break;
         case KEYDB_RESOURCE_TYPE_KEYBOX:
           rc = keybox_search (hd->active[hd->current].u.kb, desc,
-                              ndesc, descindex);
+                              ndesc, KEYBOX_BLOBTYPE_PGP,
+                              descindex, &hd->skipped_long_blobs);
           break;
         }
       if (rc == -1 || gpg_err_code (rc) == GPG_ERR_EOF)
@@ -1396,11 +1473,13 @@ keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
         : rc);
 
   keyblock_cache_clear ();
-  if (!rc && ndesc == 1 && desc[0].mode == KEYDB_SEARCH_MODE_LONG_KID)
+  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;
-      keyblock_cache.kid[0] = desc[0].u.kid[0];
-      keyblock_cache.kid[1] = desc[0].u.kid[1];
+      memcpy (keyblock_cache.fpr, desc[0].u.fpr, 20);
     }
 
   if (DBG_CLOCK)