gpg: Improve the keyblock cache's transparency.
[gnupg.git] / common / audit.c
index 40cbb82..efd5fcd 100644 (file)
@@ -1,5 +1,5 @@
 /* audit.c - GnuPG's audit subsystem
- *     Copyright (C) 2007 Free Software Foundation, Inc.
+ *     Copyright (C) 2007, 2009 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -42,7 +42,7 @@ struct log_item_s
 {
   audit_event_t event; /* The event.  */
   gpg_error_t err;     /* The logged error code.  */
-  int intvalue;        /* A logged interger value.  */
+  int intvalue;        /* A logged integer value.  */
   char *string;        /* A malloced string or NULL.  */
   ksba_cert_t cert;    /* A certifciate or NULL. */
   int have_err:1;
@@ -57,7 +57,7 @@ struct audit_ctx_s
 {
   const char *failure;  /* If set a description of the internal failure.  */
   audit_type_t type;
-  
+
   log_item_t log;       /* The table with the log entries.  */
   size_t logsize;       /* The allocated size for LOG.  */
   size_t logused;       /* The used size of LOG.  */
@@ -71,17 +71,17 @@ struct audit_ctx_s
 
 
 \f
-static void writeout_para (audit_ctx_t ctx, 
-                           const char *format, ...) JNLIB_GCC_A_PRINTF(2,3);
+static void writeout_para (audit_ctx_t ctx,
+                           const char *format, ...) GPGRT_ATTR_PRINTF(2,3);
 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);
+                         const char *format, ...) GPGRT_ATTR_PRINTF(3,4);
+static void writeout_rem (audit_ctx_t ctx,
+                          const char *format, ...) GPGRT_ATTR_PRINTF(2,3);
 
 
 /* Add NAME to the list of help tags.  NAME needs to be a const string
    an this function merly stores this pointer.  */
