aboutsummaryrefslogtreecommitdiffstats
path: root/g10/import.c
diff options
context:
space:
mode:
authorWerner Koch <[email protected]>2020-08-25 08:33:57 +0000
committerWerner Koch <[email protected]>2020-08-25 08:33:57 +0000
commit633c1fea5f0dc4cb270c22ee41c24e1ec0706204 (patch)
treed40ad7f6f2c591a7eceb606243f7b5af14733555 /g10/import.c
parentAdd a new dist signing key (diff)
downloadgnupg-633c1fea5f0dc4cb270c22ee41c24e1ec0706204.tar.gz
gnupg-633c1fea5f0dc4cb270c22ee41c24e1ec0706204.zip
gpg: Collapse duplicate subkeys.
* g10/options.h (IMPORT_COLLAPSE_UIDS): New. (IMPORT_COLLAPSE_SUBKEYS): New. * g10/gpg.c (main): Make them the default. * g10/import.c (parse_import_options): New import options "no-collapse-uids" and "no-collapse_subkeys". (collapse_subkeys): New. (import_one_real): Collapse subkeys and allow disabling the collapsing using the new options. (read_key_from_file_or_buffer): Always collapse subkeys. * g10/keyedit.c (fix_keyblock): Call collapse_subkeys. -- GnuPG-bug-id: 4421 Signed-off-by: Werner Koch <[email protected]>
Diffstat (limited to '')
-rw-r--r--g10/import.c124
1 files changed, 122 insertions, 2 deletions
diff --git a/g10/import.c b/g10/import.c
index 1e9532d38..23c162cb7 100644
--- a/g10/import.c
+++ b/g10/import.c
@@ -210,6 +210,11 @@ parse_import_options(char *str,unsigned int *options,int noisy)
{"show-only", (IMPORT_SHOW | IMPORT_DRY_RUN), NULL,
NULL},
+ /* Hidden options which are enabled by default and are provided
+ * in case of problems with the respective implementation. */
+ {"collapse-uids", IMPORT_COLLAPSE_UIDS, NULL, NULL},
+ {"collapse-subkeys", IMPORT_COLLAPSE_SUBKEYS, NULL, NULL},
+
/* Aliases for backward compatibility */
{"allow-local-sigs",IMPORT_LOCAL_SIGS,NULL,NULL},
{"repair-hkp-subkey-bug",IMPORT_REPAIR_PKS_SUBKEY_BUG,NULL,NULL},
@@ -403,7 +408,10 @@ read_key_from_file_or_buffer (ctrl_t ctrl, const char *fname,
goto leave;
}
+ /* We do the collapsing unconditionally although it is expected that
+ * clean keys are provided here. */
collapse_uids (&keyblock);
+ collapse_subkeys (&keyblock);
clear_kbnode_flags (keyblock);
if (chk_self_sigs (ctrl, keyblock, keyid, &non_self))
@@ -1947,9 +1955,12 @@ import_one_real (ctrl_t ctrl,
/* Remove or collapse the user ids. */
if ((options & IMPORT_DROP_UIDS))
remove_all_uids (&keyblock);
- else
+ else if ((options & IMPORT_COLLAPSE_UIDS))
collapse_uids (&keyblock);
+ if ((options & IMPORT_COLLAPSE_SUBKEYS))
+ collapse_subkeys (&keyblock);
+
/* Clean the key that we're about to import, to cut down on things
that we have to clean later. This has no practical impact on the
end result, but does result in less logging which might confuse
@@ -3010,7 +3021,7 @@ import_secret_one (ctrl_t ctrl, kbnode_t keyblock,
_("rejected by import screener"));
release_kbnode (keyblock);
return 0;
- }
+ }
if (opt.verbose && !for_migration)
{
@@ -4100,6 +4111,115 @@ collapse_uids (kbnode_t *keyblock)
}
+/*
+ * It may happen that the imported keyblock has duplicated subkeys.
+ * We check this here and collapse those subkeys along with their
+ * binding self-signatures.
+ * Returns: True if the keyblock has changed.
+ */
+int
+collapse_subkeys (kbnode_t *keyblock)
+{
+ kbnode_t kb1, kb2, sig1, sig2, last;
+ int any = 0;
+
+ log_debug ("enter collapse_subkeys\n");
+ for (kb1 = *keyblock; kb1; kb1 = kb1->next)
+ {
+ if (is_deleted_kbnode (kb1))
+ continue;
+
+ if (kb1->pkt->pkttype != PKT_PUBLIC_SUBKEY
+ && kb1->pkt->pkttype != PKT_SECRET_SUBKEY)
+ continue;
+
+ /* We assume just a few duplicates and use a straightforward
+ * algorithm. */
+ for (kb2 = kb1->next; kb2; kb2 = kb2->next)
+ {
+ if (is_deleted_kbnode (kb2))
+ continue;
+
+ if (kb2->pkt->pkttype != PKT_PUBLIC_SUBKEY
+ && kb2->pkt->pkttype != PKT_SECRET_SUBKEY)
+ continue;
+
+ if (cmp_public_keys (kb1->pkt->pkt.public_key,
+ kb2->pkt->pkt.public_key))
+ continue;
+
+ /* We have a duplicated subkey. */
+ any = 1;
+
+ /* Take subkey-2's signatures, and attach them to subkey-1. */
+ for (last = kb2; last->next; last = last->next)
+ {
+ if (is_deleted_kbnode (last))
+ continue;
+
+ if (last->next->pkt->pkttype != PKT_SIGNATURE)
+ break;
+ }
+
+ /* Snip out subkye-2 */
+ find_prev_kbnode (*keyblock, kb2, 0)->next = last->next;
+
+ /* Put subkey-2 in place as part of subkey-1 */
+ last->next = kb1->next;
+ kb1->next = kb2;
+ delete_kbnode (kb2);
+
+ /* Now dedupe kb1 */
+ for (sig1 = kb1->next; sig1; sig1 = sig1->next)
+ {
+ if (is_deleted_kbnode (sig1))
+ continue;
+
+ if (sig1->pkt->pkttype != PKT_SIGNATURE)
+ break;
+
+ for (sig2 = sig1->next, last = sig1;
+ sig2;
+ last = sig2, sig2 = sig2->next)
+ {
+ if (is_deleted_kbnode (sig2))
+ continue;
+
+ if (sig2->pkt->pkttype != PKT_SIGNATURE)
+ break;
+
+ if (!cmp_signatures (sig1->pkt->pkt.signature,
+ sig2->pkt->pkt.signature))
+ {
+ /* We have a match, so delete the second
+ signature */
+ delete_kbnode (sig2);
+ sig2 = last;
+ }
+ }
+ }
+ }
+ }
+
+ commit_kbnode (keyblock);
+
+ log_debug ("leave collapse_subkeys (any=%d)\n", any);
+ if (any && !opt.quiet)
+ {
+ const char *key="???";
+
+ if ((kb1 = find_kbnode (*keyblock, PKT_PUBLIC_KEY)) )
+ key = keystr_from_pk (kb1->pkt->pkt.public_key);
+ else if ((kb1 = find_kbnode (*keyblock, PKT_SECRET_KEY)) )
+ key = keystr_from_pk (kb1->pkt->pkt.public_key);
+
+ log_info (_("key %s: duplicated subkeys detected - merged\n"), key);
+ }
+
+ return any;
+}
+
+
/* Check for a 0x20 revocation from a revocation key that is not
present. This may be called without the benefit of merge_xxxx so
you can't rely on pk->revkey and friends. */