More code for the audit log.
authorWerner Koch <wk@gnupg.org>
Thu, 6 Dec 2007 15:55:03 +0000 (15:55 +0000)
committerWerner Koch <wk@gnupg.org>
Thu, 6 Dec 2007 15:55:03 +0000 (15:55 +0000)
NEWS
common/audit.c
common/audit.h
sm/gpgsm.c
sm/server.c
sm/verify.c

diff --git a/NEWS b/NEWS
index 2a5b0ac..87ea82f 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -2,8 +2,8 @@ Noteworthy changes in version 2.0.8
 ------------------------------------------------
 
  * Make sure that under Windows the file permissions of the socket are
-   taken into account.  This required a change of our the socket
-   emulation code; thus old GnuPG modules can't be used anymore.
+   taken into account.  This required a change of our socket emulation
+   code; thus old GnuPG modules can't be used anymore.
 
  * Fixed a crash in gpgconf.
 
index d820523..bf502a8 100644 (file)
 
 #include <config.h>
 #include <stdlib.h>
-
+#include <string.h>
+#include <stdarg.h>
+#include <assert.h>
 
 #include "util.h"
+#include "i18n.h"
 #include "audit.h"
 #include "audit-events.h"
 
@@ -50,11 +53,21 @@ struct audit_ctx_s
   size_t logsize;       /* The allocated size for LOG.  */
   size_t logused;       /* The used size of LOG.  */
 
+  estream_t outstream;  /* The current output stream.  */
+  int use_html;         /* The output shall be HTML formatted.  */
+  int indentlevel;      /* Current level of indentation.  */
 };
 
 
 
+\f
+static void writeout_li (audit_ctx_t ctx, const char *oktext,
+                         const char *format, ...) JNLIB_GCC_A_PRINTF(3,4);
+static void writeout_rem (audit_ctx_t ctx, 
+                          const char *format, ...) JNLIB_GCC_A_PRINTF(2,3);
+
 
