common/iobuf.c: Improve iobuf_peek.
[gnupg.git] / common / audit.c
1 /* audit.c - GnuPG's audit subsystem
2  *      Copyright (C) 2007, 2009 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <stdarg.h>
24 #include <assert.h>
25
26 #include "util.h"
27 #include "i18n.h"
28 #include "audit.h"
29 #include "audit-events.h"
30
31 /* A list to maintain a list of helptags.  */
32 struct helptag_s
33 {
34   struct helptag_s *next;
35   const char *name;
36 };
37 typedef struct helptag_s *helptag_t;
38
39
40 /* One log entry.  */
41 struct log_item_s
42 {
43   audit_event_t event; /* The event.  */
44   gpg_error_t err;     /* The logged error code.  */
45   int intvalue;        /* A logged integer value.  */
46   char *string;        /* A malloced string or NULL.  */
47   ksba_cert_t cert;    /* A certifciate or NULL. */
48   int have_err:1;
49   int have_intvalue:1;
50 };
51 typedef struct log_item_s *log_item_t;
52
53
54
55 /* The main audit object.  */
56 struct audit_ctx_s
57 {
58   const char *failure;  /* If set a description of the internal failure.  */
59   audit_type_t type;
60
61   log_item_t log;       /* The table with the log entries.  */
62   size_t logsize;       /* The allocated size for LOG.  */
63   size_t logused;       /* The used size of LOG.  */
64
65   estream_t outstream;  /* The current output stream.  */
66   int use_html;         /* The output shall be HTML formatted.  */
67   int indentlevel;      /* Current level of indentation.  */
68   helptag_t helptags;   /* List of help keys.  */
69 };
70
71
72
73 \f
74 static void writeout_para (audit_ctx_t ctx,
75                            const char *format, ...) GPGRT_ATTR_PRINTF(2,3);
76 static void writeout_li (audit_ctx_t ctx, const char *oktext,
77                          const char *format, ...) GPGRT_ATTR_PRINTF(3,4);
78 static void writeout_rem (audit_ctx_t ctx,
79                           const char *format, ...) GPGRT_ATTR_PRINTF(2,3);
80
81
82 /* Add NAME to the list of help tags.  NAME needs to be a const string
83    an this function merly stores this pointer.  */
84 static void
85 add_helptag (audit_ctx_t ctx, const char *name)
86 {
87   helptag_t item;
88
89   for (item=ctx->helptags; item; item = item->next)
90     if (!strcmp (item->name, name))
91       return;  /* Already in the list.  */
92   item = xtrycalloc (1, sizeof *item);
93   if (!item)
94     return;  /* Don't care about memory problems.  */
95   item->name = name;
96   item->next = ctx->helptags;
97   ctx->helptags = item;
98 }
99
100
101 /* Remove all help tags from the context.  */
102 static void
103 clear_helptags (audit_ctx_t ctx)
104 {
105   while (ctx->helptags)
106     {
107       helptag_t tmp = ctx->helptags->next;
108       xfree (ctx->helptags);
109       ctx->helptags = tmp;
110     }
111 }
112
113
114 \f
115 static const char *
116 event2str (audit_event_t event)
117 {
118   /* We need the cast so that compiler does not complain about an
119      always true comparison (>= 0) for an unsigned value.  */
120   int idx = eventstr_msgidxof ((int)event);
121   if (idx == -1)
122     return "Unknown event";
123   else
124     return eventstr_msgstr + eventstr_msgidx[idx];
125 }
126
127
128
129 /* Create a new audit context.  In case of an error NULL is returned
130    and errno set appropriately. */
131 audit_ctx_t
132 audit_new (void)
133 {
134   audit_ctx_t ctx;
135
136   ctx = xtrycalloc (1, sizeof *ctx);
137
138   return ctx;
139 }
140
141
142 /* Release an audit context.  Passing NULL for CTX is allowed and does
143    nothing.  */
144 void
145 audit_release (audit_ctx_t ctx)
146 {
147   int idx;
148   if (!ctx)
149     return;
150   if (ctx->log)
151     {
152       for (idx=0; idx < ctx->logused; idx++)
153         {
154           if (ctx->log[idx].string)
155             xfree (ctx->log[idx].string);
156           if (ctx->log[idx].cert)
157             ksba_cert_release (ctx->log[idx].cert);
158         }
159       xfree (ctx->log);
160     }
161   clear_helptags (ctx);
162   xfree (ctx);
163 }
164
165
166 /* Set the type for the audit operation.  If CTX is NULL, this is a
167    dummy fucntion.  */
168 void
169 audit_set_type (audit_ctx_t ctx, audit_type_t type)
170 {
171   if (!ctx || ctx->failure)
172     return;  /* Audit not enabled or an internal error has occurred. */
173
174   if (ctx->type && ctx->type != type)
175     {
176       ctx->failure = "conflict in type initialization";
177       return;
178     }
179   ctx->type = type;
180 }
181
182
183 /* Create a new log item and put it into the table.  Return that log
184    item on success; return NULL on memory failure and mark that in
185    CTX. */
186 static log_item_t
187 create_log_item (audit_ctx_t ctx)
188 {
189   log_item_t item, table;
190   size_t size;
191
192   if (!ctx->log)
193     {
194       size = 10;
195       table = xtrymalloc (size * sizeof *table);
196       if (!table)
197         {
198           ctx->failure = "Out of memory in create_log_item";
199           return NULL;
200         }
201       ctx->log = table;
202       ctx->logsize = size;
203       item = ctx->log + 0;
204       ctx->logused = 1;
205     }
206   else if (ctx->logused >= ctx->logsize)
207     {
208       size = ctx->logsize + 10;
209       table = xtryrealloc (ctx->log, size * sizeof *table);
210       if (!table)
211         {
212           ctx->failure = "Out of memory while reallocating in create_log_item";
213           return NULL;
214         }
215       ctx->log = table;
216       ctx->logsize = size;
217       item = ctx->log + ctx->logused++;
218     }
219   else
220     item = ctx->log + ctx->logused++;
221
222   item->event = AUDIT_NULL_EVENT;
223   item->err = 0;
224   item->have_err = 0;
225   item->intvalue = 0;
226   item->have_intvalue = 0;
227   item->string = NULL;
228   item->cert = NULL;
229
230   return item;
231
232 }
233
234 /* Add a new event to the audit log.  If CTX is NULL, this function
235    does nothing.  */
236 void
237 audit_log (audit_ctx_t ctx, audit_event_t event)
238 {
239   log_item_t item;
240
241   if (!ctx || ctx->failure)
242     return;  /* Audit not enabled or an internal error has occurred. */
243   if (!event)
244     {
245       ctx->failure = "Invalid event passed to audit_log";
246       return;
247     }
248   if (!(item = create_log_item (ctx)))
249     return;
250   item->event = event;
251 }
252
253 /* Add a new event to the audit log.  If CTX is NULL, this function
254    does nothing.  This version also adds the result of the operation
255    to the log.  */
256 void
257 audit_log_ok (audit_ctx_t ctx, audit_event_t event, gpg_error_t err)
258 {
259   log_item_t item;
260
261   if (!ctx || ctx->failure)
262     return;  /* Audit not enabled or an internal error has occurred. */
263   if (!event)
264     {
265       ctx->failure = "Invalid event passed to audit_log_ok";
266       return;
267     }
268   if (!(item = create_log_item (ctx)))
269     return;
270   item->event = event;
271   item->err = err;
272   item->have_err = 1;
273 }
274
275
276 /* Add a new event to the audit log.  If CTX is NULL, this function
277    does nothing.  This version also add the integer VALUE to the log.  */
278 void
279 audit_log_i (audit_ctx_t ctx, audit_event_t event, int value)
280 {
281   log_item_t item;
282
283   if (!ctx || ctx->failure)
284     return;  /* Audit not enabled or an internal error has occurred. */
285   if (!event)
286     {
287       ctx->failure = "Invalid event passed to audit_log_i";
288       return;
289     }
290   if (!(item = create_log_item (ctx)))
291     return;
292   item->event = event;
293   item->intvalue = value;
294   item->have_intvalue = 1;
295 }
296
297
298 /* Add a new event to the audit log.  If CTX is NULL, this function
299    does nothing.  This version also add the integer VALUE to the log.  */
300 void
301 audit_log_s (audit_ctx_t ctx, audit_event_t event, const char *value)
302 {
303   log_item_t item;
304   char *tmp;
305
306   if (!ctx || ctx->failure)
307     return;  /* Audit not enabled or an internal error has occurred. */
308   if (!event)
309     {
310       ctx->failure = "Invalid event passed to audit_log_s";
311       return;
312     }
313   tmp = xtrystrdup (value? value : "");
314   if (!tmp)
315     {
316       ctx->failure = "Out of memory in audit_event";
317       return;
318     }
319   if (!(item = create_log_item (ctx)))
320     {
321       xfree (tmp);
322       return;
323     }
324   item->event = event;
325   item->string = tmp;
326 }
327
328 /* Add a new event to the audit log.  If CTX is NULL, this function
329    does nothing.  This version also adds the certificate CERT and the
330    result of an operation to the log.  */
331 void
332 audit_log_cert (audit_ctx_t ctx, audit_event_t event,
333                 ksba_cert_t cert, gpg_error_t err)
334 {
335   log_item_t item;
336
337   if (!ctx || ctx->failure)
338     return;  /* Audit not enabled or an internal error has occurred. */
339   if (!event)
340     {
341       ctx->failure = "Invalid event passed to audit_log_cert";
342       return;
343     }
344   if (!(item = create_log_item (ctx)))
345     return;
346   item->event = event;
347   item->err = err;
348   item->have_err = 1;
349   if (cert)
350     {
351       ksba_cert_ref (cert);
352       item->cert = cert;
353     }
354 }
355
356
357 /* Write TEXT to the outstream.  */
358 static void
359 writeout (audit_ctx_t ctx, const char *text)
360 {
361   if (ctx->use_html)
362     {
363       for (; *text; text++)
364         {
365           if (*text == '<')
366             es_fputs ("&lt;", ctx->outstream);
367           else if (*text == '&')
368             es_fputs ("&amp;", ctx->outstream);
369           else
370             es_putc (*text, ctx->outstream);
371         }
372     }
373   else
374     es_fputs (text, ctx->outstream);
375 }
376
377
378 /* Write TEXT to the outstream using a variable argument list.  */
379 static void
380 writeout_v (audit_ctx_t ctx, const char *format, va_list arg_ptr)
381 {
382   char *buf;
383
384   gpgrt_vasprintf (&buf, format, arg_ptr);
385   if (buf)
386     {
387       writeout (ctx, buf);
388       xfree (buf);
389     }
390   else
391     writeout (ctx, "[!!Out of core!!]");
392 }
393
394
395 /* Write TEXT as a paragraph.  */
396 static void
397 writeout_para (audit_ctx_t ctx, const char *format, ...)
398 {
399   va_list arg_ptr;
400
401   if (ctx->use_html)
402     es_fputs ("<p>", ctx->outstream);
403   va_start (arg_ptr, format) ;
404   writeout_v (ctx, format, arg_ptr);
405   va_end (arg_ptr);
406   if (ctx->use_html)
407     es_fputs ("</p>\n", ctx->outstream);
408   else
409     es_fputc ('\n', ctx->outstream);
410 }
411
412
413 static void
414 enter_li (audit_ctx_t ctx)
415 {
416   if (ctx->use_html)
417     {
418       if (!ctx->indentlevel)
419         {
420           es_fputs ("<table border=\"0\">\n"
421                     "  <colgroup>\n"
422                     "    <col width=\"80%\" />\n"
423                     "    <col width=\"20%\" />\n"
424                     "   </colgroup>\n",
425                     ctx->outstream);
426         }
427     }
428   ctx->indentlevel++;
429 }
430
431
432 static void
433 leave_li (audit_ctx_t ctx)
434 {
435   ctx->indentlevel--;
436   if (ctx->use_html)
437     {
438       if (!ctx->indentlevel)
439         es_fputs ("</table>\n", ctx->outstream);
440     }
441 }
442
443
444 /* Write TEXT as a list element.  If OKTEXT is not NULL, append it to
445    the last line. */
446 static void
447 writeout_li (audit_ctx_t ctx, const char *oktext, const char *format, ...)
448 {
449   va_list arg_ptr;
450   const char *color = NULL;
451
452   if (ctx->use_html && format && oktext)
453     {
454       if (!strcmp (oktext, "Yes")
455           || !strcmp (oktext, "good") )
456         color = "green";
457       else if (!strcmp (oktext, "No")
458                || !strcmp (oktext, "bad") )
459         color = "red";
460     }
461
462   if (format && oktext)
463     {
464       const char *s = NULL;
465
466       if (!strcmp (oktext, "Yes"))
467         oktext = _("Yes");
468       else if (!strcmp (oktext, "No"))
469         oktext = _("No");
470       else if (!strcmp (oktext, "good"))
471         {
472           /* TRANSLATORS: Copy the prefix between the vertical bars
473              verbatim.  It will not be printed.  */
474           oktext = _("|audit-log-result|Good");
475         }
476       else if (!strcmp (oktext, "bad"))
477         oktext = _("|audit-log-result|Bad");
478       else if (!strcmp (oktext, "unsupported"))
479         oktext = _("|audit-log-result|Not supported");
480       else if (!strcmp (oktext, "no-cert"))
481         oktext = _("|audit-log-result|No certificate");
482       else if (!strcmp (oktext, "disabled"))
483         oktext = _("|audit-log-result|Not enabled");
484       else if (!strcmp (oktext, "error"))
485         oktext = _("|audit-log-result|Error");
486       else if (!strcmp (oktext, "not-used"))
487         oktext = _("|audit-log-result|Not used");
488       else if (!strcmp (oktext, "okay"))
489         oktext = _("|audit-log-result|Okay");
490       else if (!strcmp (oktext, "skipped"))
491         oktext = _("|audit-log-result|Skipped");
492       else if (!strcmp (oktext, "some"))
493         oktext = _("|audit-log-result|Some");
494       else
495         s = "";
496
497       /* If we have set a prefix, skip it.  */
498       if (!s && *oktext == '|' && (s=strchr (oktext+1,'|')))
499         oktext = s+1;
500     }
501
502   if (ctx->use_html)
503     {
504       int i;
505
506       es_fputs ("  <tr><td><table><tr><td>", ctx->outstream);
507       if (color)
508         es_fprintf (ctx->outstream, "<font color=\"%s\">*</font>", color);
509       else
510         es_fputs ("*", ctx->outstream);
511       for (i=1; i < ctx->indentlevel; i++)
512         es_fputs ("&nbsp;&nbsp;", ctx->outstream);
513       es_fputs ("</td><td>", ctx->outstream);
514     }
515   else
516     es_fprintf (ctx->outstream, "* %*s", (ctx->indentlevel-1)*2, "");
517   if (format)
518     {
519       va_start (arg_ptr, format) ;
520       writeout_v (ctx, format, arg_ptr);
521       va_end (arg_ptr);
522     }
523   if (ctx->use_html)
524     es_fputs ("</td></tr></table>", ctx->outstream);
525   if (format && oktext)
526     {
527       if (ctx->use_html)
528         {
529           es_fputs ("</td><td>", ctx->outstream);
530           if (color)
531             es_fprintf (ctx->outstream, "<font color=\"%s\">", color);
532         }
533       else
534         writeout (ctx, ":         ");
535       writeout (ctx, oktext);
536       if (color)
537         es_fputs ("</font>", ctx->outstream);
538     }
539
540   if (ctx->use_html)
541     es_fputs ("</td></tr>\n", ctx->outstream);
542   else
543     es_fputc ('\n', ctx->outstream);
544 }
545
546
547 /* Write a remark line.  */
548 static void
549 writeout_rem (audit_ctx_t ctx, const char *format, ...)
550 {
551   va_list arg_ptr;
552
553   if (ctx->use_html)
554     {
555       int i;
556
557       es_fputs ("  <tr><td><table><tr><td>*", ctx->outstream);
558       for (i=1; i < ctx->indentlevel; i++)
559         es_fputs ("&nbsp;&nbsp;", ctx->outstream);
560       es_fputs ("&nbsp;&nbsp;&nbsp;</td><td> (", ctx->outstream);
561
562     }
563   else
564     es_fprintf (ctx->outstream, "* %*s  (", (ctx->indentlevel-1)*2, "");
565   if (format)
566     {
567       va_start (arg_ptr, format) ;
568       writeout_v (ctx, format, arg_ptr);
569       va_end (arg_ptr);
570     }
571   if (ctx->use_html)
572     es_fputs (")</td></tr></table></td></tr>\n", ctx->outstream);
573   else
574     es_fputs (")\n", ctx->outstream);
575 }
576
577
578 /* Return the first log item for EVENT.  If STOPEVENT is not 0 never
579    look behind that event in the log. If STARTITEM is not NULL start
580    search _after_that item.  */
581 static log_item_t
582 find_next_log_item (audit_ctx_t ctx, log_item_t startitem,
583                     audit_event_t event, audit_event_t stopevent)
584 {
585   int idx;
586
587   for (idx=0; idx < ctx->logused; idx++)
588     {
589       if (startitem)
590         {
591           if (ctx->log + idx == startitem)
592             startitem = NULL;
593         }
594       else if (stopevent && ctx->log[idx].event == stopevent)
595         break;
596       else if (ctx->log[idx].event == event)
597         return ctx->log + idx;
598     }
599   return NULL;
600 }
601
602
603 static log_item_t
604 find_log_item (audit_ctx_t ctx, audit_event_t event, audit_event_t stopevent)
605 {
606   return find_next_log_item (ctx, NULL, event, stopevent);
607 }
608
609
610 /* Helper to a format a serial number.  */
611 static char *
612 format_serial (ksba_const_sexp_t sn)
613 {
614   const char *p = (const char *)sn;
615   unsigned long n;
616   char *endp;
617
618   if (!p)
619     return NULL;
620   if (*p != '(')
621     BUG (); /* Not a valid S-expression. */
622   n = strtoul (p+1, &endp, 10);
623   p = endp;
624   if (*p != ':')
625     BUG (); /* Not a valid S-expression. */
626   return bin2hex (p+1, n, NULL);
627 }
628
629
630 /* Return a malloced string with the serial number and the issuer DN
631    of the certificate.  */
632 static char *
633 get_cert_name (ksba_cert_t cert)
634 {
635   char *result;
636   ksba_sexp_t sn;
637   char *issuer, *p;
638
639   if (!cert)
640     return xtrystrdup ("[no certificate]");
641
642   issuer = ksba_cert_get_issuer (cert, 0);
643   sn = ksba_cert_get_serial (cert);
644   if (issuer && sn)
645     {
646       p = format_serial (sn);
647       if (!p)
648         result = xtrystrdup ("[invalid S/N]");
649       else
650         {
651           result = xtrymalloc (strlen (p) + strlen (issuer) + 2 + 1);
652           if (result)
653             {
654               *result = '#';
655               strcpy (stpcpy (stpcpy (result+1, p),"/"), issuer);
656             }
657           xfree (p);
658         }
659     }
660   else
661     result = xtrystrdup ("[missing S/N or issuer]");
662   ksba_free (sn);
663   xfree (issuer);
664   return result;
665 }
666
667 /* Return a malloced string with the serial number and the issuer DN
668    of the certificate.  */
669 static char *
670 get_cert_subject (ksba_cert_t cert, int idx)
671 {
672   char *result;
673   char *subject;
674
675   if (!cert)
676     return xtrystrdup ("[no certificate]");
677
678   subject = ksba_cert_get_subject (cert, idx);
679   if (subject)
680     {
681       result = xtrymalloc (strlen (subject) + 1 + 1);
682       if (result)
683         {
684           *result = '/';
685           strcpy (result+1, subject);
686         }
687     }
688   else
689     result = NULL;
690   xfree (subject);
691   return result;
692 }
693
694
695 /* List the given certificiate.  If CERT is NULL, this is a NOP.  */
696 static void
697 list_cert (audit_ctx_t ctx, ksba_cert_t cert, int with_subj)
698 {
699   char *name;
700   int idx;
701
702   name = get_cert_name (cert);
703   writeout_rem (ctx, "%s", name);
704   xfree (name);
705   if (with_subj)
706     {
707       enter_li (ctx);
708       for (idx=0; (name = get_cert_subject (cert, idx)); idx++)
709         {
710           writeout_rem (ctx, "%s", name);
711           xfree (name);
712         }
713       leave_li (ctx);
714     }
715 }
716
717
718 /* List the chain of certificates from STARTITEM up to STOPEVENT.  The
719    certifcates are written out as comments.  */
720 static void
721 list_certchain (audit_ctx_t ctx, log_item_t startitem, audit_event_t stopevent)
722 {
723   log_item_t item;
724
725   startitem = find_next_log_item (ctx, startitem, AUDIT_CHAIN_BEGIN,stopevent);
726   writeout_li (ctx, startitem? "Yes":"No", _("Certificate chain available"));
727   if (!startitem)
728     return;
729
730   item = find_next_log_item (ctx, startitem,
731                              AUDIT_CHAIN_ROOTCERT, AUDIT_CHAIN_END);
732   if (!item)
733     writeout_rem (ctx, "%s", _("root certificate missing"));
734   else
735     {
736       list_cert (ctx, item->cert, 0);
737     }
738   item = startitem;
739   while ( ((item = find_next_log_item (ctx, item,
740                                        AUDIT_CHAIN_CERT, AUDIT_CHAIN_END))))
741     {
742       list_cert (ctx, item->cert, 1);
743     }
744 }
745
746
747 \f
748 /* Process an encrypt operation's log.  */
749 static void
750 proc_type_encrypt (audit_ctx_t ctx)
751 {
752   log_item_t loopitem, item;
753   int recp_no, idx;
754   char numbuf[35];
755   int algo;
756   char *name;
757
758   item = find_log_item (ctx, AUDIT_ENCRYPTION_DONE, 0);
759   writeout_li (ctx, item?"Yes":"No", "%s", _("Data encryption succeeded"));
760
761   enter_li (ctx);
762
763   item = find_log_item (ctx, AUDIT_GOT_DATA, 0);
764   writeout_li (ctx, item? "Yes":"No", "%s", _("Data available"));
765
766   item = find_log_item (ctx, AUDIT_SESSION_KEY, 0);
767   writeout_li (ctx, item? "Yes":"No", "%s", _("Session key created"));
768   if (item)
769     {
770       algo = gcry_cipher_map_name (item->string);
771       if (algo)
772         writeout_rem (ctx, _("algorithm: %s"), gnupg_cipher_algo_name (algo));
773       else if (item->string && !strcmp (item->string, "1.2.840.113549.3.2"))
774         writeout_rem (ctx, _("unsupported algorithm: %s"), "RC2");
775       else if (item->string)
776         writeout_rem (ctx, _("unsupported algorithm: %s"), item->string);
777       else
778         writeout_rem (ctx, _("seems to be not encrypted"));
779     }
780
781   item = find_log_item (ctx, AUDIT_GOT_RECIPIENTS, 0);
782   snprintf (numbuf, sizeof numbuf, "%d",
783             item && item->have_intvalue? item->intvalue : 0);
784   writeout_li (ctx, numbuf, "%s", _("Number of recipients"));
785
786   /* Loop over all recipients.  */
787   loopitem = NULL;
788   recp_no = 0;
789   while ((loopitem=find_next_log_item (ctx, loopitem, AUDIT_ENCRYPTED_TO, 0)))
790     {
791       recp_no++;
792       writeout_li (ctx, NULL, _("Recipient %d"), recp_no);
793       if (loopitem->cert)
794         {
795           name = get_cert_name (loopitem->cert);
796           writeout_rem (ctx, "%s", name);
797           xfree (name);
798           enter_li (ctx);
799           for (idx=0; (name = get_cert_subject (loopitem->cert, idx)); idx++)
800             {
801               writeout_rem (ctx, "%s", name);
802               xfree (name);
803             }
804           leave_li (ctx);
805         }
806     }
807
808   leave_li (ctx);
809 }
810
811
812 \f
813 /* Process a sign operation's log.  */
814 static void
815 proc_type_sign (audit_ctx_t ctx)
816 {
817   log_item_t item, loopitem;
818   int signer, idx;
819   const char *result;
820   ksba_cert_t cert;
821   char *name;
822   int lastalgo;
823
824   item = find_log_item (ctx, AUDIT_SIGNING_DONE, 0);
825   writeout_li (ctx, item?"Yes":"No", "%s", _("Data signing succeeded"));
826
827   enter_li (ctx);
828
829   item = find_log_item (ctx, AUDIT_GOT_DATA, 0);
830   writeout_li (ctx, item? "Yes":"No", "%s", _("Data available"));
831   /* Write remarks with the data hash algorithms.  We use a very
832      simple scheme to avoid some duplicates.  */
833   loopitem = NULL;
834   lastalgo = 0;
835   while ((loopitem = find_next_log_item
836           (ctx, loopitem, AUDIT_DATA_HASH_ALGO, AUDIT_NEW_SIG)))
837     {
838       if (loopitem->intvalue && loopitem->intvalue != lastalgo)
839         writeout_rem (ctx, _("data hash algorithm: %s"),
840                       gcry_md_algo_name (loopitem->intvalue));
841       lastalgo = loopitem->intvalue;
842     }
843
844   /* Loop over all signer.  */
845   loopitem = NULL;
846   signer = 0;
847   while ((loopitem=find_next_log_item (ctx, loopitem, AUDIT_NEW_SIG, 0)))
848     {
849       signer++;
850
851       item = find_next_log_item (ctx, loopitem, AUDIT_SIGNED_BY, AUDIT_NEW_SIG);
852       if (!item)
853         result = "error";
854       else if (!item->err)
855         result = "okay";
856       else if (gpg_err_code (item->err) == GPG_ERR_CANCELED)
857         result = "skipped";
858       else
859         result = gpg_strerror (item->err);
860       cert = item? item->cert : NULL;
861
862       writeout_li (ctx, result, _("Signer %d"), signer);
863       item = find_next_log_item (ctx, loopitem,
864                                  AUDIT_ATTR_HASH_ALGO, AUDIT_NEW_SIG);
865       if (item)
866         writeout_rem (ctx, _("attr hash algorithm: %s"),
867                       gcry_md_algo_name (item->intvalue));
868
869       if (cert)
870         {
871           name = get_cert_name (cert);
872           writeout_rem (ctx, "%s", name);
873           xfree (name);
874           enter_li (ctx);
875           for (idx=0; (name = get_cert_subject (cert, idx)); idx++)
876             {
877               writeout_rem (ctx, "%s", name);
878               xfree (name);
879             }
880           leave_li (ctx);
881         }
882     }
883
884   leave_li (ctx);
885 }
886
887
888 \f
889 /* Process a decrypt operation's log.  */
890 static void
891 proc_type_decrypt (audit_ctx_t ctx)
892 {
893   log_item_t loopitem, item;
894   int algo, recpno;
895   char *name;
896   char numbuf[35];
897   int idx;
898
899   item = find_log_item (ctx, AUDIT_DECRYPTION_RESULT, 0);
900   writeout_li (ctx, item && !item->err?"Yes":"No",
901                "%s", _("Data decryption succeeded"));
902
903   enter_li (ctx);
904
905   item = find_log_item (ctx, AUDIT_GOT_DATA, 0);
906   writeout_li (ctx, item? "Yes":"No", "%s", _("Data available"));
907
908   item = find_log_item (ctx, AUDIT_DATA_CIPHER_ALGO, 0);
909   algo = item? item->intvalue : 0;
910   writeout_li (ctx, algo?"Yes":"No", "%s", _("Encryption algorithm supported"));
911   if (algo)
912     writeout_rem (ctx, _("algorithm: %s"), gnupg_cipher_algo_name (algo));
913
914   item = find_log_item (ctx, AUDIT_BAD_DATA_CIPHER_ALGO, 0);
915   if (item && item->string)
916     {
917       algo = gcry_cipher_map_name (item->string);
918       if (algo)
919         writeout_rem (ctx, _("algorithm: %s"), gnupg_cipher_algo_name (algo));
920       else if (item->string && !strcmp (item->string, "1.2.840.113549.3.2"))
921         writeout_rem (ctx, _("unsupported algorithm: %s"), "RC2");
922       else if (item->string)
923         writeout_rem (ctx, _("unsupported algorithm: %s"), item->string);
924       else
925         writeout_rem (ctx, _("seems to be not encrypted"));
926     }
927
928
929   for (recpno = 0, item = NULL;
930        (item = find_next_log_item (ctx, item, AUDIT_NEW_RECP, 0)); recpno++)
931     ;
932   snprintf (numbuf, sizeof numbuf, "%d", recpno);
933   writeout_li (ctx, numbuf, "%s", _("Number of recipients"));
934
935   /* Loop over all recipients.  */
936   loopitem = NULL;
937   while ((loopitem = find_next_log_item (ctx, loopitem, AUDIT_NEW_RECP, 0)))
938     {
939       const char *result;
940
941       recpno = loopitem->have_intvalue? loopitem->intvalue : -1;
942
943       item = find_next_log_item (ctx, loopitem,
944                                  AUDIT_RECP_RESULT, AUDIT_NEW_RECP);
945       if (!item)
946         result = "not-used";
947       else if (!item->err)
948         result = "okay";
949       else if (gpg_err_code (item->err) == GPG_ERR_CANCELED)
950         result = "skipped";
951       else
952         result = gpg_strerror (item->err);
953
954       item = find_next_log_item (ctx, loopitem,
955                                  AUDIT_RECP_NAME, AUDIT_NEW_RECP);
956       writeout_li (ctx, result, _("Recipient %d"), recpno);
957       if (item && item->string)
958         writeout_rem (ctx, "%s", item->string);
959
960       /* If we have a certificate write out more infos.  */
961       item = find_next_log_item (ctx, loopitem,
962                                  AUDIT_SAVE_CERT, AUDIT_NEW_RECP);
963       if (item && item->cert)
964         {
965           enter_li (ctx);
966           for (idx=0; (name = get_cert_subject (item->cert, idx)); idx++)
967             {
968               writeout_rem (ctx, "%s", name);
969               xfree (name);
970             }
971           leave_li (ctx);
972         }
973     }
974
975   leave_li (ctx);
976 }
977
978
979 \f
980 /* Process a verification operation's log.  */
981 static void
982 proc_type_verify (audit_ctx_t ctx)
983 {
984   log_item_t loopitem, item;
985   int signo, count, idx, n_good, n_bad;
986   char numbuf[35];
987   const char *result;
988
989   /* If there is at least one signature status we claim that the
990      verification succeeded.  This does not mean that the data has
991      verified okay.  */
992   item = find_log_item (ctx, AUDIT_SIG_STATUS, 0);
993   writeout_li (ctx, item?"Yes":"No", "%s", _("Data verification succeeded"));
994   enter_li (ctx);
995
996   item = find_log_item (ctx, AUDIT_GOT_DATA, AUDIT_NEW_SIG);
997   writeout_li (ctx, item? "Yes":"No", "%s", _("Data available"));
998   if (!item)
999     goto leave;
1000
1001   item = find_log_item (ctx, AUDIT_NEW_SIG, 0);
1002   writeout_li (ctx, item? "Yes":"No", "%s", _("Signature available"));
1003   if (!item)
1004     goto leave;
1005
1006   /* Print info about the used data hashing algorithms.  */
1007   for (idx=0, n_good=n_bad=0; idx < ctx->logused; idx++)
1008     {
1009       item = ctx->log + idx;
1010       if (item->event == AUDIT_NEW_SIG)
1011         break;
1012       else if (item->event == AUDIT_DATA_HASH_ALGO)
1013         n_good++;
1014       else if (item->event == AUDIT_BAD_DATA_HASH_ALGO)
1015         n_bad++;
1016     }
1017   item = find_log_item (ctx, AUDIT_DATA_HASHING, AUDIT_NEW_SIG);
1018   if (!item || item->err || !n_good)
1019     result = "No";
1020   else if (n_good && !n_bad)
1021     result = "Yes";
1022   else
1023     result = "Some";
1024   writeout_li (ctx, result, "%s", _("Parsing data succeeded"));
1025   if (n_good || n_bad)
1026     {
1027       for (idx=0; idx < ctx->logused; idx++)
1028         {
1029           item = ctx->log + idx;
1030           if (item->event == AUDIT_NEW_SIG)
1031             break;
1032           else if (item->event == AUDIT_DATA_HASH_ALGO)
1033             writeout_rem (ctx, _("data hash algorithm: %s"),
1034                           gcry_md_algo_name (item->intvalue));
1035           else if (item->event == AUDIT_BAD_DATA_HASH_ALGO)
1036             writeout_rem (ctx, _("bad data hash algorithm: %s"),
1037                           item->string? item->string:"?");
1038         }
1039     }
1040
1041
1042   /* Loop over all signatures.  */
1043   loopitem = find_log_item (ctx, AUDIT_NEW_SIG, 0);
1044   assert (loopitem);
1045   do
1046     {
1047       signo = loopitem->have_intvalue? loopitem->intvalue : -1;
1048
1049       item = find_next_log_item (ctx, loopitem,
1050                                  AUDIT_SIG_STATUS, AUDIT_NEW_SIG);
1051       writeout_li (ctx, item? item->string:"?", _("Signature %d"), signo);
1052       item = find_next_log_item (ctx, loopitem,
1053                                  AUDIT_SIG_NAME, AUDIT_NEW_SIG);
1054       if (item)
1055         writeout_rem (ctx, "%s", item->string);
1056
1057       item = find_next_log_item (ctx, loopitem,
1058                                  AUDIT_DATA_HASH_ALGO, AUDIT_NEW_SIG);
1059       if (item)
1060         writeout_rem (ctx, _("data hash algorithm: %s"),
1061                       gcry_md_algo_name (item->intvalue));
1062       item = find_next_log_item (ctx, loopitem,
1063                                  AUDIT_ATTR_HASH_ALGO, AUDIT_NEW_SIG);
1064       if (item)
1065         writeout_rem (ctx, _("attr hash algorithm: %s"),
1066                       gcry_md_algo_name (item->intvalue));
1067
1068       enter_li (ctx);
1069
1070       /* List the certificate chain.  */
1071       list_certchain (ctx, loopitem, AUDIT_NEW_SIG);
1072
1073       /* Show the result of the chain validation.  */
1074       item = find_next_log_item (ctx, loopitem,
1075                                  AUDIT_CHAIN_STATUS, AUDIT_NEW_SIG);
1076       if (item && item->have_err)
1077         {
1078           writeout_li (ctx, item->err? "No":"Yes",
1079                        _("Certificate chain valid"));
1080           if (item->err)
1081             writeout_rem (ctx, "%s", gpg_strerror (item->err));
1082         }
1083
1084       /* Show whether the root certificate is fine.  */
1085       item = find_next_log_item (ctx, loopitem,
1086                                  AUDIT_ROOT_TRUSTED, AUDIT_CHAIN_STATUS);
1087       if (item)
1088         {
1089           writeout_li (ctx, item->err?"No":"Yes", "%s",
1090                        _("Root certificate trustworthy"));
1091           if (item->err)
1092             {
1093               add_helptag (ctx, "gpgsm.root-cert-not-trusted");
1094               writeout_rem (ctx, "%s", gpg_strerror (item->err));
1095               list_cert (ctx, item->cert, 0);
1096             }
1097         }
1098
1099       /* Show result of the CRL/OCSP check.  */
1100       item = find_next_log_item (ctx, loopitem,
1101                                  AUDIT_CRL_CHECK, AUDIT_NEW_SIG);
1102       if (item)
1103         {
1104           const char *ok;
1105           switch (gpg_err_code (item->err))
1106             {
1107             case 0:                    ok = "good"; break;
1108             case GPG_ERR_CERT_REVOKED: ok = "bad"; break;
1109             case GPG_ERR_NOT_ENABLED:  ok = "disabled"; break;
1110             case GPG_ERR_NO_CRL_KNOWN:
1111               ok = _("no CRL found for certificate");
1112               break;
1113             case GPG_ERR_CRL_TOO_OLD:
1114               ok = _("the available CRL is too old");
1115               break;
1116             default: ok = gpg_strerror (item->err); break;
1117             }
1118
1119           writeout_li (ctx, ok, "%s", _("CRL/OCSP check of certificates"));
1120           if (item->err
1121               && gpg_err_code (item->err) != GPG_ERR_CERT_REVOKED
1122               && gpg_err_code (item->err) != GPG_ERR_NOT_ENABLED)
1123             add_helptag (ctx, "gpgsm.crl-problem");
1124         }
1125
1126       leave_li (ctx);
1127     }
1128   while ((loopitem = find_next_log_item (ctx, loopitem, AUDIT_NEW_SIG, 0)));
1129
1130
1131  leave:
1132   /* Always list the certificates stored in the signature.  */
1133   item = NULL;
1134   count = 0;
1135   while ( ((item = find_next_log_item (ctx, item,
1136                                        AUDIT_SAVE_CERT, AUDIT_NEW_SIG))))
1137     count++;
1138   snprintf (numbuf, sizeof numbuf, "%d", count);
1139   writeout_li (ctx, numbuf, _("Included certificates"));
1140   item = NULL;
1141   while ( ((item = find_next_log_item (ctx, item,
1142                                        AUDIT_SAVE_CERT, AUDIT_NEW_SIG))))
1143     {
1144       char *name = get_cert_name (item->cert);
1145       writeout_rem (ctx, "%s", name);
1146       xfree (name);
1147       enter_li (ctx);
1148       for (idx=0; (name = get_cert_subject (item->cert, idx)); idx++)
1149         {
1150           writeout_rem (ctx, "%s", name);
1151           xfree (name);
1152         }
1153       leave_li (ctx);
1154     }
1155   leave_li (ctx);
1156 }
1157
1158
1159
1160 \f
1161 /* Print the formatted audit result.    THIS IS WORK IN PROGRESS.  */
1162 void
1163 audit_print_result (audit_ctx_t ctx, estream_t out, int use_html)
1164 {
1165   int idx;
1166   size_t n;
1167   log_item_t item;
1168   helptag_t helptag;
1169   const char *s;
1170   int show_raw = 0;
1171   char *orig_codeset;
1172
1173   if (!ctx)
1174     return;
1175
1176   orig_codeset = i18n_switchto_utf8 ();
1177
1178   /* We use an environment variable to include some debug info in the
1179      log.  */
1180   if ((s = getenv ("gnupg_debug_audit")))
1181     show_raw = 1;
1182
1183   assert (!ctx->outstream);
1184   ctx->outstream = out;
1185   ctx->use_html = use_html;
1186   ctx->indentlevel = 0;
1187   clear_helptags (ctx);
1188
1189   if (use_html)
1190     es_fputs ("<div class=\"" GNUPG_NAME "AuditLog\">\n", ctx->outstream);
1191
1192   if (!ctx->log || !ctx->logused)
1193     {
1194       writeout_para (ctx, _("No audit log entries."));
1195       goto leave;
1196     }
1197
1198   if (show_raw)
1199     {
1200       int maxlen;
1201
1202       for (idx=0,maxlen=0; idx < DIM (eventstr_msgidx); idx++)
1203         {
1204           n = strlen (eventstr_msgstr + eventstr_msgidx[idx]);
1205           if (n > maxlen)
1206             maxlen = n;
1207         }
1208
1209       if (use_html)
1210         es_fputs ("<pre>\n", out);
1211       for (idx=0; idx < ctx->logused; idx++)
1212         {
1213           es_fprintf (out, "log: %-*s",
1214                       maxlen, event2str (ctx->log[idx].event));
1215           if (ctx->log[idx].have_intvalue)
1216             es_fprintf (out, " i=%d", ctx->log[idx].intvalue);
1217           if (ctx->log[idx].string)
1218             {
1219               es_fputs (" s='", out);
1220               writeout (ctx, ctx->log[idx].string);
1221               es_fputs ("'", out);
1222             }
1223           if (ctx->log[idx].cert)
1224             es_fprintf (out, " has_cert");
1225           if (ctx->log[idx].have_err)
1226             {
1227               es_fputs (" err='", out);
1228               writeout (ctx, gpg_strerror (ctx->log[idx].err));
1229               es_fputs ("'", out);
1230             }
1231           es_fputs ("\n", out);
1232         }
1233       if (use_html)
1234         es_fputs ("</pre>\n", out);
1235       else
1236         es_fputs ("\n", out);
1237     }
1238
1239   enter_li (ctx);
1240   switch (ctx->type)
1241     {
1242     case AUDIT_TYPE_NONE:
1243       writeout_li (ctx, NULL, _("Unknown operation"));
1244       break;
1245     case AUDIT_TYPE_ENCRYPT:
1246       proc_type_encrypt (ctx);
1247       break;
1248     case AUDIT_TYPE_SIGN:
1249       proc_type_sign (ctx);
1250       break;
1251     case AUDIT_TYPE_DECRYPT:
1252       proc_type_decrypt (ctx);
1253       break;
1254     case AUDIT_TYPE_VERIFY:
1255       proc_type_verify (ctx);
1256       break;
1257     }
1258   item = find_log_item (ctx, AUDIT_AGENT_READY, 0);
1259   if (item && item->have_err)
1260     {
1261       writeout_li (ctx, item->err? "No":"Yes", "%s", _("Gpg-Agent usable"));
1262       if (item->err)
1263         {
1264           writeout_rem (ctx, "%s", gpg_strerror (item->err));
1265           add_helptag (ctx, "gnupg.agent-problem");
1266         }
1267     }
1268   item = find_log_item (ctx, AUDIT_DIRMNGR_READY, 0);
1269   if (item && item->have_err)
1270     {
1271       writeout_li (ctx, item->err? "No":"Yes", "%s", _("Dirmngr usable"));
1272       if (item->err)
1273         {
1274           writeout_rem (ctx, "%s", gpg_strerror (item->err));
1275           add_helptag (ctx, "gnupg.dirmngr-problem");
1276         }
1277     }
1278   leave_li (ctx);
1279
1280
1281   /* Show the help from the collected help tags.  */
1282   if (ctx->helptags)
1283     {
1284       if (use_html)
1285         {
1286           es_fputs ("<hr/>\n", ctx->outstream);
1287           if (ctx->helptags->next)
1288             es_fputs ("<ul>\n", ctx->outstream);
1289         }
1290       else
1291         es_fputs ("\n\n", ctx->outstream);
1292     }
1293   for (helptag = ctx->helptags; helptag; helptag = helptag->next)
1294     {
1295       char *text;
1296
1297       if (use_html && ctx->helptags->next)
1298         es_fputs ("<li>\n", ctx->outstream);
1299
1300       text = gnupg_get_help_string (helptag->name, 0);
1301       if (text)
1302         {
1303           writeout_para (ctx, "%s", text);
1304           xfree (text);
1305         }
1306       else
1307         writeout_para (ctx, _("No help available for '%s'."), helptag->name);
1308       if (use_html && ctx->helptags->next)
1309         es_fputs ("</li>\n", ctx->outstream);
1310       if (helptag->next)
1311         es_fputs ("\n", ctx->outstream);
1312     }
1313   if (use_html && ctx->helptags && ctx->helptags->next)
1314     es_fputs ("</ul>\n", ctx->outstream);
1315
1316  leave:
1317   if (use_html)
1318     es_fputs ("</div>\n", ctx->outstream);
1319   ctx->outstream = NULL;
1320   ctx->use_html = 0;
1321   clear_helptags (ctx);
1322   i18n_switchback (orig_codeset);
1323 }