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