Started to implement the audit log feature.
[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
23
24 #include "util.h"
25 #include "audit.h"
26 #include "audit-events.h"
27
28 /* One log entry.  */
29 struct log_item_s
30 {
31   audit_event_t event; /* The event.  */
32   gpg_error_t err;     /* The logged error code.  */
33   int intvalue;        /* A logged interger value.  */
34   char *string;        /* A malloced string or NULL.  */
35   ksba_cert_t cert;    /* A certifciate or NULL. */
36   int have_err:1;
37   int have_intvalue:1;
38 };
39 typedef struct log_item_s *log_item_t;
40
41
42
43 /* The main audit object.  */
44 struct audit_ctx_s
45 {
46   const char *failure;  /* If set a description of the internal failure.  */
47   audit_type_t type;
48   
49   log_item_t log;       /* The table with the log entries.  */
50   size_t logsize;       /* The allocated size for LOG.  */
51   size_t logused;       /* The used size of LOG.  */
52
53 };
54
55
56
57
58 static const char *
59 event2str (audit_event_t event)
60 {
61   int idx = eventstr_msgidxof (event);
62   if (idx == -1)
63     return "Unknown event";
64   else
65     return eventstr_msgstr + eventstr_msgidx[idx];
66 }
67
68
69
70 /* Create a new audit context.  In case of an error NULL is returned
71    and errno set appropriately. */ 
72 audit_ctx_t
73 audit_new (void)
74 {
75   audit_ctx_t ctx;
76
77   ctx = xtrycalloc (1, sizeof *ctx);
78
79   return ctx;
80 }
81
82
83 /* Release an audit context.  Passing NULL for CTX is allowed and does
84    nothing.  */
85 void
86 audit_release (audit_ctx_t ctx)
87 {
88   int idx;
89   if (!ctx)
90     return;
91   if (ctx->log)
92     {
93       for (idx=0; idx < ctx->logused; idx++)
94         {
95           if (ctx->log[idx].string)
96             xfree (ctx->log[idx].string);
97           if (ctx->log[idx].cert)
98             ksba_cert_release (ctx->log[idx].cert);
99         }
100       xfree (ctx->log);
101     }
102   xfree (ctx);
103 }
104
105
106 /* Set the type for the audit operation.  If CTX is NULL, this is a
107    dummy fucntion.  */
108 void
109 audit_set_type (audit_ctx_t ctx, audit_type_t type)
110 {
111   if (!ctx || ctx->failure)
112     return;  /* Audit not enabled or an internal error has occurred. */
113
114   if (ctx->type && ctx->type != type)
115     {
116       ctx->failure = "conflict in type initialization";
117       return;
118     }
119   ctx->type = type;
120 }
121
122
123 /* Create a new log item and put it into the table.  Return that log
124    item on success; return NULL on memory failure and mark that in
125    CTX. */
126 static log_item_t
127 create_log_item (audit_ctx_t ctx)
128 {
129   log_item_t item, table;
130   size_t size;
131
132   if (!ctx->log)
133     {
134       size = 10;
135       table = xtrymalloc (size * sizeof *table);
136       if (!table)
137         {
138           ctx->failure = "Out of memory in create_log_item";
139           return NULL;
140         }
141       ctx->log = table;
142       ctx->logsize = size;
143       item = ctx->log + 0;
144       ctx->logused = 1;
145     }
146   else if (ctx->logused >= ctx->logsize)
147     {
148       size = ctx->logsize + 10;
149       table = xtryrealloc (ctx->log, size * sizeof *table);
150       if (!table)
151         {
152           ctx->failure = "Out of memory while reallocating in create_log_item";
153           return NULL;
154         }
155       ctx->log = table;
156       ctx->logsize = size;
157       item = ctx->log + ctx->logused++;
158     }
159   else
160     item = ctx->log + ctx->logused++;
161
162   item->event = AUDIT_NULL_EVENT;
163   item->err = 0;
164   item->have_err = 0;
165   item->intvalue = 0;
166   item->have_intvalue = 0;
167   item->string = NULL;
168   item->cert = NULL;
169
170   return item;
171  
172 }
173
174 /* Add a new event to the audit log.  If CTX is NULL, this function
175    does nothing.  */
176 void
177 audit_log (audit_ctx_t ctx, audit_event_t event)
178 {
179   log_item_t item;
180
181   if (!ctx || ctx->failure)
182     return;  /* Audit not enabled or an internal error has occurred. */
183   if (!event)
184     {
185       ctx->failure = "Invalid event passed to audit_log";
186       return;
187     }
188   if (!(item = create_log_item (ctx)))
189     return;
190   item->event = event;
191 }
192
193 /* Add a new event to the audit log.  If CTX is NULL, this function
194    does nothing.  This version also adds the result of the oepration
195    to the log.. */
196 void
197 audit_log_ok (audit_ctx_t ctx, audit_event_t event, gpg_error_t err)
198 {
199   log_item_t item;
200
201   if (!ctx || ctx->failure)
202     return;  /* Audit not enabled or an internal error has occurred. */
203   if (!event)
204     {
205       ctx->failure = "Invalid event passed to audit_log_ok";
206       return;
207     }
208   if (!(item = create_log_item (ctx)))
209     return;
210   item->event = event;
211   item->err = err;
212   item->have_err = 1;
213 }
214
215
216 /* Add a new event to the audit log.  If CTX is NULL, this function
217    does nothing.  This version also add the integer VALUE to the log.  */
218 void
219 audit_log_i (audit_ctx_t ctx, audit_event_t event, int value)
220 {
221   log_item_t item;
222
223   if (!ctx || ctx->failure)
224     return;  /* Audit not enabled or an internal error has occurred. */
225   if (!event)
226     {
227       ctx->failure = "Invalid event passed to audit_log_i";
228       return;
229     }
230   if (!(item = create_log_item (ctx)))
231     return;
232   item->event = event;
233   item->intvalue = value;
234   item->have_intvalue = 1;
235 }
236
237
238 /* Add a new event to the audit log.  If CTX is NULL, this function
239    does nothing.  This version also add the integer VALUE to the log.  */
240 void
241 audit_log_s (audit_ctx_t ctx, audit_event_t event, const char *value)
242 {
243   log_item_t item;
244   char *tmp;
245
246   if (!ctx || ctx->failure)
247     return;  /* Audit not enabled or an internal error has occurred. */
248   if (!event)
249     {
250       ctx->failure = "Invalid event passed to audit_log_s";
251       return;
252     }
253   tmp = xtrystrdup (value? value : "");
254   if (!tmp)
255     {
256       ctx->failure = "Out of memory in audit_event";
257       return;
258     }
259   if (!(item = create_log_item (ctx)))
260     {
261       xfree (tmp);
262       return;
263     }
264   item->event = event;
265   item->string = tmp;
266 }
267
268 /* Add a new event to the audit log.  If CTX is NULL, this function
269    does nothing.  This version also adds the certificate CERT and the
270    result of an operation to the log.  */
271 void
272 audit_log_cert (audit_ctx_t ctx, audit_event_t event, 
273                 ksba_cert_t cert, gpg_error_t err)
274 {
275   log_item_t item;
276
277   if (!ctx || ctx->failure)
278     return;  /* Audit not enabled or an internal error has occurred. */
279   if (!event)
280     {
281       ctx->failure = "Invalid event passed to audit_log_cert";
282       return;
283     }
284   if (!(item = create_log_item (ctx)))
285     return;
286   item->event = event;
287   item->err = err;
288   item->have_err = 1;
289   if (cert)
290     {
291       ksba_cert_ref (cert); 
292       item->cert = cert;
293     }
294 }
295
296
297
298 /* Print the formatted audit result.    THIS IS WORK IN PROGRESS.  */
299 void
300 audit_print_result (audit_ctx_t ctx, FILE *fp)
301 {
302   int idx;
303   int maxlen;
304   size_t n;
305
306   if (!ctx)
307     return;
308   if (!ctx->log || !ctx->logused)
309     {
310       fprintf (fp, "AUDIT-LOG: No entries\n");
311       return;
312     }
313
314   for (idx=0,maxlen=0; idx < DIM (eventstr_msgidx); idx++)
315     {
316       n = strlen (eventstr_msgstr + eventstr_msgidx[idx]);    
317       if (n > maxlen)
318         maxlen = n;
319     }
320
321   for (idx=0; idx < ctx->logused; idx++)
322     {
323       fprintf (fp, "AUDIT-LOG[%d]: %-*s", 
324                idx, maxlen, event2str (ctx->log[idx].event));
325       if (ctx->log[idx].have_intvalue)
326         fprintf (fp, " i=%d", ctx->log[idx].intvalue); 
327       if (ctx->log[idx].string)
328         fprintf (fp, " s=`%s'", ctx->log[idx].string); 
329       if (ctx->log[idx].cert)
330         fprintf (fp, " has_cert"); 
331       if (ctx->log[idx].have_err)
332         fprintf (fp, " err=\"%s\"", gpg_strerror (ctx->log[idx].err)); 
333       putc ('\n', fp);
334     }
335 }
336