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