From bd825ead36af8d47c96645ebeabac30621a4434b Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Fri, 28 Oct 2022 09:29:30 +0200 Subject: [PATCH] gpg: Import stray revocation certificates. * g10/kbnode.c (new_kbnode2): New. * g10/import.c (delete_inv_parts): New arg r_otherrevsigs to store misplaced revocations. (import_revoke_cert): Allow to pass an entire list. (import_one): Import revocations found by delete_inv_parts. -- It might be useful to distribute revocations of old keys along with new keys. This is in particicualrr useful for WKD stored keys. This patch allows to put unrelated standalone revocations into a key. For example they can simply appended to a keyblock. Right now it is a bit inaesthetic to see diagnostics about misplaced or bad revocation signatures. Backported-from-master: 7aaedfb10767c74f3e6868dd1563cbbf1282ab2f --- g10/import.c | 65 +++++++++++++++++++++++++++++++++++++++++----------- g10/kbnode.c | 13 +++++++++++ g10/keydb.h | 1 + 3 files changed, 66 insertions(+), 13 deletions(-) diff --git a/g10/import.c b/g10/import.c index b2d5c1d27..cd3363fc7 100644 --- a/g10/import.c +++ b/g10/import.c @@ -126,7 +126,8 @@ static int import_revoke_cert (ctrl_t ctrl, kbnode_t node, unsigned int options, static int chk_self_sigs (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, int *non_self); static int delete_inv_parts (ctrl_t ctrl, kbnode_t keyblock, - u32 *keyid, unsigned int options); + u32 *keyid, unsigned int options, + kbnode_t *r_otherrevsigs); 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, @@ -420,7 +421,7 @@ read_key_from_file_or_buffer (ctrl_t ctrl, const char *fname, goto leave; } - if (!delete_inv_parts (ctrl, keyblock, keyid, 0) ) + if (!delete_inv_parts (ctrl, keyblock, keyid, 0, NULL) ) { err = gpg_error (GPG_ERR_NO_USER_ID); goto leave; @@ -1830,8 +1831,9 @@ update_key_origin (kbnode_t keyblock, u32 curtime, int origin, const char *url) * even most error messages are suppressed. ORIGIN is the origin of * the key (0 for unknown) and URL the corresponding URL. FROM_SK * indicates that the key has been made from a secret key. If R_SAVED - * is not NULL a boolean will be stored indicating whether the keyblock - * has valid parts. + * is not NULL a boolean will be stored indicating whether the + * keyblock has valid parts. Unless OTHERREVSIGS is NULL it is + * updated with encountered new revocation signatures. */ static gpg_error_t import_one_real (ctrl_t ctrl, @@ -1839,7 +1841,8 @@ import_one_real (ctrl_t ctrl, 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) + int origin, const char *url, int *r_valid, + kbnode_t *otherrevsigs) { gpg_error_t err = 0; PKT_public_key *pk; @@ -1974,7 +1977,8 @@ import_one_real (ctrl_t ctrl, } } - if (!delete_inv_parts (ctrl, keyblock, keyid, options ) ) + /* Delete invalid parts and bail out if there are no user ids left. */ + if (!delete_inv_parts (ctrl, keyblock, keyid, options, otherrevsigs)) { if (!silent) { @@ -2366,10 +2370,12 @@ import_one (ctrl_t ctrl, int origin, const char *url, int *r_valid) { gpg_error_t err; + kbnode_t otherrevsigs = NULL; + kbnode_t node; err = import_one_real (ctrl, keyblock, stats, fpr, fpr_len, options, from_sk, silent, screener, screener_arg, - origin, url, r_valid); + origin, url, r_valid, &otherrevsigs); if (gpg_err_code (err) == GPG_ERR_TOO_LARGE && gpg_err_source (err) == GPG_ERR_SOURCE_KEYBOX && ((options & (IMPORT_SELF_SIGS_ONLY | IMPORT_CLEAN)) @@ -2385,8 +2391,17 @@ import_one (ctrl_t ctrl, 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); + origin, url, r_valid, &otherrevsigs); } + + /* Finally try to import other revocation certificates. For example + * those of a former key appended to the current key. */ + if (!err) + { + for (node = otherrevsigs; node; node = node->next) + import_revoke_cert (ctrl, node, options, stats); + } + release_kbnode (otherrevsigs); return err; } @@ -3358,9 +3373,8 @@ list_standalone_revocation (ctrl_t ctrl, PKT_signature *sig, int sigrc) } -/**************** - * Import a revocation certificate; this is a single signature packet. - */ +/* Import a revocation certificate; only the first packet in the + * NODE-list is considered. */ static int import_revoke_cert (ctrl_t ctrl, kbnode_t node, unsigned int options, struct import_stats_s *stats) @@ -3377,10 +3391,12 @@ import_revoke_cert (ctrl_t ctrl, kbnode_t node, unsigned int options, /* No error output for --show-keys. */ silent = (options & (IMPORT_SHOW | IMPORT_DRY_RUN)); - log_assert (!node->next ); log_assert (node->pkt->pkttype == PKT_SIGNATURE ); log_assert (IS_KEY_REV (node->pkt->pkt.signature)); + /* FIXME: We can do better here by using the issuer fingerprint if + * available. We should also make use of get_keyblock_byfprint_fast. */ + keyid[0] = node->pkt->pkt.signature->keyid[0]; keyid[1] = node->pkt->pkt.signature->keyid[1]; @@ -3726,12 +3742,15 @@ chk_self_sigs (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, int *non_self) /* Delete all parts which are invalid and those signatures whose * public key algorithm is not available in this implementation; but * consider RSA as valid, because parse/build_packets knows about it. + * If R_OTHERREVSIGS is not NULL, it is used to return a list of + * revocation certificates which have been deleted from KEYBLOCK but + * should be handled later. * * Returns: True if at least one valid user-id is left over. */ static int delete_inv_parts (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, - unsigned int options) + unsigned int options, kbnode_t *r_otherrevsigs) { kbnode_t node; int nvalid=0, uid_seen=0, subkey_seen=0; @@ -3820,6 +3839,16 @@ delete_inv_parts (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, if(opt.verbose) log_info( _("key %s: revocation certificate" " at wrong place - skipped\n"),keystr(keyid)); + if (r_otherrevsigs) + { + PACKET *pkt; + + pkt = xcalloc (1, sizeof *pkt); + pkt->pkttype = PKT_SIGNATURE; + pkt->pkt.signature = copy_signature + (NULL, node->pkt->pkt.signature); + *r_otherrevsigs = new_kbnode2 (*r_otherrevsigs, pkt); + } delete_kbnode( node ); } else @@ -3842,6 +3871,16 @@ delete_inv_parts (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, delete_kbnode( node ); } } + else if (r_otherrevsigs) + { + PACKET *pkt; + + pkt = xcalloc (1, sizeof *pkt); + pkt->pkttype = PKT_SIGNATURE; + pkt->pkt.signature = copy_signature + (NULL, node->pkt->pkt.signature); + *r_otherrevsigs = new_kbnode2 (*r_otherrevsigs, pkt); + } } } else if (node->pkt->pkttype == PKT_SIGNATURE diff --git a/g10/kbnode.c b/g10/kbnode.c index 9ed6cafbf..aa1e17c94 100644 --- a/g10/kbnode.c +++ b/g10/kbnode.c @@ -98,6 +98,19 @@ new_kbnode( PACKET *pkt ) } +/* Same as new_kbnode but insert the new node in front of LIST. Returns + * the new list. */ +kbnode_t +new_kbnode2 (kbnode_t list, PACKET *pkt) +{ + kbnode_t n; + + n = new_kbnode (pkt); + n->next = list; + return n; +} + + KBNODE clone_kbnode( KBNODE node ) { diff --git a/g10/keydb.h b/g10/keydb.h index 851d29317..4be889e07 100644 --- a/g10/keydb.h +++ b/g10/keydb.h @@ -552,6 +552,7 @@ gpg_error_t hexkeygrip_from_pk (PKT_public_key *pk, char **r_grip); /*-- kbnode.c --*/ KBNODE new_kbnode( PACKET *pkt ); +kbnode_t new_kbnode2 (kbnode_t list, PACKET *pkt); KBNODE clone_kbnode( KBNODE node ); void release_kbnode( KBNODE n ); void delete_kbnode( KBNODE node );