xref: /freebsd/contrib/openbsm/libbsm/bsm_audit.c (revision ca0716f5714781ac39461f60647d795321921363)
1 /*
2  * Copyright (c) 2004 Apple Computer, 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#18 $
34  */
35 
36 #include <sys/types.h>
37 #include <sys/queue.h>
38 
39 #include <bsm/audit_internal.h>
40 #include <bsm/libbsm.h>
41 
42 #include <errno.h>
43 #include <pthread.h>
44 #include <stdlib.h>
45 #include <string.h>
46 
47 /* array of used descriptors */
48 static au_record_t	*open_desc_table[MAX_AUDIT_RECORDS];
49 
50 /* The current number of active record descriptors */
51 static int	bsm_rec_count = 0;
52 
53 /*
54  * Records that can be recycled are maintained in the list given below.  The
55  * maximum number of elements that can be present in this list is bounded by
56  * MAX_AUDIT_RECORDS.  Memory allocated for these records are never freed.
57  */
58 static LIST_HEAD(, au_record)	bsm_free_q;
59 
60 static pthread_mutex_t	mutex = PTHREAD_MUTEX_INITIALIZER;
61 
62 /*
63  * This call frees a token_t and its internal data.
64  */
65 void
66 au_free_token(token_t *tok)
67 {
68 
69 	if (tok != NULL) {
70 		if (tok->t_data)
71 			free(tok->t_data);
72 		free(tok);
73 	}
74 }
75 
76 /*
77  * This call reserves memory for the audit record.  Memory must be guaranteed
78  * before any auditable event can be generated.  The au_record_t structure
79  * maintains a reference to the memory allocated above and also the list of
80  * tokens associated with this record.  Descriptors are recyled once the
81  * records are added to the audit trail following au_close().
82  */
83 int
84 au_open(void)
85 {
86 	au_record_t *rec = NULL;
87 
88 	pthread_mutex_lock(&mutex);
89 
90 	if (bsm_rec_count == 0)
91 		LIST_INIT(&bsm_free_q);
92 
93 	/*
94 	 * Find an unused descriptor, remove it from the free list, mark as
95 	 * used.
96 	 */
97 	if (!LIST_EMPTY(&bsm_free_q)) {
98 		rec = LIST_FIRST(&bsm_free_q);
99 		rec->used = 1;
100 		LIST_REMOVE(rec, au_rec_q);
101 	}
102 
103 	pthread_mutex_unlock(&mutex);
104 
105 	if (rec == NULL) {
106 		/*
107 		 * Create a new au_record_t if no descriptors are available.
108 		 */
109 		rec = malloc (sizeof(au_record_t));
110 		if (rec == NULL)
111 			return (-1);
112 
113 		rec->data = malloc (MAX_AUDIT_RECORD_SIZE * sizeof(u_char));
114 		if (rec->data == NULL) {
115 			free(rec);
116 			errno = ENOMEM;
117 			return (-1);
118 		}
119 
120 		pthread_mutex_lock(&mutex);
121 
122 		if (bsm_rec_count == MAX_AUDIT_RECORDS) {
123 			pthread_mutex_unlock(&mutex);
124 			free(rec->data);
125 			free(rec);
126 
127 			/* XXX We need to increase size of MAX_AUDIT_RECORDS */
128 			errno = ENOMEM;
129 			return (-1);
130 		}
131 		rec->desc = bsm_rec_count;
132 		open_desc_table[bsm_rec_count] = rec;
133 		bsm_rec_count++;
134 
135 		pthread_mutex_unlock(&mutex);
136 
137 	}
138 
139 	memset(rec->data, 0, MAX_AUDIT_RECORD_SIZE);
140 
141 	TAILQ_INIT(&rec->token_q);
142 	rec->len = 0;
143 	rec->used = 1;
144 
145 	return (rec->desc);
146 }
147 
148 /*
149  * Store the token with the record descriptor.
150  *
151  * Don't permit writing more to the buffer than would let the trailer be
152  * appended later.
153  */
154 int
155 au_write(int d, token_t *tok)
156 {
157 	au_record_t *rec;
158 
159 	if (tok == NULL) {
160 		errno = EINVAL;
161 		return (-1); /* Invalid Token */
162 	}
163 
164 	/* Write the token to the record descriptor */
165 	rec = open_desc_table[d];
166 	if ((rec == NULL) || (rec->used == 0)) {
167 		errno = EINVAL;
168 		return (-1); /* Invalid descriptor */
169 	}
170 
171 	if (rec->len + tok->len + BSM_TRAILER_SIZE > MAX_AUDIT_RECORD_SIZE) {
172 		errno = ENOMEM;
173 		return (-1);
174 	}
175 
176 	/* Add the token to the tail */
177 	/*
178 	 * XXX Not locking here -- we should not be writing to
179 	 * XXX the same descriptor from different threads
180 	 */
181 	TAILQ_INSERT_TAIL(&rec->token_q, tok, tokens);
182 
183 	rec->len += tok->len; /* grow record length by token size bytes */
184 
185 	/* Token should not be available after this call */
186 	tok = NULL;
187 	return (0); /* Success */
188 }
189 
190 /*
191  * Assemble an audit record out of its tokens, including allocating header and
192  * trailer tokens.  Does not free the token chain, which must be done by the
193  * caller if desirable.
194  *
195  * XXX: Assumes there is sufficient space for the header and trailer.
196  */
197 static int
198 au_assemble(au_record_t *rec, short event)
199 {
200 	token_t *header, *tok, *trailer;
201 	size_t tot_rec_size;
202 	u_char *dptr;
203 	int error;
204 
205 	tot_rec_size = rec->len + BSM_HEADER_SIZE + BSM_TRAILER_SIZE;
206 	header = au_to_header32(tot_rec_size, event, 0);
207 	if (header == NULL)
208 		return (-1);
209 
210 	trailer = au_to_trailer(tot_rec_size);
211 	if (trailer == NULL) {
212 		error = errno;
213 		au_free_token(header);
214 		errno = error;
215 		return (-1);
216 	}
217 
218 	TAILQ_INSERT_HEAD(&rec->token_q, header, tokens);
219 	TAILQ_INSERT_TAIL(&rec->token_q, trailer, tokens);
220 
221 	rec->len = tot_rec_size;
222 	dptr = rec->data;
223 
224 	TAILQ_FOREACH(tok, &rec->token_q, tokens) {
225 		memcpy(dptr, tok->t_data, tok->len);
226 		dptr += tok->len;
227 	}
228 
229 	return (0);
230 }
231 
232 /*
233  * Given a record that is no longer of interest, tear it down and convert to a
234  * free record.
235  */
236 static void
237 au_teardown(au_record_t *rec)
238 {
239 	token_t *tok;
240 
241 	/* Free the token list */
242 	while ((tok = TAILQ_FIRST(&rec->token_q)) != NULL) {
243 		TAILQ_REMOVE(&rec->token_q, tok, tokens);
244 		free(tok->t_data);
245 		free(tok);
246 	}
247 
248 	rec->used = 0;
249 	rec->len = 0;
250 
251 	pthread_mutex_lock(&mutex);
252 
253 	/* Add the record to the freelist tail */
254 	LIST_INSERT_HEAD(&bsm_free_q, rec, au_rec_q);
255 
256 	pthread_mutex_unlock(&mutex);
257 }
258 
259 /*
260  * Add the header token, identify any missing tokens.  Write out the tokens to
261  * the record memory and finally, call audit.
262  */
263 int au_close(int d, int keep, short event)
264 {
265 	au_record_t *rec;
266 	size_t tot_rec_size;
267 	int retval = 0;
268 
269 	rec = open_desc_table[d];
270 	if ((rec == NULL) || (rec->used == 0)) {
271 		errno = EINVAL;
272 		return (-1); /* Invalid descriptor */
273 	}
274 
275 	if (!keep) {
276 		retval = 0;
277 		goto cleanup;
278 	}
279 
280 
281 	tot_rec_size = rec->len + BSM_HEADER_SIZE + BSM_TRAILER_SIZE;
282 
283 	if (tot_rec_size > MAX_AUDIT_RECORD_SIZE) {
284 		/*
285 		 * XXXRW: Since au_write() is supposed to prevent this, spew
286 		 * an error here.
287 		 */
288 		fprintf(stderr, "au_close failed");
289 		errno = ENOMEM;
290 		retval = -1;
291 		goto cleanup;
292 	}
293 
294 	if (au_assemble(rec, event) < 0) {
295 		/*
296 		 * XXXRW: This is also not supposed to happen, but might if we
297 		 * are unable to allocate header and trailer memory.
298 		 */
299 		retval = -1;
300 		goto cleanup;
301 	}
302 
303 	/* Call the kernel interface to audit */
304 	retval = audit(rec->data, rec->len);
305 
306 cleanup:
307 	/* CLEANUP */
308 	au_teardown(rec);
309 	return (retval);
310 }
311 
312 /*
313  * au_close(), except onto an in-memory buffer.  Buffer size as an argument,
314  * record size returned via same argument on success.
315  */
316 int
317 au_close_buffer(int d, short event, u_char *buffer, size_t *buflen)
318 {
319 	size_t tot_rec_size;
320 	au_record_t *rec;
321 	int retval;
322 
323 	rec = open_desc_table[d];
324 	if ((rec == NULL) || (rec->used == 0)) {
325 		errno = EINVAL;
326 		return (-1);
327 	}
328 
329 	retval = 0;
330 	tot_rec_size = rec->len + BSM_HEADER_SIZE + BSM_TRAILER_SIZE;
331 	if ((tot_rec_size > MAX_AUDIT_RECORD_SIZE) ||
332 	    (tot_rec_size > *buflen)) {
333 		/*
334 		 * XXXRW: See au_close() comment.
335 		 */
336 		fprintf(stderr, "au_close_buffer failed %zd", tot_rec_size);
337 		errno = ENOMEM;
338 		retval = -1;
339 		goto cleanup;
340 	}
341 
342 	if (au_assemble(rec, event) < 0) {
343 		/* XXXRW: See au_close() comment. */
344 		retval = -1;
345 		goto cleanup;
346 	}
347 
348 	memcpy(buffer, rec->data, rec->len);
349 	*buflen = rec->len;
350 
351 cleanup:
352 	au_teardown(rec);
353 	return (retval);
354 }
355