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 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
34 #include <sys/types.h>
35
36 #include <config/config.h>
37 #ifdef HAVE_FULL_QUEUE_H
38 #include <sys/queue.h>
39 #else
40 #include <compat/queue.h>
41 #endif
42
43 #include <bsm/audit_internal.h>
44 #include <bsm/libbsm.h>
45
46 #include <netinet/in.h>
47
48 #include <errno.h>
49 #ifdef HAVE_PTHREAD_MUTEX_LOCK
50 #include <pthread.h>
51 #endif
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 #ifdef HAVE_PTHREAD_MUTEX_LOCK
69 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
70 #endif
71
72 /*
73 * This call frees a token_t and its internal data.
74 */
75 void
au_free_token(token_t * tok)76 au_free_token(token_t *tok)
77 {
78
79 if (tok != NULL) {
80 if (tok->t_data)
81 free(tok->t_data);
82 free(tok);
83 }
84 }
85
86 /*
87 * This call reserves memory for the audit record. Memory must be guaranteed
88 * before any auditable event can be generated. The au_record_t structure
89 * maintains a reference to the memory allocated above and also the list of
90 * tokens associated with this record. Descriptors are recyled once the
91 * records are added to the audit trail following au_close().
92 */
93 int
au_open(void)94 au_open(void)
95 {
96 au_record_t *rec = NULL;
97
98 #ifdef HAVE_PTHREAD_MUTEX_LOCK
99 pthread_mutex_lock(&mutex);
100 #endif
101
102 if (audit_rec_count == 0)
103 LIST_INIT(&audit_free_q);
104
105 /*
106 * Find an unused descriptor, remove it from the free list, mark as
107 * used.
108 */
109 if (!LIST_EMPTY(&audit_free_q)) {
110 rec = LIST_FIRST(&audit_free_q);
111 rec->used = 1;
112 LIST_REMOVE(rec, au_rec_q);
113 }
114
115 #ifdef HAVE_PTHREAD_MUTEX_LOCK
116 pthread_mutex_unlock(&mutex);
117 #endif
118
119 if (rec == NULL) {
120 /*
121 * Create a new au_record_t if no descriptors are available.
122 */
123 rec = malloc (sizeof(au_record_t));
124 if (rec == NULL)
125 return (-1);
126
127 rec->data = malloc (MAX_AUDIT_RECORD_SIZE * sizeof(u_char));
128 if (rec->data == NULL) {
129 free(rec);
130 errno = ENOMEM;
131 return (-1);
132 }
133
134 #ifdef HAVE_PTHREAD_MUTEX_LOCK
135 pthread_mutex_lock(&mutex);
136 #endif
137
138 if (audit_rec_count == MAX_AUDIT_RECORDS) {
139 #ifdef HAVE_PTHREAD_MUTEX_LOCK
140 pthread_mutex_unlock(&mutex);
141 #endif
142 free(rec->data);
143 free(rec);
144
145 /* XXX We need to increase size of MAX_AUDIT_RECORDS */
146 errno = ENOMEM;
147 return (-1);
148 }
149 rec->desc = audit_rec_count;
150 open_desc_table[audit_rec_count] = rec;
151 audit_rec_count++;
152
153 #ifdef HAVE_PTHREAD_MUTEX_LOCK
154 pthread_mutex_unlock(&mutex);
155 #endif
156
157 }
158
159 memset(rec->data, 0, MAX_AUDIT_RECORD_SIZE);
160
161 TAILQ_INIT(&rec->token_q);
162 rec->len = 0;
163 rec->used = 1;
164
165 return (rec->desc);
166 }
167
168 /*
169 * Store the token with the record descriptor.
170 *
171 * Don't permit writing more to the buffer than would let the trailer be
172 * appended later.
173 */
174 int
au_write(int d,token_t * tok)175 au_write(int d, token_t *tok)
176 {
177 au_record_t *rec;
178
179 if (tok == NULL) {
180 errno = EINVAL;
181 return (-1); /* Invalid Token */
182 }
183
184 /* Write the token to the record descriptor */
185 rec = open_desc_table[d];
186 if ((rec == NULL) || (rec->used == 0)) {
187 errno = EINVAL;
188 return (-1); /* Invalid descriptor */
189 }
190
191 if (rec->len + tok->len + AUDIT_TRAILER_SIZE > MAX_AUDIT_RECORD_SIZE) {
192 errno = ENOMEM;
193 return (-1);
194 }
195
196 /* Add the token to the tail */
197 /*
198 * XXX Not locking here -- we should not be writing to
199 * XXX the same descriptor from different threads
200 */
201 TAILQ_INSERT_TAIL(&rec->token_q, tok, tokens);
202
203 rec->len += tok->len; /* grow record length by token size bytes */
204
205 /* Token should not be available after this call */
206 tok = NULL;
207 return (0); /* Success */
208 }
209
210 /*
211 * Assemble an audit record out of its tokens, including allocating header and
212 * trailer tokens. Does not free the token chain, which must be done by the
213 * caller if desirable.
214 *
215 * XXX: Assumes there is sufficient space for the header and trailer.
216 */
217 static int
au_assemble(au_record_t * rec,short event)218 au_assemble(au_record_t *rec, short event)
219 {
220 #ifdef HAVE_AUDIT_SYSCALLS
221 struct in6_addr *aptr;
222 struct auditinfo_addr aia;
223 struct timeval tm;
224 size_t hdrsize;
225 #endif /* HAVE_AUDIT_SYSCALLS */
226 token_t *header, *tok, *trailer;
227 size_t tot_rec_size;
228 u_char *dptr;
229 int error;
230
231 #ifdef HAVE_AUDIT_SYSCALLS
232 /*
233 * Grab the size of the address family stored in the kernel's audit
234 * state.
235 */
236 aia.ai_termid.at_type = AU_IPv4;
237 aia.ai_termid.at_addr[0] = INADDR_ANY;
238 if (audit_get_kaudit(&aia, sizeof(aia)) != 0) {
239 if (errno != ENOSYS && errno != EPERM)
240 return (-1);
241 #endif /* HAVE_AUDIT_SYSCALLS */
242 tot_rec_size = rec->len + AUDIT_HEADER_SIZE +
243 AUDIT_TRAILER_SIZE;
244 header = au_to_header(tot_rec_size, event, 0);
245 #ifdef HAVE_AUDIT_SYSCALLS
246 } else {
247 if (gettimeofday(&tm, NULL) < 0)
248 return (-1);
249 switch (aia.ai_termid.at_type) {
250 case AU_IPv4:
251 hdrsize = (aia.ai_termid.at_addr[0] == INADDR_ANY) ?
252 AUDIT_HEADER_SIZE : AUDIT_HEADER_EX_SIZE(&aia);
253 break;
254 case AU_IPv6:
255 aptr = (struct in6_addr *)&aia.ai_termid.at_addr[0];
256 hdrsize =
257 (IN6_IS_ADDR_UNSPECIFIED(aptr)) ?
258 AUDIT_HEADER_SIZE : AUDIT_HEADER_EX_SIZE(&aia);
259 break;
260 default:
261 return (-1);
262 }
263 tot_rec_size = rec->len + hdrsize + AUDIT_TRAILER_SIZE;
264 /*
265 * A header size greater then AUDIT_HEADER_SIZE means
266 * that we are using an extended header.
267 */
268 if (hdrsize > AUDIT_HEADER_SIZE)
269 header = au_to_header32_ex_tm(tot_rec_size, event,
270 0, tm, &aia);
271 else
272 header = au_to_header(tot_rec_size, event, 0);
273 }
274 #endif /* HAVE_AUDIT_SYSCALLS */
275 if (header == NULL)
276 return (-1);
277
278 trailer = au_to_trailer(tot_rec_size);
279 if (trailer == NULL) {
280 error = errno;
281 au_free_token(header);
282 errno = error;
283 return (-1);
284 }
285
286 TAILQ_INSERT_HEAD(&rec->token_q, header, tokens);
287 TAILQ_INSERT_TAIL(&rec->token_q, trailer, tokens);
288
289 rec->len = tot_rec_size;
290 dptr = rec->data;
291
292 TAILQ_FOREACH(tok, &rec->token_q, tokens) {
293 memcpy(dptr, tok->t_data, tok->len);
294 dptr += tok->len;
295 }
296
297 return (0);
298 }
299
300 /*
301 * Given a record that is no longer of interest, tear it down and convert to a
302 * free record.
303 */
304 static void
au_teardown(au_record_t * rec)305 au_teardown(au_record_t *rec)
306 {
307 token_t *tok;
308
309 /* Free the token list */
310 while ((tok = TAILQ_FIRST(&rec->token_q)) != NULL) {
311 TAILQ_REMOVE(&rec->token_q, tok, tokens);
312 free(tok->t_data);
313 free(tok);
314 }
315
316 rec->used = 0;
317 rec->len = 0;
318
319 #ifdef HAVE_PTHREAD_MUTEX_LOCK
320 pthread_mutex_lock(&mutex);
321 #endif
322
323 /* Add the record to the freelist tail */
324 LIST_INSERT_HEAD(&audit_free_q, rec, au_rec_q);
325
326 #ifdef HAVE_PTHREAD_MUTEX_LOCK
327 pthread_mutex_unlock(&mutex);
328 #endif
329 }
330
331 #ifdef HAVE_AUDIT_SYSCALLS
332 /*
333 * Add the header token, identify any missing tokens. Write out the tokens to
334 * the record memory and finally, call audit.
335 */
336 int
au_close(int d,int keep,short event)337 au_close(int d, int keep, short event)
338 {
339 au_record_t *rec;
340 size_t tot_rec_size;
341 int retval = 0;
342
343 rec = open_desc_table[d];
344 if ((rec == NULL) || (rec->used == 0)) {
345 errno = EINVAL;
346 return (-1); /* Invalid descriptor */
347 }
348
349 if (keep == AU_TO_NO_WRITE) {
350 retval = 0;
351 goto cleanup;
352 }
353
354 tot_rec_size = rec->len + MAX_AUDIT_HEADER_SIZE + AUDIT_TRAILER_SIZE;
355
356 if (tot_rec_size > MAX_AUDIT_RECORD_SIZE) {
357 /*
358 * XXXRW: Since au_write() is supposed to prevent this, spew
359 * an error here.
360 */
361 fprintf(stderr, "au_close failed");
362 errno = ENOMEM;
363 retval = -1;
364 goto cleanup;
365 }
366
367 if (au_assemble(rec, event) < 0) {
368 /*
369 * XXXRW: This is also not supposed to happen, but might if we
370 * are unable to allocate header and trailer memory.
371 */
372 retval = -1;
373 goto cleanup;
374 }
375
376 /* Call the kernel interface to audit */
377 retval = audit(rec->data, rec->len);
378
379 cleanup:
380 /* CLEANUP */
381 au_teardown(rec);
382 return (retval);
383 }
384 #endif /* HAVE_AUDIT_SYSCALLS */
385
386 /*
387 * au_close(), except onto an in-memory buffer. Buffer size as an argument,
388 * record size returned via same argument on success.
389 */
390 int
au_close_buffer(int d,short event,u_char * buffer,size_t * buflen)391 au_close_buffer(int d, short event, u_char *buffer, size_t *buflen)
392 {
393 size_t tot_rec_size;
394 au_record_t *rec;
395 int retval;
396
397 rec = open_desc_table[d];
398 if ((rec == NULL) || (rec->used == 0)) {
399 errno = EINVAL;
400 return (-1);
401 }
402
403 retval = 0;
404 tot_rec_size = rec->len + MAX_AUDIT_HEADER_SIZE + AUDIT_TRAILER_SIZE;
405 if ((tot_rec_size > MAX_AUDIT_RECORD_SIZE) ||
406 (tot_rec_size > *buflen)) {
407 /*
408 * XXXRW: See au_close() comment.
409 */
410 fprintf(stderr, "au_close_buffer failed %zd", tot_rec_size);
411 errno = ENOMEM;
412 retval = -1;
413 goto cleanup;
414 }
415
416 if (au_assemble(rec, event) < 0) {
417 /* XXXRW: See au_close() comment. */
418 retval = -1;
419 goto cleanup;
420 }
421
422 memcpy(buffer, rec->data, rec->len);
423 *buflen = rec->len;
424
425 cleanup:
426 au_teardown(rec);
427 return (retval);
428 }
429
430 /*
431 * au_close_token() returns the byte format of a token_t. This won't
432 * generally be used by applications, but is quite useful for writing test
433 * tools. Will free the token on either success or failure.
434 */
435 int
au_close_token(token_t * tok,u_char * buffer,size_t * buflen)436 au_close_token(token_t *tok, u_char *buffer, size_t *buflen)
437 {
438
439 if (tok->len > *buflen) {
440 au_free_token(tok);
441 errno = ENOMEM;
442 return (EINVAL);
443 }
444
445 memcpy(buffer, tok->t_data, tok->len);
446 *buflen = tok->len;
447 au_free_token(tok);
448 return (0);
449 }
450