+\f
 static const char *
 event2str (audit_event_t event)
 {
@@ -294,22 +307,486 @@ audit_log_cert (audit_ctx_t ctx, audit_event_t event,
 }
 
 
+/* Write TEXT to the outstream.  */
+static void 
+writeout (audit_ctx_t ctx, const char *text)
+{
+  if (ctx->use_html)
+    {
+      for (; *text; text++)
+        {
+          if (*text == '<')
+            es_fputs ("&lt;", ctx->outstream);
+          else if (*text == '&')
+            es_fputs ("&amp;", ctx->outstream);
+          else
+            es_putc (*text, ctx->outstream);
+        }
+    }
+  else
+    es_fputs (text, ctx->outstream);
+}
+
+
+/* Write TEXT to the outstream using a variable argument list.  */
+static void 
+writeout_v (audit_ctx_t ctx, const char *format, va_list arg_ptr)
+{
+  char *buf;
+
+  estream_vasprintf (&buf, format, arg_ptr);
+  if (buf)
+    {
+      writeout (ctx, buf);
+      xfree (buf);
+    }
+  else
+    writeout (ctx, "[!!Out of core!!]");
+}
+
+
+/* Write TEXT as a paragraph.  */
+static void
+writeout_para (audit_ctx_t ctx, const char *text)
+{
+  if (ctx->use_html)
+    es_fputs ("<p>", ctx->outstream);
+  writeout (ctx, text);
+  if (ctx->use_html)
+    es_fputs ("</p>\n", ctx->outstream);
+  else
+    es_fputc ('\n', ctx->outstream);
+}
+
+
+static void
+enter_li (audit_ctx_t ctx)
+{
+  if (ctx->use_html)
+    {
+      if (!ctx->indentlevel)
+        {
+          es_fputs ("<table border=\"0\">\n"
+                    "  <colgroup>\n"
+                    "    <col width=\"80%\" />\n"
+                    "    <col width=\"20%\" />\n"
+                    "   </colgroup>\n",
+                    ctx->outstream);
+        }
+    }
+  ctx->indentlevel++;
+}
+
+
+static void
+leave_li (audit_ctx_t ctx)
+{
+  ctx->indentlevel--;
+  if (ctx->use_html)
+    {
+      if (!ctx->indentlevel)
+        es_fputs ("</table>\n", ctx->outstream);
+    }
+}
+
+  
+/* Write TEXT as a list element.  If OKTEXT is not NULL, append it to
+   the last line. */
+static void
+writeout_li (audit_ctx_t ctx, const char *oktext, const char *format, ...)
+{
+  va_list arg_ptr;
+  const char *color = NULL;
+
+  if (ctx->use_html && format && oktext)
+    {
+      if (!strcmp (oktext, "OK") || !strcmp (oktext, "Yes"))
+        color = "green";
+      else if (!strcmp (oktext, "FAIL") || !strcmp (oktext, "No"))
+        color = "red";
+    }
+
+  if (ctx->use_html)
+    {
+      int i;
+
+      es_fputs ("  <tr><td><table><tr><td>", ctx->outstream);
+      if (color)
+        es_fprintf (ctx->outstream, "<font color=\"%s\">*</font>", color);
+      else
+        es_fputs ("*", ctx->outstream);
+      for (i=1; i < ctx->indentlevel; i++)
+        es_fputs ("&nbsp;&nbsp;", ctx->outstream);
+      es_fputs ("</td><td>", ctx->outstream);
+    }
+  else
+    es_fprintf (ctx->outstream, "* %*s", (ctx->indentlevel-1)*2, "");
+  if (format)
+    {
+      va_start (arg_ptr, format) ;
+      writeout_v (ctx, format, arg_ptr);
+      va_end (arg_ptr);
+    }
+  if (ctx->use_html)
+    es_fputs ("</td></tr></table>", ctx->outstream);
+  if (format && oktext)
+    {
+      if (ctx->use_html)
+        {
+          es_fputs ("</td><td>", ctx->outstream);
+          if (color)
+            es_fprintf (ctx->outstream, "<font color=\"%s\">", color);
+        }
+      else  
+        writeout (ctx, ":         ");
+      writeout (ctx, oktext);
+      if (color)
+        es_fputs ("</font>", ctx->outstream);
+    }
+  
+  if (ctx->use_html)
+    es_fputs ("</td></tr>\n", ctx->outstream);
+  else
+    es_fputc ('\n', ctx->outstream);
+}
+
+
+/* Write a remark line.  */
+static void
+writeout_rem (audit_ctx_t ctx, const char *format, ...)
+{
+  va_list arg_ptr;
+
+  if (ctx->use_html)
+    {
+      int i;
+
+      es_fputs ("  <tr><td><table><tr><td>*", ctx->outstream);
+      for (i=1; i < ctx->indentlevel; i++)
+        es_fputs ("&nbsp;&nbsp;", ctx->outstream);
+      es_fputs ("&nbsp;&nbsp;&nbsp;</td><td> (", ctx->outstream);
+
+    }
+  else
+    es_fprintf (ctx->outstream, "* %*s  (", (ctx->indentlevel-1)*2, "");
+  if (format)
+    {
+      va_start (arg_ptr, format) ;
+      writeout_v (ctx, format, arg_ptr);
+      va_end (arg_ptr);
+    }
+  if (ctx->use_html)
+    es_fputs (")</td></tr></table></td></tr>\n", ctx->outstream);
+  else
+    es_fputs (")\n", ctx->outstream);
+}
+
+
+/* Return the first log item for EVENT.  If STOPEVENT is not 0 never
+   look behind that event in the log. If STARTITEM is not NULL start
+   search _after_that item.  */
+static log_item_t
+find_next_log_item (audit_ctx_t ctx, log_item_t startitem, 
+                    audit_event_t event, audit_event_t stopevent)
+{
+  int idx;
+
+  for (idx=0; idx < ctx->logused; idx++)
+    {
+      if (startitem)
+        {
+          if (ctx->log + idx == startitem)
+            startitem = NULL;
+        }
+      else if (stopevent && ctx->log[idx].event == stopevent)
+        break;
+      else if (ctx->log[idx].event == event)
+        return ctx->log + idx;
+    }
+  return NULL;
+}
+
+
+static log_item_t
+find_log_item (audit_ctx_t ctx, audit_event_t event, audit_event_t stopevent)
+{
+  return find_next_log_item (ctx, NULL, event, stopevent);
+}
+
+
+/* Helper to a format a serial number.  */
+static char *
+format_serial (ksba_const_sexp_t sn)
+{
+  const char *p = (const char *)sn;
+  unsigned long n;
+  char *endp;
+
+  if (!p)
+    return NULL;
+  if (*p != '(')
+    BUG (); /* Not a valid S-expression. */
+  n = strtoul (p+1, &endp, 10);
+  p = endp;
+  if (*p != ':')
+    BUG (); /* Not a valid S-expression. */
+  return bin2hex (p+1, n, NULL);
+}
+
+
+/* Return a malloced string with the serial number and the issuer DN
+   of the certificate.  */
+static char *
+get_cert_name (ksba_cert_t cert)
+{
+  char *result;
+  ksba_sexp_t sn;
+  char *issuer, *p;
+
+  if (!cert)
+    return xtrystrdup ("[no certificate]");
+
+  issuer = ksba_cert_get_issuer (cert, 0);
+  sn = ksba_cert_get_serial (cert);
+  if (issuer && sn)
+    {
+      p = format_serial (sn);
+      if (!p)
+        result = xtrystrdup ("[invalid S/N]");
+      else
+        {
+          result = xtrymalloc (strlen (p) + strlen (issuer) + 2 + 1);
+          if (result)
+            {
+              *result = '#';
+              strcpy (stpcpy (stpcpy (result+1, p),"/"), issuer);
+            }
+          xfree (p);
+        }
+    }
+  else
+    result = xtrystrdup ("[missing S/N or issuer]");
+  ksba_free (sn);
+  xfree (issuer);
+  return result;
+}
+
+/* Return a malloced string with the serial number and the issuer DN
+   of the certificate.  */
+static char *
+get_cert_subject (ksba_cert_t cert, int idx)
+{
+  char *result;
+  char *subject;
+
+  if (!cert)
+    return xtrystrdup ("[no certificate]");
+
+  subject = ksba_cert_get_subject (cert, idx);
+  if (subject)
+    {
+      result = xtrymalloc (strlen (subject) + 1 + 1);
+      if (result)
+        {
+          *result = '/';
+          strcpy (result+1, subject);
+        }
+    }
+  else
+    result = NULL;
+  xfree (subject);
+  return result;
+}
+
+
+/* List the chain of certificates from STARTITEM up to STOPEVENT.  The
+   certifcates are written out as comments.  */
+static void
+list_certchain (audit_ctx_t ctx, log_item_t startitem, audit_event_t stopevent)
+{
+  log_item_t item;
+  char *name;
+  int idx;
+
+  startitem = find_next_log_item (ctx, startitem, AUDIT_CHAIN_BEGIN,stopevent);
+  if (!startitem)
+    {
+      writeout_li (ctx, gpg_strerror (GPG_ERR_MISSING_CERT)
+                   , _("Certificate chain"));
+      return; 
+    }
+  writeout_li (ctx, "OK", _("Certificate chain"));
+  item = find_next_log_item (ctx, startitem, 
+                             AUDIT_CHAIN_ROOTCERT, AUDIT_CHAIN_END);
+  if (!item)
+    writeout_rem (ctx, "%s", _("root certificate missing"));
+  else
+    {
+      name = get_cert_name (item->cert);
+      writeout_rem (ctx, "%s", name);
+      xfree (name);
+    }
+  item = startitem;
+  while ( ((item = find_next_log_item (ctx, item, 
+                                       AUDIT_CHAIN_CERT, AUDIT_CHAIN_END))))
+    {
+      name = get_cert_name (item->cert);
+      writeout_rem (ctx, "%s", name);
+      xfree (name);
+      enter_li (ctx);
+      for (idx=0; (name = get_cert_subject (item->cert, idx)); idx++)
+        {
+          writeout_rem (ctx, "%s", name);
+          xfree (name);
+        }
+      leave_li (ctx);
+    }
+}
+
+
+\f
+/* Process a verification operation.  */
+static void
+proc_type_verify (audit_ctx_t ctx)
+{
+  log_item_t loopitem, item;
+  int signo, count, idx;
+  char numbuf[35];
+
+  enter_li (ctx);
+  
+  writeout_li (ctx, "fixme", "%s", _("Signature verification"));
+  enter_li (ctx);
 
+  writeout_li (ctx, "fixme", "%s", _("Gpg-Agent ready"));
+  writeout_li (ctx, "fixme", "%s", _("Dirmngr ready"));
+
+  item = find_log_item (ctx, AUDIT_GOT_DATA, AUDIT_NEW_SIG);
+  writeout_li (ctx, item? "Yes":"No", "%s", _("Data available"));
+  if (!item)
+    goto leave;
+
+  item = find_log_item (ctx, AUDIT_NEW_SIG, 0);
+  writeout_li (ctx, item? "Yes":"No", "%s", _("Signature available"));
+  if (!item)
+    goto leave;
+
+  item = find_log_item (ctx, AUDIT_DATA_HASH_ALGO, AUDIT_NEW_SIG);
+  if (item)
+    writeout_li (ctx, "OK", "%s", _("Parsing signature"));
+  else 
+    {
+      item = find_log_item (ctx, AUDIT_BAD_DATA_HASH_ALGO, AUDIT_NEW_SIG);
+      if (item)
+        {
+          writeout_li (ctx,"FAIL", "%s",  _("Parsing signature"));
+          writeout_rem (ctx, _("Bad hash algorithm: %s"), 
+                        item->string? item->string:"?");
+        }
+      else
+        writeout_li (ctx, "FAIL", "%s", _("Parsing signature") );
+      goto leave;
+    }
+
+  /* Loop over all signatures.  */
+  loopitem = find_log_item (ctx, AUDIT_NEW_SIG, 0);
+  assert (loopitem);
+  do
+    {
+      signo = loopitem->have_intvalue? loopitem->intvalue : -1;
+
+      item = find_next_log_item (ctx, loopitem,
+                                 AUDIT_SIG_STATUS, AUDIT_NEW_SIG);
+      writeout_li (ctx, item? item->string:"?", _("Signature %d"), signo);
+      item = find_next_log_item (ctx, loopitem,
+                                 AUDIT_SIG_NAME, AUDIT_NEW_SIG);
+      if (item)
+        writeout_rem (ctx, "%s", item->string);
+      enter_li (ctx);
+      
+      /* List the certificate chain.  */
+      list_certchain (ctx, loopitem, AUDIT_NEW_SIG);
+
+      /* Show the result of the chain validation.  */
+      item = find_next_log_item (ctx, loopitem,
+                                 AUDIT_CHAIN_STATUS, AUDIT_NEW_SIG);
+      if (item && item->have_err)
+        {
+          writeout_li (ctx, item->err? "FAIL":"OK", 
+                       _("Validation of certificate chain"));
+          if (item->err)
+            writeout_rem (ctx, "%s", gpg_strerror (item->err));
+        }
+      
+      /* Show whether the root certificate is fine.  */
+      writeout_li (ctx, "No", "%s", _("Root certificate trustworthy"));
+
+      /* Show result of the CRL/OCSP check.  */
+      writeout_li (ctx, "-", "%s", _("CRL/OCSP check of certificates"));
+
+
+      leave_li (ctx);
+    }
+  while ((loopitem = find_next_log_item (ctx, loopitem, AUDIT_NEW_SIG, 0)));
+
+
+ leave:
+  /* Always list the certificates stored in the signature.  */
+  item = NULL;
+  count = 0;
+  while ( ((item = find_next_log_item (ctx, item, 
+                                       AUDIT_SAVE_CERT, AUDIT_NEW_SIG))))
+    count++;
+  snprintf (numbuf, sizeof numbuf, "%d", count);
+  writeout_li (ctx, numbuf, _("Included certificates"));
+  item = NULL;
+  while ( ((item = find_next_log_item (ctx, item, 
+                                       AUDIT_SAVE_CERT, AUDIT_NEW_SIG))))
+    {
+      char *name = get_cert_name (item->cert);
+      writeout_rem (ctx, "%s", name);
+      xfree (name);
+      enter_li (ctx);
+      for (idx=0; (name = get_cert_subject (item->cert, idx)); idx++)
+        {
+          writeout_rem (ctx, "%s", name);
+          xfree (name);
+        }
+      leave_li (ctx);
+    }
+
+  leave_li (ctx);
+  leave_li (ctx);
+}
+
+
+
+\f
 /* Print the formatted audit result.    THIS IS WORK IN PROGRESS.  */
 void
-audit_print_result (audit_ctx_t ctx, estream_t out)
+audit_print_result (audit_ctx_t ctx, estream_t out, int use_html)
 {
   int idx;
   int maxlen;
   size_t n;
 
-  es_fputs ("<div class=\"GnuPGAuditLog\">\n", out);
+  if (getenv ("use_html"))
+    use_html = 1;
 
   if (!ctx)
-    goto leave;
+    return;
+
+  assert (!ctx->outstream);
+  ctx->outstream = out;
+  ctx->use_html = use_html;
+  ctx->indentlevel = 0;
+
+  if (use_html)
+    es_fputs ("<div class=\"GnuPGAuditLog\">\n", ctx->outstream);
+
   if (!ctx->log || !ctx->logused)
     {
-      es_fprintf (out, "<p>AUDIT-LOG: No entries</p>\n");
+      writeout_para (ctx, _("No audit log entries."));
       goto leave;
     }
 
@@ -320,24 +797,49 @@ audit_print_result (audit_ctx_t ctx, estream_t out)
         maxlen = n;
     }
 
-  es_fputs ("<ul>\n", out);
+  if (use_html)
+    es_fputs ("<pre>\n", out);
   for (idx=0; idx < ctx->logused; idx++)
     {
-      es_fprintf (out, " <li>%-*s", 
+      es_fprintf (out, "log: %-*s", 
                   maxlen, event2str (ctx->log[idx].event));
       if (ctx->log[idx].have_intvalue)
         es_fprintf (out, " i=%d", ctx->log[idx].intvalue); 
       if (ctx->log[idx].string)
-        es_fprintf (out, " s=`%s'", ctx->log[idx].string); 
+        {
+          es_fputs (" s=`", out); 
+          writeout (ctx, ctx->log[idx].string); 
+          es_fputs ("'", out); 
+        }
       if (ctx->log[idx].cert)
         es_fprintf (out, " has_cert"); 
       if (ctx->log[idx].have_err)
-        es_fprintf (out, " err=\"%s\"", gpg_strerror (ctx->log[idx].err)); 
-      es_fputs ("</li>\n", out);
+        {
+          es_fputs (" err=`", out);
+          writeout (ctx, gpg_strerror (ctx->log[idx].err)); 
+          es_fputs ("'", out);
+        }
+      es_fputs ("\n", out);
+    }
+  if (use_html)
+    es_fputs ("</pre>\n", out);
+  else
+    es_fputs ("\n", out);
+
+  switch (ctx->type)
+    {
+    case AUDIT_TYPE_NONE:
+      writeout_para (ctx, _("Audit of this operation is not supported."));
+      break;
+    case AUDIT_TYPE_VERIFY:
+      proc_type_verify (ctx);
+      break;
     }
-  es_fputs ("</ul>\n", out);
 
  leave:
-  es_fputs ("</div>\n", out);
+  if (use_html)
+    es_fputs ("</div>\n", ctx->outstream);
+  ctx->outstream = NULL;
+  ctx->use_html = 0;
 }
 
index 83baa41..514ef12 100644 (file)
@@ -49,6 +49,9 @@ typedef enum
        now.  This indicates that all parameters are okay and we can
        start to process the actual data.  */
 
+    AUDIT_GOT_DATA,
+    /* Data to be processed has been seen.  */
+
     AUDIT_DETACHED_SIGNATURE,
     /* The signature is a detached one. */
 
@@ -91,7 +94,7 @@ typedef enum
     /* The name of a signer.  This is the name or other identification
        data as known from the signature and not the name from the
        certificate used for verification.  An example for STRING when
-       using CMS is:b "#1234/CN=Prostetnic Vogon Jeltz".  */
+       using CMS is: "#1234/CN=Prostetnic Vogon Jeltz".  */
 
     AUDIT_SIG_STATUS,     /* string */
     /* The signature status of the current signer.  This is the last
@@ -116,6 +119,8 @@ typedef enum
        certificate chain.  ROOTCERT is used for the trustanchor and
        CERT for all other certificates.  */ 
 
+    AUDIT_CHAIN_STATUS,  /* err */
+    /* Tells the final status of the chain validation.  */
 
 
     AUDIT_LAST_EVENT  /* Marker for parsing this list.  */
@@ -133,7 +138,7 @@ void audit_log_s (audit_ctx_t ctx, audit_event_t event, const char *value);
 void audit_log_cert (audit_ctx_t ctx, audit_event_t event, 
                      ksba_cert_t cert, gpg_error_t err);
 
-void audit_print_result (audit_ctx_t ctx, estream_t stream);
+void audit_print_result (audit_ctx_t ctx, estream_t stream, int use_html);
 
 
 
index 21a29be..10e3915 100644 (file)
@@ -1676,7 +1676,7 @@ main ( int argc, char **argv)
 
         if (auditlog)
           {
-            audit_print_result (ctrl.audit, auditfp);
+            audit_print_result (ctrl.audit, auditfp, 0);
             audit_release (ctrl.audit);
             ctrl.audit = NULL;
           }
index d54865a..e61f9d6 100644 (file)
@@ -934,12 +934,15 @@ cmd_genkey (assuan_context_t ctx, char *line)
 
 
 \f
-/* GETAUDITLOG [--data]
+/* GETAUDITLOG [--data] [--html]
 
    !!!WORK in PROGRESS!!!
 
    If --data is used, the output is send using D-lines and not to the
    source given by an OUTPUT command.
+
+   If --html is used the output is formated as an XHTML block. This is
+   designed to be incorporated into a HTML document.
  */
 static int 
 cmd_getauditlog (assuan_context_t ctx, char *line)
@@ -947,10 +950,11 @@ cmd_getauditlog (assuan_context_t ctx, char *line)
   ctrl_t ctrl = assuan_get_pointer (ctx);
   int  out_fd;
   estream_t out_stream;
-  int opt_data;
+  int opt_data, opt_html;
   int rc;
 
   opt_data = has_option (line, "--data"); 
+  opt_html = has_option (line, "--html"); 
   line = skip_options (line);
 
   if (!ctrl->audit)
@@ -976,7 +980,7 @@ cmd_getauditlog (assuan_context_t ctx, char *line)
         }
     }
 
-  audit_print_result (ctrl->audit, out_stream);
+  audit_print_result (ctrl->audit, out_stream, opt_html);
   rc = 0;
 
   es_fclose (out_stream);
index b0ced00..2a455a5 100644 (file)
@@ -184,7 +184,10 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, FILE *out_fp)
 
       if (stopreason == KSBA_SR_NEED_HASH
           || stopreason == KSBA_SR_BEGIN_DATA)
-        { /* We are now able to enable the hash algorithms */
+        { 
+          audit_log (ctrl->audit, AUDIT_GOT_DATA);
+
+          /* We are now able to enable the hash algorithms */
           for (i=0; (algoid=ksba_cms_get_digest_algo_list (cms, i)); i++)
             {
               algo = gcry_md_map_name (algoid);
@@ -535,6 +538,7 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, FILE *out_fp)
         xfree (buf);
       }
 
+      audit_log_ok (ctrl->audit, AUDIT_CHAIN_STATUS, rc);
       if (rc) /* of validate_chain */
         {
           log_error ("invalid certification chain: %s\n", gpg_strerror (rc));