From 3f825b044b2f1db8773f27a96034c925177fe9f0 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Fri, 20 Jun 2025 15:17:19 +0200 Subject: [PATCH] gpg: Show revocation reason with a standard -k listing. * g10/packet.h (struct revoke_info): Extend to carry the recocation reason. * g10/getkey.c (sig_to_revoke_info): Extend to strore the reason. (merge_selfsigs): Extend to also store the reason in the public key. * g10/keylist.c (list_signature_print): Factor some code out to ... (print_revocation_reason_comment): new function. (print_revocation_reason): New. (print_key_line): Call new function to print the reason. * g10/import.c (get_revocation_reason): Use print_revocation_reason_comment and factor some code out to ... (revocation_reason_code_to_str): new function. * g10/gpgv.c (revocation_reason_code_to_str): Add stub. * g10/test-stubs.c (revocation_reason_code_to_str): Ditto. -- With this change the revocation reason of a revoked key (but not for a revoked uid or subkey) is now displayed in "gpg -k" listing right below the primary key fingerprint. Before that "gpg --checks-sigs" was required to do show this info. GnuPG-bug-id: 7083 --- g10/free-packet.c | 21 ++++++++++++ g10/getkey.c | 49 ++++++++++++++++++++++++++-- g10/gpgv.c | 8 +++++ g10/import.c | 77 ++++++++++++++++++++------------------------ g10/keylist.c | 82 ++++++++++++++++++++++++++++++++--------------- g10/main.h | 2 ++ g10/packet.h | 14 ++++++-- g10/test-stubs.c | 9 ++++++ 8 files changed, 188 insertions(+), 74 deletions(-) diff --git a/g10/free-packet.c b/g10/free-packet.c index 0962af0fc..3a9e17665 100644 --- a/g10/free-packet.c +++ b/g10/free-packet.c @@ -161,6 +161,11 @@ release_public_key_parts (PKT_public_key *pk) xfree (pk->updateurl); pk->updateurl = NULL; } + if (pk->revoked.reason_comment) + { + xfree (pk->revoked.reason_comment); + pk->revoked.reason_comment = NULL; + } } @@ -231,6 +236,10 @@ copy_public_key_basics (PKT_public_key *d, PKT_public_key *s) d->seckey_info = NULL; d->user_id = NULL; d->prefs = NULL; + d->revoked.got_reason = 0; + d->revoked.reason_code = 0; + d->revoked.reason_comment = NULL; + d->revoked.reason_comment_len = 0; n = pubkey_get_npkey (s->pubkey_algo); i = 0; @@ -274,6 +283,18 @@ copy_public_key (PKT_public_key *d, PKT_public_key *s) d->serialno = xstrdup (s->serialno); if (s->updateurl) d->updateurl = xstrdup (s->updateurl); + if (s->revoked.got_reason) + { + d->revoked.got_reason = s->revoked.got_reason; + d->revoked.reason_code = s->revoked.reason_code; + if (s->revoked.reason_comment_len) + { + d->revoked.reason_comment = xmalloc (s->revoked.reason_comment_len); + memcpy (d->revoked.reason_comment, s->revoked.reason_comment, + s->revoked.reason_comment_len); + d->revoked.reason_comment_len = s->revoked.reason_comment_len; + } + } return d; } diff --git a/g10/getkey.c b/g10/getkey.c index e438859f4..6af6dc0a5 100644 --- a/g10/getkey.c +++ b/g10/getkey.c @@ -2738,13 +2738,42 @@ fixup_uidnode (KBNODE uidnode, KBNODE signode, u32 keycreated) uid->flags.ks_modify = 0; } + +/* Store the revocation signature into the RINFO struct. */ static void sig_to_revoke_info (PKT_signature * sig, struct revoke_info *rinfo) { + int reason_seq = 0; + size_t reason_n; + const byte *reason_p; + rinfo->date = sig->timestamp; rinfo->algo = sig->pubkey_algo; rinfo->keyid[0] = sig->keyid[0]; rinfo->keyid[1] = sig->keyid[1]; + xfree (rinfo->reason_comment); + rinfo->reason_comment = NULL; + rinfo->reason_comment_len = 0; + rinfo->reason_code = 0; + rinfo->got_reason = 0; + + while ((reason_p = enum_sig_subpkt (sig, 1, SIGSUBPKT_REVOC_REASON, + &reason_n, &reason_seq, NULL)) + && !reason_n) + ; /* Skip over empty reason packets. */ + + if (reason_p) + { + rinfo->got_reason = 1; + rinfo->reason_code = *reason_p; + reason_n--; reason_p++; + if (reason_n) + { + rinfo->reason_comment = xmalloc (reason_n); + memcpy (rinfo->reason_comment, reason_p, reason_n); + rinfo->reason_comment_len = reason_n; + } + } } @@ -3564,7 +3593,7 @@ merge_selfsigs (ctrl_t ctrl, kbnode_t keyblock) { KBNODE k; int revoked; - struct revoke_info rinfo; + struct revoke_info rinfo = { 0 }; PKT_public_key *main_pk; prefitem_t *prefs; unsigned int mdc_feature; @@ -3611,8 +3640,19 @@ merge_selfsigs (ctrl_t ctrl, kbnode_t keyblock) pk->flags.valid = 0; if (revoked && !pk->flags.revoked) { + /* Copy RINFO reason part only the first time + * because we don't want to propagate the reason to + * the subkeys. This assumes that we get the public + * key first. */ pk->flags.revoked = revoked; - memcpy (&pk->revoked, &rinfo, sizeof (rinfo)); + memcpy (&pk->revoked, &rinfo, sizeof (rinfo)); + if (rinfo.got_reason) + { + rinfo.got_reason = 0; + rinfo.reason_code = 0; + rinfo.reason_comment = NULL; /*(owner is pk->revoked)*/ + rinfo.reason_comment_len = 0; + } } if (main_pk->has_expired) { @@ -3622,7 +3662,7 @@ merge_selfsigs (ctrl_t ctrl, kbnode_t keyblock) } } } - return; + goto leave; } /* Set the preference list of all keys to those of the primary real @@ -3660,6 +3700,9 @@ merge_selfsigs (ctrl_t ctrl, kbnode_t keyblock) pk->flags.aead = aead_feature; } } + + leave: + xfree (rinfo.reason_comment); } diff --git a/g10/gpgv.c b/g10/gpgv.c index b97548d9b..6d3d25f50 100644 --- a/g10/gpgv.c +++ b/g10/gpgv.c @@ -831,6 +831,14 @@ tofu_notice_key_changed (ctrl_t ctrl, kbnode_t kb) } +const char * +revocation_reason_code_to_str (int code, char **freeme) +{ + (void)code; + *freeme = NULL; + return ""; +} + int get_revocation_reason (PKT_signature *sig, char **r_reason, char **r_comment, size_t *r_commentlen) diff --git a/g10/import.c b/g10/import.c index 48f0d5459..5dad290ca 100644 --- a/g10/import.c +++ b/g10/import.c @@ -3366,6 +3366,33 @@ import_secret_one (ctrl_t ctrl, kbnode_t keyblock, } +/* Return a string for the revocation reason CODE. R_FREEM must be an + * possibly unintialized ptr which should be freed by the caller after + * the return value has been consumed. */ +const char * +revocation_reason_code_to_str (int code, char **freeme) +{ + /* Take care: get_revocation_reason has knowledge of the internal + * working of this fucntion. */ + const char *result; + + *freeme = NULL; + switch (code) + { + case 0x00: result = _("No reason specified"); break; + case 0x01: result = _("Key is superseded"); break; + case 0x02: result = _("Key has been compromised"); break; + case 0x03: result = _("Key is no longer used"); break; + case 0x20: result = _("User ID is no longer valid"); break; + default: + *freeme = xasprintf ("code=%02x", code); + result = *freeme; + break; + } + + return result; +} + /* Return the recocation reason from signature SIG. If no revocation * reason is available 0 is returned, in other cases the reason @@ -3383,9 +3410,8 @@ get_revocation_reason (PKT_signature *sig, char **r_reason, int reason_seq = 0; size_t reason_n; const byte *reason_p; - char reason_code_buf[20]; - const char *reason_text = NULL; int reason_code = 0; + char *freeme; if (r_reason) *r_reason = NULL; @@ -3397,26 +3423,15 @@ get_revocation_reason (PKT_signature *sig, char **r_reason, &reason_n, &reason_seq, NULL)) && !reason_n) ; - if (reason_p) + if (reason_p && reason_n) { reason_code = *reason_p; reason_n--; reason_p++; - switch (reason_code) - { - case 0x00: reason_text = _("No reason specified"); break; - case 0x01: reason_text = _("Key is superseded"); break; - case 0x02: reason_text = _("Key has been compromised"); break; - case 0x03: reason_text = _("Key is no longer used"); break; - case 0x20: reason_text = _("User ID is no longer valid"); break; - default: - snprintf (reason_code_buf, sizeof reason_code_buf, - "code=%02x", reason_code); - reason_text = reason_code_buf; - break; - } - + revocation_reason_code_to_str (reason_code, &freeme); if (r_reason) - *r_reason = xstrdup (reason_text); + *r_reason = freeme; + else + xfree (freeme); if (r_comment && reason_n) { @@ -3533,31 +3548,7 @@ list_standalone_revocation (ctrl_t ctrl, PKT_signature *sig, int sigrc) { es_fprintf (es_stdout, " %s%s\n", _("reason for revocation: "), reason_text); - if (reason_comment) - { - const byte *s, *s_lf; - size_t n, n_lf; - - s = reason_comment; - n = reason_commentlen; - s_lf = NULL; - do - { - /* We don't want any empty lines, so we skip them. */ - for (;n && *s == '\n'; s++, n--) - ; - if (n) - { - s_lf = memchr (s, '\n', n); - n_lf = s_lf? s_lf - s : n; - es_fprintf (es_stdout, " %s", - _("revocation comment: ")); - es_write_sanitized (es_stdout, s, n_lf, NULL, NULL); - es_putc ('\n', es_stdout); - s += n_lf; n -= n_lf; - } - } while (s_lf); - } + print_revocation_reason_comment (reason_comment, reason_commentlen); } } diff --git a/g10/keylist.c b/g10/keylist.c index d1e9a90ff..8b679987b 100644 --- a/g10/keylist.c +++ b/g10/keylist.c @@ -1324,6 +1324,58 @@ parse_trust_name (const char *name, size_t namelen) } +void +print_revocation_reason_comment (const char *comment, size_t comment_len) +{ + const byte *s, *s_lf; + size_t n, n_lf; + + if (!comment || !comment_len) + return; + + s = comment; + n = comment_len; + s_lf = NULL; + do + { + /* We don't want any empty lines, so we skip them. */ + for (;n && *s == '\n'; s++, n--) + ; + if (n) + { + s_lf = memchr (s, '\n', n); + n_lf = s_lf? s_lf - s : n; + es_fprintf (es_stdout, " %s", + _("revocation comment: ")); + es_write_sanitized (es_stdout, s, n_lf, NULL, NULL); + es_putc ('\n', es_stdout); + s += n_lf; n -= n_lf; + } + } while (s_lf); +} + + +static void +print_revocation_reason (PKT_public_key *pk) +{ + char *freeme; + const char *codestr; + + if (!pk->revoked.got_reason) + return; + + if (!pk->revoked.reason_code && !pk->revoked.reason_comment) + return; /* Do not print "revocation reason: No reason specified". */ + + codestr = revocation_reason_code_to_str (pk->revoked.reason_code, &freeme); + es_fprintf (es_stdout, " %s%s\n", + _("reason for revocation: "), codestr); + xfree (freeme); + print_revocation_reason_comment (pk->revoked.reason_comment, + pk->revoked.reason_comment_len); +} + + /* Helper for list_keyblock_print. The caller must have set * NODFLG_MARK_B to indicate self-signatures. */ static void @@ -1502,31 +1554,7 @@ list_signature_print (ctrl_t ctrl, kbnode_t keyblock, kbnode_t node, { es_fprintf (es_stdout, " %s%s\n", _("reason for revocation: "), reason_text); - if (reason_comment) - { - const byte *s, *s_lf; - size_t n, n_lf; - - s = reason_comment; - n = reason_commentlen; - s_lf = NULL; - do - { - /* We don't want any empty lines, so we skip them. */ - for (;n && *s == '\n'; s++, n--) - ; - if (n) - { - s_lf = memchr (s, '\n', n); - n_lf = s_lf? s_lf - s : n; - es_fprintf (es_stdout, " %s", - _("revocation comment: ")); - es_write_sanitized (es_stdout, s, n_lf, NULL, NULL); - es_putc ('\n', es_stdout); - s += n_lf; n -= n_lf; - } - } while (s_lf); - } + print_revocation_reason_comment (reason_comment, reason_commentlen); } xfree (reason_text); @@ -2763,6 +2791,10 @@ print_key_line (ctrl_t ctrl, estream_t fp, PKT_public_key *pk, int secret) if (pk->flags.primary && !opt.fingerprint && !opt.with_fingerprint) print_fingerprint (ctrl, fp, pk, 20); + + /* Print the revocation reason. */ + if (pk->flags.revoked) + print_revocation_reason (pk); } diff --git a/g10/main.h b/g10/main.h index 546a0b5b8..c0a3d5fa2 100644 --- a/g10/main.h +++ b/g10/main.h @@ -407,6 +407,7 @@ gpg_error_t transfer_secret_keys (ctrl_t ctrl, struct import_stats_s *stats, int collapse_uids (kbnode_t *keyblock); int collapse_subkeys (kbnode_t *keyblock); +const char *revocation_reason_code_to_str (int code, char **r_freeme); int get_revocation_reason (PKT_signature *sig, char **r_reason, char **r_comment, size_t *r_commentlen); @@ -495,6 +496,7 @@ void print_key_info_log (ctrl_t ctrl, int loglevel, int indent, PKT_public_key *pk, int secret); void print_card_key_info (estream_t fp, KBNODE keyblock); void print_key_line (ctrl_t ctrl, estream_t fp, PKT_public_key *pk, int secret); +void print_revocation_reason_comment (const char *comment, size_t comment_len); /*-- verify.c --*/ void print_file_status( int status, const char *name, int what ); diff --git a/g10/packet.h b/g10/packet.h index 29e58d2df..ac6df7d5c 100644 --- a/g10/packet.h +++ b/g10/packet.h @@ -339,12 +339,20 @@ typedef struct struct revoke_info { - /* revoked at this date */ + /* Revoked at this date */ u32 date; - /* the keyid of the revoking key (selfsig or designated revoker) */ + /* The keyid of the revoking key (selfsig or designated revoker) */ u32 keyid[2]; - /* the algo of the revoking key */ + /* A malloced string of len reason_comment_len with the raw reason + * string or NULL if not given. */ + char *reason_comment; + size_t reason_comment_len; + /* The algo of the revoking key */ byte algo; + /* The reason code. */ + byte reason_code; + /* Whether the above reason fields are valid. */ + byte got_reason; }; diff --git a/g10/test-stubs.c b/g10/test-stubs.c index e9685b715..c6d6cdae5 100644 --- a/g10/test-stubs.c +++ b/g10/test-stubs.c @@ -571,6 +571,15 @@ tofu_notice_key_changed (ctrl_t ctrl, kbnode_t kb) return 0; } + +const char * +revocation_reason_code_to_str (int code, char **freeme) +{ + (void)code; + *freeme = NULL; + return ""; +} + int get_revocation_reason (PKT_signature *sig, char **r_reason, char **r_comment, size_t *r_commentlen)