]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/openbsm/libbsm/bsm_audit.c
Merge OpenBSM 1.1 alpha 2 from the OpenBSM vendor branch to head, both
[FreeBSD/FreeBSD.git] / contrib / openbsm / libbsm / bsm_audit.c
1 /*-
2  * Copyright (c) 2004 Apple Inc.
3  * Copyright (c) 2005 SPARTA, Inc.
4  * All rights reserved.
5  *
6  * This code was developed in part by Robert N. M. Watson, Senior Principal
7  * Scientist, SPARTA, Inc.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1.  Redistributions of source code must retain the above copyright
13  *     notice, this list of conditions and the following disclaimer.
14  * 2.  Redistributions in binary form must reproduce the above copyright
15  *     notice, this list of conditions and the following disclaimer in the
16  *     documentation and/or other materials provided with the distribution.
17  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
18  *     its contributors may be used to endorse or promote products derived
19  *     from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR
25  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
30  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  *
33  * $P4: //depot/projects/trustedbsd/openbsm/libbsm/bsm_audit.c#31 $
34  */
35
36 #include <sys/types.h>
37
38 #include <config/config.h>
39 #ifdef HAVE_FULL_QUEUE_H
40 #include <sys/queue.h>
41 #else
42 #include <compat/queue.h>
43 #endif
44
45 #include <bsm/audit_internal.h>
46 #include <bsm/libbsm.h>
47
48 #include <netinet/in.h>
49
50 #include <errno.h>
51 #include <pthread.h>
52 #include <stdlib.h>
53 #include <string.h>
54
55 /* array of used descriptors */
56 static au_record_t      *open_desc_table[MAX_AUDIT_RECORDS];
57
58 /* The current number of active record descriptors */
59 static int      audit_rec_count = 0;
60
61 /*
62  * Records that can be recycled are maintained in the list given below.  The
63  * maximum number of elements that can be present in this list is bounded by
64  * MAX_AUDIT_RECORDS.  Memory allocated for these records are never freed.
65  */
66 static LIST_HEAD(, au_record)   audit_free_q;
67
68 static pthread_mutex_t  mutex = PTHREAD_MUTEX_INITIALIZER;
69
70 /*
71  * This call frees a token_t and its internal data.
72  */
73 void
74 au_free_token(token_t *tok)
75 {
76
77         if (tok != NULL) {
78                 if (tok->t_data)
79                         free(tok->t_data);
80                 free(tok);
81         }
82 }
83
84 /*
85  * This call reserves memory for the audit record.  Memory must be guaranteed
86  * before any auditable event can be generated.  The au_record_t structure
87  * maintains a reference to the memory allocated above and also the list of
88  * tokens associated with this record.  Descriptors are recyled once the
89  * records are added to the audit trail following au_close().
90  */
91 int
92 au_open(void)
93 {
94         au_record_t *rec = NULL;
95
96         pthread_mutex_lock(&mutex);
97
98         if (audit_rec_count == 0)
99                 LIST_INIT(&audit_free_q);
100
101         /*
102          * Find an unused descriptor, remove it from the free list, mark as
103          * used.
104          */
105         if (!LIST_EMPTY(&audit_free_q)) {
106                 rec = LIST_FIRST(&audit_free_q);
107                 rec->used = 1;
108                 LIST_REMOVE(rec, au_rec_q);
109         }
110
111         pthread_mutex_unlock(&mutex);
112
113         if (rec == NULL) {
114                 /*
115                  * Create a new au_record_t if no descriptors are available.
116                  */
117                 rec = malloc (sizeof(au_record_t));
118                 if (rec == NULL)
119                         return (-1);
120
121                 rec->data = malloc (MAX_AUDIT_RECORD_SIZE * sizeof(u_char));
122                 if (rec->data == NULL) {
123                         free(rec);
124                         errno = ENOMEM;
125                         return (-1);
126                 }
127
128                 pthread_mutex_lock(&mutex);
129
130                 if (audit_rec_count == MAX_AUDIT_RECORDS) {
131                         pthread_mutex_unlock(&mutex);
132                         free(rec->data);
133                         free(rec);
134
135                         /* XXX We need to increase size of MAX_AUDIT_RECORDS */
136                         errno = ENOMEM;
137                         return (-1);
138                 }
139                 rec->desc = audit_rec_count;
140                 open_desc_table[audit_rec_count] = rec;
141                 audit_rec_count++;
142
143                 pthread_mutex_unlock(&mutex);
144
145         }
146
147         memset(rec->data, 0, MAX_AUDIT_RECORD_SIZE);
148
149         TAILQ_INIT(&rec->token_q);
150         rec->len = 0;
151         rec->used = 1;
152
153         return (rec->desc);
154 }
155
156 /*
157  * Store the token with the record descriptor.
158  *
159  * Don't permit writing more to the buffer than would let the trailer be
160  * appended later.
161  */
162 int
163 au_write(int d, token_t *tok)
164 {
165         au_record_t *rec;
166
167         if (tok == NULL) {
168                 errno = EINVAL;
169                 return (-1); /* Invalid Token */
170         }
171
172         /* Write the token to the record descriptor */
173         rec = open_desc_table[d];
174         if ((rec == NULL) || (rec->used == 0)) {
175                 errno = EINVAL;
176                 return (-1); /* Invalid descriptor */
177         }
178
179         if (rec->len + tok->len + AUDIT_TRAILER_SIZE > MAX_AUDIT_RECORD_SIZE) {
180                 errno = ENOMEM;
181                 return (-1);
182         }
183
184         /* Add the token to the tail */
185         /*
186          * XXX Not locking here -- we should not be writing to
187          * XXX the same descriptor from different threads
188          */
189         TAILQ_INSERT_TAIL(&rec->token_q, tok, tokens);
190
191         rec->len += tok->len; /* grow record length by token size bytes */
192
193         /* Token should not be available after this call */
194         tok = NULL;
195         return (0); /* Success */
196 }
197
198 /*
199  * Assemble an audit record out of its tokens, including allocating header and
200  * trailer tokens.  Does not free the token chain, which must be done by the
201  * caller if desirable.
202  *
203  * XXX: Assumes there is sufficient space for the header and trailer.
204  */
205 static int
206 au_assemble(au_record_t *rec, short event)
207 {
208         token_t *header, *tok, *trailer;
209         size_t tot_rec_size, hdrsize;
210         u_char *dptr;
211         struct in6_addr *aptr;
212         int error;
213         struct auditinfo_addr aia;
214         struct timeval tm;
215
216 #ifdef HAVE_AUDIT_SYSCALLS
217         /*
218          * Grab the size of the address family stored in the kernel's audit
219          * state.
220          */
221         aia.ai_termid.at_type = AU_IPv4;
222         aia.ai_termid.at_addr[0] = INADDR_ANY;
223         if (auditon(A_GETKAUDIT, &aia, sizeof(aia)) < 0) {
224                 if (errno != ENOSYS)
225                         return (-1);
226 #endif /* HAVE_AUDIT_SYSCALLS */
227                 tot_rec_size = rec->len + AUDIT_HEADER_SIZE +
228                     AUDIT_TRAILER_SIZE;
229                 header = au_to_header(tot_rec_size, event, 0);
230 #ifdef HAVE_AUDIT_SYSCALLS
231         } else {
232                 if (gettimeofday(&tm, NULL) < 0)
233                         return (-1);
234                 switch (aia.ai_termid.at_type) {
235                 case AU_IPv4:
236                         hdrsize = (aia.ai_termid.at_addr[0] == INADDR_ANY) ?
237                             AUDIT_HEADER_SIZE : AUDIT_HEADER_EX_SIZE(&aia);
238                         break;
239                 case AU_IPv6:
240                         aptr = (struct in6_addr *)&aia.ai_termid.at_addr[0];
241                         hdrsize =
242                             (IN6_IS_ADDR_UNSPECIFIED(aptr)) ?
243                             AUDIT_HEADER_SIZE : AUDIT_HEADER_EX_SIZE(&aia);
244                         break;
245                 }
246                 tot_rec_size = rec->len + hdrsize + AUDIT_TRAILER_SIZE;
247                 /*
248                  * A header size greater then AUDIT_HEADER_SIZE means
249                  * that we are using an extended header.
250                  */
251                 if (hdrsize > AUDIT_HEADER_SIZE)
252                         header = au_to_header32_ex_tm(tot_rec_size, event,
253                             0, tm, &aia);
254                 else
255                         header = au_to_header(tot_rec_size, event, 0);
256         }
257 #endif /* HAVE_AUDIT_SYSCALLS */
258         if (header == NULL)
259                 return (-1);
260
261         trailer = au_to_trailer(tot_rec_size);
262         if (trailer == NULL) {
263                 error = errno;
264                 au_free_token(header);
265                 errno = error;
266                 return (-1);
267         }
268
269         TAILQ_INSERT_HEAD(&rec->token_q, header, tokens);
270         TAILQ_INSERT_TAIL(&rec->token_q, trailer, tokens);
271
272         rec->len = tot_rec_size;
273         dptr = rec->data;
274
275         TAILQ_FOREACH(tok, &rec->token_q, tokens) {
276                 memcpy(dptr, tok->t_data, tok->len);
277                 dptr += tok->len;
278         }
279
280         return (0);
281 }
282
283 /*
284  * Given a record that is no longer of interest, tear it down and convert to a
285  * free record.
286  */
287 static void
288 au_teardown(au_record_t *rec)
289 {
290         token_t *tok;
291
292         /* Free the token list */
293         while ((tok = TAILQ_FIRST(&rec->token_q)) != NULL) {
294                 TAILQ_REMOVE(&rec->token_q, tok, tokens);
295                 free(tok->t_data);
296                 free(tok);
297         }
298
299         rec->used = 0;
300         rec->len = 0;
301
302         pthread_mutex_lock(&mutex);
303
304         /* Add the record to the freelist tail */
305         LIST_INSERT_HEAD(&audit_free_q, rec, au_rec_q);
306
307         pthread_mutex_unlock(&mutex);
308 }
309
310 #ifdef HAVE_AUDIT_SYSCALLS
311 /*
312  * Add the header token, identify any missing tokens.  Write out the tokens to
313  * the record memory and finally, call audit.
314  */
315 int
316 au_close(int d, int keep, short event)
317 {
318         au_record_t *rec;
319         size_t tot_rec_size;
320         int retval = 0;
321
322         rec = open_desc_table[d];
323         if ((rec == NULL) || (rec->used == 0)) {
324                 errno = EINVAL;
325                 return (-1); /* Invalid descriptor */
326         }
327
328         if (keep == AU_TO_NO_WRITE) {
329                 retval = 0;
330                 goto cleanup;
331         }
332
333         tot_rec_size = rec->len + MAX_AUDIT_HEADER_SIZE + AUDIT_TRAILER_SIZE;
334
335         if (tot_rec_size > MAX_AUDIT_RECORD_SIZE) {
336                 /*
337                  * XXXRW: Since au_write() is supposed to prevent this, spew
338                  * an error here.
339                  */
340                 fprintf(stderr, "au_close failed");
341                 errno = ENOMEM;
342                 retval = -1;
343                 goto cleanup;
344         }
345
346         if (au_assemble(rec, event) < 0) {
347                 /*
348                  * XXXRW: This is also not supposed to happen, but might if we
349                  * are unable to allocate header and trailer memory.
350                  */
351                 retval = -1;
352                 goto cleanup;
353         }
354
355         /* Call the kernel interface to audit */
356         retval = audit(rec->data, rec->len);
357
358 cleanup:
359         /* CLEANUP */
360         au_teardown(rec);
361         return (retval);
362 }
363 #endif /* HAVE_AUDIT_SYSCALLS */
364
365 /*
366  * au_close(), except onto an in-memory buffer.  Buffer size as an argument,
367  * record size returned via same argument on success.
368  */
369 int
370 au_close_buffer(int d, short event, u_char *buffer, size_t *buflen)
371 {
372         size_t tot_rec_size;
373         au_record_t *rec;
374         int retval;
375
376         rec = open_desc_table[d];
377         if ((rec == NULL) || (rec->used == 0)) {
378                 errno = EINVAL;
379                 return (-1);
380         }
381
382         retval = 0;
383         tot_rec_size = rec->len + MAX_AUDIT_HEADER_SIZE + AUDIT_TRAILER_SIZE;
384         if ((tot_rec_size > MAX_AUDIT_RECORD_SIZE) ||
385             (tot_rec_size > *buflen)) {
386                 /*
387                  * XXXRW: See au_close() comment.
388                  */
389                 fprintf(stderr, "au_close_buffer failed %zd", tot_rec_size);
390                 errno = ENOMEM;
391                 retval = -1;
392                 goto cleanup;
393         }
394
395         if (au_assemble(rec, event) < 0) {
396                 /* XXXRW: See au_close() comment. */
397                 retval = -1;
398                 goto cleanup;
399         }
400
401         memcpy(buffer, rec->data, rec->len);
402         *buflen = rec->len;
403
404 cleanup:
405         au_teardown(rec);
406         return (retval);
407 }
408
409 /*
410  * au_close_token() returns the byte format of a token_t.  This won't
411  * generally be used by applications, but is quite useful for writing test
412  * tools.  Will free the token on either success or failure.
413  */
414 int
415 au_close_token(token_t *tok, u_char *buffer, size_t *buflen)
416 {
417
418         if (tok->len > *buflen) {
419                 au_free_token(tok);
420                 errno = ENOMEM;
421                 return (EINVAL);
422         }
423
424         memcpy(buffer, tok->t_data, tok->len);
425         *buflen = tok->len;
426         au_free_token(tok);
427         return (0);
428 }