diff options
author | Werner Koch <[email protected]> | 2020-08-25 08:33:57 +0000 |
---|---|---|
committer | Werner Koch <[email protected]> | 2020-08-25 08:33:57 +0000 |
commit | 633c1fea5f0dc4cb270c22ee41c24e1ec0706204 (patch) | |
tree | d40ad7f6f2c591a7eceb606243f7b5af14733555 /g10/import.c | |
parent | Add a new dist signing key (diff) | |
download | gnupg-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.c | 124 |
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. */ |