", ctx->outstream);
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_fputc ('\n', ctx->outstream);
}
static void
enter_li (audit_ctx_t ctx)
{
if (ctx->use_html)
{
if (!ctx->indentlevel)
{
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 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_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 ("\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 ("
\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 ("
\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 ("
\n", ctx->outstream);
if (ctx->helptags->next)
es_fputs ("
\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 ("- \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 ("
\n", ctx->outstream);
if (helptag->next)
es_fputs ("\n", ctx->outstream);
}
if (use_html && ctx->helptags && ctx->helptags->next)
es_fputs ("
\n", ctx->outstream);
leave:
if (use_html)
es_fputs ("
\n", ctx->outstream);
ctx->outstream = NULL;
ctx->use_html = 0;
clear_helptags (ctx);
i18n_switchback (orig_codeset);
}