bsm_audit.c revision 161630
1155131Srwatson/*
2155131Srwatson * Copyright (c) 2004 Apple Computer, Inc.
3155131Srwatson * Copyright (c) 2005 SPARTA, Inc.
4155131Srwatson * All rights reserved.
5155131Srwatson *
6155131Srwatson * This code was developed in part by Robert N. M. Watson, Senior Principal
7155131Srwatson * Scientist, SPARTA, Inc.
8155131Srwatson *
9155131Srwatson * Redistribution and use in source and binary forms, with or without
10155131Srwatson * modification, are permitted provided that the following conditions
11155131Srwatson * are met:
12155131Srwatson * 1.  Redistributions of source code must retain the above copyright
13155131Srwatson *     notice, this list of conditions and the following disclaimer.
14155131Srwatson * 2.  Redistributions in binary form must reproduce the above copyright
15155131Srwatson *     notice, this list of conditions and the following disclaimer in the
16155131Srwatson *     documentation and/or other materials provided with the distribution.
17155131Srwatson * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
18155131Srwatson *     its contributors may be used to endorse or promote products derived
19155131Srwatson *     from this software without specific prior written permission.
20155131Srwatson *
21155131Srwatson * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND
22155131Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23155131Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24155131Srwatson * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR
25155131Srwatson * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26155131Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27155131Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28155131Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29155131Srwatson * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
30155131Srwatson * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31155131Srwatson * POSSIBILITY OF SUCH DAMAGE.
32155131Srwatson *
33161630Srwatson * $P4: //depot/projects/trustedbsd/openbsm/libbsm/bsm_audit.c#28 $
34155131Srwatson */
35155131Srwatson
36155131Srwatson#include <sys/types.h>
37156283Srwatson
38156283Srwatson#include <config/config.h>
39156283Srwatson#ifdef HAVE_FULL_QUEUE_H
40155131Srwatson#include <sys/queue.h>
41156283Srwatson#else
42156283Srwatson#include <compat/queue.h>
43156283Srwatson#endif
44155131Srwatson
45155131Srwatson#include <bsm/audit_internal.h>
46155131Srwatson#include <bsm/libbsm.h>
47155131Srwatson
48155131Srwatson#include <errno.h>
49155131Srwatson#include <pthread.h>
50155131Srwatson#include <stdlib.h>
51155131Srwatson#include <string.h>
52155131Srwatson
53155131Srwatson/* array of used descriptors */
54155131Srwatsonstatic au_record_t	*open_desc_table[MAX_AUDIT_RECORDS];
55155131Srwatson
56155131Srwatson/* The current number of active record descriptors */
57161630Srwatsonstatic int	audit_rec_count = 0;
58155131Srwatson
59155131Srwatson/*
60155131Srwatson * Records that can be recycled are maintained in the list given below.  The
61155131Srwatson * maximum number of elements that can be present in this list is bounded by
62155131Srwatson * MAX_AUDIT_RECORDS.  Memory allocated for these records are never freed.
63155131Srwatson */
64161630Srwatsonstatic LIST_HEAD(, au_record)	audit_free_q;
65155131Srwatson
66155131Srwatsonstatic pthread_mutex_t	mutex = PTHREAD_MUTEX_INITIALIZER;
67155131Srwatson
68155131Srwatson/*
69155131Srwatson * This call frees a token_t and its internal data.
70155131Srwatson */
71155131Srwatsonvoid
72155131Srwatsonau_free_token(token_t *tok)
73155131Srwatson{
74155131Srwatson
75155131Srwatson	if (tok != NULL) {
76155131Srwatson		if (tok->t_data)
77155131Srwatson			free(tok->t_data);
78155131Srwatson		free(tok);
79155131Srwatson	}
80155131Srwatson}
81155131Srwatson
82155131Srwatson/*
83155131Srwatson * This call reserves memory for the audit record.  Memory must be guaranteed
84155131Srwatson * before any auditable event can be generated.  The au_record_t structure
85155131Srwatson * maintains a reference to the memory allocated above and also the list of
86155131Srwatson * tokens associated with this record.  Descriptors are recyled once the
87155131Srwatson * records are added to the audit trail following au_close().
88155131Srwatson */
89155131Srwatsonint
90155131Srwatsonau_open(void)
91155131Srwatson{
92155131Srwatson	au_record_t *rec = NULL;
93155131Srwatson
94155131Srwatson	pthread_mutex_lock(&mutex);
95155131Srwatson
96161630Srwatson	if (audit_rec_count == 0)
97161630Srwatson		LIST_INIT(&audit_free_q);
98155131Srwatson
99155131Srwatson	/*
100155131Srwatson	 * Find an unused descriptor, remove it from the free list, mark as
101155131Srwatson	 * used.
102155131Srwatson	 */
103161630Srwatson	if (!LIST_EMPTY(&audit_free_q)) {
104161630Srwatson		rec = LIST_FIRST(&audit_free_q);
105155131Srwatson		rec->used = 1;
106155131Srwatson		LIST_REMOVE(rec, au_rec_q);
107155131Srwatson	}
108155131Srwatson
109155131Srwatson	pthread_mutex_unlock(&mutex);
110155131Srwatson
111155131Srwatson	if (rec == NULL) {
112155131Srwatson		/*
113155131Srwatson		 * Create a new au_record_t if no descriptors are available.
114155131Srwatson		 */
115155131Srwatson		rec = malloc (sizeof(au_record_t));
116155131Srwatson		if (rec == NULL)
117155131Srwatson			return (-1);
118155131Srwatson
119155131Srwatson		rec->data = malloc (MAX_AUDIT_RECORD_SIZE * sizeof(u_char));
120155131Srwatson		if (rec->data == NULL) {
121155131Srwatson			free(rec);
122155131Srwatson			errno = ENOMEM;
123155131Srwatson			return (-1);
124155131Srwatson		}
125155131Srwatson
126155131Srwatson		pthread_mutex_lock(&mutex);
127155131Srwatson
128161630Srwatson		if (audit_rec_count == MAX_AUDIT_RECORDS) {
129155131Srwatson			pthread_mutex_unlock(&mutex);
130155131Srwatson			free(rec->data);
131155131Srwatson			free(rec);
132155131Srwatson
133155131Srwatson			/* XXX We need to increase size of MAX_AUDIT_RECORDS */
134155131Srwatson			errno = ENOMEM;
135155131Srwatson			return (-1);
136155131Srwatson		}
137161630Srwatson		rec->desc = audit_rec_count;
138161630Srwatson		open_desc_table[audit_rec_count] = rec;
139161630Srwatson		audit_rec_count++;
140155131Srwatson
141155131Srwatson		pthread_mutex_unlock(&mutex);
142155131Srwatson
143155131Srwatson	}
144155131Srwatson
145155131Srwatson	memset(rec->data, 0, MAX_AUDIT_RECORD_SIZE);
146155131Srwatson
147155131Srwatson	TAILQ_INIT(&rec->token_q);
148155131Srwatson	rec->len = 0;
149155131Srwatson	rec->used = 1;
150155131Srwatson
151155131Srwatson	return (rec->desc);
152155131Srwatson}
153155131Srwatson
154155131Srwatson/*
155155131Srwatson * Store the token with the record descriptor.
156155131Srwatson *
157155131Srwatson * Don't permit writing more to the buffer than would let the trailer be
158155131Srwatson * appended later.
159155131Srwatson */
160155131Srwatsonint
161155131Srwatsonau_write(int d, token_t *tok)
162155131Srwatson{
163155131Srwatson	au_record_t *rec;
164155131Srwatson
165155131Srwatson	if (tok == NULL) {
166155131Srwatson		errno = EINVAL;
167155131Srwatson		return (-1); /* Invalid Token */
168155131Srwatson	}
169155131Srwatson
170155131Srwatson	/* Write the token to the record descriptor */
171155131Srwatson	rec = open_desc_table[d];
172155131Srwatson	if ((rec == NULL) || (rec->used == 0)) {
173155131Srwatson		errno = EINVAL;
174155131Srwatson		return (-1); /* Invalid descriptor */
175155131Srwatson	}
176155131Srwatson
177161630Srwatson	if (rec->len + tok->len + AUDIT_TRAILER_SIZE > MAX_AUDIT_RECORD_SIZE) {
178155131Srwatson		errno = ENOMEM;
179155131Srwatson		return (-1);
180155131Srwatson	}
181155131Srwatson
182155131Srwatson	/* Add the token to the tail */
183155131Srwatson	/*
184155131Srwatson	 * XXX Not locking here -- we should not be writing to
185155131Srwatson	 * XXX the same descriptor from different threads
186155131Srwatson	 */
187155131Srwatson	TAILQ_INSERT_TAIL(&rec->token_q, tok, tokens);
188155131Srwatson
189155131Srwatson	rec->len += tok->len; /* grow record length by token size bytes */
190155131Srwatson
191155131Srwatson	/* Token should not be available after this call */
192155131Srwatson	tok = NULL;
193155131Srwatson	return (0); /* Success */
194155131Srwatson}
195155131Srwatson
196155131Srwatson/*
197155131Srwatson * Assemble an audit record out of its tokens, including allocating header and
198155131Srwatson * trailer tokens.  Does not free the token chain, which must be done by the
199155131Srwatson * caller if desirable.
200155131Srwatson *
201155131Srwatson * XXX: Assumes there is sufficient space for the header and trailer.
202155131Srwatson */
203155131Srwatsonstatic int
204155131Srwatsonau_assemble(au_record_t *rec, short event)
205155131Srwatson{
206155131Srwatson	token_t *header, *tok, *trailer;
207155131Srwatson	size_t tot_rec_size;
208155131Srwatson	u_char *dptr;
209155131Srwatson	int error;
210155131Srwatson
211161630Srwatson	tot_rec_size = rec->len + AUDIT_HEADER_SIZE + AUDIT_TRAILER_SIZE;
212155131Srwatson	header = au_to_header32(tot_rec_size, event, 0);
213155131Srwatson	if (header == NULL)
214155131Srwatson		return (-1);
215155131Srwatson
216155131Srwatson	trailer = au_to_trailer(tot_rec_size);
217155131Srwatson	if (trailer == NULL) {
218155131Srwatson		error = errno;
219155131Srwatson		au_free_token(header);
220155131Srwatson		errno = error;
221155131Srwatson		return (-1);
222155131Srwatson	}
223155131Srwatson
224155131Srwatson	TAILQ_INSERT_HEAD(&rec->token_q, header, tokens);
225155131Srwatson	TAILQ_INSERT_TAIL(&rec->token_q, trailer, tokens);
226155131Srwatson
227155131Srwatson	rec->len = tot_rec_size;
228155131Srwatson	dptr = rec->data;
229155131Srwatson
230155131Srwatson	TAILQ_FOREACH(tok, &rec->token_q, tokens) {
231155131Srwatson		memcpy(dptr, tok->t_data, tok->len);
232155131Srwatson		dptr += tok->len;
233155131Srwatson	}
234155131Srwatson
235155131Srwatson	return (0);
236155131Srwatson}
237155131Srwatson
238155131Srwatson/*
239155131Srwatson * Given a record that is no longer of interest, tear it down and convert to a
240155131Srwatson * free record.
241155131Srwatson */
242155131Srwatsonstatic void
243155131Srwatsonau_teardown(au_record_t *rec)
244155131Srwatson{
245155131Srwatson	token_t *tok;
246155131Srwatson
247155131Srwatson	/* Free the token list */
248155131Srwatson	while ((tok = TAILQ_FIRST(&rec->token_q)) != NULL) {
249155131Srwatson		TAILQ_REMOVE(&rec->token_q, tok, tokens);
250155131Srwatson		free(tok->t_data);
251155131Srwatson		free(tok);
252155131Srwatson	}
253155131Srwatson
254155131Srwatson	rec->used = 0;
255155131Srwatson	rec->len = 0;
256155131Srwatson
257155131Srwatson	pthread_mutex_lock(&mutex);
258155131Srwatson
259155131Srwatson	/* Add the record to the freelist tail */
260161630Srwatson	LIST_INSERT_HEAD(&audit_free_q, rec, au_rec_q);
261155131Srwatson
262155131Srwatson	pthread_mutex_unlock(&mutex);
263155131Srwatson}
264155131Srwatson
265156283Srwatson#ifdef HAVE_AUDIT_SYSCALLS
266155131Srwatson/*
267155131Srwatson * Add the header token, identify any missing tokens.  Write out the tokens to
268155131Srwatson * the record memory and finally, call audit.
269155131Srwatson */
270156283Srwatsonint
271156283Srwatsonau_close(int d, int keep, short event)
272155131Srwatson{
273155131Srwatson	au_record_t *rec;
274155131Srwatson	size_t tot_rec_size;
275155131Srwatson	int retval = 0;
276155131Srwatson
277155131Srwatson	rec = open_desc_table[d];
278155131Srwatson	if ((rec == NULL) || (rec->used == 0)) {
279155131Srwatson		errno = EINVAL;
280155131Srwatson		return (-1); /* Invalid descriptor */
281155131Srwatson	}
282155131Srwatson
283159248Srwatson	if (keep == AU_TO_NO_WRITE) {
284155131Srwatson		retval = 0;
285155131Srwatson		goto cleanup;
286155131Srwatson	}
287155131Srwatson
288161630Srwatson	tot_rec_size = rec->len + AUDIT_HEADER_SIZE + AUDIT_TRAILER_SIZE;
289155131Srwatson
290155131Srwatson	if (tot_rec_size > MAX_AUDIT_RECORD_SIZE) {
291155131Srwatson		/*
292155131Srwatson		 * XXXRW: Since au_write() is supposed to prevent this, spew
293155131Srwatson		 * an error here.
294155131Srwatson		 */
295155131Srwatson		fprintf(stderr, "au_close failed");
296155131Srwatson		errno = ENOMEM;
297155131Srwatson		retval = -1;
298155131Srwatson		goto cleanup;
299155131Srwatson	}
300155131Srwatson
301155131Srwatson	if (au_assemble(rec, event) < 0) {
302155131Srwatson		/*
303155131Srwatson		 * XXXRW: This is also not supposed to happen, but might if we
304155131Srwatson		 * are unable to allocate header and trailer memory.
305155131Srwatson		 */
306155131Srwatson		retval = -1;
307155131Srwatson		goto cleanup;
308155131Srwatson	}
309155131Srwatson
310155131Srwatson	/* Call the kernel interface to audit */
311155131Srwatson	retval = audit(rec->data, rec->len);
312155131Srwatson
313155131Srwatsoncleanup:
314155131Srwatson	/* CLEANUP */
315155131Srwatson	au_teardown(rec);
316155131Srwatson	return (retval);
317155131Srwatson}
318156283Srwatson#endif /* HAVE_AUDIT_SYSCALLS */
319155131Srwatson
320155131Srwatson/*
321155131Srwatson * au_close(), except onto an in-memory buffer.  Buffer size as an argument,
322155131Srwatson * record size returned via same argument on success.
323155131Srwatson */
324155131Srwatsonint
325155131Srwatsonau_close_buffer(int d, short event, u_char *buffer, size_t *buflen)
326155131Srwatson{
327155131Srwatson	size_t tot_rec_size;
328155131Srwatson	au_record_t *rec;
329155131Srwatson	int retval;
330155131Srwatson
331155131Srwatson	rec = open_desc_table[d];
332155131Srwatson	if ((rec == NULL) || (rec->used == 0)) {
333155131Srwatson		errno = EINVAL;
334155131Srwatson		return (-1);
335155131Srwatson	}
336155131Srwatson
337155131Srwatson	retval = 0;
338161630Srwatson	tot_rec_size = rec->len + AUDIT_HEADER_SIZE + AUDIT_TRAILER_SIZE;
339155131Srwatson	if ((tot_rec_size > MAX_AUDIT_RECORD_SIZE) ||
340155131Srwatson	    (tot_rec_size > *buflen)) {
341155131Srwatson		/*
342155131Srwatson		 * XXXRW: See au_close() comment.
343155131Srwatson		 */
344155131Srwatson		fprintf(stderr, "au_close_buffer failed %zd", tot_rec_size);
345155131Srwatson		errno = ENOMEM;
346155131Srwatson		retval = -1;
347155131Srwatson		goto cleanup;
348155131Srwatson	}
349155131Srwatson
350155131Srwatson	if (au_assemble(rec, event) < 0) {
351155131Srwatson		/* XXXRW: See au_close() comment. */
352155131Srwatson		retval = -1;
353155131Srwatson		goto cleanup;
354155131Srwatson	}
355155131Srwatson
356155131Srwatson	memcpy(buffer, rec->data, rec->len);
357155131Srwatson	*buflen = rec->len;
358155131Srwatson
359155131Srwatsoncleanup:
360155131Srwatson	au_teardown(rec);
361155131Srwatson	return (retval);
362155131Srwatson}
363159248Srwatson
364159248Srwatson/*
365159248Srwatson * au_close_token() returns the byte format of a token_t.  This won't
366159248Srwatson * generally be used by applications, but is quite useful for writing test
367159248Srwatson * tools.  Will free the token on either success or failure.
368159248Srwatson */
369159248Srwatsonint
370159248Srwatsonau_close_token(token_t *tok, u_char *buffer, size_t *buflen)
371159248Srwatson{
372159248Srwatson
373159248Srwatson	if (tok->len > *buflen) {
374159248Srwatson		au_free_token(tok);
375159248Srwatson		errno = ENOMEM;
376159248Srwatson		return (EINVAL);
377159248Srwatson	}
378159248Srwatson
379159248Srwatson	memcpy(buffer, tok->t_data, tok->len);
380159248Srwatson	*buflen = tok->len;
381159248Srwatson	au_free_token(tok);
382159248Srwatson	return (0);
383159248Srwatson}
384