bsm_audit.c revision 187214
1185573Srwatson/*-
2185573Srwatson * Copyright (c) 2004 Apple 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 *
33187214Srwatson * $P4: //depot/projects/trustedbsd/openbsm/libbsm/bsm_audit.c#35 $
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
48185573Srwatson#include <netinet/in.h>
49185573Srwatson
50155131Srwatson#include <errno.h>
51186647Srwatson#ifdef HAVE_PTHREAD_MUTEX_LOCK
52155131Srwatson#include <pthread.h>
53186647Srwatson#endif
54155131Srwatson#include <stdlib.h>
55155131Srwatson#include <string.h>
56155131Srwatson
57155131Srwatson/* array of used descriptors */
58155131Srwatsonstatic au_record_t	*open_desc_table[MAX_AUDIT_RECORDS];
59155131Srwatson
60155131Srwatson/* The current number of active record descriptors */
61161630Srwatsonstatic int	audit_rec_count = 0;
62155131Srwatson
63155131Srwatson/*
64155131Srwatson * Records that can be recycled are maintained in the list given below.  The
65155131Srwatson * maximum number of elements that can be present in this list is bounded by
66155131Srwatson * MAX_AUDIT_RECORDS.  Memory allocated for these records are never freed.
67155131Srwatson */
68161630Srwatsonstatic LIST_HEAD(, au_record)	audit_free_q;
69155131Srwatson
70186647Srwatson#ifdef HAVE_PTHREAD_MUTEX_LOCK
71155131Srwatsonstatic pthread_mutex_t	mutex = PTHREAD_MUTEX_INITIALIZER;
72186647Srwatson#endif
73155131Srwatson
74155131Srwatson/*
75155131Srwatson * This call frees a token_t and its internal data.
76155131Srwatson */
77155131Srwatsonvoid
78155131Srwatsonau_free_token(token_t *tok)
79155131Srwatson{
80155131Srwatson
81155131Srwatson	if (tok != NULL) {
82155131Srwatson		if (tok->t_data)
83155131Srwatson			free(tok->t_data);
84155131Srwatson		free(tok);
85155131Srwatson	}
86155131Srwatson}
87155131Srwatson
88155131Srwatson/*
89155131Srwatson * This call reserves memory for the audit record.  Memory must be guaranteed
90155131Srwatson * before any auditable event can be generated.  The au_record_t structure
91155131Srwatson * maintains a reference to the memory allocated above and also the list of
92155131Srwatson * tokens associated with this record.  Descriptors are recyled once the
93155131Srwatson * records are added to the audit trail following au_close().
94155131Srwatson */
95155131Srwatsonint
96155131Srwatsonau_open(void)
97155131Srwatson{
98155131Srwatson	au_record_t *rec = NULL;
99155131Srwatson
100186647Srwatson#ifdef HAVE_PTHREAD_MUTEX_LOCK
101155131Srwatson	pthread_mutex_lock(&mutex);
102186647Srwatson#endif
103155131Srwatson
104161630Srwatson	if (audit_rec_count == 0)
105161630Srwatson		LIST_INIT(&audit_free_q);
106155131Srwatson
107155131Srwatson	/*
108155131Srwatson	 * Find an unused descriptor, remove it from the free list, mark as
109155131Srwatson	 * used.
110155131Srwatson	 */
111161630Srwatson	if (!LIST_EMPTY(&audit_free_q)) {
112161630Srwatson		rec = LIST_FIRST(&audit_free_q);
113155131Srwatson		rec->used = 1;
114155131Srwatson		LIST_REMOVE(rec, au_rec_q);
115155131Srwatson	}
116155131Srwatson
117186647Srwatson#ifdef HAVE_PTHREAD_MUTEX_LOCK
118155131Srwatson	pthread_mutex_unlock(&mutex);
119186647Srwatson#endif
120155131Srwatson
121155131Srwatson	if (rec == NULL) {
122155131Srwatson		/*
123155131Srwatson		 * Create a new au_record_t if no descriptors are available.
124155131Srwatson		 */
125155131Srwatson		rec = malloc (sizeof(au_record_t));
126155131Srwatson		if (rec == NULL)
127155131Srwatson			return (-1);
128155131Srwatson
129155131Srwatson		rec->data = malloc (MAX_AUDIT_RECORD_SIZE * sizeof(u_char));
130155131Srwatson		if (rec->data == NULL) {
131155131Srwatson			free(rec);
132155131Srwatson			errno = ENOMEM;
133155131Srwatson			return (-1);
134155131Srwatson		}
135155131Srwatson
136186647Srwatson#ifdef HAVE_PTHREAD_MUTEX_LOCK
137155131Srwatson		pthread_mutex_lock(&mutex);
138186647Srwatson#endif
139155131Srwatson
140161630Srwatson		if (audit_rec_count == MAX_AUDIT_RECORDS) {
141186647Srwatson#ifdef HAVE_PTHREAD_MUTEX_LOCK
142155131Srwatson			pthread_mutex_unlock(&mutex);
143186647Srwatson#endif
144155131Srwatson			free(rec->data);
145155131Srwatson			free(rec);
146155131Srwatson
147155131Srwatson			/* XXX We need to increase size of MAX_AUDIT_RECORDS */
148155131Srwatson			errno = ENOMEM;
149155131Srwatson			return (-1);
150155131Srwatson		}
151161630Srwatson		rec->desc = audit_rec_count;
152161630Srwatson		open_desc_table[audit_rec_count] = rec;
153161630Srwatson		audit_rec_count++;
154155131Srwatson
155186647Srwatson#ifdef HAVE_PTHREAD_MUTEX_LOCK
156155131Srwatson		pthread_mutex_unlock(&mutex);
157186647Srwatson#endif
158155131Srwatson
159155131Srwatson	}
160155131Srwatson
161155131Srwatson	memset(rec->data, 0, MAX_AUDIT_RECORD_SIZE);
162155131Srwatson
163155131Srwatson	TAILQ_INIT(&rec->token_q);
164155131Srwatson	rec->len = 0;
165155131Srwatson	rec->used = 1;
166155131Srwatson
167155131Srwatson	return (rec->desc);
168155131Srwatson}
169155131Srwatson
170155131Srwatson/*
171155131Srwatson * Store the token with the record descriptor.
172155131Srwatson *
173155131Srwatson * Don't permit writing more to the buffer than would let the trailer be
174155131Srwatson * appended later.
175155131Srwatson */
176155131Srwatsonint
177155131Srwatsonau_write(int d, token_t *tok)
178155131Srwatson{
179155131Srwatson	au_record_t *rec;
180155131Srwatson
181155131Srwatson	if (tok == NULL) {
182155131Srwatson		errno = EINVAL;
183155131Srwatson		return (-1); /* Invalid Token */
184155131Srwatson	}
185155131Srwatson
186155131Srwatson	/* Write the token to the record descriptor */
187155131Srwatson	rec = open_desc_table[d];
188155131Srwatson	if ((rec == NULL) || (rec->used == 0)) {
189155131Srwatson		errno = EINVAL;
190155131Srwatson		return (-1); /* Invalid descriptor */
191155131Srwatson	}
192155131Srwatson
193161630Srwatson	if (rec->len + tok->len + AUDIT_TRAILER_SIZE > MAX_AUDIT_RECORD_SIZE) {
194155131Srwatson		errno = ENOMEM;
195155131Srwatson		return (-1);
196155131Srwatson	}
197155131Srwatson
198155131Srwatson	/* Add the token to the tail */
199155131Srwatson	/*
200155131Srwatson	 * XXX Not locking here -- we should not be writing to
201155131Srwatson	 * XXX the same descriptor from different threads
202155131Srwatson	 */
203155131Srwatson	TAILQ_INSERT_TAIL(&rec->token_q, tok, tokens);
204155131Srwatson
205155131Srwatson	rec->len += tok->len; /* grow record length by token size bytes */
206155131Srwatson
207155131Srwatson	/* Token should not be available after this call */
208155131Srwatson	tok = NULL;
209155131Srwatson	return (0); /* Success */
210155131Srwatson}
211155131Srwatson
212155131Srwatson/*
213155131Srwatson * Assemble an audit record out of its tokens, including allocating header and
214155131Srwatson * trailer tokens.  Does not free the token chain, which must be done by the
215155131Srwatson * caller if desirable.
216155131Srwatson *
217155131Srwatson * XXX: Assumes there is sufficient space for the header and trailer.
218155131Srwatson */
219155131Srwatsonstatic int
220155131Srwatsonau_assemble(au_record_t *rec, short event)
221155131Srwatson{
222187214Srwatson#ifdef HAVE_AUDIT_SYSCALLS
223187214Srwatson	struct in6_addr *aptr;
224187214Srwatson	struct auditinfo_addr aia;
225187214Srwatson	struct timeval tm;
226187214Srwatson	size_t hdrsize;
227187214Srwatson#endif /* HAVE_AUDIT_SYSCALLS */
228155131Srwatson	token_t *header, *tok, *trailer;
229187214Srwatson	size_t tot_rec_size;
230155131Srwatson	u_char *dptr;
231155131Srwatson	int error;
232155131Srwatson
233185573Srwatson#ifdef HAVE_AUDIT_SYSCALLS
234185573Srwatson	/*
235185573Srwatson	 * Grab the size of the address family stored in the kernel's audit
236185573Srwatson	 * state.
237185573Srwatson	 */
238185573Srwatson	aia.ai_termid.at_type = AU_IPv4;
239185573Srwatson	aia.ai_termid.at_addr[0] = INADDR_ANY;
240185573Srwatson	if (auditon(A_GETKAUDIT, &aia, sizeof(aia)) < 0) {
241186647Srwatson		if (errno != ENOSYS && errno != EPERM)
242185573Srwatson			return (-1);
243185573Srwatson#endif /* HAVE_AUDIT_SYSCALLS */
244185573Srwatson		tot_rec_size = rec->len + AUDIT_HEADER_SIZE +
245185573Srwatson		    AUDIT_TRAILER_SIZE;
246185573Srwatson		header = au_to_header(tot_rec_size, event, 0);
247185573Srwatson#ifdef HAVE_AUDIT_SYSCALLS
248185573Srwatson	} else {
249185573Srwatson		if (gettimeofday(&tm, NULL) < 0)
250185573Srwatson			return (-1);
251185573Srwatson		switch (aia.ai_termid.at_type) {
252185573Srwatson		case AU_IPv4:
253185573Srwatson			hdrsize = (aia.ai_termid.at_addr[0] == INADDR_ANY) ?
254185573Srwatson			    AUDIT_HEADER_SIZE : AUDIT_HEADER_EX_SIZE(&aia);
255185573Srwatson			break;
256185573Srwatson		case AU_IPv6:
257185573Srwatson			aptr = (struct in6_addr *)&aia.ai_termid.at_addr[0];
258185573Srwatson			hdrsize =
259185573Srwatson			    (IN6_IS_ADDR_UNSPECIFIED(aptr)) ?
260185573Srwatson			    AUDIT_HEADER_SIZE : AUDIT_HEADER_EX_SIZE(&aia);
261185573Srwatson			break;
262186647Srwatson		default:
263186647Srwatson			return (-1);
264185573Srwatson		}
265185573Srwatson		tot_rec_size = rec->len + hdrsize + AUDIT_TRAILER_SIZE;
266185573Srwatson		/*
267185573Srwatson		 * A header size greater then AUDIT_HEADER_SIZE means
268185573Srwatson		 * that we are using an extended header.
269185573Srwatson		 */
270185573Srwatson		if (hdrsize > AUDIT_HEADER_SIZE)
271185573Srwatson			header = au_to_header32_ex_tm(tot_rec_size, event,
272185573Srwatson			    0, tm, &aia);
273185573Srwatson		else
274185573Srwatson			header = au_to_header(tot_rec_size, event, 0);
275185573Srwatson	}
276185573Srwatson#endif /* HAVE_AUDIT_SYSCALLS */
277155131Srwatson	if (header == NULL)
278155131Srwatson		return (-1);
279155131Srwatson
280155131Srwatson	trailer = au_to_trailer(tot_rec_size);
281155131Srwatson	if (trailer == NULL) {
282155131Srwatson		error = errno;
283155131Srwatson		au_free_token(header);
284155131Srwatson		errno = error;
285155131Srwatson		return (-1);
286155131Srwatson	}
287155131Srwatson
288155131Srwatson	TAILQ_INSERT_HEAD(&rec->token_q, header, tokens);
289155131Srwatson	TAILQ_INSERT_TAIL(&rec->token_q, trailer, tokens);
290155131Srwatson
291155131Srwatson	rec->len = tot_rec_size;
292155131Srwatson	dptr = rec->data;
293155131Srwatson
294155131Srwatson	TAILQ_FOREACH(tok, &rec->token_q, tokens) {
295155131Srwatson		memcpy(dptr, tok->t_data, tok->len);
296155131Srwatson		dptr += tok->len;
297155131Srwatson	}
298155131Srwatson
299155131Srwatson	return (0);
300155131Srwatson}
301155131Srwatson
302155131Srwatson/*
303155131Srwatson * Given a record that is no longer of interest, tear it down and convert to a
304155131Srwatson * free record.
305155131Srwatson */
306155131Srwatsonstatic void
307155131Srwatsonau_teardown(au_record_t *rec)
308155131Srwatson{
309155131Srwatson	token_t *tok;
310155131Srwatson
311155131Srwatson	/* Free the token list */
312155131Srwatson	while ((tok = TAILQ_FIRST(&rec->token_q)) != NULL) {
313155131Srwatson		TAILQ_REMOVE(&rec->token_q, tok, tokens);
314155131Srwatson		free(tok->t_data);
315155131Srwatson		free(tok);
316155131Srwatson	}
317155131Srwatson
318155131Srwatson	rec->used = 0;
319155131Srwatson	rec->len = 0;
320155131Srwatson
321186647Srwatson#ifdef HAVE_PTHREAD_MUTEX_LOCK
322155131Srwatson	pthread_mutex_lock(&mutex);
323186647Srwatson#endif
324155131Srwatson
325155131Srwatson	/* Add the record to the freelist tail */
326161630Srwatson	LIST_INSERT_HEAD(&audit_free_q, rec, au_rec_q);
327155131Srwatson
328186647Srwatson#ifdef HAVE_PTHREAD_MUTEX_LOCK
329155131Srwatson	pthread_mutex_unlock(&mutex);
330186647Srwatson#endif
331155131Srwatson}
332155131Srwatson
333156283Srwatson#ifdef HAVE_AUDIT_SYSCALLS
334155131Srwatson/*
335155131Srwatson * Add the header token, identify any missing tokens.  Write out the tokens to
336155131Srwatson * the record memory and finally, call audit.
337155131Srwatson */
338156283Srwatsonint
339156283Srwatsonau_close(int d, int keep, short event)
340155131Srwatson{
341155131Srwatson	au_record_t *rec;
342155131Srwatson	size_t tot_rec_size;
343155131Srwatson	int retval = 0;
344155131Srwatson
345155131Srwatson	rec = open_desc_table[d];
346155131Srwatson	if ((rec == NULL) || (rec->used == 0)) {
347155131Srwatson		errno = EINVAL;
348155131Srwatson		return (-1); /* Invalid descriptor */
349155131Srwatson	}
350155131Srwatson
351159248Srwatson	if (keep == AU_TO_NO_WRITE) {
352155131Srwatson		retval = 0;
353155131Srwatson		goto cleanup;
354155131Srwatson	}
355155131Srwatson
356185573Srwatson	tot_rec_size = rec->len + MAX_AUDIT_HEADER_SIZE + AUDIT_TRAILER_SIZE;
357155131Srwatson
358155131Srwatson	if (tot_rec_size > MAX_AUDIT_RECORD_SIZE) {
359155131Srwatson		/*
360155131Srwatson		 * XXXRW: Since au_write() is supposed to prevent this, spew
361155131Srwatson		 * an error here.
362155131Srwatson		 */
363155131Srwatson		fprintf(stderr, "au_close failed");
364155131Srwatson		errno = ENOMEM;
365155131Srwatson		retval = -1;
366155131Srwatson		goto cleanup;
367155131Srwatson	}
368155131Srwatson
369155131Srwatson	if (au_assemble(rec, event) < 0) {
370155131Srwatson		/*
371155131Srwatson		 * XXXRW: This is also not supposed to happen, but might if we
372155131Srwatson		 * are unable to allocate header and trailer memory.
373155131Srwatson		 */
374155131Srwatson		retval = -1;
375155131Srwatson		goto cleanup;
376155131Srwatson	}
377155131Srwatson
378155131Srwatson	/* Call the kernel interface to audit */
379155131Srwatson	retval = audit(rec->data, rec->len);
380155131Srwatson
381155131Srwatsoncleanup:
382155131Srwatson	/* CLEANUP */
383155131Srwatson	au_teardown(rec);
384155131Srwatson	return (retval);
385155131Srwatson}
386156283Srwatson#endif /* HAVE_AUDIT_SYSCALLS */
387155131Srwatson
388155131Srwatson/*
389155131Srwatson * au_close(), except onto an in-memory buffer.  Buffer size as an argument,
390155131Srwatson * record size returned via same argument on success.
391155131Srwatson */
392155131Srwatsonint
393155131Srwatsonau_close_buffer(int d, short event, u_char *buffer, size_t *buflen)
394155131Srwatson{
395155131Srwatson	size_t tot_rec_size;
396155131Srwatson	au_record_t *rec;
397155131Srwatson	int retval;
398155131Srwatson
399155131Srwatson	rec = open_desc_table[d];
400155131Srwatson	if ((rec == NULL) || (rec->used == 0)) {
401155131Srwatson		errno = EINVAL;
402155131Srwatson		return (-1);
403155131Srwatson	}
404155131Srwatson
405155131Srwatson	retval = 0;
406185573Srwatson	tot_rec_size = rec->len + MAX_AUDIT_HEADER_SIZE + AUDIT_TRAILER_SIZE;
407155131Srwatson	if ((tot_rec_size > MAX_AUDIT_RECORD_SIZE) ||
408155131Srwatson	    (tot_rec_size > *buflen)) {
409155131Srwatson		/*
410155131Srwatson		 * XXXRW: See au_close() comment.
411155131Srwatson		 */
412155131Srwatson		fprintf(stderr, "au_close_buffer failed %zd", tot_rec_size);
413155131Srwatson		errno = ENOMEM;
414155131Srwatson		retval = -1;
415155131Srwatson		goto cleanup;
416155131Srwatson	}
417155131Srwatson
418155131Srwatson	if (au_assemble(rec, event) < 0) {
419155131Srwatson		/* XXXRW: See au_close() comment. */
420155131Srwatson		retval = -1;
421155131Srwatson		goto cleanup;
422155131Srwatson	}
423155131Srwatson
424155131Srwatson	memcpy(buffer, rec->data, rec->len);
425155131Srwatson	*buflen = rec->len;
426155131Srwatson
427155131Srwatsoncleanup:
428155131Srwatson	au_teardown(rec);
429155131Srwatson	return (retval);
430155131Srwatson}
431159248Srwatson
432159248Srwatson/*
433159248Srwatson * au_close_token() returns the byte format of a token_t.  This won't
434159248Srwatson * generally be used by applications, but is quite useful for writing test
435159248Srwatson * tools.  Will free the token on either success or failure.
436159248Srwatson */
437159248Srwatsonint
438159248Srwatsonau_close_token(token_t *tok, u_char *buffer, size_t *buflen)
439159248Srwatson{
440159248Srwatson
441159248Srwatson	if (tok->len > *buflen) {
442159248Srwatson		au_free_token(tok);
443159248Srwatson		errno = ENOMEM;
444159248Srwatson		return (EINVAL);
445159248Srwatson	}
446159248Srwatson
447159248Srwatson	memcpy(buffer, tok->t_data, tok->len);
448159248Srwatson	*buflen = tok->len;
449159248Srwatson	au_free_token(tok);
450159248Srwatson	return (0);
451159248Srwatson}
452