3bb128d519a9cfdbc15227294f17f954c515d6b6
[gnupg.git] / common / audit.c
1 /* audit.c - GnuPG's audit subsystem
2  *      Copyright (C) 2007 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 interger 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, ...) JNLIB_GCC_A_PRINTF(2,3);
76 static void writeout_li (audit_ctx_t ctx, const char *oktext,
77                          const char *format, ...) JNLIB_GCC_A_PRINTF(3,4);
78 static void writeout_rem (audit_ctx_t ctx, 
79                           const char *format, ...) JNLIB_GCC_A_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 oepration
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   estream_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         color = "green";
456       else if (!strcmp (oktext, "No"))
457         color = "red";
458     }
459
460   if (format && oktext)
461     {
462       if (!strcmp (oktext, "Yes"))
463         oktext = _("Yes");
464       else if (!strcmp (oktext, "No"))
465         oktext = _("No");
466     }
467
468   if (ctx->use_html)
469     {
470       int i;
471
472       es_fputs ("  <tr><td><table><tr><td>", ctx->outstream);
473       if (color)
474         es_fprintf (ctx->outstream, "<font color=\"%s\">*</font>", color);
475       else
476         es_fputs ("*", ctx->outstream);
477       for (i=1; i < ctx->indentlevel; i++)
478         es_fputs ("&nbsp;&nbsp;", ctx->outstream);
479       es_fputs ("</td><td>", ctx->outstream);
480     }
481   else
482     es_fprintf (ctx->outstream, "* %*s", (ctx->indentlevel-1)*2, "");
483   if (format)
484     {
485       va_start (arg_ptr, format) ;
486       writeout_v (ctx, format, arg_ptr);
487       va_end (arg_ptr);
488     }
489   if (ctx->use_html)
490     es_fputs ("</td></tr></table>", ctx->outstream);
491   if (format && oktext)
492     {
493       if (ctx->use_html)
494         {
495           es_fputs ("</td><td>", ctx->outstream);
496           if (color)
497             es_fprintf (ctx->outstream, "<font color=\"%s\">", color);
498         }
499       else  
500         writeout (ctx, ":         ");
501       writeout (ctx, oktext);
502       if (color)
503         es_fputs ("</font>", ctx->outstream);
504     }
505   
506   if (ctx->use_html)
507     es_fputs ("</td></tr>\n", ctx->outstream);
508   else
509     es_fputc ('\n', ctx->outstream);
510 }
511
512
513 /* Write a remark line.  */
514 static void
515 writeout_rem (audit_ctx_t ctx, const char *format, ...)
516 {
517   va_list arg_ptr;
518
519   if (ctx->use_html)
520     {
521       int i;
522
523       es_fputs ("  <tr><td><table><tr><td>*", ctx->outstream);
524       for (i=1; i < ctx->indentlevel; i++)
525         es_fputs ("&nbsp;&nbsp;", ctx->outstream);
526       es_fputs ("&nbsp;&nbsp;&nbsp;</td><td> (", ctx->outstream);
527
528     }
529   else
530     es_fprintf (ctx->outstream, "* %*s  (", (ctx->indentlevel-1)*2, "");
531   if (format)
532     {
533       va_start (arg_ptr, format) ;
534       writeout_v (ctx, format, arg_ptr);
535       va_end (arg_ptr);
536     }
537   if (ctx->use_html)
538     es_fputs (")</td></tr></table></td></tr>\n", ctx->outstream);
539   else
540     es_fputs (")\n", ctx->outstream);
541 }
542
543
544 /* Return the first log item for EVENT.  If STOPEVENT is not 0 never
545    look behind that event in the log. If STARTITEM is not NULL start
546    search _after_that item.  */
547 static log_item_t
548 find_next_log_item (audit_ctx_t ctx, log_item_t startitem, 
549                     audit_event_t event, audit_event_t stopevent)
550 {
551   int idx;
552
553   for (idx=0; idx < ctx->logused; idx++)
554     {
555       if (startitem)
556         {
557           if (ctx->log + idx == startitem)
558             startitem = NULL;
559         }
560       else if (stopevent && ctx->log[idx].event == stopevent)
561         break;
562       else if (ctx->log[idx].event == event)
563         return ctx->log + idx;
564     }
565   return NULL;
566 }
567
568
569 static log_item_t
570 find_log_item (audit_ctx_t ctx, audit_event_t event, audit_event_t stopevent)
571 {
572   return find_next_log_item (ctx, NULL, event, stopevent);
573 }
574
575
576 /* Helper to a format a serial number.  */
577 static char *
578 format_serial (ksba_const_sexp_t sn)
579 {
580   const char *p = (const char *)sn;
581   unsigned long n;
582   char *endp;
583
584   if (!p)
585     return NULL;
586   if (*p != '(')
587     BUG (); /* Not a valid S-expression. */
588   n = strtoul (p+1, &endp, 10);
589   p = endp;
590   if (*p != ':')
591     BUG (); /* Not a valid S-expression. */
592   return bin2hex (p+1, n, NULL);
593 }
594
595
596 /* Return a malloced string with the serial number and the issuer DN
597    of the certificate.  */
598 static char *
599 get_cert_name (ksba_cert_t cert)
600 {
601   char *result;
602   ksba_sexp_t sn;
603   char *issuer, *p;
604
605   if (!cert)
606     return xtrystrdup ("[no certificate]");
607
608   issuer = ksba_cert_get_issuer (cert, 0);
609   sn = ksba_cert_get_serial (cert);
610   if (issuer && sn)
611     {
612       p = format_serial (sn);
613       if (!p)
614         result = xtrystrdup ("[invalid S/N]");
615       else
616         {
617           result = xtrymalloc (strlen (p) + strlen (issuer) + 2 + 1);
618           if (result)
619             {
620               *result = '#';
621               strcpy (stpcpy (stpcpy (result+1, p),"/"), issuer);
622             }
623           xfree (p);
624         }
625     }
626   else
627     result = xtrystrdup ("[missing S/N or issuer]");
628   ksba_free (sn);
629   xfree (issuer);
630   return result;
631 }
632
633 /* Return a malloced string with the serial number and the issuer DN
634    of the certificate.  */
635 static char *
636 get_cert_subject (ksba_cert_t cert, int idx)
637 {
638   char *result;
639   char *subject;
640
641   if (!cert)
642     return xtrystrdup ("[no certificate]");
643
644   subject = ksba_cert_get_subject (cert, idx);
645   if (subject)
646     {
647       result = xtrymalloc (strlen (subject) + 1 + 1);
648       if (result)
649         {
650           *result = '/';
651           strcpy (result+1, subject);
652         }
653     }
654   else
655     result = NULL;
656   xfree (subject);
657   return result;
658 }
659
660
661 /* List the given certificiate.  If CERT is NULL, this is a NOP.  */
662 static void
663 list_cert (audit_ctx_t ctx, ksba_cert_t cert, int with_subj)
664 {
665   char *name;
666   int idx;
667
668   name = get_cert_name (cert);
669   writeout_rem (ctx, "%s", name);
670   xfree (name);
671   if (with_subj)
672     {
673       enter_li (ctx);
674       for (idx=0; (name = get_cert_subject (cert, idx)); idx++)
675         {
676           writeout_rem (ctx, "%s", name);
677           xfree (name);
678         }
679       leave_li (ctx);
680     }
681 }
682
683
684 /* List the chain of certificates from STARTITEM up to STOPEVENT.  The
685    certifcates are written out as comments.  */
686 static void
687 list_certchain (audit_ctx_t ctx, log_item_t startitem, audit_event_t stopevent)
688 {
689   log_item_t item;
690
691   startitem = find_next_log_item (ctx, startitem, AUDIT_CHAIN_BEGIN,stopevent);
692   writeout_li (ctx, startitem? "Yes":"No", _("Certificate chain available"));
693   if (!startitem)
694     return; 
695
696   item = find_next_log_item (ctx, startitem, 
697                              AUDIT_CHAIN_ROOTCERT, AUDIT_CHAIN_END);
698   if (!item)
699     writeout_rem (ctx, "%s", _("root certificate missing"));
700   else
701     {
702       list_cert (ctx, item->cert, 0);
703     }
704   item = startitem;
705   while ( ((item = find_next_log_item (ctx, item, 
706                                        AUDIT_CHAIN_CERT, AUDIT_CHAIN_END))))
707     {
708       list_cert (ctx, item->cert, 1);
709     }
710 }
711
712
713 \f
714 /* Process an encrypt operation's log.  */
715 static void
716 proc_type_encrypt (audit_ctx_t ctx)
717 {
718   log_item_t loopitem, item;
719   int recp_no, idx;
720   char numbuf[35];
721   int algo;
722   char *name;
723
724   item = find_log_item (ctx, AUDIT_ENCRYPTION_DONE, 0);
725   writeout_li (ctx, item?"Yes":"No", "%s", _("Data encryption succeeded"));
726
727   enter_li (ctx);
728
729   item = find_log_item (ctx, AUDIT_GOT_DATA, 0);
730   writeout_li (ctx, item? "Yes":"No", "%s", _("Data available"));
731
732   item = find_log_item (ctx, AUDIT_SESSION_KEY, 0);
733   writeout_li (ctx, item? "Yes":"No", "%s", _("Session key created"));
734   if (item)
735     {
736       algo = gcry_cipher_map_name (item->string);
737       if (algo)
738         writeout_rem (ctx, _("algorithm: %s"), gcry_cipher_algo_name (algo));
739       else if (item->string && !strcmp (item->string, "1.2.840.113549.3.2"))
740         writeout_rem (ctx, _("unsupported algorithm: %s"), "RC2");
741       else if (item->string)
742         writeout_rem (ctx, _("unsupported algorithm: %s"), item->string);
743       else
744         writeout_rem (ctx, _("seems to be not encrypted"));
745     }
746
747   item = find_log_item (ctx, AUDIT_GOT_RECIPIENTS, 0);
748   snprintf (numbuf, sizeof numbuf, "%d", 
749             item && item->have_intvalue? item->intvalue : 0);
750   writeout_li (ctx, numbuf, "%s", _("Number of recipients"));
751
752   /* Loop over all recipients.  */
753   loopitem = NULL;
754   recp_no = 0;
755   while ((loopitem=find_next_log_item (ctx, loopitem, AUDIT_ENCRYPTED_TO, 0)))
756     {
757       recp_no++;
758       writeout_li (ctx, NULL, _("Recipient %d"), recp_no);
759       if (loopitem->cert)
760         {
761           name = get_cert_name (loopitem->cert);
762           writeout_rem (ctx, "%s", name);
763           xfree (name);
764           enter_li (ctx);
765           for (idx=0; (name = get_cert_subject (loopitem->cert, idx)); idx++)
766             {
767               writeout_rem (ctx, "%s", name);
768               xfree (name);
769             }
770           leave_li (ctx);
771         }
772     }
773
774   leave_li (ctx);
775 }
776
777
778 \f
779 /* Process a sign operation's log.  */
780 static void
781 proc_type_sign (audit_ctx_t ctx)
782 {
783   log_item_t item;
784
785   item = NULL;
786   writeout_li (ctx, item?"Yes":"No", "%s", _("Data signing succeeded"));
787
788   enter_li (ctx);
789
790   item = find_log_item (ctx, AUDIT_GOT_DATA, 0);
791   writeout_li (ctx, item? "Yes":"No", "%s", _("Data available"));
792
793
794   leave_li (ctx);
795 }
796
797
798 \f
799 /* Process a decrypt operation's log.  */
800 static void
801 proc_type_decrypt (audit_ctx_t ctx)
802 {
803   log_item_t item;
804
805   item = NULL;
806   writeout_li (ctx, item?"Yes":"No", "%s", _("Data decryption succeeded"));
807
808   enter_li (ctx);
809
810   item = find_log_item (ctx, AUDIT_GOT_DATA, 0);
811   writeout_li (ctx, item? "Yes":"No", "%s", _("Data available"));
812
813
814   leave_li (ctx);
815 }
816
817
818 \f
819 /* Process a verification operation's log.  */
820 static void
821 proc_type_verify (audit_ctx_t ctx)
822 {
823   log_item_t loopitem, item;
824   int signo, count, idx;
825   char numbuf[35];
826
827   /* If there is at least one signature status we claim that the
828      verifciation succeeded.  This does not mean that the data has
829      verified okay.  */
830   item = find_log_item (ctx, AUDIT_SIG_STATUS, 0);
831   writeout_li (ctx, item?"Yes":"No", "%s", _("Data verification succeeded"));
832   enter_li (ctx);
833
834   item = find_log_item (ctx, AUDIT_GOT_DATA, AUDIT_NEW_SIG);
835   writeout_li (ctx, item? "Yes":"No", "%s", _("Data available"));
836   if (!item)
837     goto leave;
838
839   item = find_log_item (ctx, AUDIT_NEW_SIG, 0);
840   writeout_li (ctx, item? "Yes":"No", "%s", _("Signature available"));
841   if (!item)
842     goto leave;
843
844   item = find_log_item (ctx, AUDIT_DATA_HASH_ALGO, AUDIT_NEW_SIG);
845   writeout_li (ctx, item?"Yes":"No", "%s", _("Parsing signature succeeded"));
846   if (!item)
847     {
848       item = find_log_item (ctx, AUDIT_BAD_DATA_HASH_ALGO, AUDIT_NEW_SIG);
849       if (item)
850         writeout_rem (ctx, _("Bad hash algorithm: %s"), 
851                       item->string? item->string:"?");
852
853       goto leave;
854     }
855
856   /* Loop over all signatures.  */
857   loopitem = find_log_item (ctx, AUDIT_NEW_SIG, 0);
858   assert (loopitem);
859   do
860     {
861       signo = loopitem->have_intvalue? loopitem->intvalue : -1;
862
863       item = find_next_log_item (ctx, loopitem,
864                                  AUDIT_SIG_STATUS, AUDIT_NEW_SIG);
865       writeout_li (ctx, item? item->string:"?", _("Signature %d"), signo);
866       item = find_next_log_item (ctx, loopitem,
867                                  AUDIT_SIG_NAME, AUDIT_NEW_SIG);
868       if (item)
869         writeout_rem (ctx, "%s", item->string);
870       enter_li (ctx);
871       
872       /* List the certificate chain.  */
873       list_certchain (ctx, loopitem, AUDIT_NEW_SIG);
874
875       /* Show the result of the chain validation.  */
876       item = find_next_log_item (ctx, loopitem,
877                                  AUDIT_CHAIN_STATUS, AUDIT_NEW_SIG);
878       if (item && item->have_err)
879         {
880           writeout_li (ctx, item->err? "No":"Yes", 
881                        _("Certificate chain valid"));
882           if (item->err)
883             writeout_rem (ctx, "%s", gpg_strerror (item->err));
884         }
885       
886       /* Show whether the root certificate is fine.  */
887       item = find_next_log_item (ctx, loopitem,
888                                  AUDIT_ROOT_TRUSTED, AUDIT_CHAIN_STATUS);
889       if (item)
890         {
891           writeout_li (ctx, item->err?"No":"Yes", "%s",
892                        _("Root certificate trustworthy"));
893           if (item->err)
894             {
895               add_helptag (ctx, "gpgsm.root-cert-not-trusted");
896               writeout_rem (ctx, "%s", gpg_strerror (item->err));
897               list_cert (ctx, item->cert, 0);
898             }
899         }
900
901       /* Show result of the CRL/OCSP check.  */
902       writeout_li (ctx, "-", "%s", _("CRL/OCSP check of certificates"));
903  /*      add_helptag (ctx, "gpgsm.ocsp-problem"); */
904
905
906       leave_li (ctx);
907     }
908   while ((loopitem = find_next_log_item (ctx, loopitem, AUDIT_NEW_SIG, 0)));
909
910
911  leave:
912   /* Always list the certificates stored in the signature.  */
913   item = NULL;
914   count = 0;
915   while ( ((item = find_next_log_item (ctx, item, 
916                                        AUDIT_SAVE_CERT, AUDIT_NEW_SIG))))
917     count++;
918   snprintf (numbuf, sizeof numbuf, "%d", count);
919   writeout_li (ctx, numbuf, _("Included certificates"));
920   item = NULL;
921   while ( ((item = find_next_log_item (ctx, item, 
922                                        AUDIT_SAVE_CERT, AUDIT_NEW_SIG))))
923     {
924       char *name = get_cert_name (item->cert);
925       writeout_rem (ctx, "%s", name);
926       xfree (name);
927       enter_li (ctx);
928       for (idx=0; (name = get_cert_subject (item->cert, idx)); idx++)
929         {
930           writeout_rem (ctx, "%s", name);
931           xfree (name);
932         }
933       leave_li (ctx);
934     }
935   leave_li (ctx);
936 }
937
938
939
940 \f
941 /* Print the formatted audit result.    THIS IS WORK IN PROGRESS.  */
942 void
943 audit_print_result (audit_ctx_t ctx, estream_t out, int use_html)
944 {
945   int idx;
946   size_t n;
947   log_item_t item;
948   helptag_t helptag;
949   const char *s;
950   int show_raw = 0;
951   char *orig_codeset;
952   
953   if (!ctx)
954     return;
955
956   orig_codeset = i18n_switchto_utf8 ();
957
958   /* We use an environment variable to include some debug info in the
959      log.  */
960   if ((s = getenv ("gnupg_debug_audit")))
961     {
962       show_raw = 1;
963       if (!strcmp (s, "html"))
964         use_html = 1;
965     }
966
967   assert (!ctx->outstream);
968   ctx->outstream = out;
969   ctx->use_html = use_html;
970   ctx->indentlevel = 0;
971   clear_helptags (ctx);
972
973   if (use_html)
974     es_fputs ("<div class=\"GnuPGAuditLog\">\n", ctx->outstream);
975
976   if (!ctx->log || !ctx->logused)
977     {
978       writeout_para (ctx, _("No audit log entries."));
979       goto leave;
980     }
981
982   if (show_raw)
983     {
984       int maxlen;
985
986       for (idx=0,maxlen=0; idx < DIM (eventstr_msgidx); idx++)
987         {
988           n = strlen (eventstr_msgstr + eventstr_msgidx[idx]);    
989           if (n > maxlen)
990             maxlen = n;
991         }
992       
993       if (use_html)
994         es_fputs ("<pre>\n", out);
995       for (idx=0; idx < ctx->logused; idx++)
996         {
997           es_fprintf (out, "log: %-*s", 
998                       maxlen, event2str (ctx->log[idx].event));
999           if (ctx->log[idx].have_intvalue)
1000             es_fprintf (out, " i=%d", ctx->log[idx].intvalue); 
1001           if (ctx->log[idx].string)
1002             {
1003               es_fputs (" s=`", out); 
1004               writeout (ctx, ctx->log[idx].string); 
1005               es_fputs ("'", out); 
1006             }
1007           if (ctx->log[idx].cert)
1008             es_fprintf (out, " has_cert"); 
1009           if (ctx->log[idx].have_err)
1010             {
1011               es_fputs (" err=`", out);
1012               writeout (ctx, gpg_strerror (ctx->log[idx].err)); 
1013               es_fputs ("'", out);
1014             }
1015           es_fputs ("\n", out);
1016         }
1017       if (use_html)
1018         es_fputs ("</pre>\n", out);
1019       else
1020         es_fputs ("\n", out);
1021     }
1022
1023   enter_li (ctx);
1024   switch (ctx->type)
1025     {
1026     case AUDIT_TYPE_NONE:
1027       writeout_li (ctx, NULL, _("Unknown operation"));
1028       break;
1029     case AUDIT_TYPE_ENCRYPT:
1030       proc_type_encrypt (ctx);
1031       break;
1032     case AUDIT_TYPE_SIGN:
1033       proc_type_sign (ctx);
1034       break;
1035     case AUDIT_TYPE_DECRYPT:
1036       proc_type_decrypt (ctx);
1037       break;
1038     case AUDIT_TYPE_VERIFY:
1039       proc_type_verify (ctx);
1040       break;
1041     }
1042   item = find_log_item (ctx, AUDIT_AGENT_READY, 0);
1043   if (item && item->have_err)
1044     {
1045       writeout_li (ctx, item->err? "No":"Yes", "%s", _("Gpg-Agent usable"));
1046       if (item->err)
1047         {
1048           writeout_rem (ctx, "%s", gpg_strerror (item->err));
1049           add_helptag (ctx, "gnupg.agent-problem");
1050         }
1051     }
1052   item = find_log_item (ctx, AUDIT_DIRMNGR_READY, 0);
1053   if (item && item->have_err)
1054     {
1055       writeout_li (ctx, item->err? "No":"Yes", "%s", _("Dirmngr usable"));
1056       if (item->err)
1057         {
1058           writeout_rem (ctx, "%s", gpg_strerror (item->err));
1059           add_helptag (ctx, "gnupg.dirmngr-problem");
1060         }
1061     }
1062   leave_li (ctx);
1063
1064
1065   /* Show the help from the collected help tags.  */
1066   if (ctx->helptags)
1067     {
1068       if (use_html)
1069         {
1070           es_fputs ("<hr/>\n", ctx->outstream);
1071           if (ctx->helptags->next)
1072             es_fputs ("<ul>\n", ctx->outstream);
1073         }
1074       else
1075         es_fputs ("\n\n", ctx->outstream);
1076     }
1077   for (helptag = ctx->helptags; helptag; helptag = helptag->next)
1078     {
1079       char *text;
1080
1081       if (use_html && ctx->helptags->next)
1082         es_fputs ("<li>\n", ctx->outstream);
1083
1084       text = gnupg_get_help_string (helptag->name, 0);
1085       if (text)
1086         {
1087           writeout_para (ctx, "%s", text);
1088           xfree (text);
1089         }
1090       else
1091         writeout_para (ctx, _("No help available for `%s'."), helptag->name);
1092       if (use_html && ctx->helptags->next)
1093         es_fputs ("</li>\n", ctx->outstream);
1094       if (helptag->next)
1095         es_fputs ("\n", ctx->outstream);
1096     }
1097   if (use_html && ctx->helptags && ctx->helptags->next)
1098     es_fputs ("</ul>\n", ctx->outstream);
1099
1100  leave:
1101   if (use_html)
1102     es_fputs ("</div>\n", ctx->outstream);
1103   ctx->outstream = NULL;
1104   ctx->use_html = 0;
1105   clear_helptags (ctx);
1106   i18n_switchback (orig_codeset);
1107 }
1108