summaryrefslogtreecommitdiffstats
path: root/common/audit.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/audit.c')
-rw-r--r--common/audit.c1324
1 files changed, 1324 insertions, 0 deletions
diff --git a/common/audit.c b/common/audit.c
new file mode 100644
index 0000000..718f729
--- /dev/null
+++ b/common/audit.c
@@ -0,0 +1,1324 @@
+/* audit.c - GnuPG's audit subsystem
+ * Copyright (C) 2007, 2009 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#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"
+
+/* A list to maintain a list of helptags. */
+struct helptag_s
+{
+ struct helptag_s *next;
+ const char *name;
+};
+typedef struct helptag_s *helptag_t;
+
+
+/* One log entry. */
+struct log_item_s
+{
+ audit_event_t event; /* The event. */
+ gpg_error_t err; /* The logged error code. */
+ 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;
+ int have_intvalue:1;
+};
+typedef struct log_item_s *log_item_t;
+
+
+
+/* The main audit object. */
+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. */
+
+ estream_t outstream; /* The current output stream. */
+ int use_html; /* The output shall be HTML formatted. */
+ int indentlevel; /* Current level of indentation. */
+ helptag_t helptags; /* List of help keys. */
+};
+
+
+
+
+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, ...) 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
+add_helptag (audit_ctx_t ctx, const char *name)
+{
+ helptag_t item;
+
+ for (item=ctx->helptags; item; item = item->next)
+ if (!strcmp (item->name, name))
+ return; /* Already in the list. */
+ item = xtrycalloc (1, sizeof *item);
+ if (!item)
+ return; /* Don't care about memory problems. */
+ item->name = name;
+ item->next = ctx->helptags;
+ ctx->helptags = item;
+}
+
+
+/* Remove all help tags from the context. */
+static void
+clear_helptags (audit_ctx_t ctx)
+{
+ while (ctx->helptags)
+ {
+ helptag_t tmp = ctx->helptags->next;
+ xfree (ctx->helptags);
+ ctx->helptags = tmp;
+ }
+}
+
+
+
+static const char *
+event2str (audit_event_t 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
+ return eventstr_msgstr + eventstr_msgidx[idx];
+}
+
+
+
+/* Create a new audit context. In case of an error NULL is returned
+ and errno set appropriately. */
+audit_ctx_t
+audit_new (void)
+{
+ audit_ctx_t ctx;
+
+ ctx = xtrycalloc (1, sizeof *ctx);
+
+ return ctx;
+}
+
+
+/* Release an audit context. Passing NULL for CTX is allowed and does
+ nothing. */
+void
+audit_release (audit_ctx_t ctx)
+{
+ int idx;
+ if (!ctx)
+ return;
+ if (ctx->log)
+ {
+ for (idx=0; idx < ctx->logused; idx++)
+ {
+ if (ctx->log[idx].string)
+ xfree (ctx->log[idx].string);
+ if (ctx->log[idx].cert)
+ ksba_cert_release (ctx->log[idx].cert);
+ }
+ xfree (ctx->log);
+ }
+ clear_helptags (ctx);
+ xfree (ctx);
+}
+
+
+/* Set the type for the audit operation. If CTX is NULL, this is a
+ dummy function. */
+void
+audit_set_type (audit_ctx_t ctx, audit_type_t type)
+{
+ if (!ctx || ctx->failure)
+ return; /* Audit not enabled or an internal error has occurred. */
+
+ if (ctx->type && ctx->type != type)
+ {
+ ctx->failure = "conflict in type initialization";
+ return;
+ }
+ ctx->type = type;
+}
+
+
+/* Create a new log item and put it into the table. Return that log
+ item on success; return NULL on memory failure and mark that in
+ CTX. */
+static log_item_t
+create_log_item (audit_ctx_t ctx)
+{
+ log_item_t item, table;
+ size_t size;
+
+ if (!ctx->log)
+ {
+ size = 10;
+ table = xtrymalloc (size * sizeof *table);
+ if (!table)
+ {
+ ctx->failure = "Out of memory in create_log_item";
+ return NULL;
+ }
+ ctx->log = table;
+ ctx->logsize = size;
+ item = ctx->log + 0;
+ ctx->logused = 1;
+ }
+ else if (ctx->logused >= ctx->logsize)
+ {
+ size = ctx->logsize + 10;
+ table = xtryrealloc (ctx->log, size * sizeof *table);
+ if (!table)
+ {
+ ctx->failure = "Out of memory while reallocating in create_log_item";
+ return NULL;
+ }
+ ctx->log = table;
+ ctx->logsize = size;
+ item = ctx->log + ctx->logused++;
+ }
+ else
+ item = ctx->log + ctx->logused++;
+
+ item->event = AUDIT_NULL_EVENT;
+ item->err = 0;
+ item->have_err = 0;
+ item->intvalue = 0;
+ item->have_intvalue = 0;
+ item->string = NULL;
+ item->cert = NULL;
+
+ return item;
+
+}
+
+/* Add a new event to the audit log. If CTX is NULL, this function
+ does nothing. */
+void
+audit_log (audit_ctx_t ctx, audit_event_t event)
+{
+ log_item_t item;
+
+ if (!ctx || ctx->failure)
+ return; /* Audit not enabled or an internal error has occurred. */
+ if (!event)
+ {
+ ctx->failure = "Invalid event passed to audit_log";
+ return;
+ }
+ if (!(item = create_log_item (ctx)))
+ return;
+ item->event = 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 operation
+ to the log. */
+void
+audit_log_ok (audit_ctx_t ctx, audit_event_t event, gpg_error_t err)
+{
+ log_item_t item;
+
+ if (!ctx || ctx->failure)
+ return; /* Audit not enabled or an internal error has occurred. */
+ if (!event)
+ {
+ ctx->failure = "Invalid event passed to audit_log_ok";
+ return;
+ }
+ if (!(item = create_log_item (ctx)))
+ return;
+ item->event = event;
+ item->err = err;
+ item->have_err = 1;
+}
+
+
+/* Add a new event to the audit log. If CTX is NULL, this function
+ does nothing. This version also add the integer VALUE to the log. */
+void
+audit_log_i (audit_ctx_t ctx, audit_event_t event, int value)
+{
+ log_item_t item;
+
+ if (!ctx || ctx->failure)
+ return; /* Audit not enabled or an internal error has occurred. */
+ if (!event)
+ {
+ ctx->failure = "Invalid event passed to audit_log_i";
+ return;
+ }
+ if (!(item = create_log_item (ctx)))
+ return;
+ item->event = event;
+ item->intvalue = value;
+ item->have_intvalue = 1;
+}
+
+
+/* Add a new event to the audit log. If CTX is NULL, this function
+ does nothing. This version also add the integer VALUE to the log. */
+void
+audit_log_s (audit_ctx_t ctx, audit_event_t event, const char *value)
+{
+ log_item_t item;
+ char *tmp;
+
+ if (!ctx || ctx->failure)
+ return; /* Audit not enabled or an internal error has occurred. */
+ if (!event)
+ {
+ ctx->failure = "Invalid event passed to audit_log_s";
+ return;
+ }
+ tmp = xtrystrdup (value? value : "");
+ if (!tmp)
+ {
+ ctx->failure = "Out of memory in audit_event";
+ return;
+ }
+ if (!(item = create_log_item (ctx)))
+ {
+ xfree (tmp);
+ return;
+ }
+ item->event = event;
+ item->string = tmp;
+}
+
+/* Add a new event to the audit log. If CTX is NULL, this function
+ 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,
+ ksba_cert_t cert, gpg_error_t err)
+{
+ log_item_t item;
+
+ if (!ctx || ctx->failure)
+ return; /* Audit not enabled or an internal error has occurred. */
+ if (!event)
+ {
+ ctx->failure = "Invalid event passed to audit_log_cert";
+ return;
+ }
+ if (!(item = create_log_item (ctx)))
+ return;
+ item->event = event;
+ item->err = err;
+ item->have_err = 1;
+ if (cert)
+ {
+ ksba_cert_ref (cert);
+ item->cert = cert;
+ }
+}
+
+
+/* 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;
+
+ gpgrt_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 *format, ...)
+{
+ va_list arg_ptr;
+
+ if (ctx->use_html)
+ es_fputs ("<p>", ctx->outstream);
+ va_start (arg_ptr, format) ;
+ writeout_v (ctx, format, arg_ptr);
+ va_end (arg_ptr);
+ 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, "Yes")
+ || !strcmp (oktext, "good") )
+ color = "green";
+ 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;
+
+ 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 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
+ certificates 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;
+
+ startitem = find_next_log_item (ctx, startitem, AUDIT_CHAIN_BEGIN,stopevent);
+ writeout_li (ctx, startitem? "Yes":"No", _("Certificate chain available"));
+ if (!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
+ {
+ list_cert (ctx, item->cert, 0);
+ }
+ item = startitem;
+ while ( ((item = find_next_log_item (ctx, item,
+ AUDIT_CHAIN_CERT, AUDIT_CHAIN_END))))
+ {
+ list_cert (ctx, item->cert, 1);
+ }
+}
+
+
+
+/* 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);
+}
+
+
+
+/* Process a sign operation's log. */
+static void
+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);
+}
+
+
+
+/* Process a decrypt operation's log. */
+static void
+proc_type_decrypt (audit_ctx_t ctx)
+{
+ log_item_t loopitem, item;
+ 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);
+
+ 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);
+}
+
+
+
+/* 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"));
+ 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;
+
+ /* Print info about the used data hashing algorithms. */
+ for (idx=0, n_good=n_bad=0; idx < ctx->logused; idx++)
+ {
+ 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++)
+ {
+ 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:"?");
+ }
+ }
+
+
+ /* 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);
+
+ 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);
+
+ /* 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? "No":"Yes",
+ _("Certificate chain valid"));
+ if (item->err)
+ writeout_rem (ctx, "%s", gpg_strerror (item->err));
+ }
+
+ /* Show whether the root certificate is fine. */
+ 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. */
+ 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_TRUE: ok = "n/a"; 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);
+ }
+ 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);
+}
+
+
+
+
+/* Print the formatted audit result. THIS IS WORK IN PROGRESS. */
+void
+audit_print_result (audit_ctx_t ctx, estream_t out, int use_html)
+{
+ int idx;
+ size_t n;
+ log_item_t item;
+ helptag_t helptag;
+ 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;
+ ctx->indentlevel = 0;
+ clear_helptags (ctx);
+
+ if (use_html)
+ es_fputs ("<div class=\"" GNUPG_NAME "AuditLog\">\n", ctx->outstream);
+
+ if (!ctx->log || !ctx->logused)
+ {
+ writeout_para (ctx, _("No audit log entries."));
+ goto leave;
+ }
+
+ if (show_raw)
+ {
+ int maxlen;
+
+ for (idx=0,maxlen=0; idx < DIM (eventstr_msgidx); idx++)
+ {
+ n = strlen (eventstr_msgstr + eventstr_msgidx[idx]);
+ if (n > maxlen)
+ maxlen = n;
+ }
+
+ 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)
+ {
+ 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);
+ }
+ if (use_html)
+ es_fputs ("</pre>\n", out);
+ else
+ es_fputs ("\n", out);
+ }
+
+ enter_li (ctx);
+ switch (ctx->type)
+ {
+ case AUDIT_TYPE_NONE:
+ 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. */
+ if (ctx->helptags)
+ {
+ if (use_html)
+ {
+ es_fputs ("<hr/>\n", ctx->outstream);
+ if (ctx->helptags->next)
+ es_fputs ("<ul>\n", ctx->outstream);
+ }
+ else
+ es_fputs ("\n\n", ctx->outstream);
+ }
+ for (helptag = ctx->helptags; helptag; helptag = helptag->next)
+ {
+ char *text;
+
+ if (use_html && ctx->helptags->next)
+ es_fputs ("<li>\n", ctx->outstream);
+
+ text = gnupg_get_help_string (helptag->name, 0);
+ if (text)
+ {
+ writeout_para (ctx, "%s", text);
+ xfree (text);
+ }
+ else
+ 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)
+ es_fputs ("\n", ctx->outstream);
+ }
+ if (use_html && ctx->helptags && ctx->helptags->next)
+ es_fputs ("</ul>\n", ctx->outstream);
+
+ leave:
+ if (use_html)
+ es_fputs ("</div>\n", ctx->outstream);
+ ctx->outstream = NULL;
+ ctx->use_html = 0;
+ clear_helptags (ctx);
+ i18n_switchback (orig_codeset);
+}