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