diff options
author | Werner Koch <wk@gnupg.org> | 2007-12-06 16:55:03 +0100 |
---|---|---|
committer | Werner Koch <wk@gnupg.org> | 2007-12-06 16:55:03 +0100 |
commit | 89671cdd6425e0c2247bd02c7ce6bb2f6339f94d (patch) | |
tree | df62d4ec104e82ffce7f163e357d3e9ab45d81c0 /common/audit.c | |
parent | Created help files form the current po entries. (diff) | |
download | gnupg2-89671cdd6425e0c2247bd02c7ce6bb2f6339f94d.tar.xz gnupg2-89671cdd6425e0c2247bd02c7ce6bb2f6339f94d.zip |
More code for the audit log.
Diffstat (limited to 'common/audit.c')
-rw-r--r-- | common/audit.c | 526 |
1 files changed, 514 insertions, 12 deletions
diff --git a/common/audit.c b/common/audit.c index d82052363..bf502a848 100644 --- a/common/audit.c +++ b/common/audit.c @@ -19,9 +19,12 @@ #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. */ }; + +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); + + 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 ("<", ctx->outstream); + else if (*text == '&') + es_fputs ("&", 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 (" ", 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 (" ", 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></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); + } +} + + + +/* 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); +} + + + + /* 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; } |