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