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. 17243750Srwatson * 3. Neither the name of Apple 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 */ 33155131Srwatson 34155131Srwatson#include <sys/types.h> 35156283Srwatson 36156283Srwatson#include <config/config.h> 37156283Srwatson#ifdef HAVE_FULL_QUEUE_H 38155131Srwatson#include <sys/queue.h> 39156283Srwatson#else 40156283Srwatson#include <compat/queue.h> 41156283Srwatson#endif 42155131Srwatson 43155131Srwatson#include <bsm/audit_internal.h> 44155131Srwatson#include <bsm/libbsm.h> 45155131Srwatson 46185573Srwatson#include <netinet/in.h> 47185573Srwatson 48155131Srwatson#include <errno.h> 49186647Srwatson#ifdef HAVE_PTHREAD_MUTEX_LOCK 50155131Srwatson#include <pthread.h> 51186647Srwatson#endif 52155131Srwatson#include <stdlib.h> 53155131Srwatson#include <string.h> 54155131Srwatson 55155131Srwatson/* array of used descriptors */ 56155131Srwatsonstatic au_record_t *open_desc_table[MAX_AUDIT_RECORDS]; 57155131Srwatson 58155131Srwatson/* The current number of active record descriptors */ 59161630Srwatsonstatic int audit_rec_count = 0; 60155131Srwatson 61155131Srwatson/* 62155131Srwatson * Records that can be recycled are maintained in the list given below. The 63155131Srwatson * maximum number of elements that can be present in this list is bounded by 64155131Srwatson * MAX_AUDIT_RECORDS. Memory allocated for these records are never freed. 65155131Srwatson */ 66161630Srwatsonstatic LIST_HEAD(, au_record) audit_free_q; 67155131Srwatson 68186647Srwatson#ifdef HAVE_PTHREAD_MUTEX_LOCK 69155131Srwatsonstatic pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 70186647Srwatson#endif 71155131Srwatson 72155131Srwatson/* 73155131Srwatson * This call frees a token_t and its internal data. 74155131Srwatson */ 75155131Srwatsonvoid 76155131Srwatsonau_free_token(token_t *tok) 77155131Srwatson{ 78155131Srwatson 79155131Srwatson if (tok != NULL) { 80155131Srwatson if (tok->t_data) 81155131Srwatson free(tok->t_data); 82155131Srwatson free(tok); 83155131Srwatson } 84155131Srwatson} 85155131Srwatson 86155131Srwatson/* 87155131Srwatson * This call reserves memory for the audit record. Memory must be guaranteed 88155131Srwatson * before any auditable event can be generated. The au_record_t structure 89155131Srwatson * maintains a reference to the memory allocated above and also the list of 90155131Srwatson * tokens associated with this record. Descriptors are recyled once the 91155131Srwatson * records are added to the audit trail following au_close(). 92155131Srwatson */ 93155131Srwatsonint 94155131Srwatsonau_open(void) 95155131Srwatson{ 96155131Srwatson au_record_t *rec = NULL; 97155131Srwatson 98186647Srwatson#ifdef HAVE_PTHREAD_MUTEX_LOCK 99155131Srwatson pthread_mutex_lock(&mutex); 100186647Srwatson#endif 101155131Srwatson 102161630Srwatson if (audit_rec_count == 0) 103161630Srwatson LIST_INIT(&audit_free_q); 104155131Srwatson 105155131Srwatson /* 106155131Srwatson * Find an unused descriptor, remove it from the free list, mark as 107155131Srwatson * used. 108155131Srwatson */ 109161630Srwatson if (!LIST_EMPTY(&audit_free_q)) { 110161630Srwatson rec = LIST_FIRST(&audit_free_q); 111155131Srwatson rec->used = 1; 112155131Srwatson LIST_REMOVE(rec, au_rec_q); 113155131Srwatson } 114155131Srwatson 115186647Srwatson#ifdef HAVE_PTHREAD_MUTEX_LOCK 116155131Srwatson pthread_mutex_unlock(&mutex); 117186647Srwatson#endif 118155131Srwatson 119155131Srwatson if (rec == NULL) { 120155131Srwatson /* 121155131Srwatson * Create a new au_record_t if no descriptors are available. 122155131Srwatson */ 123155131Srwatson rec = malloc (sizeof(au_record_t)); 124155131Srwatson if (rec == NULL) 125155131Srwatson return (-1); 126155131Srwatson 127155131Srwatson rec->data = malloc (MAX_AUDIT_RECORD_SIZE * sizeof(u_char)); 128155131Srwatson if (rec->data == NULL) { 129155131Srwatson free(rec); 130155131Srwatson errno = ENOMEM; 131155131Srwatson return (-1); 132155131Srwatson } 133155131Srwatson 134186647Srwatson#ifdef HAVE_PTHREAD_MUTEX_LOCK 135155131Srwatson pthread_mutex_lock(&mutex); 136186647Srwatson#endif 137155131Srwatson 138161630Srwatson if (audit_rec_count == MAX_AUDIT_RECORDS) { 139186647Srwatson#ifdef HAVE_PTHREAD_MUTEX_LOCK 140155131Srwatson pthread_mutex_unlock(&mutex); 141186647Srwatson#endif 142155131Srwatson free(rec->data); 143155131Srwatson free(rec); 144155131Srwatson 145155131Srwatson /* XXX We need to increase size of MAX_AUDIT_RECORDS */ 146155131Srwatson errno = ENOMEM; 147155131Srwatson return (-1); 148155131Srwatson } 149161630Srwatson rec->desc = audit_rec_count; 150161630Srwatson open_desc_table[audit_rec_count] = rec; 151161630Srwatson audit_rec_count++; 152155131Srwatson 153186647Srwatson#ifdef HAVE_PTHREAD_MUTEX_LOCK 154155131Srwatson pthread_mutex_unlock(&mutex); 155186647Srwatson#endif 156155131Srwatson 157155131Srwatson } 158155131Srwatson 159155131Srwatson memset(rec->data, 0, MAX_AUDIT_RECORD_SIZE); 160155131Srwatson 161155131Srwatson TAILQ_INIT(&rec->token_q); 162155131Srwatson rec->len = 0; 163155131Srwatson rec->used = 1; 164155131Srwatson 165155131Srwatson return (rec->desc); 166155131Srwatson} 167155131Srwatson 168155131Srwatson/* 169155131Srwatson * Store the token with the record descriptor. 170155131Srwatson * 171155131Srwatson * Don't permit writing more to the buffer than would let the trailer be 172155131Srwatson * appended later. 173155131Srwatson */ 174155131Srwatsonint 175155131Srwatsonau_write(int d, token_t *tok) 176155131Srwatson{ 177155131Srwatson au_record_t *rec; 178155131Srwatson 179155131Srwatson if (tok == NULL) { 180155131Srwatson errno = EINVAL; 181155131Srwatson return (-1); /* Invalid Token */ 182155131Srwatson } 183155131Srwatson 184155131Srwatson /* Write the token to the record descriptor */ 185155131Srwatson rec = open_desc_table[d]; 186155131Srwatson if ((rec == NULL) || (rec->used == 0)) { 187155131Srwatson errno = EINVAL; 188155131Srwatson return (-1); /* Invalid descriptor */ 189155131Srwatson } 190155131Srwatson 191161630Srwatson if (rec->len + tok->len + AUDIT_TRAILER_SIZE > MAX_AUDIT_RECORD_SIZE) { 192155131Srwatson errno = ENOMEM; 193155131Srwatson return (-1); 194155131Srwatson } 195155131Srwatson 196155131Srwatson /* Add the token to the tail */ 197155131Srwatson /* 198155131Srwatson * XXX Not locking here -- we should not be writing to 199155131Srwatson * XXX the same descriptor from different threads 200155131Srwatson */ 201155131Srwatson TAILQ_INSERT_TAIL(&rec->token_q, tok, tokens); 202155131Srwatson 203155131Srwatson rec->len += tok->len; /* grow record length by token size bytes */ 204155131Srwatson 205155131Srwatson /* Token should not be available after this call */ 206155131Srwatson tok = NULL; 207155131Srwatson return (0); /* Success */ 208155131Srwatson} 209155131Srwatson 210155131Srwatson/* 211155131Srwatson * Assemble an audit record out of its tokens, including allocating header and 212155131Srwatson * trailer tokens. Does not free the token chain, which must be done by the 213155131Srwatson * caller if desirable. 214155131Srwatson * 215155131Srwatson * XXX: Assumes there is sufficient space for the header and trailer. 216155131Srwatson */ 217155131Srwatsonstatic int 218155131Srwatsonau_assemble(au_record_t *rec, short event) 219155131Srwatson{ 220187214Srwatson#ifdef HAVE_AUDIT_SYSCALLS 221187214Srwatson struct in6_addr *aptr; 222187214Srwatson struct auditinfo_addr aia; 223187214Srwatson struct timeval tm; 224187214Srwatson size_t hdrsize; 225187214Srwatson#endif /* HAVE_AUDIT_SYSCALLS */ 226155131Srwatson token_t *header, *tok, *trailer; 227187214Srwatson size_t tot_rec_size; 228155131Srwatson u_char *dptr; 229155131Srwatson int error; 230155131Srwatson 231185573Srwatson#ifdef HAVE_AUDIT_SYSCALLS 232185573Srwatson /* 233185573Srwatson * Grab the size of the address family stored in the kernel's audit 234185573Srwatson * state. 235185573Srwatson */ 236185573Srwatson aia.ai_termid.at_type = AU_IPv4; 237185573Srwatson aia.ai_termid.at_addr[0] = INADDR_ANY; 238191273Srwatson if (audit_get_kaudit(&aia, sizeof(aia)) != 0) { 239186647Srwatson if (errno != ENOSYS && errno != EPERM) 240185573Srwatson return (-1); 241185573Srwatson#endif /* HAVE_AUDIT_SYSCALLS */ 242185573Srwatson tot_rec_size = rec->len + AUDIT_HEADER_SIZE + 243185573Srwatson AUDIT_TRAILER_SIZE; 244185573Srwatson header = au_to_header(tot_rec_size, event, 0); 245185573Srwatson#ifdef HAVE_AUDIT_SYSCALLS 246185573Srwatson } else { 247185573Srwatson if (gettimeofday(&tm, NULL) < 0) 248185573Srwatson return (-1); 249185573Srwatson switch (aia.ai_termid.at_type) { 250185573Srwatson case AU_IPv4: 251185573Srwatson hdrsize = (aia.ai_termid.at_addr[0] == INADDR_ANY) ? 252185573Srwatson AUDIT_HEADER_SIZE : AUDIT_HEADER_EX_SIZE(&aia); 253185573Srwatson break; 254185573Srwatson case AU_IPv6: 255185573Srwatson aptr = (struct in6_addr *)&aia.ai_termid.at_addr[0]; 256185573Srwatson hdrsize = 257185573Srwatson (IN6_IS_ADDR_UNSPECIFIED(aptr)) ? 258185573Srwatson AUDIT_HEADER_SIZE : AUDIT_HEADER_EX_SIZE(&aia); 259185573Srwatson break; 260186647Srwatson default: 261186647Srwatson return (-1); 262185573Srwatson } 263185573Srwatson tot_rec_size = rec->len + hdrsize + AUDIT_TRAILER_SIZE; 264185573Srwatson /* 265185573Srwatson * A header size greater then AUDIT_HEADER_SIZE means 266185573Srwatson * that we are using an extended header. 267185573Srwatson */ 268185573Srwatson if (hdrsize > AUDIT_HEADER_SIZE) 269185573Srwatson header = au_to_header32_ex_tm(tot_rec_size, event, 270185573Srwatson 0, tm, &aia); 271185573Srwatson else 272185573Srwatson header = au_to_header(tot_rec_size, event, 0); 273185573Srwatson } 274185573Srwatson#endif /* HAVE_AUDIT_SYSCALLS */ 275155131Srwatson if (header == NULL) 276155131Srwatson return (-1); 277155131Srwatson 278155131Srwatson trailer = au_to_trailer(tot_rec_size); 279155131Srwatson if (trailer == NULL) { 280155131Srwatson error = errno; 281155131Srwatson au_free_token(header); 282155131Srwatson errno = error; 283155131Srwatson return (-1); 284155131Srwatson } 285155131Srwatson 286155131Srwatson TAILQ_INSERT_HEAD(&rec->token_q, header, tokens); 287155131Srwatson TAILQ_INSERT_TAIL(&rec->token_q, trailer, tokens); 288155131Srwatson 289155131Srwatson rec->len = tot_rec_size; 290155131Srwatson dptr = rec->data; 291155131Srwatson 292155131Srwatson TAILQ_FOREACH(tok, &rec->token_q, tokens) { 293155131Srwatson memcpy(dptr, tok->t_data, tok->len); 294155131Srwatson dptr += tok->len; 295155131Srwatson } 296155131Srwatson 297155131Srwatson return (0); 298155131Srwatson} 299155131Srwatson 300155131Srwatson/* 301155131Srwatson * Given a record that is no longer of interest, tear it down and convert to a 302155131Srwatson * free record. 303155131Srwatson */ 304155131Srwatsonstatic void 305155131Srwatsonau_teardown(au_record_t *rec) 306155131Srwatson{ 307155131Srwatson token_t *tok; 308155131Srwatson 309155131Srwatson /* Free the token list */ 310155131Srwatson while ((tok = TAILQ_FIRST(&rec->token_q)) != NULL) { 311155131Srwatson TAILQ_REMOVE(&rec->token_q, tok, tokens); 312155131Srwatson free(tok->t_data); 313155131Srwatson free(tok); 314155131Srwatson } 315155131Srwatson 316155131Srwatson rec->used = 0; 317155131Srwatson rec->len = 0; 318155131Srwatson 319186647Srwatson#ifdef HAVE_PTHREAD_MUTEX_LOCK 320155131Srwatson pthread_mutex_lock(&mutex); 321186647Srwatson#endif 322155131Srwatson 323155131Srwatson /* Add the record to the freelist tail */ 324161630Srwatson LIST_INSERT_HEAD(&audit_free_q, rec, au_rec_q); 325155131Srwatson 326186647Srwatson#ifdef HAVE_PTHREAD_MUTEX_LOCK 327155131Srwatson pthread_mutex_unlock(&mutex); 328186647Srwatson#endif 329155131Srwatson} 330155131Srwatson 331156283Srwatson#ifdef HAVE_AUDIT_SYSCALLS 332155131Srwatson/* 333155131Srwatson * Add the header token, identify any missing tokens. Write out the tokens to 334155131Srwatson * the record memory and finally, call audit. 335155131Srwatson */ 336156283Srwatsonint 337156283Srwatsonau_close(int d, int keep, short event) 338155131Srwatson{ 339155131Srwatson au_record_t *rec; 340155131Srwatson size_t tot_rec_size; 341155131Srwatson int retval = 0; 342155131Srwatson 343155131Srwatson rec = open_desc_table[d]; 344155131Srwatson if ((rec == NULL) || (rec->used == 0)) { 345155131Srwatson errno = EINVAL; 346155131Srwatson return (-1); /* Invalid descriptor */ 347155131Srwatson } 348155131Srwatson 349159248Srwatson if (keep == AU_TO_NO_WRITE) { 350155131Srwatson retval = 0; 351155131Srwatson goto cleanup; 352155131Srwatson } 353155131Srwatson 354185573Srwatson tot_rec_size = rec->len + MAX_AUDIT_HEADER_SIZE + AUDIT_TRAILER_SIZE; 355155131Srwatson 356155131Srwatson if (tot_rec_size > MAX_AUDIT_RECORD_SIZE) { 357155131Srwatson /* 358155131Srwatson * XXXRW: Since au_write() is supposed to prevent this, spew 359155131Srwatson * an error here. 360155131Srwatson */ 361155131Srwatson fprintf(stderr, "au_close failed"); 362155131Srwatson errno = ENOMEM; 363155131Srwatson retval = -1; 364155131Srwatson goto cleanup; 365155131Srwatson } 366155131Srwatson 367155131Srwatson if (au_assemble(rec, event) < 0) { 368155131Srwatson /* 369155131Srwatson * XXXRW: This is also not supposed to happen, but might if we 370155131Srwatson * are unable to allocate header and trailer memory. 371155131Srwatson */ 372155131Srwatson retval = -1; 373155131Srwatson goto cleanup; 374155131Srwatson } 375155131Srwatson 376155131Srwatson /* Call the kernel interface to audit */ 377155131Srwatson retval = audit(rec->data, rec->len); 378155131Srwatson 379155131Srwatsoncleanup: 380155131Srwatson /* CLEANUP */ 381155131Srwatson au_teardown(rec); 382155131Srwatson return (retval); 383155131Srwatson} 384156283Srwatson#endif /* HAVE_AUDIT_SYSCALLS */ 385155131Srwatson 386155131Srwatson/* 387155131Srwatson * au_close(), except onto an in-memory buffer. Buffer size as an argument, 388155131Srwatson * record size returned via same argument on success. 389155131Srwatson */ 390155131Srwatsonint 391155131Srwatsonau_close_buffer(int d, short event, u_char *buffer, size_t *buflen) 392155131Srwatson{ 393155131Srwatson size_t tot_rec_size; 394155131Srwatson au_record_t *rec; 395155131Srwatson int retval; 396155131Srwatson 397155131Srwatson rec = open_desc_table[d]; 398155131Srwatson if ((rec == NULL) || (rec->used == 0)) { 399155131Srwatson errno = EINVAL; 400155131Srwatson return (-1); 401155131Srwatson } 402155131Srwatson 403155131Srwatson retval = 0; 404185573Srwatson tot_rec_size = rec->len + MAX_AUDIT_HEADER_SIZE + AUDIT_TRAILER_SIZE; 405155131Srwatson if ((tot_rec_size > MAX_AUDIT_RECORD_SIZE) || 406155131Srwatson (tot_rec_size > *buflen)) { 407155131Srwatson /* 408155131Srwatson * XXXRW: See au_close() comment. 409155131Srwatson */ 410155131Srwatson fprintf(stderr, "au_close_buffer failed %zd", tot_rec_size); 411155131Srwatson errno = ENOMEM; 412155131Srwatson retval = -1; 413155131Srwatson goto cleanup; 414155131Srwatson } 415155131Srwatson 416155131Srwatson if (au_assemble(rec, event) < 0) { 417155131Srwatson /* XXXRW: See au_close() comment. */ 418155131Srwatson retval = -1; 419155131Srwatson goto cleanup; 420155131Srwatson } 421155131Srwatson 422155131Srwatson memcpy(buffer, rec->data, rec->len); 423155131Srwatson *buflen = rec->len; 424155131Srwatson 425155131Srwatsoncleanup: 426155131Srwatson au_teardown(rec); 427155131Srwatson return (retval); 428155131Srwatson} 429159248Srwatson 430159248Srwatson/* 431159248Srwatson * au_close_token() returns the byte format of a token_t. This won't 432159248Srwatson * generally be used by applications, but is quite useful for writing test 433159248Srwatson * tools. Will free the token on either success or failure. 434159248Srwatson */ 435159248Srwatsonint 436159248Srwatsonau_close_token(token_t *tok, u_char *buffer, size_t *buflen) 437159248Srwatson{ 438159248Srwatson 439159248Srwatson if (tok->len > *buflen) { 440159248Srwatson au_free_token(tok); 441159248Srwatson errno = ENOMEM; 442159248Srwatson return (EINVAL); 443159248Srwatson } 444159248Srwatson 445159248Srwatson memcpy(buffer, tok->t_data, tok->len); 446159248Srwatson *buflen = tok->len; 447159248Srwatson au_free_token(tok); 448159248Srwatson return (0); 449159248Srwatson} 450