audit_pipe.c revision 155408
1/*- 2 * Copyright (c) 2006 Robert N. M. Watson 3 * All rights reserved. 4 * 5 * This software was developed by Robert Watson for the TrustedBSD Project. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * $FreeBSD: head/sys/security/audit/audit_pipe.c 155408 2006-02-06 22:50:39Z rwatson $ 29 */ 30 31#include <sys/param.h> 32#include <sys/condvar.h> 33#include <sys/conf.h> 34#include <sys/eventhandler.h> 35#include <sys/filio.h> 36#include <sys/kernel.h> 37#include <sys/lock.h> 38#include <sys/malloc.h> 39#include <sys/mutex.h> 40#include <sys/poll.h> 41#include <sys/proc.h> 42#include <sys/queue.h> 43#include <sys/selinfo.h> 44#include <sys/sigio.h> 45#include <sys/signal.h> 46#include <sys/signalvar.h> 47#include <sys/systm.h> 48#include <sys/uio.h> 49 50#include <security/audit/audit.h> 51#include <security/audit/audit_private.h> 52 53/* 54 * Implementation of a clonable special device providing a live stream of BSM 55 * audit data. This is a "tee" of the data going to the file. It provides 56 * unreliable but timely access to audit events. Consumers of this interface 57 * should be very careful to avoid introducing event cycles. 58 */ 59 60/* 61 * Memory types. 62 */ 63static MALLOC_DEFINE(M_AUDIT_PIPE, "audit_pipe", "Audit pipes"); 64static MALLOC_DEFINE(M_AUDIT_PIPE_ENTRY, "audit_pipeent", 65 "Audit pipe entries and buffers"); 66 67/* 68 * Audit pipe buffer parameters. 69 */ 70#define AUDIT_PIPE_QLIMIT_DEFAULT (32) 71#define AUDIT_PIPE_QLIMIT_MAX (1024) 72 73/* 74 * Description of an entry in an audit_pipe. 75 */ 76struct audit_pipe_entry { 77 void *ape_record; 78 u_int ape_record_len; 79 TAILQ_ENTRY(audit_pipe_entry) ape_queue; 80}; 81 82/* 83 * Description of an individual audit_pipe. Consists largely of a bounded 84 * length queue. 85 */ 86#define AUDIT_PIPE_ASYNC 0x00000001 87#define AUDIT_PIPE_NBIO 0x00000002 88struct audit_pipe { 89 int ap_open; /* Device open? */ 90 u_int ap_flags; 91 92 struct selinfo ap_selinfo; 93 struct sigio *ap_sigio; 94 95 u_int ap_qlen; 96 u_int ap_qlimit; 97 98 u_int64_t ap_inserts; /* Records added. */ 99 u_int64_t ap_reads; /* Records read. */ 100 u_int64_t ap_drops; /* Records dropped. */ 101 u_int64_t ap_truncates; /* Records too long. */ 102 103 TAILQ_HEAD(, audit_pipe_entry) ap_queue; 104 105 TAILQ_ENTRY(audit_pipe) ap_list; 106}; 107 108/* 109 * Global list of audit pipes, mutex to protect it and the pipes. Finder 110 * grained locking may be desirable at some point. 111 */ 112static TAILQ_HEAD(, audit_pipe) audit_pipe_list; 113static struct mtx audit_pipe_mtx; 114 115/* 116 * This CV is used to wakeup on an audit record write. Eventually, it should 117 * probably be per-pipe. 118 */ 119static struct cv audit_pipe_cv; 120 121/* 122 * Cloning related variables and constants. 123 */ 124#define AUDIT_PIPE_NAME "auditpipe" 125static eventhandler_tag audit_pipe_eh_tag; 126static struct clonedevs *audit_pipe_clones; 127 128/* 129 * Special device methods and definition. 130 */ 131static d_open_t audit_pipe_open; 132static d_close_t audit_pipe_close; 133static d_read_t audit_pipe_read; 134static d_ioctl_t audit_pipe_ioctl; 135static d_poll_t audit_pipe_poll; 136 137static struct cdevsw audit_pipe_cdevsw = { 138 .d_version = D_VERSION, 139 .d_flags = D_PSEUDO, 140 .d_open = audit_pipe_open, 141 .d_close = audit_pipe_close, 142 .d_read = audit_pipe_read, 143 .d_ioctl = audit_pipe_ioctl, 144 .d_poll = audit_pipe_poll, 145 .d_name = AUDIT_PIPE_NAME, 146}; 147 148/* 149 * Some global statistics on audit pipes. 150 */ 151static int audit_pipe_count; /* Current number of pipes. */ 152static u_int64_t audit_pipe_ever; /* Pipes ever allocated. */ 153static u_int64_t audit_pipe_records; /* Records seen. */ 154static u_int64_t audit_pipe_drops; /* Global record drop count. */ 155 156/* 157 * Free an audit pipe entry. 158 */ 159static void 160audit_pipe_entry_free(struct audit_pipe_entry *ape) 161{ 162 163 free(ape->ape_record, M_AUDIT_PIPE_ENTRY); 164 free(ape, M_AUDIT_PIPE_ENTRY); 165} 166 167/* 168 * Apparent individual record to a queue -- allocate queue-local buffer, and 169 * add to the queue. We try to drop from the head of the queue so that more 170 * recent events take precedence over older ones, but if allocation fails we 171 * do drop the new event. 172 */ 173static void 174audit_pipe_append(struct audit_pipe *ap, void *record, u_int record_len) 175{ 176 struct audit_pipe_entry *ape, *ape_remove; 177 178 mtx_assert(&audit_pipe_mtx, MA_OWNED); 179 180 ape = malloc(sizeof(*ape), M_AUDIT_PIPE_ENTRY, M_NOWAIT | M_ZERO); 181 if (ape == NULL) { 182 ap->ap_drops++; 183 return; 184 } 185 186 ape->ape_record = malloc(record_len, M_AUDIT_PIPE_ENTRY, M_NOWAIT); 187 if (ape->ape_record == NULL) { 188 free(ape, M_AUDIT_PIPE_ENTRY); 189 ap->ap_drops++; 190 audit_pipe_drops++; 191 return; 192 } 193 194 bcopy(record, ape->ape_record, record_len); 195 ape->ape_record_len = record_len; 196 197 if (ap->ap_qlen >= ap->ap_qlimit) { 198 ape_remove = TAILQ_FIRST(&ap->ap_queue); 199 TAILQ_REMOVE(&ap->ap_queue, ape_remove, ape_queue); 200 audit_pipe_entry_free(ape_remove); 201 ap->ap_drops++; 202 audit_pipe_drops++; 203 } 204 205 TAILQ_INSERT_TAIL(&ap->ap_queue, ape, ape_queue); 206 ap->ap_inserts++; 207 ap->ap_qlen++; 208 selwakeuppri(&ap->ap_selinfo, PSOCK); 209 if (ap->ap_flags & AUDIT_PIPE_ASYNC) 210 pgsigio(&ap->ap_sigio, SIGIO, 0); 211} 212 213/* 214 * audit_pipe_submit(): audit_worker submits audit records via this 215 * interface, which arranges for them to be delivered to pipe queues. 216 */ 217void 218audit_pipe_submit(void *record, u_int record_len) 219{ 220 struct audit_pipe *ap; 221 222 /* 223 * Lockless read to avoid mutex overhead if pipes are not in use. 224 */ 225 if (TAILQ_FIRST(&audit_pipe_list) == NULL) 226 return; 227 228 mtx_lock(&audit_pipe_mtx); 229 TAILQ_FOREACH(ap, &audit_pipe_list, ap_list) 230 audit_pipe_append(ap, record, record_len); 231 audit_pipe_records++; 232 mtx_unlock(&audit_pipe_mtx); 233 cv_signal(&audit_pipe_cv); 234} 235 236/* 237 * Read the next record off of an audit pipe. 238 */ 239static struct audit_pipe_entry * 240audit_pipe_pop(struct audit_pipe *ap) 241{ 242 struct audit_pipe_entry *ape; 243 244 mtx_assert(&audit_pipe_mtx, MA_OWNED); 245 246 ape = TAILQ_FIRST(&ap->ap_queue); 247 KASSERT((ape == NULL && ap->ap_qlen == 0) || 248 (ape != NULL && ap->ap_qlen != 0), ("audit_pipe_pop: qlen")); 249 if (ape == NULL) 250 return (NULL); 251 TAILQ_REMOVE(&ap->ap_queue, ape, ape_queue); 252 ap->ap_qlen--; 253 return (ape); 254} 255 256/* 257 * Allocate a new audit pipe. Connects the pipe, on success, to the global 258 * list and updates statistics. 259 */ 260static struct audit_pipe * 261audit_pipe_alloc(void) 262{ 263 struct audit_pipe *ap; 264 265 mtx_assert(&audit_pipe_mtx, MA_OWNED); 266 267 ap = malloc(sizeof(*ap), M_AUDIT_PIPE, M_NOWAIT | M_ZERO); 268 if (ap == NULL) 269 return (NULL); 270 ap->ap_qlimit = AUDIT_PIPE_QLIMIT_DEFAULT; 271 TAILQ_INIT(&ap->ap_queue); 272 TAILQ_INSERT_HEAD(&audit_pipe_list, ap, ap_list); 273 audit_pipe_count++; 274 audit_pipe_ever++; 275 return (ap); 276} 277 278/* 279 * Free an audit pipe. Assumes mutex is held, audit_pipe is still on the 280 * global list. Frees any audit pipe entries in the queue. 281 */ 282static void 283audit_pipe_free(struct audit_pipe *ap) 284{ 285 struct audit_pipe_entry *ape; 286 287 mtx_assert(&audit_pipe_mtx, MA_OWNED); 288 289 TAILQ_REMOVE(&audit_pipe_list, ap, ap_list); 290 while ((ape = TAILQ_FIRST(&ap->ap_queue)) != NULL) { 291 TAILQ_REMOVE(&ap->ap_queue, ape, ape_queue); 292 audit_pipe_entry_free(ape); 293 ap->ap_qlen--; 294 } 295 KASSERT(ap->ap_qlen == 0, ("audit_pipe_free: ap_qlen")); 296 free(ap, M_AUDIT_PIPE); 297 audit_pipe_count--; 298} 299 300/* 301 * Audit pipe clone routine -- provide specific requested audit pipe, or a 302 * fresh one if a specific one is not requested. 303 */ 304static void 305audit_pipe_clone(void *arg, struct ucred *cred, char *name, int namelen, 306 struct cdev **dev) 307{ 308 int i, u; 309 310 if (*dev != NULL) 311 return; 312 313 if (strcmp(name, AUDIT_PIPE_NAME) == 0) 314 u = -1; 315 else if (dev_stdclone(name, NULL, AUDIT_PIPE_NAME, &u) != 1) 316 return; 317 318 i = clone_create(&audit_pipe_clones, &audit_pipe_cdevsw, &u, dev, 0); 319 if (i) { 320 *dev = make_dev(&audit_pipe_cdevsw, unit2minor(u), UID_ROOT, 321 GID_WHEEL, 0600, "%s%d", AUDIT_PIPE_NAME, u); 322 if (*dev != NULL) { 323 dev_ref(*dev); 324 (*dev)->si_flags |= SI_CHEAPCLONE; 325 } 326 } 327} 328 329/* 330 * Audit pipe open method. Explicit suser check isn't used as this allows 331 * file permissions on the special device to be used to grant audit review 332 * access. 333 */ 334static int 335audit_pipe_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 336{ 337 struct audit_pipe *ap; 338 339 mtx_lock(&audit_pipe_mtx); 340 ap = dev->si_drv1; 341 if (ap == NULL) { 342 ap = audit_pipe_alloc(); 343 if (ap == NULL) { 344 mtx_unlock(&audit_pipe_mtx); 345 return (ENOMEM); 346 } 347 dev->si_drv1 = ap; 348 } else { 349 KASSERT(ap->ap_open, ("audit_pipe_open: ap && !ap_open")); 350 mtx_unlock(&audit_pipe_mtx); 351 return (EBUSY); 352 } 353 ap->ap_open = 1; 354 mtx_unlock(&audit_pipe_mtx); 355 fsetown(td->td_proc->p_pid, &ap->ap_sigio); 356 return (0); 357} 358 359/* 360 * Close audit pipe, tear down all records, etc. 361 */ 362static int 363audit_pipe_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 364{ 365 struct audit_pipe *ap; 366 367 ap = dev->si_drv1; 368 KASSERT(ap != NULL, ("audit_pipe_close: ap == NULL")); 369 KASSERT(ap->ap_open, ("audit_pipe_close: !ap_open")); 370 funsetown(&ap->ap_sigio); 371 mtx_lock(&audit_pipe_mtx); 372 ap->ap_open = 0; 373 audit_pipe_free(ap); 374 dev->si_drv1 = NULL; 375 mtx_unlock(&audit_pipe_mtx); 376 return (0); 377} 378 379/* 380 * Audit pipe ioctl() routine. Nothing for now, but eventually will allow 381 * setting and retrieval of current queue depth, queue limit, flush, etc. 382 * 383 * Would be desirable to support filtering, although perhaps something simple 384 * like an event mask, as opposed to something complicated like BPF. 385 */ 386static int 387audit_pipe_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, 388 struct thread *td) 389{ 390 struct audit_pipe *ap; 391 int error; 392 393 ap = dev->si_drv1; 394 KASSERT(ap != NULL, ("audit_pipe_ioctl: ap == NULL")); 395 switch (cmd) { 396 case FIONBIO: 397 mtx_lock(&audit_pipe_mtx); 398 if (*(int *)data) 399 ap->ap_flags |= AUDIT_PIPE_NBIO; 400 else 401 ap->ap_flags &= ~AUDIT_PIPE_NBIO; 402 mtx_unlock(&audit_pipe_mtx); 403 error = 0; 404 break; 405 406 case FIONREAD: 407 mtx_lock(&audit_pipe_mtx); 408 if (TAILQ_FIRST(&ap->ap_queue) != NULL) 409 *(int *)data = 410 TAILQ_FIRST(&ap->ap_queue)->ape_record_len; 411 else 412 *(int *)data = 0; 413 mtx_unlock(&audit_pipe_mtx); 414 error = 0; 415 break; 416 417 case FIOASYNC: 418 mtx_lock(&audit_pipe_mtx); 419 if (*(int *)data) 420 ap->ap_flags |= AUDIT_PIPE_ASYNC; 421 else 422 ap->ap_flags &= ~AUDIT_PIPE_ASYNC; 423 mtx_unlock(&audit_pipe_mtx); 424 error = 0; 425 break; 426 427 case FIOSETOWN: 428 error = fsetown(*(int *)data, &ap->ap_sigio); 429 break; 430 431 case FIOGETOWN: 432 *(int *)data = fgetown(&ap->ap_sigio); 433 error = 0; 434 435 default: 436 error = ENOTTY; 437 } 438 return (error); 439} 440 441/* 442 * Audit pipe read. Pull one record off the queue and copy to user space. 443 * On error, the record is dropped. 444 */ 445static int 446audit_pipe_read(struct cdev *dev, struct uio *uio, int flag) 447{ 448 struct audit_pipe_entry *ape; 449 struct audit_pipe *ap; 450 int error; 451 452 ap = dev->si_drv1; 453 KASSERT(ap != NULL, ("audit_pipe_read: ap == NULL")); 454 mtx_lock(&audit_pipe_mtx); 455 do { 456 /* 457 * Wait for a record that fits into the read buffer, dropping 458 * records that would be truncated if actually passed to the 459 * process. This helps maintain the discreet record read 460 * interface. 461 */ 462 while ((ape = audit_pipe_pop(ap)) == NULL) { 463 if (ap->ap_flags & AUDIT_PIPE_NBIO) { 464 mtx_unlock(&audit_pipe_mtx); 465 return (EAGAIN); 466 } 467 error = cv_wait_sig(&audit_pipe_cv, &audit_pipe_mtx); 468 if (error) { 469 mtx_unlock(&audit_pipe_mtx); 470 return (error); 471 } 472 } 473 if (ape->ape_record_len <= uio->uio_resid) 474 break; 475 audit_pipe_entry_free(ape); 476 ap->ap_truncates++; 477 } while (1); 478 mtx_unlock(&audit_pipe_mtx); 479 480 /* 481 * Now read record to user space memory. Even if the read is short, 482 * we abandon the remainder of the record, supporting only discreet 483 * record reads. 484 */ 485 error = uiomove(ape->ape_record, ape->ape_record_len, uio); 486 audit_pipe_entry_free(ape); 487 return (error); 488} 489 490/* 491 * Audit pipe poll. 492 */ 493static int 494audit_pipe_poll(struct cdev *dev, int events, struct thread *td) 495{ 496 struct audit_pipe *ap; 497 int revents; 498 499 revents = 0; 500 ap = dev->si_drv1; 501 KASSERT(ap != NULL, ("audit_pipe_poll: ap == NULL")); 502 if (events & (POLLIN | POLLRDNORM)) { 503 mtx_lock(&audit_pipe_mtx); 504 if (TAILQ_FIRST(&ap->ap_queue) != NULL) 505 revents |= events & (POLLIN | POLLRDNORM); 506 else 507 selrecord(td, &ap->ap_selinfo); 508 mtx_unlock(&audit_pipe_mtx); 509 } 510 return (revents); 511} 512 513/* 514 * Initialize the audit pipe system. 515 */ 516static void 517audit_pipe_init(void *unused) 518{ 519 520 TAILQ_INIT(&audit_pipe_list); 521 mtx_init(&audit_pipe_mtx, "audit_pipe_mtx", NULL, MTX_DEF); 522 cv_init(&audit_pipe_cv, "audit_pipe_cv"); 523 524 clone_setup(&audit_pipe_clones); 525 audit_pipe_eh_tag = EVENTHANDLER_REGISTER(dev_clone, 526 audit_pipe_clone, 0, 1000); 527 if (audit_pipe_eh_tag == NULL) 528 panic("audit_pipe_init: EVENTHANDLER_REGISTER"); 529} 530 531SYSINIT(audit_pipe_init, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, audit_pipe_init, 532 NULL); 533