gpg: Allow importing keys with duplicated long key ids.
authorWerner Koch <wk@gnupg.org>
Mon, 13 Oct 2014 12:01:29 +0000 (14:01 +0200)
committerWerner Koch <wk@gnupg.org>
Mon, 13 Oct 2014 12:01:29 +0000 (14:01 +0200)
* g10/keydb.c (keydb_handle): Add field no_caching.
(keyblock_cache): Repalce field kid by fpr.
(keydb_disable_caching): New.
(keydb_search): Use the fingerprint as cache index.

* g10/import.c (import_one): Use the fingerprint and not the kid to
lookup the key.  Call keydb_disable_caching beofre re-searching for
update.

* tests/openpgp/import.test: Add a test case.

Signed-off-by: Werner Koch <wk@gnupg.org>
g10/import.c
g10/keydb.c
g10/keydb.h
tests/openpgp/import.test

index ca35ce1..be2fd63 100644 (file)
@@ -855,12 +855,15 @@ import_one (ctrl_t ctrl,
     PKT_public_key *pk_orig;
     KBNODE node, uidnode;
     KBNODE keyblock_orig = NULL;
+    byte fpr2[MAX_FINGERPRINT_LEN];
+    size_t fpr2len;
     u32 keyid[2];
     int rc = 0;
     int new_key = 0;
     int mod_key = 0;
     int same_key = 0;
     int non_self = 0;
+    size_t an;
     char pkstrbuf[PUBKEY_STRING_SIZE];
 
     /* get the key and print some info about it */
@@ -870,6 +873,9 @@ import_one (ctrl_t ctrl,
 
     pk = node->pkt->pkt.public_key;
 
+    fingerprint_from_pk (pk, fpr2, &fpr2len);
+    for (an = fpr2len; an < MAX_FINGERPRINT_LEN; an++)
+      fpr2[an] = 0;
     keyid_from_pk( pk, keyid );
     uidnode = find_next_kbnode( keyblock, PKT_USER_ID );
 
@@ -957,7 +963,7 @@ import_one (ctrl_t ctrl,
 
     /* do we have this key already in one of our pubrings ? */
     pk_orig = xmalloc_clear( sizeof *pk_orig );
-    rc = get_pubkey_fast ( pk_orig, keyid );
+    rc = get_pubkey_byfprint_fast (pk_orig, fpr2, fpr2len);
     if( rc && rc != G10ERR_NO_PUBKEY && rc != G10ERR_UNU_PUBKEY )
       {
         if (!silent)
@@ -1033,17 +1039,11 @@ import_one (ctrl_t ctrl,
            goto leave;
          }
 
-       /* now read the original keyblock */
+       /* Now read the original keyblock again so that we can use
+           that handle for updating the keyblock.  */
         hd = keydb_new ();
-        {
-            byte afp[MAX_FINGERPRINT_LEN];
-            size_t an;
-
-            fingerprint_from_pk (pk_orig, afp, &an);
-            while (an < MAX_FINGERPRINT_LEN)
-                afp[an++] = 0;
-            rc = keydb_search_fpr (hd, afp);
-        }
+        keydb_disable_caching (hd);
+        rc = keydb_search_fpr (hd, fpr2);
        if( rc )
          {
            log_error (_("key %s: can't locate original keyblock: %s\n"),
@@ -1051,7 +1051,7 @@ import_one (ctrl_t ctrl,
             keydb_release (hd);
            goto leave;
          }
-       rc = keydb_get_keyblock (hd, &keyblock_orig );
+       rc = keydb_get_keyblock (hd, &keyblock_orig);
        if (rc)
          {
            log_error (_("key %s: can't read original keyblock: %s\n"),
index a9a9753..c192e06 100644 (file)
@@ -68,6 +68,7 @@ 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];
@@ -75,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.  */
@@ -87,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;
@@ -570,6 +571,7 @@ keydb_new (void)
   return hd;
 }
 
+
 void
 keydb_release (KEYDB_HANDLE hd)
 {
@@ -600,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.  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
@@ -1407,10 +1420,12 @@ keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
   if (DBG_CACHE)
     dump_search_desc ("keydb_search", desc, ndesc);
 
-  if (ndesc == 1 && desc[0].mode == KEYDB_SEARCH_MODE_LONG_KID
+  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)
@@ -1450,11 +1465,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)
index 78d151a..55f8fc2 100644 (file)
@@ -135,6 +135,7 @@ gpg_error_t keydb_add_resource (const char *url, unsigned int flags);
 
 KEYDB_HANDLE keydb_new (void);
 void keydb_release (KEYDB_HANDLE hd);
+void keydb_disable_caching (KEYDB_HANDLE hd);
 const char *keydb_get_resource_name (KEYDB_HANDLE hd);
 gpg_error_t keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb);
 gpg_error_t keydb_update_keyblock (KEYDB_HANDLE hd, kbnode_t kb);
index eb6860e..a58db40 100755 (executable)
@@ -31,3 +31,16 @@ if $GPG --list-keys --with-colons $keyid \
 else
   error "$goodkey: import failed (bug 1223)"
 fi
+
+
+key1=$srcdir/samplekeys/dda252ebb8ebe1af-1.asc
+key2=$srcdir/samplekeys/dda252ebb8ebe1af-2.asc
+fpr1=9E669861368BCA0BE42DAF7DDDA252EBB8EBE1AF
+fpr2=A55120427374F3F7AA5F1166DDA252EBB8EBE1AF
+info "Checking import of two keys with colliding long key ids."
+$GPG --delete-key --batch --yes $fpr1 $fpr2 2>/dev/null || true
+$GPG --import $key1 || true
+$GPG --import $key2 || true
+n=$($GPG --list-keys --with-colons $fpr1 $fpr2 2>/dev/null \
+    | grep '^pub:.:4096:1:DDA252EBB8EBE1AF:' | wc -l)
+[ $n -ne 2 ] && error "Importing keys with long id collision failed"