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