From a1f2f38dfb2ba5ed66d3aef66fc3be9b67f9b800 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Mon, 1 Jul 2019 21:53:55 +0200 Subject: [PATCH] gpg: Fallback to import with self-sigs-only on too large keyblocks. * g10/import.c (import_one): Rename to ... (import_one_real): this. Do not print and update stats on keyring write errors. (import_one): New. Add fallback code. -- GnuPG-bug-id: 4591 Signed-off-by: Werner Koch (cherry picked from commit 3a403ab04eeb45f12b34f9d9c421dac93eaf2160) --- g10/import.c | 122 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 101 insertions(+), 21 deletions(-) diff --git a/g10/import.c b/g10/import.c index e247d1d91..35596ad14 100644 --- a/g10/import.c +++ b/g10/import.c @@ -128,6 +128,7 @@ static int chk_self_sigs (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, static int delete_inv_parts (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, unsigned int options); static int any_uid_left (kbnode_t keyblock); +static void remove_all_non_self_sigs (kbnode_t *keyblock, u32 *keyid); static int merge_blocks (ctrl_t ctrl, unsigned int options, kbnode_t keyblock_orig, kbnode_t keyblock, u32 *keyid, @@ -1756,12 +1757,12 @@ update_key_origin (kbnode_t keyblock, u32 curtime, int origin, const char *url) * has valid parts. */ static gpg_error_t -import_one (ctrl_t ctrl, - kbnode_t keyblock, struct import_stats_s *stats, - unsigned char **fpr, size_t *fpr_len, unsigned int options, - int from_sk, int silent, - import_screener_t screener, void *screener_arg, - int origin, const char *url, int *r_valid) +import_one_real (ctrl_t ctrl, + kbnode_t keyblock, struct import_stats_s *stats, + unsigned char **fpr, size_t *fpr_len, unsigned int options, + int from_sk, int silent, + import_screener_t screener, void *screener_arg, + int origin, const char *url, int *r_valid) { gpg_error_t err = 0; PKT_public_key *pk; @@ -1842,6 +1843,13 @@ import_one (ctrl_t ctrl, return 0; } + /* Remove all non-self-sigs if requested. Noe that this is a NOP if + * that option has been globally set but we may also be called + * latter with the already parsed keyblock and a locally changed + * option. This is why we need to remove them here as well. */ + if ((options & IMPORT_SELF_SIGS_ONLY)) + remove_all_non_self_sigs (&keyblock, keyid); + collapse_uids(&keyblock); /* Clean the key that we're about to import, to cut down on things @@ -2044,22 +2052,25 @@ import_one (ctrl_t ctrl, hd = NULL; /* We are ready. */ - if (!opt.quiet && !silent) + if (!err && !opt.quiet && !silent) { char *p = get_user_id_byfpr_native (ctrl, fpr2); log_info (_("key %s: public key \"%s\" imported\n"), keystr(keyid), p); xfree(p); } - if (is_status_enabled()) + if (!err && is_status_enabled()) { char *us = get_long_user_id_string (ctrl, keyid); write_status_text( STATUS_IMPORTED, us ); xfree(us); print_import_ok (pk, 1); } - stats->imported++; - new_key = 1; + if (!err) + { + stats->imported++; + new_key = 1; + } } else /* Key already exists - merge. */ { @@ -2129,8 +2140,10 @@ import_one (ctrl_t ctrl, keydb_release (hd); hd = NULL; - /* We are ready. */ - if (!opt.quiet && !silent) + /* We are ready. Print and update stats if we got no error. + * An error here comes from writing the keyblock and thus + * very likely means that no update happened. */ + if (!err && !opt.quiet && !silent) { char *p = get_user_id_byfpr_native (ctrl, fpr2); if (n_uids == 1 ) @@ -2166,14 +2179,17 @@ import_one (ctrl_t ctrl, xfree(p); } - stats->n_uids +=n_uids; - stats->n_sigs +=n_sigs; - stats->n_subk +=n_subk; - stats->n_sigs_cleaned +=n_sigs_cleaned; - stats->n_uids_cleaned +=n_uids_cleaned; + if (!err) + { + stats->n_uids +=n_uids; + stats->n_sigs +=n_sigs; + stats->n_subk +=n_subk; + stats->n_sigs_cleaned +=n_sigs_cleaned; + stats->n_uids_cleaned +=n_uids_cleaned; - if (is_status_enabled () && !silent) - print_import_ok (pk, ((n_uids?2:0)|(n_sigs?4:0)|(n_subk?8:0))); + if (is_status_enabled () && !silent) + print_import_ok (pk, ((n_uids?2:0)|(n_sigs?4:0)|(n_subk?8:0))); + } } else { @@ -2260,6 +2276,39 @@ import_one (ctrl_t ctrl, } +/* Wrapper around import_one_real to retry the import in some cases. */ +static gpg_error_t +import_one (ctrl_t ctrl, + kbnode_t keyblock, struct import_stats_s *stats, + unsigned char **fpr, size_t *fpr_len, unsigned int options, + int from_sk, int silent, + import_screener_t screener, void *screener_arg, + int origin, const char *url, int *r_valid) +{ + gpg_error_t err; + + err = import_one_real (ctrl, keyblock, stats, fpr, fpr_len, options, + from_sk, silent, screener, screener_arg, + origin, url, r_valid); + if (gpg_err_code (err) == GPG_ERR_TOO_LARGE + && gpg_err_source (err) == GPG_ERR_SOURCE_KEYBOX) + { + /* We hit the maximum image length. Ask the wrapper to do + * everything again but this time with some extra options. */ + u32 keyid[2]; + + keyid_from_pk (keyblock->pkt->pkt.public_key, keyid); + log_info ("key %s: keyblock too large, retrying with self-sigs-only\n", + keystr (keyid)); + options |= IMPORT_SELF_SIGS_ONLY | IMPORT_CLEAN; + err = import_one_real (ctrl, keyblock, stats, fpr, fpr_len, options, + from_sk, silent, screener, screener_arg, + origin, url, r_valid); + } + return err; +} + + /* Transfer all the secret keys in SEC_KEYBLOCK to the gpg-agent. The * function prints diagnostics and returns an error code. If BATCH is * true the secret keys are stored by gpg-agent in the transfer format @@ -2937,7 +2986,7 @@ import_secret_one (ctrl_t ctrl, kbnode_t keyblock, /* The secret keyblock may not have nodes which are deleted in * the public keyblock. Otherwise we would import just the * secret key without having the public key. That would be - * surprising and clutters out private-keys-v1.d. */ + * surprising and clutters our private-keys-v1.d. */ err = resync_sec_with_pub_keyblock (&keyblock, pub_keyblock, &attic); if (err) goto leave; @@ -3750,8 +3799,39 @@ any_uid_left (kbnode_t keyblock) } +/* Delete all non-self-sigs from KEYBLOCK. + * Returns: True if the keyblock has changed. */ +static void +remove_all_non_self_sigs (kbnode_t *keyblock, u32 *keyid) +{ + kbnode_t node; + unsigned int dropped = 0; -/**************** + for (node = *keyblock; node; node = node->next) + { + if (is_deleted_kbnode (node)) + continue; + + if (node->pkt->pkttype != PKT_SIGNATURE) + continue; + + if (node->pkt->pkt.signature->keyid[0] == keyid[0] + && node->pkt->pkt.signature->keyid[1] == keyid[1]) + continue; + delete_kbnode (node); + dropped++; + } + + if (dropped) + commit_kbnode (keyblock); + + if (dropped && opt.verbose) + log_info ("key %s: number of dropped non-self-signatures: %u\n", + keystr (keyid), dropped); +} + + +/* * It may happen that the imported keyblock has duplicated user IDs. * We check this here and collapse those user IDs together with their * sigs into one.