gpg: Print revocation reason for "rvs" records.
authorWerner Koch <wk@gnupg.org>
Thu, 21 Jun 2018 16:32:13 +0000 (18:32 +0200)
committerWerner Koch <wk@gnupg.org>
Thu, 21 Jun 2018 18:03:44 +0000 (20:03 +0200)
* g10/import.c (get_revocation_reason): New.
(list_standalone_revocation): Extend function.
--

Note that this function extends the "rvs" field signature-class (field
11) with the revocation reason.  GPGME does not yet parse this but it
can be expected that the comma delimiter does not break other parsers.

A new field is added to the "rvs" (and in future also the "rev")
record to carry a record specific comment.  Hopefully all parsers
meanwhile learned the lesson from other new fields and don't bail out
on more fields than they know about.

This is partial solution to
GnuPG-bug-id: 1173

Signed-off-by: Werner Koch <wk@gnupg.org>
doc/DETAILS
g10/import.c
g10/pkclist.c

index 1bfc04d..eb6d7dd 100644 (file)
@@ -180,6 +180,9 @@ described here.
     revocation key is also given here, 'x' and 'l' is used the same
     way.  This field if not used for X.509.
 
+    "rev" and "rvs" may be followed by a comma and a 2 digit hexnumber
+    with the revocation reason.
+
 *** Field 12 - Key capabilities
 
     The defined capabilities are:
@@ -262,6 +265,12 @@ described here.
     optionally followed by a space and an URL.  This goes along with
     the previous field.  The URL is quoted in C style.
 
+*** Field 21 - Comment
+
+    This is currently only used in "rev" and "rvs" records to carry
+    the the comment field of the recocation reason.  The value is
+    quoted in C style.
+
 ** Special fields
 
 *** PKD - Public key data
index 022f077..b20879c 100644 (file)
@@ -2633,6 +2633,69 @@ import_secret_one (ctrl_t ctrl, kbnode_t keyblock,
 
 
 
+/* Return the recocation reason from signature SIG.  If no revocation
+ * reason is availabale 0 is returned, in other cases the reason
+ * (0..255).  If R_REASON is not NULL a malloced textual
+ * representation of the code is stored there.  If R_COMMENT is not
+ * NULL the comment from the reason is stored there and its length at
+ * R_COMMENTLEN.  Note that the value at R_COMMENT is not filtered but
+ * user supplied data in UTF8; thus it needs to be escaped for display
+ * purposes.  Both return values are either NULL or a malloced
+ * string/buffer.  */
+int
+get_revocation_reason (PKT_signature *sig, char **r_reason,
+                       char **r_comment, size_t *r_commentlen)
+{
+  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;
+
+  if (r_reason)
+    *r_reason = NULL;
+  if (r_comment)
+    *r_comment = NULL;
+
+  /* Skip over empty reason packets.  */
+  while ((reason_p = enum_sig_subpkt (sig->hashed, SIGSUBPKT_REVOC_REASON,
+                                      &reason_n, &reason_seq, NULL))
+         && !reason_n)
+    ;
+  if (reason_p)
+    {
+      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;
+        }
+
+      if (r_reason)
+        *r_reason = xstrdup (reason_text);
+
+      if (r_comment && reason_n)
+        {
+          *r_comment = xmalloc (reason_n);
+          memcpy (*r_comment, reason_p, reason_n);
+          *r_commentlen = reason_n;
+        }
+    }
+
+  return reason_code;
+}
+
+
 /* List the recocation signature as a "rvs" record.  SIGRC shows the
  * character from the signature verification or 0 if no public key was
  * found.  */
@@ -2642,6 +2705,10 @@ list_standalone_revocation (ctrl_t ctrl, PKT_signature *sig, int sigrc)
   char *siguid = NULL;
   size_t siguidlen = 0;
   char *issuer_fpr = NULL;
+  int reason_code = 0;
+  char *reason_text = NULL;
+  char *reason_comment = NULL;
+  size_t reason_commentlen;
 
   if (sigrc != '%' && sigrc != '?' && !opt.fast_list_mode)
     {
@@ -2651,6 +2718,9 @@ list_standalone_revocation (ctrl_t ctrl, PKT_signature *sig, int sigrc)
         sigrc = '?';
     }
 
+  reason_code = get_revocation_reason (sig, &reason_text,
+                                       &reason_comment, &reason_commentlen);
+
   if (opt.with_colons)
     {
       es_fputs ("rvs:", es_stdout);
@@ -2665,13 +2735,25 @@ list_standalone_revocation (ctrl_t ctrl, PKT_signature *sig, int sigrc)
       if (siguid)
         es_write_sanitized (es_stdout, siguid, siguidlen, ":", NULL);
 
-      es_fprintf (es_stdout, ":%02x%c::", sig->sig_class,
+      es_fprintf (es_stdout, ":%02x%c", sig->sig_class,
                   sig->flags.exportable ? 'x' : 'l');
+      if (reason_text)
+        es_fprintf (es_stdout, ",%02x", reason_code);
+      es_fputs ("::", es_stdout);
 
       if ((issuer_fpr = issuer_fpr_string (sig)))
         es_fputs (issuer_fpr, es_stdout);
 
-      es_fprintf (es_stdout, ":::%d:\n", sig->digest_algo);
+      es_fprintf (es_stdout, ":::%d:", sig->digest_algo);
+
+      if (reason_comment)
+        {
+          es_fputs ("::::", es_stdout);
+          es_write_sanitized (es_stdout, reason_comment, reason_commentlen,
+                              ":", NULL);
+          es_putc (':', es_stdout);
+        }
+      es_putc ('\n', es_stdout);
 
       if (opt.show_subpackets)
         print_subpackets_colon (sig);
@@ -2712,10 +2794,43 @@ list_standalone_revocation (ctrl_t ctrl, PKT_signature *sig, int sigrc)
       if (sig->flags.pref_ks
           && (opt.list_options & LIST_SHOW_KEYSERVER_URLS))
         show_keyserver_url (sig, 3, 0);
+
+      if (reason_text)
+        {
+          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);
+            }
+        }
     }
 
   es_fflush (es_stdout);
 
+  xfree (reason_text);
+  xfree (reason_comment);
   xfree (siguid);
   xfree (issuer_fpr);
 }
index 6aec138..2322f78 100644 (file)
@@ -113,7 +113,7 @@ void
 show_revocation_reason (ctrl_t ctrl, PKT_public_key *pk, int mode)
 {
     /* Hmmm, this is not so easy because we have to duplicate the code
-     * used in the trustbd to calculate the keyflags.  We need to find
+     * used in the trustdb to calculate the keyflags.  We need to find
      * a clean way to check revocation certificates on keys and
      * signatures.  And there should be no duplicate code.  Because we
      * enter this function only when the trustdb told us that we have