Poems for AllowSetForegroundWindow (W32)
[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   int idx = eventstr_msgidxof (event);
119   if (idx == -1)
120     return "Unknown event";
121   else
122     return eventstr_msgstr + eventstr_msgidx[idx];
123 }
124
125
126
127 /* Create a new audit context.  In case of an error NULL is returned
128    and errno set appropriately. */ 
129 audit_ctx_t
130 audit_new (void)
131 {
132   audit_ctx_t ctx;
133
134   ctx = xtrycalloc (1, sizeof *ctx);
135
136   return ctx;
137 }
138
139
140 /* Release an audit context.  Passing NULL for CTX is allowed and does
141    nothing.  */
142 void
143 audit_release (audit_ctx_t ctx)
144 {
145   int idx;
146   if (!ctx)
147     return;
148   if (ctx->log)
149     {
150       for (idx=0; idx < ctx->logused; idx++)
151         {
152           if (ctx->log[idx].string)
153             xfree (ctx->log[idx].string);
154           if (ctx->log[idx].cert)
155             ksba_cert_release (ctx->log[idx].cert);
156         }
157       xfree (ctx->log);
158     }
159   clear_helptags (ctx);
160   xfree (ctx);
161 }
162
163
164 /* Set the type for the audit operation.  If CTX is NULL, this is a
165    dummy fucntion.  */
166 void
167 audit_set_type (audit_ctx_t ctx, audit_type_t type)
168 {
169   if (!ctx || ctx->failure)
170     return;  /* Audit not enabled or an internal error has occurred. */
171
172   if (ctx->type && ctx->type != type)
173     {
174       ctx->failure = "conflict in type initialization";
175       return;
176     }
177   ctx->type = type;
178 }
179
180
181 /* Create a new log item and put it into the table.  Return that log
182    item on success; return NULL on memory failure and mark that in
183    CTX. */
184 static log_item_t
185 create_log_item (audit_ctx_t ctx)
186 {
187   log_item_t item, table;
188   size_t size;
189
190   if (!ctx->log)
191     {
192       size = 10;
193       table = xtrymalloc (size * sizeof *table);
194       if (!table)
195         {
196           ctx->failure = "Out of memory in create_log_item";
197           return NULL;
198         }
199       ctx->log = table;
200       ctx->logsize = size;
201       item = ctx->log + 0;
202       ctx->logused = 1;
203     }
204   else if (ctx->logused >= ctx->logsize)
205     {
206       size = ctx->logsize + 10;
207       table = xtryrealloc (ctx->log, size * sizeof *table);
208       if (!table)
209         {
210           ctx->failure = "Out of memory while reallocating in create_log_item";
211           return NULL;
212         }
213       ctx->log = table;
214       ctx->logsize = size;
215       item = ctx->log + ctx->logused++;
216     }
217   else
218     item = ctx->log + ctx->logused++;
219
220   item->event = AUDIT_NULL_EVENT;
221   item->err = 0;
222   item->have_err = 0;
223   item->intvalue = 0;
224   item->have_intvalue = 0;
225   item->string = NULL;
226   item->cert = NULL;
227
228   return item;
229  
230 }
231
232 /* Add a new event to the audit log.  If CTX is NULL, this function
233    does nothing.  */
234 void
235 audit_log (audit_ctx_t ctx, audit_event_t event)
236 {
237   log_item_t item;
238
239   if (!ctx || ctx->failure)
240     return;  /* Audit not enabled or an internal error has occurred. */
241   if (!event)
242     {
243       ctx->failure = "Invalid event passed to audit_log";
244       return;
245     }
246   if (!(item = create_log_item (ctx)))
247     return;
248   item->event = event;
249 }
250
251 /* Add a new event to the audit log.  If CTX is NULL, this function
252    does nothing.  This version also adds the result of the oepration
253    to the log.. */
254 void
255 audit_log_ok (audit_ctx_t ctx, audit_event_t event, gpg_error_t err)
256 {
257   log_item_t item;
258
259   if (!ctx || ctx->failure)
260     return;  /* Audit not enabled or an internal error has occurred. */
261   if (!event)
262     {
263       ctx->failure = "Invalid event passed to audit_log_ok";
264       return;
265     }
266   if (!(item = create_log_item (ctx)))
267     return;
268   item->event = event;
269   item->err = err;
270   item->have_err = 1;
271 }
272
273
274 /* Add a new event to the audit log.  If CTX is NULL, this function
275    does nothing.  This version also add the integer VALUE to the log.  */
276 void
277 audit_log_i (audit_ctx_t ctx, audit_event_t event, int value)
278 {
279   log_item_t item;
280
281   if (!ctx || ctx->failure)
282     return;  /* Audit not enabled or an internal error has occurred. */
283   if (!event)
284     {
285       ctx->failure = "Invalid event passed to audit_log_i";
286       return;
287     }
288   if (!(item = create_log_item (ctx)))
289     return;
290   item->event = event;
291   item->intvalue = value;
292   item->have_intvalue = 1;
293 }
294
295
296 /* Add a new event to the audit log.  If CTX is NULL, this function
297    does nothing.  This version also add the integer VALUE to the log.  */
298 void
299 audit_log_s (audit_ctx_t ctx, audit_event_t event, const char *value)
300 {
301   log_item_t item;
302   char *tmp;
303
304   if (!ctx || ctx->failure)
305     return;  /* Audit not enabled or an internal error has occurred. */
306   if (!event)
307     {
308       ctx->failure = "Invalid event passed to audit_log_s";
309       return;
310     }
311   tmp = xtrystrdup (value? value : "");
312   if (!tmp)
313     {
314       ctx->failure = "Out of memory in audit_event";
315       return;
316     }
317   if (!(item = create_log_item (ctx)))
318     {
319       xfree (tmp);
320       return;
321     }
322   item->event = event;
323   item->string = tmp;
324 }
325
326 /* Add a new event to the audit log.  If CTX is NULL, this function
327    does nothing.  This version also adds the certificate CERT and the
328    result of an operation to the log.  */
329 void
330 audit_log_cert (audit_ctx_t ctx, audit_event_t event, 
331                 ksba_cert_t cert, gpg_error_t err)
332 {
333   log_item_t item;
334
335   if (!ctx || ctx->failure)
336     return;  /* Audit not enabled or an internal error has occurred. */
337   if (!event)
338     {
339       ctx->failure = "Invalid event passed to audit_log_cert";
340       return;
341     }
342   if (!(item = create_log_item (ctx)))
343     return;
344   item->event = event;
345   item->err = err;
346   item->have_err = 1;
347   if (cert)
348     {
349       ksba_cert_ref (cert); 
350       item->cert = cert;
351     }
352 }
353
354
355 /* Write TEXT to the outstream.  */
356 static void 
357 writeout (audit_ctx_t ctx, const char *text)
358 {
359   if (ctx->use_html)
360     {
361       for (; *text; text++)
362         {
363           if (*text == '<')
364             es_fputs ("&lt;", ctx->outstream);
365           else if (*text == '&')
366             es_fputs ("&amp;", ctx->outstream);
367           else
368             es_putc (*text, ctx->outstream);
369         }
370     }
371   else
372     es_fputs (text, ctx->outstream);
373 }
374
375
376 /* Write TEXT to the outstream using a variable argument list.  */
377 static void 
378 writeout_v (audit_ctx_t ctx, const char *format, va_list arg_ptr)
379 {
380   char *buf;
381
382   estream_vasprintf (&buf, format, arg_ptr);
383   if (buf)
384     {
385       writeout (ctx, buf);
386       xfree (buf);
387     }
388   else
389     writeout (ctx, "[!!Out of core!!]");
390 }
391
392
393 /* Write TEXT as a paragraph.  */
394 static void
395 writeout_para (audit_ctx_t ctx, const char *format, ...)
396 {
397   va_list arg_ptr;
398
399   if (ctx->use_html)
400     es_fputs ("<p>", ctx->outstream);
401   va_start (arg_ptr, format) ;
402   writeout_v (ctx, format, arg_ptr);
403   va_end (arg_ptr);
404   if (ctx->use_html)
405     es_fputs ("</p>\n", ctx->outstream);
406   else
407     es_fputc ('\n', ctx->outstream);
408 }
409
410
411 static void
412 enter_li (audit_ctx_t ctx)
413 {
414   if (ctx->use_html)
415     {
416       if (!ctx->indentlevel)
417         {
418           es_fputs ("<table border=\"0\">\n"
419                     "  <colgroup>\n"
420                     "    <col width=\"80%\" />\n"
421                     "    <col width=\"20%\" />\n"
422                     "   </colgroup>\n",
423                     ctx->outstream);
424         }
425     }
426   ctx->indentlevel++;
427 }
428
429
430 static void
431 leave_li (audit_ctx_t ctx)
432 {
433   ctx->indentlevel--;
434   if (ctx->use_html)
435     {
436       if (!ctx->indentlevel)
437         es_fputs ("</table>\n", ctx->outstream);
438     }
439 }
440
441   
442 /* Write TEXT as a list element.  If OKTEXT is not NULL, append it to
443    the last line. */
444 static void
445 writeout_li (audit_ctx_t ctx, const char *oktext, const char *format, ...)
446 {
447   va_list arg_ptr;
448   const char *color = NULL;
449
450   if (ctx->use_html && format && oktext)
451     {
452       if (!strcmp (oktext, "Yes"))
453         color = "green";
454       else if (!strcmp (oktext, "No"))
455         color = "red";
456     }
457
458   if (ctx->use_html)
459     {
460       int i;
461
462       es_fputs ("  <tr><td><table><tr><td>", ctx->outstream);
463       if (color)
464         es_fprintf (ctx->outstream, "<font color=\"%s\">*</font>", color);
465       else
466         es_fputs ("*", ctx->outstream);
467       for (i=1; i < ctx->indentlevel; i++)
468         es_fputs ("&nbsp;&nbsp;", ctx->outstream);
469       es_fputs ("</td><td>", ctx->outstream);
470     }
471   else
472     es_fprintf (ctx->outstream, "* %*s", (ctx->indentlevel-1)*2, "");
473   if (format)
474     {
475       va_start (arg_ptr, format) ;
476       writeout_v (ctx, format, arg_ptr);
477       va_end (arg_ptr);
478     }
479   if (ctx->use_html)
480     es_fputs ("</td></tr></table>", ctx->outstream);
481   if (format && oktext)
482     {
483       if (ctx->use_html)
484         {
485           es_fputs ("</td><td>", ctx->outstream);
486           if (color)
487             es_fprintf (ctx->outstream, "<font color=\"%s\">", color);
488         }
489       else  
490         writeout (ctx, ":         ");
491       writeout (ctx, oktext);
492       if (color)
493         es_fputs ("</font>", ctx->outstream);
494     }
495   
496   if (ctx->use_html)
497     es_fputs ("</td></tr>\n", ctx->outstream);
498   else
499     es_fputc ('\n', ctx->outstream);
500 }
501
502
503 /* Write a remark line.  */
504 static void
505 writeout_rem (audit_ctx_t ctx, const char *format, ...)
506 {
507   va_list arg_ptr;
508
509   if (ctx->use_html)
510     {
511       int i;
512
513       es_fputs ("  <tr><td><table><tr><td>*", ctx->outstream);
514       for (i=1; i < ctx->indentlevel; i++)
515         es_fputs ("&nbsp;&nbsp;", ctx->outstream);
516       es_fputs ("&nbsp;&nbsp;&nbsp;</td><td> (", ctx->outstream);
517
518     }
519   else
520     es_fprintf (ctx->outstream, "* %*s  (", (ctx->indentlevel-1)*2, "");
521   if (format)
522     {
523       va_start (arg_ptr, format) ;
524       writeout_v (ctx, format, arg_ptr);
525       va_end (arg_ptr);
526     }
527   if (ctx->use_html)
528     es_fputs (")</td></tr></table></td></tr>\n", ctx->outstream);
529   else
530     es_fputs (")\n", ctx->outstream);
531 }
532
533
534 /* Return the first log item for EVENT.  If STOPEVENT is not 0 never
535    look behind that event in the log. If STARTITEM is not NULL start
536    search _after_that item.  */
537 static log_item_t
538 find_next_log_item (audit_ctx_t ctx, log_item_t startitem, 
539                     audit_event_t event, audit_event_t stopevent)
540 {
541   int idx;
542
543   for (idx=0; idx < ctx->logused; idx++)
544     {
545       if (startitem)
546         {
547           if (ctx->log + idx == startitem)
548             startitem = NULL;
549         }
550       else if (stopevent && ctx->log[idx].event == stopevent)
551         break;
552       else if (ctx->log[idx].event == event)
553         return ctx->log + idx;
554     }
555   return NULL;
556 }
557
558
559 static log_item_t
560 find_log_item (audit_ctx_t ctx, audit_event_t event, audit_event_t stopevent)
561 {
562   return find_next_log_item (ctx, NULL, event, stopevent);
563 }
564
565
566 /* Helper to a format a serial number.  */
567 static char *
568 format_serial (ksba_const_sexp_t sn)
569 {
570   const char *p = (const char *)sn;
571   unsigned long n;
572   char *endp;
573
574   if (!p)
575     return NULL;
576   if (*p != '(')
577     BUG (); /* Not a valid S-expression. */
578   n = strtoul (p+1, &endp, 10);
579   p = endp;
580   if (*p != ':')
581     BUG (); /* Not a valid S-expression. */
582   return bin2hex (p+1, n, NULL);
583 }
584
585
586 /* Return a malloced string with the serial number and the issuer DN
587    of the certificate.  */
588 static char *
589 get_cert_name (ksba_cert_t cert)
590 {
591   char *result;
592   ksba_sexp_t sn;
593   char *issuer, *p;
594
595   if (!cert)
596     return xtrystrdup ("[no certificate]");
597
598   issuer = ksba_cert_get_issuer (cert, 0);
599   sn = ksba_cert_get_serial (cert);
600   if (issuer && sn)
601     {
602       p = format_serial (sn);
603       if (!p)
604         result = xtrystrdup ("[invalid S/N]");
605       else
606         {
607           result = xtrymalloc (strlen (p) + strlen (issuer) + 2 + 1);
608           if (result)
609             {
610               *result = '#';
611               strcpy (stpcpy (stpcpy (result+1, p),"/"), issuer);
612             }
613           xfree (p);
614         }
615     }
616   else
617     result = xtrystrdup ("[missing S/N or issuer]");
618   ksba_free (sn);
619   xfree (issuer);
620   return result;
621 }
622
623 /* Return a malloced string with the serial number and the issuer DN
624    of the certificate.  */
625 static char *
626 get_cert_subject (ksba_cert_t cert, int idx)
627 {
628   char *result;
629   char *subject;
630
631   if (!cert)
632     return xtrystrdup ("[no certificate]");
633
634   subject = ksba_cert_get_subject (cert, idx);
635   if (subject)
636     {
637       result = xtrymalloc (strlen (subject) + 1 + 1);
638       if (result)
639         {
640           *result = '/';
641           strcpy (result+1, subject);
642         }
643     }
644   else
645     result = NULL;
646   xfree (subject);
647   return result;
648 }
649
650
651 /* List the given certificiate.  If CERT is NULL, this is a NOP.  */
652 static void
653 list_cert (audit_ctx_t ctx, ksba_cert_t cert, int with_subj)
654 {
655   char *name;
656   int idx;
657
658   name = get_cert_name (cert);
659   writeout_rem (ctx, "%s", name);
660   xfree (name);
661   if (with_subj)
662     {
663       enter_li (ctx);
664       for (idx=0; (name = get_cert_subject (cert, idx)); idx++)
665         {
666           writeout_rem (ctx, "%s", name);
667           xfree (name);
668         }
669       leave_li (ctx);
670     }
671 }
672
673
674 /* List the chain of certificates from STARTITEM up to STOPEVENT.  The
675    certifcates are written out as comments.  */
676 static void
677 list_certchain (audit_ctx_t ctx, log_item_t startitem, audit_event_t stopevent)
678 {
679   log_item_t item;
680
681   startitem = find_next_log_item (ctx, startitem, AUDIT_CHAIN_BEGIN,stopevent);
682   writeout_li (ctx, startitem? "Yes":"No", _("Certificate chain available"));
683   if (!startitem)
684     return; 
685
686   item = find_next_log_item (ctx, startitem, 
687                              AUDIT_CHAIN_ROOTCERT, AUDIT_CHAIN_END);
688   if (!item)
689     writeout_rem (ctx, "%s", _("root certificate missing"));
690   else
691     {
692       list_cert (ctx, item->cert, 0);
693     }
694   item = startitem;
695   while ( ((item = find_next_log_item (ctx, item, 
696                                        AUDIT_CHAIN_CERT, AUDIT_CHAIN_END))))
697     {
698       list_cert (ctx, item->cert, 1);
699     }
700 }
701
702
703 \f
704 /* Process an encrypt operation's log.  */
705 static void
706 proc_type_encrypt (audit_ctx_t ctx)
707 {
708   log_item_t loopitem, item;
709   int recp_no, idx;
710   char numbuf[35];
711   int algo;
712   char *name;
713
714   item = find_log_item (ctx, AUDIT_ENCRYPTION_DONE, 0);
715   writeout_li (ctx, item?"Yes":"No", "%s", _("Data encryption succeeded"));
716
717   enter_li (ctx);
718
719   item = find_log_item (ctx, AUDIT_GOT_DATA, 0);
720   writeout_li (ctx, item? "Yes":"No", "%s", _("Data available"));
721
722   item = find_log_item (ctx, AUDIT_SESSION_KEY, 0);
723   writeout_li (ctx, item? "Yes":"No", "%s", _("Session key created"));
724   if (item)
725     {
726       algo = gcry_cipher_map_name (item->string);
727       if (algo)
728         writeout_rem (ctx, _("algorithm: %s"), gcry_cipher_algo_name (algo));
729       else if (item->string && !strcmp (item->string, "1.2.840.113549.3.2"))
730         writeout_rem (ctx, _("unsupported algorithm: %s"), "RC2");
731       else if (item->string)
732         writeout_rem (ctx, _("unsupported algorithm: %s"), item->string);
733       else
734         writeout_rem (ctx, _("seems to be not encrypted"));
735     }
736
737   item = find_log_item (ctx, AUDIT_GOT_RECIPIENTS, 0);
738   snprintf (numbuf, sizeof numbuf, "%d", 
739             item && item->have_intvalue? item->intvalue : 0);
740   writeout_li (ctx, numbuf, "%s", _("Number of recipients"));
741
742   /* Loop over all recipients.  */
743   loopitem = NULL;
744   recp_no = 0;
745   while ((loopitem=find_next_log_item (ctx, loopitem, AUDIT_ENCRYPTED_TO, 0)))
746     {
747       recp_no++;
748       writeout_li (ctx, NULL, _("Recipient %d"), recp_no);
749       if (loopitem->cert)
750         {
751           name = get_cert_name (loopitem->cert);
752           writeout_rem (ctx, "%s", name);
753           xfree (name);
754           enter_li (ctx);
755           for (idx=0; (name = get_cert_subject (loopitem->cert, idx)); idx++)
756             {
757               writeout_rem (ctx, "%s", name);
758               xfree (name);
759             }
760           leave_li (ctx);
761         }
762     }
763
764   leave_li (ctx);
765 }
766
767
768 \f
769 /* Process a sign operation's log.  */
770 static void
771 proc_type_sign (audit_ctx_t ctx)
772 {
773   log_item_t item;
774
775   item = NULL;
776   writeout_li (ctx, item?"Yes":"No", "%s", _("Data signing succeeded"));
777
778   enter_li (ctx);
779
780   item = find_log_item (ctx, AUDIT_GOT_DATA, 0);
781   writeout_li (ctx, item? "Yes":"No", "%s", _("Data available"));
782
783
784   leave_li (ctx);
785 }
786
787
788 \f
789 /* Process a decrypt operation's log.  */
790 static void
791 proc_type_decrypt (audit_ctx_t ctx)
792 {
793   log_item_t item;
794
795   item = NULL;
796   writeout_li (ctx, item?"Yes":"No", "%s", _("Data decryption succeeded"));
797
798   enter_li (ctx);
799
800   item = find_log_item (ctx, AUDIT_GOT_DATA, 0);
801   writeout_li (ctx, item? "Yes":"No", "%s", _("Data available"));
802
803
804   leave_li (ctx);
805 }
806
807
808 \f
809 /* Process a verification operation's log.  */
810 static void
811 proc_type_verify (audit_ctx_t ctx)
812 {
813   log_item_t loopitem, item;
814   int signo, count, idx;
815   char numbuf[35];
816
817   /* If there is at least one signature status we claim that the
818      verifciation succeeded.  This does not mean that the data has
819      verified okay.  */
820   item = find_log_item (ctx, AUDIT_SIG_STATUS, 0);
821   writeout_li (ctx, item?"Yes":"No", "%s", _("Data verification succeeded"));
822   enter_li (ctx);
823
824   item = find_log_item (ctx, AUDIT_GOT_DATA, AUDIT_NEW_SIG);
825   writeout_li (ctx, item? "Yes":"No", "%s", _("Data available"));
826   if (!item)
827     goto leave;
828
829   item = find_log_item (ctx, AUDIT_NEW_SIG, 0);
830   writeout_li (ctx, item? "Yes":"No", "%s", _("Signature available"));
831   if (!item)
832     goto leave;
833
834   item = find_log_item (ctx, AUDIT_DATA_HASH_ALGO, AUDIT_NEW_SIG);
835   writeout_li (ctx, item?"Yes":"No", "%s", _("Parsing signature succeeded"));
836   if (!item)
837     {
838       item = find_log_item (ctx, AUDIT_BAD_DATA_HASH_ALGO, AUDIT_NEW_SIG);
839       if (item)
840         writeout_rem (ctx, _("Bad hash algorithm: %s"), 
841                       item->string? item->string:"?");
842
843       goto leave;
844     }
845
846   /* Loop over all signatures.  */
847   loopitem = find_log_item (ctx, AUDIT_NEW_SIG, 0);
848   assert (loopitem);
849   do
850     {
851       signo = loopitem->have_intvalue? loopitem->intvalue : -1;
852
853       item = find_next_log_item (ctx, loopitem,
854                                  AUDIT_SIG_STATUS, AUDIT_NEW_SIG);
855       writeout_li (ctx, item? item->string:"?", _("Signature %d"), signo);
856       item = find_next_log_item (ctx, loopitem,
857                                  AUDIT_SIG_NAME, AUDIT_NEW_SIG);
858       if (item)
859         writeout_rem (ctx, "%s", item->string);
860       enter_li (ctx);
861       
862       /* List the certificate chain.  */
863       list_certchain (ctx, loopitem, AUDIT_NEW_SIG);
864
865       /* Show the result of the chain validation.  */
866       item = find_next_log_item (ctx, loopitem,
867                                  AUDIT_CHAIN_STATUS, AUDIT_NEW_SIG);
868       if (item && item->have_err)
869         {
870           writeout_li (ctx, item->err? "No":"Yes", 
871                        _("Certificate chain valid"));
872           if (item->err)
873             writeout_rem (ctx, "%s", gpg_strerror (item->err));
874         }
875       
876       /* Show whether the root certificate is fine.  */
877       item = find_next_log_item (ctx, loopitem,
878                                  AUDIT_ROOT_TRUSTED, AUDIT_CHAIN_STATUS);
879       if (item)
880         {
881           writeout_li (ctx, item->err?"No":"Yes", "%s",
882                        _("Root certificate trustworthy"));
883           if (item->err)
884             {
885               add_helptag (ctx, "gpgsm.root-cert-not-trusted");
886               writeout_rem (ctx, "%s", gpg_strerror (item->err));
887               list_cert (ctx, item->cert, 0);
888             }
889         }
890
891       /* Show result of the CRL/OCSP check.  */
892       writeout_li (ctx, "-", "%s", _("CRL/OCSP check of certificates"));
893  /*      add_helptag (ctx, "gpgsm.ocsp-problem"); */
894
895
896       leave_li (ctx);
897     }
898   while ((loopitem = find_next_log_item (ctx, loopitem, AUDIT_NEW_SIG, 0)));
899
900
901  leave:
902   /* Always list the certificates stored in the signature.  */
903   item = NULL;
904   count = 0;
905   while ( ((item = find_next_log_item (ctx, item, 
906                                        AUDIT_SAVE_CERT, AUDIT_NEW_SIG))))
907     count++;
908   snprintf (numbuf, sizeof numbuf, "%d", count);
909   writeout_li (ctx, numbuf, _("Included certificates"));
910   item = NULL;
911   while ( ((item = find_next_log_item (ctx, item, 
912                                        AUDIT_SAVE_CERT, AUDIT_NEW_SIG))))
913     {
914       char *name = get_cert_name (item->cert);
915       writeout_rem (ctx, "%s", name);
916       xfree (name);
917       enter_li (ctx);
918       for (idx=0; (name = get_cert_subject (item->cert, idx)); idx++)
919         {
920           writeout_rem (ctx, "%s", name);
921           xfree (name);
922         }
923       leave_li (ctx);
924     }
925   leave_li (ctx);
926 }
927
928
929
930 \f
931 /* Print the formatted audit result.    THIS IS WORK IN PROGRESS.  */
932 void
933 audit_print_result (audit_ctx_t ctx, estream_t out, int use_html)
934 {
935   int idx;
936   size_t n;
937   log_item_t item;
938   helptag_t helptag;
939   const char *s;
940   int show_raw = 0;
941   char *orig_codeset;
942   
943   if (!ctx)
944     return;
945
946   orig_codeset = i18n_switchto_utf8 ();
947
948   /* We use an environment variable to include some debug info in the
949      log.  */
950   if ((s = getenv ("gnupg_debug_audit")))
951     {
952       show_raw = 1;
953       if (!strcmp (s, "html"))
954         use_html = 1;
955     }
956
957   assert (!ctx->outstream);
958   ctx->outstream = out;
959   ctx->use_html = use_html;
960   ctx->indentlevel = 0;
961   clear_helptags (ctx);
962
963   if (use_html)
964     es_fputs ("<div class=\"GnuPGAuditLog\">\n", ctx->outstream);
965
966   if (!ctx->log || !ctx->logused)
967     {
968       writeout_para (ctx, _("No audit log entries."));
969       goto leave;
970     }
971
972   if (show_raw)
973     {
974       int maxlen;
975
976       for (idx=0,maxlen=0; idx < DIM (eventstr_msgidx); idx++)
977         {
978           n = strlen (eventstr_msgstr + eventstr_msgidx[idx]);    
979           if (n > maxlen)
980             maxlen = n;
981         }
982       
983       if (use_html)
984         es_fputs ("<pre>\n", out);
985       for (idx=0; idx < ctx->logused; idx++)
986         {
987           es_fprintf (out, "log: %-*s", 
988                       maxlen, event2str (ctx->log[idx].event));
989           if (ctx->log[idx].have_intvalue)
990             es_fprintf (out, " i=%d", ctx->log[idx].intvalue); 
991           if (ctx->log[idx].string)
992             {
993               es_fputs (" s=`", out); 
994               writeout (ctx, ctx->log[idx].string); 
995               es_fputs ("'", out); 
996             }
997           if (ctx->log[idx].cert)
998             es_fprintf (out, " has_cert"); 
999           if (ctx->log[idx].have_err)
1000             {
1001               es_fputs (" err=`", out);
1002               writeout (ctx, gpg_strerror (ctx->log[idx].err)); 
1003               es_fputs ("'", out);
1004             }
1005           es_fputs ("\n", out);
1006         }
1007       if (use_html)
1008         es_fputs ("</pre>\n", out);
1009       else
1010         es_fputs ("\n", out);
1011     }
1012
1013   enter_li (ctx);
1014   switch (ctx->type)
1015     {
1016     case AUDIT_TYPE_NONE:
1017       writeout_li (ctx, NULL, _("Unknown operation"));
1018       break;
1019     case AUDIT_TYPE_ENCRYPT:
1020       proc_type_encrypt (ctx);
1021       break;
1022     case AUDIT_TYPE_SIGN:
1023       proc_type_sign (ctx);
1024       break;
1025     case AUDIT_TYPE_DECRYPT:
1026       proc_type_decrypt (ctx);
1027       break;
1028     case AUDIT_TYPE_VERIFY:
1029       proc_type_verify (ctx);
1030       break;
1031     }
1032   item = find_log_item (ctx, AUDIT_AGENT_READY, 0);
1033   if (item && item->have_err)
1034     {
1035       writeout_li (ctx, item->err? "No":"Yes", "%s", _("Gpg-Agent usable"));
1036       if (item->err)
1037         {
1038           writeout_rem (ctx, "%s", gpg_strerror (item->err));
1039           add_helptag (ctx, "gnupg.agent-problem");
1040         }
1041     }
1042   item = find_log_item (ctx, AUDIT_DIRMNGR_READY, 0);
1043   if (item && item->have_err)
1044     {
1045       writeout_li (ctx, item->err? "No":"Yes", "%s", _("Dirmngr usable"));
1046       if (item->err)
1047         {
1048           writeout_rem (ctx, "%s", gpg_strerror (item->err));
1049           add_helptag (ctx, "gnupg.dirmngr-problem");
1050         }
1051     }
1052   leave_li (ctx);
1053
1054
1055   /* Show the help from the collected help tags.  */
1056   if (ctx->helptags)
1057     {
1058       if (use_html)
1059         {
1060           es_fputs ("<hr/>\n", ctx->outstream);
1061           if (ctx->helptags->next)
1062             es_fputs ("<ul>\n", ctx->outstream);
1063         }
1064       else
1065         es_fputs ("\n\n", ctx->outstream);
1066     }
1067   for (helptag = ctx->helptags; helptag; helptag = helptag->next)
1068     {
1069       char *text;
1070
1071       if (use_html && ctx->helptags->next)
1072         es_fputs ("<li>\n", ctx->outstream);
1073
1074       text = gnupg_get_help_string (helptag->name, 0);
1075       if (text)
1076         {
1077           writeout_para (ctx, "%s", text);
1078           xfree (text);
1079         }
1080       else
1081         writeout_para (ctx, _("No help available for `%s'."), helptag->name);
1082       if (use_html && ctx->helptags->next)
1083         es_fputs ("</li>\n", ctx->outstream);
1084       if (helptag->next)
1085         es_fputs ("\n", ctx->outstream);
1086     }
1087   if (use_html && ctx->helptags && ctx->helptags->next)
1088     es_fputs ("</ul>\n", ctx->outstream);
1089
1090  leave:
1091   if (use_html)
1092     es_fputs ("</div>\n", ctx->outstream);
1093   ctx->outstream = NULL;
1094   ctx->use_html = 0;
1095   clear_helptags (ctx);
1096   i18n_switchback (orig_codeset);
1097 }
1098