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