diff --git a/NEWS b/NEWS index 2a5b0acdd..87ea82f77 100644 --- 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. 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 #include - +#include +#include +#include #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 ("

", ctx->outstream); + writeout (ctx, text); + if (ctx->use_html) + es_fputs ("

\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 ("\n" + " \n" + " \n" + " \n" + " \n", + ctx->outstream); + } + } + ctx->indentlevel++; +} + + +static void +leave_li (audit_ctx_t ctx) +{ + ctx->indentlevel--; + if (ctx->use_html) + { + if (!ctx->indentlevel) + es_fputs ("
\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 ("
", ctx->outstream); + if (color) + es_fprintf (ctx->outstream, "*", color); + else + es_fputs ("*", ctx->outstream); + for (i=1; i < ctx->indentlevel; i++) + es_fputs ("  ", ctx->outstream); + es_fputs ("", 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 ("
", ctx->outstream); + if (format && oktext) + { + if (ctx->use_html) + { + es_fputs ("", ctx->outstream); + if (color) + es_fprintf (ctx->outstream, "", color); + } + else + writeout (ctx, ": "); + writeout (ctx, oktext); + if (color) + es_fputs ("", ctx->outstream); + } + + if (ctx->use_html) + es_fputs ("\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 ("
*", ctx->outstream); + for (i=1; i < ctx->indentlevel; i++) + es_fputs ("  ", ctx->outstream); + es_fputs ("    (", 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 (")
\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 ("
\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 ("
\n", ctx->outstream); + if (!ctx->log || !ctx->logused) { - es_fprintf (out, "

AUDIT-LOG: No entries

\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 ("
    \n", out); + if (use_html) + es_fputs ("
    \n", out);
       for (idx=0; idx < ctx->logused; idx++)
         {
    -      es_fprintf (out, " 
  • %-*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 ("
  • \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 ("
    \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 ("
\n", out); leave: - es_fputs ("
\n", out); + if (use_html) + es_fputs ("
\n", ctx->outstream); + ctx->outstream = NULL; + ctx->use_html = 0; } diff --git a/common/audit.h b/common/audit.h index 83baa41c7..514ef120e 100644 --- a/common/audit.h +++ b/common/audit.h @@ -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); diff --git a/sm/gpgsm.c b/sm/gpgsm.c index 21a29bee5..10e39159d 100644 --- a/sm/gpgsm.c +++ b/sm/gpgsm.c @@ -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; } diff --git a/sm/server.c b/sm/server.c index d54865ac9..e61f9d600 100644 --- a/sm/server.c +++ b/sm/server.c @@ -934,12 +934,15 @@ cmd_genkey (assuan_context_t ctx, char *line) -/* 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); diff --git a/sm/verify.c b/sm/verify.c index b0ced0062..2a455a5ce 100644 --- a/sm/verify.c +++ b/sm/verify.c @@ -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));