-static void 
+static void
 add_helptag (audit_ctx_t ctx, const char *name)
 {
   helptag_t item;
@@ -115,7 +115,9 @@ clear_helptags (audit_ctx_t ctx)
 static const char *
 event2str (audit_event_t event)
 {
-  int idx = eventstr_msgidxof (event);
+  /* We need the cast so that compiler does not complain about an
+     always true comparison (>= 0) for an unsigned value.  */
+  int idx = eventstr_msgidxof ((int)event);
   if (idx == -1)
     return "Unknown event";
   else
@@ -125,7 +127,7 @@ event2str (audit_event_t event)
 
 
 /* Create a new audit context.  In case of an error NULL is returned
-   and errno set appropriately. */ 
+   and errno set appropriately. */
 audit_ctx_t
 audit_new (void)
 {
@@ -162,7 +164,7 @@ audit_release (audit_ctx_t ctx)
 
 
 /* Set the type for the audit operation.  If CTX is NULL, this is a
-   dummy fucntion.  */
+   dummy function.  */
 void
 audit_set_type (audit_ctx_t ctx, audit_type_t type)
 {
@@ -226,7 +228,7 @@ create_log_item (audit_ctx_t ctx)
   item->cert = NULL;
 
   return item;
+
 }
 
 /* Add a new event to the audit log.  If CTX is NULL, this function
@@ -249,8 +251,8 @@ audit_log (audit_ctx_t ctx, audit_event_t event)
 }
 
 /* Add a new event to the audit log.  If CTX is NULL, this function
-   does nothing.  This version also adds the result of the oepration
-   to the log.. */
+   does nothing.  This version also adds the result of the operation
+   to the log.  */
 void
 audit_log_ok (audit_ctx_t ctx, audit_event_t event, gpg_error_t err)
 {
@@ -327,7 +329,7 @@ audit_log_s (audit_ctx_t ctx, audit_event_t event, const char *value)
    does nothing.  This version also adds the certificate CERT and the
    result of an operation to the log.  */
 void
-audit_log_cert (audit_ctx_t ctx, audit_event_t event, 
+audit_log_cert (audit_ctx_t ctx, audit_event_t event,
                 ksba_cert_t cert, gpg_error_t err)
 {
   log_item_t item;
@@ -346,14 +348,14 @@ audit_log_cert (audit_ctx_t ctx, audit_event_t event,
   item->have_err = 1;
   if (cert)
     {
-      ksba_cert_ref (cert); 
+      ksba_cert_ref (cert);
       item->cert = cert;
     }
 }
 
 
 /* Write TEXT to the outstream.  */
-static void 
+static void
 writeout (audit_ctx_t ctx, const char *text)
 {
   if (ctx->use_html)
@@ -374,12 +376,12 @@ writeout (audit_ctx_t ctx, const char *text)
 
 
 /* Write TEXT to the outstream using a variable argument list.  */
-static void 
+static void
 writeout_v (audit_ctx_t ctx, const char *format, va_list arg_ptr)
 {
   char *buf;
 
-  estream_vasprintf (&buf, format, arg_ptr);
+  gpgrt_vasprintf (&buf, format, arg_ptr);
   if (buf)
     {
       writeout (ctx, buf);
@@ -438,7 +440,7 @@ leave_li (audit_ctx_t ctx)
     }
 }
 
-  
+
 /* Write TEXT as a list element.  If OKTEXT is not NULL, append it to
    the last line. */
 static void
@@ -449,12 +451,54 @@ writeout_li (audit_ctx_t ctx, const char *oktext, const char *format, ...)
 
   if (ctx->use_html && format && oktext)
     {
-      if (!strcmp (oktext, "OK") || !strcmp (oktext, "Yes"))
+      if (!strcmp (oktext, "Yes")
+          || !strcmp (oktext, "good") )
         color = "green";
-      else if (!strcmp (oktext, "FAIL") || !strcmp (oktext, "No"))
+      else if (!strcmp (oktext, "No")
+               || !strcmp (oktext, "bad") )
         color = "red";
     }
 
+  if (format && oktext)
+    {
+      const char *s = NULL;
+
+      if (!strcmp (oktext, "Yes"))
+        oktext = _("Yes");
+      else if (!strcmp (oktext, "No"))
+        oktext = _("No");
+      else if (!strcmp (oktext, "good"))
+        {
+          /* TRANSLATORS: Copy the prefix between the vertical bars
+             verbatim.  It will not be printed.  */
+          oktext = _("|audit-log-result|Good");
+        }
+      else if (!strcmp (oktext, "bad"))
+        oktext = _("|audit-log-result|Bad");
+      else if (!strcmp (oktext, "unsupported"))
+        oktext = _("|audit-log-result|Not supported");
+      else if (!strcmp (oktext, "no-cert"))
+        oktext = _("|audit-log-result|No certificate");
+      else if (!strcmp (oktext, "disabled"))
+        oktext = _("|audit-log-result|Not enabled");
+      else if (!strcmp (oktext, "error"))
+        oktext = _("|audit-log-result|Error");
+      else if (!strcmp (oktext, "not-used"))
+        oktext = _("|audit-log-result|Not used");
+      else if (!strcmp (oktext, "okay"))
+        oktext = _("|audit-log-result|Okay");
+      else if (!strcmp (oktext, "skipped"))
+        oktext = _("|audit-log-result|Skipped");
+      else if (!strcmp (oktext, "some"))
+        oktext = _("|audit-log-result|Some");
+      else
+        s = "";
+
+      /* If we have set a prefix, skip it.  */
+      if (!s && *oktext == '|' && (s=strchr (oktext+1,'|')))
+        oktext = s+1;
+    }
+
   if (ctx->use_html)
     {
       int i;
@@ -486,13 +530,13 @@ writeout_li (audit_ctx_t ctx, const char *oktext, const char *format, ...)
           if (color)
             es_fprintf (ctx->outstream, "<font color=\"%s\">", color);
         }
-      else  
+      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
@@ -535,7 +579,7 @@ writeout_rem (audit_ctx_t ctx, const char *format, ...)
    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, 
+find_next_log_item (audit_ctx_t ctx, log_item_t startitem,
                     audit_event_t event, audit_event_t stopevent)
 {
   int idx;
@@ -648,67 +692,306 @@ get_cert_subject (ksba_cert_t cert, int idx)
 }
 
 
+/* List the given certificiate.  If CERT is NULL, this is a NOP.  */
+static void
+list_cert (audit_ctx_t ctx, ksba_cert_t cert, int with_subj)
+{
+  char *name;
+  int idx;
+
+  name = get_cert_name (cert);
+  writeout_rem (ctx, "%s", name);
+  xfree (name);
+  if (with_subj)
+    {
+      enter_li (ctx);
+      for (idx=0; (name = get_cert_subject (cert, idx)); idx++)
+        {
+          writeout_rem (ctx, "%s", name);
+          xfree (name);
+        }
+      leave_li (ctx);
+    }
+}
+
+
 /* 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);
+  writeout_li (ctx, startitem? "Yes":"No", _("Certificate chain available"));
   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, 
+    return;
+
+  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);
+      list_cert (ctx, item->cert, 0);
     }
   item = startitem;
-  while ( ((item = find_next_log_item (ctx, item, 
+  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++)
+      list_cert (ctx, item->cert, 1);
+    }
+}
+
+
+\f
+/* Process an encrypt operation's log.  */
+static void
+proc_type_encrypt (audit_ctx_t ctx)
+{
+  log_item_t loopitem, item;
+  int recp_no, idx;
+  char numbuf[35];
+  int algo;
+  char *name;
+
+  item = find_log_item (ctx, AUDIT_ENCRYPTION_DONE, 0);
+  writeout_li (ctx, item?"Yes":"No", "%s", _("Data encryption succeeded"));
+
+  enter_li (ctx);
+
+  item = find_log_item (ctx, AUDIT_GOT_DATA, 0);
+  writeout_li (ctx, item? "Yes":"No", "%s", _("Data available"));
+
+  item = find_log_item (ctx, AUDIT_SESSION_KEY, 0);
+  writeout_li (ctx, item? "Yes":"No", "%s", _("Session key created"));
+  if (item)
+    {
+      algo = gcry_cipher_map_name (item->string);
+      if (algo)
+        writeout_rem (ctx, _("algorithm: %s"), gnupg_cipher_algo_name (algo));
+      else if (item->string && !strcmp (item->string, "1.2.840.113549.3.2"))
+        writeout_rem (ctx, _("unsupported algorithm: %s"), "RC2");
+      else if (item->string)
+        writeout_rem (ctx, _("unsupported algorithm: %s"), item->string);
+      else
+        writeout_rem (ctx, _("seems to be not encrypted"));
+    }
+
+  item = find_log_item (ctx, AUDIT_GOT_RECIPIENTS, 0);
+  snprintf (numbuf, sizeof numbuf, "%d",
+            item && item->have_intvalue? item->intvalue : 0);
+  writeout_li (ctx, numbuf, "%s", _("Number of recipients"));
+
+  /* Loop over all recipients.  */
+  loopitem = NULL;
+  recp_no = 0;
+  while ((loopitem=find_next_log_item (ctx, loopitem, AUDIT_ENCRYPTED_TO, 0)))
+    {
+      recp_no++;
+      writeout_li (ctx, NULL, _("Recipient %d"), recp_no);
+      if (loopitem->cert)
         {
+          name = get_cert_name (loopitem->cert);
           writeout_rem (ctx, "%s", name);
           xfree (name);
+          enter_li (ctx);
+          for (idx=0; (name = get_cert_subject (loopitem->cert, idx)); idx++)
+            {
+              writeout_rem (ctx, "%s", name);
+              xfree (name);
+            }
+          leave_li (ctx);
         }
-      leave_li (ctx);
     }
+
+  leave_li (ctx);
 }
 
 
 \f
-/* Process a verification operation.  */
+/* Process a sign operation's log.  */
 static void
-proc_type_verify (audit_ctx_t ctx)
+proc_type_sign (audit_ctx_t ctx)
+{
+  log_item_t item, loopitem;
+  int signer, idx;
+  const char *result;
+  ksba_cert_t cert;
+  char *name;
+  int lastalgo;
+
+  item = find_log_item (ctx, AUDIT_SIGNING_DONE, 0);
+  writeout_li (ctx, item?"Yes":"No", "%s", _("Data signing succeeded"));
+
+  enter_li (ctx);
+
+  item = find_log_item (ctx, AUDIT_GOT_DATA, 0);
+  writeout_li (ctx, item? "Yes":"No", "%s", _("Data available"));
+  /* Write remarks with the data hash algorithms.  We use a very
+     simple scheme to avoid some duplicates.  */
+  loopitem = NULL;
+  lastalgo = 0;
+  while ((loopitem = find_next_log_item
+          (ctx, loopitem, AUDIT_DATA_HASH_ALGO, AUDIT_NEW_SIG)))
+    {
+      if (loopitem->intvalue && loopitem->intvalue != lastalgo)
+        writeout_rem (ctx, _("data hash algorithm: %s"),
+                      gcry_md_algo_name (loopitem->intvalue));
+      lastalgo = loopitem->intvalue;
+    }
+
+  /* Loop over all signer.  */
+  loopitem = NULL;
+  signer = 0;
+  while ((loopitem=find_next_log_item (ctx, loopitem, AUDIT_NEW_SIG, 0)))
+    {
+      signer++;
+
+      item = find_next_log_item (ctx, loopitem, AUDIT_SIGNED_BY, AUDIT_NEW_SIG);
+      if (!item)
+        result = "error";
+      else if (!item->err)
+        result = "okay";
+      else if (gpg_err_code (item->err) == GPG_ERR_CANCELED)
+        result = "skipped";
+      else
+        result = gpg_strerror (item->err);
+      cert = item? item->cert : NULL;
+
+      writeout_li (ctx, result, _("Signer %d"), signer);
+      item = find_next_log_item (ctx, loopitem,
+                                 AUDIT_ATTR_HASH_ALGO, AUDIT_NEW_SIG);
+      if (item)
+        writeout_rem (ctx, _("attr hash algorithm: %s"),
+                      gcry_md_algo_name (item->intvalue));
+
+      if (cert)
+        {
+          name = get_cert_name (cert);
+          writeout_rem (ctx, "%s", name);
+          xfree (name);
+          enter_li (ctx);
+          for (idx=0; (name = get_cert_subject (cert, idx)); idx++)
+            {
+              writeout_rem (ctx, "%s", name);
+              xfree (name);
+            }
+          leave_li (ctx);
+        }
+    }
+
+  leave_li (ctx);
+}
+
+
+\f
+/* Process a decrypt operation's log.  */
+static void
+proc_type_decrypt (audit_ctx_t ctx)
 {
   log_item_t loopitem, item;
-  int signo, count, idx;
+  int algo, recpno;
+  char *name;
   char numbuf[35];
+  int idx;
+
+  item = find_log_item (ctx, AUDIT_DECRYPTION_RESULT, 0);
+  writeout_li (ctx, item && !item->err?"Yes":"No",
+               "%s", _("Data decryption succeeded"));
 
   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, 0);
+  writeout_li (ctx, item? "Yes":"No", "%s", _("Data available"));
+
+  item = find_log_item (ctx, AUDIT_DATA_CIPHER_ALGO, 0);
+  algo = item? item->intvalue : 0;
+  writeout_li (ctx, algo?"Yes":"No", "%s", _("Encryption algorithm supported"));
+  if (algo)
+    writeout_rem (ctx, _("algorithm: %s"), gnupg_cipher_algo_name (algo));
+
+  item = find_log_item (ctx, AUDIT_BAD_DATA_CIPHER_ALGO, 0);
+  if (item && item->string)
+    {
+      algo = gcry_cipher_map_name (item->string);
+      if (algo)
+        writeout_rem (ctx, _("algorithm: %s"), gnupg_cipher_algo_name (algo));
+      else if (item->string && !strcmp (item->string, "1.2.840.113549.3.2"))
+        writeout_rem (ctx, _("unsupported algorithm: %s"), "RC2");
+      else if (item->string)
+        writeout_rem (ctx, _("unsupported algorithm: %s"), item->string);
+      else
+        writeout_rem (ctx, _("seems to be not encrypted"));
+    }
+
+
+  for (recpno = 0, item = NULL;
+       (item = find_next_log_item (ctx, item, AUDIT_NEW_RECP, 0)); recpno++)
+    ;
+  snprintf (numbuf, sizeof numbuf, "%d", recpno);
+  writeout_li (ctx, numbuf, "%s", _("Number of recipients"));
+
+  /* Loop over all recipients.  */
+  loopitem = NULL;
+  while ((loopitem = find_next_log_item (ctx, loopitem, AUDIT_NEW_RECP, 0)))
+    {
+      const char *result;
+
+      recpno = loopitem->have_intvalue? loopitem->intvalue : -1;
+
+      item = find_next_log_item (ctx, loopitem,
+                                 AUDIT_RECP_RESULT, AUDIT_NEW_RECP);
+      if (!item)
+        result = "not-used";
+      else if (!item->err)
+        result = "okay";
+      else if (gpg_err_code (item->err) == GPG_ERR_CANCELED)
+        result = "skipped";
+      else
+        result = gpg_strerror (item->err);
+
+      item = find_next_log_item (ctx, loopitem,
+                                 AUDIT_RECP_NAME, AUDIT_NEW_RECP);
+      writeout_li (ctx, result, _("Recipient %d"), recpno);
+      if (item && item->string)
+        writeout_rem (ctx, "%s", item->string);
+
+      /* If we have a certificate write out more infos.  */
+      item = find_next_log_item (ctx, loopitem,
+                                 AUDIT_SAVE_CERT, AUDIT_NEW_RECP);
+      if (item && item->cert)
+        {
+          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);
+}
+
+
+\f
+/* Process a verification operation's log.  */
+static void
+proc_type_verify (audit_ctx_t ctx)
+{
+  log_item_t loopitem, item;
+  int signo, count, idx, n_good, n_bad;
+  char numbuf[35];
+  const char *result;
+
+  /* If there is at least one signature status we claim that the
+     verification succeeded.  This does not mean that the data has
+     verified okay.  */
+  item = find_log_item (ctx, AUDIT_SIG_STATUS, 0);
+  writeout_li (ctx, item?"Yes":"No", "%s", _("Data verification succeeded"));
+  enter_li (ctx);
 
   item = find_log_item (ctx, AUDIT_GOT_DATA, AUDIT_NEW_SIG);
   writeout_li (ctx, item? "Yes":"No", "%s", _("Data available"));
@@ -720,23 +1003,42 @@ proc_type_verify (audit_ctx_t ctx)
   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 
+  /* Print info about the used data hashing algorithms.  */
+  for (idx=0, n_good=n_bad=0; idx < ctx->logused; idx++)
     {
-      item = find_log_item (ctx, AUDIT_BAD_DATA_HASH_ALGO, AUDIT_NEW_SIG);
-      if (item)
+      item = ctx->log + idx;
+      if (item->event == AUDIT_NEW_SIG)
+        break;
+      else if (item->event == AUDIT_DATA_HASH_ALGO)
+        n_good++;
+      else if (item->event == AUDIT_BAD_DATA_HASH_ALGO)
+        n_bad++;
+    }
+  item = find_log_item (ctx, AUDIT_DATA_HASHING, AUDIT_NEW_SIG);
+  if (!item || item->err || !n_good)
+    result = "No";
+  else if (n_good && !n_bad)
+    result = "Yes";
+  else
+    result = "Some";
+  writeout_li (ctx, result, "%s", _("Parsing data succeeded"));
+  if (n_good || n_bad)
+    {
+      for (idx=0; idx < ctx->logused; idx++)
         {
-          writeout_li (ctx,"FAIL", "%s",  _("Parsing signature"));
-          writeout_rem (ctx, _("Bad hash algorithm: %s"), 
-                        item->string? item->string:"?");
+          item = ctx->log + idx;
+          if (item->event == AUDIT_NEW_SIG)
+            break;
+          else if (item->event == AUDIT_DATA_HASH_ALGO)
+            writeout_rem (ctx, _("data hash algorithm: %s"),
+                          gcry_md_algo_name (item->intvalue));
+          else if (item->event == AUDIT_BAD_DATA_HASH_ALGO)
+            writeout_rem (ctx, _("bad data 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);
@@ -751,8 +1053,20 @@ proc_type_verify (audit_ctx_t ctx)
                                  AUDIT_SIG_NAME, AUDIT_NEW_SIG);
       if (item)
         writeout_rem (ctx, "%s", item->string);
+
+      item = find_next_log_item (ctx, loopitem,
+                                 AUDIT_DATA_HASH_ALGO, AUDIT_NEW_SIG);
+      if (item)
+        writeout_rem (ctx, _("data hash algorithm: %s"),
+                      gcry_md_algo_name (item->intvalue));
+      item = find_next_log_item (ctx, loopitem,
+                                 AUDIT_ATTR_HASH_ALGO, AUDIT_NEW_SIG);
+      if (item)
+        writeout_rem (ctx, _("attr hash algorithm: %s"),
+                      gcry_md_algo_name (item->intvalue));
+
       enter_li (ctx);
-      
+
       /* List the certificate chain.  */
       list_certchain (ctx, loopitem, AUDIT_NEW_SIG);
 
@@ -761,20 +1075,53 @@ proc_type_verify (audit_ctx_t ctx)
                                  AUDIT_CHAIN_STATUS, AUDIT_NEW_SIG);
       if (item && item->have_err)
         {
-          writeout_li (ctx, item->err? "FAIL":"OK", 
-                       _("Validation of certificate chain"));
+          writeout_li (ctx, item->err? "No":"Yes",
+                       _("Certificate chain valid"));
           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"));
-      add_helptag (ctx, "gpgsm.root-cert-not-trusted");
+      item = find_next_log_item (ctx, loopitem,
+                                 AUDIT_ROOT_TRUSTED, AUDIT_CHAIN_STATUS);
+      if (item)
+        {
+          writeout_li (ctx, item->err?"No":"Yes", "%s",
+                       _("Root certificate trustworthy"));
+          if (item->err)
+            {
+              add_helptag (ctx, "gpgsm.root-cert-not-trusted");
+              writeout_rem (ctx, "%s", gpg_strerror (item->err));
+              list_cert (ctx, item->cert, 0);
+            }
+        }
 
       /* Show result of the CRL/OCSP check.  */
-      writeout_li (ctx, "-", "%s", _("CRL/OCSP check of certificates"));
-      add_helptag (ctx, "gpgsm.ocsp-problem");
+      item = find_next_log_item (ctx, loopitem,
+                                 AUDIT_CRL_CHECK, AUDIT_NEW_SIG);
+      if (item)
+        {
+          const char *ok;
+          switch (gpg_err_code (item->err))
+            {
+            case 0:                    ok = "good"; break;
+            case GPG_ERR_CERT_REVOKED: ok = "bad"; break;
+            case GPG_ERR_NOT_ENABLED:  ok = "disabled"; break;
+            case GPG_ERR_NO_CRL_KNOWN:
+              ok = _("no CRL found for certificate");
+              break;
+            case GPG_ERR_CRL_TOO_OLD:
+              ok = _("the available CRL is too old");
+              break;
+            default: ok = gpg_strerror (item->err); break;
+            }
 
+          writeout_li (ctx, ok, "%s", _("CRL/OCSP check of certificates"));
+          if (item->err
+              && gpg_err_code (item->err) != GPG_ERR_CERT_REVOKED
+              && gpg_err_code (item->err) != GPG_ERR_NOT_ENABLED)
+            add_helptag (ctx, "gpgsm.crl-problem");
+        }
 
       leave_li (ctx);
     }
@@ -785,13 +1132,13 @@ proc_type_verify (audit_ctx_t ctx)
   /* Always list the certificates stored in the signature.  */
   item = NULL;
   count = 0;
-  while ( ((item = find_next_log_item (ctx, item, 
+  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, 
+  while ( ((item = find_next_log_item (ctx, item,
                                        AUDIT_SAVE_CERT, AUDIT_NEW_SIG))))
     {
       char *name = get_cert_name (item->cert);
@@ -805,8 +1152,6 @@ proc_type_verify (audit_ctx_t ctx)
         }
       leave_li (ctx);
     }
-
-  leave_li (ctx);
   leave_li (ctx);
 }
 
@@ -818,16 +1163,23 @@ void
 audit_print_result (audit_ctx_t ctx, estream_t out, int use_html)
 {
   int idx;
-  int maxlen;
   size_t n;
+  log_item_t item;
   helptag_t helptag;
-
-  if (getenv ("use_html"))
-    use_html = 1;
+  const char *s;
+  int show_raw = 0;
+  char *orig_codeset;
 
   if (!ctx)
     return;
 
+  orig_codeset = i18n_switchto_utf8 ();
+
+  /* We use an environment variable to include some debug info in the
+     log.  */
+  if ((s = getenv ("gnupg_debug_audit")))
+    show_raw = 1;
+
   assert (!ctx->outstream);
   ctx->outstream = out;
   ctx->use_html = use_html;
@@ -835,7 +1187,7 @@ audit_print_result (audit_ctx_t ctx, estream_t out, int use_html)
   clear_helptags (ctx);
 
   if (use_html)
-    es_fputs ("<div class=\"GnuPGAuditLog\">\n", ctx->outstream);
+    es_fputs ("<div class=\"" GNUPG_NAME "AuditLog\">\n", ctx->outstream);
 
   if (!ctx->log || !ctx->logused)
     {
@@ -843,51 +1195,87 @@ audit_print_result (audit_ctx_t ctx, estream_t out, int use_html)
       goto leave;
     }
 
-  for (idx=0,maxlen=0; idx < DIM (eventstr_msgidx); idx++)
+  if (show_raw)
     {
-      n = strlen (eventstr_msgstr + eventstr_msgidx[idx]);    
-      if (n > maxlen)
-        maxlen = n;
-    }
+      int maxlen;
 
-  if (use_html)
-    es_fputs ("<pre>\n", out);
-  for (idx=0; idx < ctx->logused; idx++)
-    {
-      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)
+      for (idx=0,maxlen=0; idx < DIM (eventstr_msgidx); idx++)
         {
-          es_fputs (" s=`", out); 
-          writeout (ctx, ctx->log[idx].string); 
-          es_fputs ("'", out); 
+          n = strlen (eventstr_msgstr + eventstr_msgidx[idx]);
+          if (n > maxlen)
+            maxlen = n;
         }
-      if (ctx->log[idx].cert)
-        es_fprintf (out, " has_cert"); 
-      if (ctx->log[idx].have_err)
+
+      if (use_html)
+        es_fputs ("<pre>\n", out);
+      for (idx=0; idx < ctx->logused; idx++)
         {
-          es_fputs (" err=`", out);
-          writeout (ctx, gpg_strerror (ctx->log[idx].err)); 
-          es_fputs ("'", out);
+          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_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_fputs (" err='", out);
+              writeout (ctx, gpg_strerror (ctx->log[idx].err));
+              es_fputs ("'", out);
+            }
+          es_fputs ("\n", out);
         }
-      es_fputs ("\n", out);
+      if (use_html)
+        es_fputs ("</pre>\n", out);
+      else
+        es_fputs ("\n", out);
     }
-  if (use_html)
-    es_fputs ("</pre>\n", out);
-  else
-    es_fputs ("\n", out);
 
+  enter_li (ctx);
   switch (ctx->type)
     {
     case AUDIT_TYPE_NONE:
-      writeout_para (ctx, _("Audit of this operation is not supported."));
+      writeout_li (ctx, NULL, _("Unknown operation"));
+      break;
+    case AUDIT_TYPE_ENCRYPT:
+      proc_type_encrypt (ctx);
+      break;
+    case AUDIT_TYPE_SIGN:
+      proc_type_sign (ctx);
+      break;
+    case AUDIT_TYPE_DECRYPT:
+      proc_type_decrypt (ctx);
       break;
     case AUDIT_TYPE_VERIFY:
       proc_type_verify (ctx);
       break;
     }
+  item = find_log_item (ctx, AUDIT_AGENT_READY, 0);
+  if (item && item->have_err)
+    {
+      writeout_li (ctx, item->err? "No":"Yes", "%s", _("Gpg-Agent usable"));
+      if (item->err)
+        {
+          writeout_rem (ctx, "%s", gpg_strerror (item->err));
+          add_helptag (ctx, "gnupg.agent-problem");
+        }
+    }
+  item = find_log_item (ctx, AUDIT_DIRMNGR_READY, 0);
+  if (item && item->have_err)
+    {
+      writeout_li (ctx, item->err? "No":"Yes", "%s", _("Dirmngr usable"));
+      if (item->err)
+        {
+          writeout_rem (ctx, "%s", gpg_strerror (item->err));
+          add_helptag (ctx, "gnupg.dirmngr-problem");
+        }
+    }
+  leave_li (ctx);
 
 
   /* Show the help from the collected help tags.  */
@@ -916,7 +1304,7 @@ audit_print_result (audit_ctx_t ctx, estream_t out, int use_html)
           xfree (text);
         }
       else
-        writeout_para (ctx, _("No help available for `%s'."), helptag->name);
+        writeout_para (ctx, _("No help available for '%s'."), helptag->name);
       if (use_html && ctx->helptags->next)
         es_fputs ("</li>\n", ctx->outstream);
       if (helptag->next)
@@ -931,5 +1319,5 @@ audit_print_result (audit_ctx_t ctx, estream_t out, int use_html)
   ctx->outstream = NULL;
   ctx->use_html = 0;
   clear_helptags (ctx);
+  i18n_switchback (orig_codeset);
 }
-