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