From deeaac89f55ef6a4dc1e859b0fb0f4e4ed6e86cd Mon Sep 17 00:00:00 2001
From: Werner Koch <wk@gnupg.org>
Date: Tue, 11 Feb 2025 14:44:23 +0100
Subject: [PATCH 1/4] gpg: Lookup key for merging/inserting only by primary
 key.

* g10/getkey.c (get_keyblock_byfpr_fast): Add arg primary_only and
implement.
* g10/import.c (import_one_real): Simplify filling the fpr buffer with
zeroes.
(import_one_real): Find key only by primary fingerprint.
--

This should have been done early: When looking up the original
keyblock we want to update, we need to lookup it up only using the
primary key.  This avoids to find a key which has the primary key also
has a subkey.

GnuPG-bug-id: 7527

(ported by dkg from commit 25d748c3dfc0102f9e54afea59ff26b3969bd8c1
from STABLE-BRANCH-2-4)

Reference link:
https://salsa.debian.org/debian/gnupg2/-/blob/debian/2.2.46-6/debian/patches/from-2.4/gpg-Lookup-key-for-merging-inserting-only-by-primary-key.patch

Signed-off-by: baogen shang <baogen.shang@windriver.com>
---
 g10/getkey.c | 23 ++++++++++++++++++++---
 g10/import.c |  6 +++---
 g10/keydb.h  |  1 +
 3 files changed, 24 insertions(+), 6 deletions(-)

diff --git a/g10/getkey.c b/g10/getkey.c
index 4642174..4747176 100644
--- a/g10/getkey.c
+++ b/g10/getkey.c
@@ -1970,7 +1970,7 @@ get_pubkey_byfprint_fast (PKT_public_key * pk,
   gpg_error_t err;
   KBNODE keyblock;
 
-  err = get_keyblock_byfprint_fast (&keyblock, NULL, fprint, fprint_len, 0);
+  err = get_keyblock_byfprint_fast (&keyblock, NULL, 0, fprint, fprint_len, 0);
   if (!err)
     {
       if (pk)
@@ -1987,10 +1987,13 @@ get_pubkey_byfprint_fast (PKT_public_key * pk,
  * R_HD may be NULL.  If LOCK is set the handle has been opend in
  * locked mode and keydb_disable_caching () has been called.  On error
  * R_KEYBLOCK is set to NULL but R_HD must be released by the caller;
- * it may have a value of NULL, though.  This allows to do an insert
- * operation on a locked keydb handle.  */
+ * it may have a value of NULL, though.  This allows one to do an insert
+ * operation on a locked keydb handle.  If PRIMARY_ONLY is set
+ * the function returns a keyblock which has the requested fingerprint
+ * has primary key.*/
 gpg_error_t
 get_keyblock_byfprint_fast (kbnode_t *r_keyblock, KEYDB_HANDLE *r_hd,
+                            int primary_only,
                             const byte *fprint, size_t fprint_len, int lock)
 {
   gpg_error_t err;
@@ -1998,6 +2001,8 @@ get_keyblock_byfprint_fast (kbnode_t *r_keyblock, KEYDB_HANDLE *r_hd,
   kbnode_t keyblock;
   byte fprbuf[MAX_FINGERPRINT_LEN];
   int i;
+  byte tmpfpr[MAX_FINGERPRINT_LEN];
+  size_t tmpfprlen;
 
   if (r_keyblock)
     *r_keyblock = NULL;
@@ -2031,6 +2036,7 @@ get_keyblock_byfprint_fast (kbnode_t *r_keyblock, KEYDB_HANDLE *r_hd,
   if (r_hd)
     *r_hd = hd;
 
+ again:
   err = keydb_search_fpr (hd, fprbuf);
   if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
     {
@@ -2050,6 +2056,17 @@ get_keyblock_byfprint_fast (kbnode_t *r_keyblock, KEYDB_HANDLE *r_hd,
   log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY
               || keyblock->pkt->pkttype == PKT_PUBLIC_SUBKEY);
 
+  if (primary_only)
+    {
+      fingerprint_from_pk (keyblock->pkt->pkt.public_key, tmpfpr, &tmpfprlen);
+      if (fprint_len != tmpfprlen || memcmp (fprint, tmpfpr, fprint_len))
+        {
+          release_kbnode (keyblock);
+          keyblock = NULL;
+          goto again;
+        }
+    }
+
   /* Not caching key here since it won't have all of the fields
      properly set. */
 
diff --git a/g10/import.c b/g10/import.c
index 0ce702e..b2a1462 100644
--- a/g10/import.c
+++ b/g10/import.c
@@ -1859,7 +1859,6 @@ import_one_real (ctrl_t ctrl,
   int mod_key = 0;
   int same_key = 0;
   int non_self = 0;
-  size_t an;
   char pkstrbuf[PUBKEY_STRING_SIZE];
   int merge_keys_done = 0;
   KEYDB_HANDLE hd = NULL;
@@ -1879,8 +1878,8 @@ import_one_real (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;
+  if (MAX_FINGERPRINT_LEN > fpr2len)
+    memset (fpr2+fpr2len, 0, MAX_FINGERPRINT_LEN - fpr2len);
   keyid_from_pk( pk, keyid );
   uidnode = find_next_kbnode( keyblock, PKT_USER_ID );
 
@@ -2028,6 +2027,7 @@ import_one_real (ctrl_t ctrl,
 
   /* Do we have this key already in one of our pubrings ? */
   err = get_keyblock_byfprint_fast (&keyblock_orig, &hd,
+                                    1 /*primary only */,
                                     fpr2, fpr2len, 1/*locked*/);
   if ((err
        && gpg_err_code (err) != GPG_ERR_NO_PUBKEY
diff --git a/g10/keydb.h b/g10/keydb.h
index 0f8d711..7457479 100644
--- a/g10/keydb.h
+++ b/g10/keydb.h
@@ -403,6 +403,7 @@ gpg_error_t get_pubkey_byfprint_fast (PKT_public_key *pk,
    the user ids.  */
 gpg_error_t get_keyblock_byfprint_fast (kbnode_t *r_keyblock,
                                         KEYDB_HANDLE *r_hd,
+                                        int primary_only,
                                         const byte *fprint, size_t fprint_len,
                                         int lock);
 
-- 
2.33.0

