audit_pipe.c revision 156880
1130803Smarcel/*- 2130803Smarcel * Copyright (c) 2006 Robert N. M. Watson 3130803Smarcel * All rights reserved. 4130803Smarcel * 5130803Smarcel * This software was developed by Robert Watson for the TrustedBSD Project. 6130803Smarcel * 7130803Smarcel * Redistribution and use in source and binary forms, with or without 8130803Smarcel * modification, are permitted provided that the following conditions 9130803Smarcel * are met: 10130803Smarcel * 1. Redistributions of source code must retain the above copyright 11130803Smarcel * notice, this list of conditions and the following disclaimer. 12130803Smarcel * 2. Redistributions in binary form must reproduce the above copyright 13130803Smarcel * notice, this list of conditions and the following disclaimer in the 14130803Smarcel * documentation and/or other materials provided with the distribution. 15130803Smarcel * 16130803Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17130803Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18130803Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19130803Smarcel * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20130803Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21130803Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22130803Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23130803Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24130803Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25130803Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26130803Smarcel * SUCH DAMAGE. 27130803Smarcel * 28130803Smarcel * $FreeBSD: head/sys/security/audit/audit_pipe.c 156880 2006-03-19 15:36:10Z rwatson $ 29130803Smarcel */ 30130803Smarcel 31130803Smarcel#include <sys/param.h> 32130803Smarcel#include <sys/condvar.h> 33130803Smarcel#include <sys/conf.h> 34130803Smarcel#include <sys/eventhandler.h> 35130803Smarcel#include <sys/filio.h> 36130803Smarcel#include <sys/kernel.h> 37130803Smarcel#include <sys/lock.h> 38130803Smarcel#include <sys/malloc.h> 39130803Smarcel#include <sys/mutex.h> 40130803Smarcel#include <sys/poll.h> 41130803Smarcel#include <sys/proc.h> 42130803Smarcel#include <sys/queue.h> 43130803Smarcel#include <sys/selinfo.h> 44130803Smarcel#include <sys/sigio.h> 45130803Smarcel#include <sys/signal.h> 46130803Smarcel#include <sys/signalvar.h> 47130803Smarcel#include <sys/systm.h> 48130803Smarcel#include <sys/uio.h> 49130803Smarcel 50130803Smarcel#include <security/audit/audit.h> 51130803Smarcel#include <security/audit/audit_ioctl.h> 52130803Smarcel#include <security/audit/audit_private.h> 53130803Smarcel 54130803Smarcel/* 55130803Smarcel * Implementation of a clonable special device providing a live stream of BSM 56130803Smarcel * audit data. This is a "tee" of the data going to the file. It provides 57130803Smarcel * unreliable but timely access to audit events. Consumers of this interface 58130803Smarcel * should be very careful to avoid introducing event cycles. 59130803Smarcel */ 60130803Smarcel 61130803Smarcel/* 62130803Smarcel * Memory types. 63130803Smarcel */ 64130803Smarcelstatic MALLOC_DEFINE(M_AUDIT_PIPE, "audit_pipe", "Audit pipes"); 65130803Smarcelstatic MALLOC_DEFINE(M_AUDIT_PIPE_ENTRY, "audit_pipeent", 66130803Smarcel "Audit pipe entries and buffers"); 67130803Smarcel 68130803Smarcel/* 69130803Smarcel * Audit pipe buffer parameters. 70130803Smarcel */ 71130803Smarcel#define AUDIT_PIPE_QLIMIT_DEFAULT (32) 72130803Smarcel#define AUDIT_PIPE_QLIMIT_MIN (0) 73130803Smarcel#define AUDIT_PIPE_QLIMIT_MAX (1024) 74130803Smarcel 75130803Smarcel/* 76130803Smarcel * Description of an entry in an audit_pipe. 77130803Smarcel */ 78130803Smarcelstruct audit_pipe_entry { 79130803Smarcel void *ape_record; 80130803Smarcel u_int ape_record_len; 81130803Smarcel TAILQ_ENTRY(audit_pipe_entry) ape_queue; 82130803Smarcel}; 83130803Smarcel 84130803Smarcel/* 85130803Smarcel * Description of an individual audit_pipe. Consists largely of a bounded 86130803Smarcel * length queue. 87130803Smarcel */ 88130803Smarcel#define AUDIT_PIPE_ASYNC 0x00000001 89130803Smarcel#define AUDIT_PIPE_NBIO 0x00000002 90130803Smarcelstruct audit_pipe { 91130803Smarcel int ap_open; /* Device open? */ 92130803Smarcel u_int ap_flags; 93130803Smarcel 94130803Smarcel struct selinfo ap_selinfo; 95130803Smarcel struct sigio *ap_sigio; 96130803Smarcel 97130803Smarcel u_int ap_qlen; 98130803Smarcel u_int ap_qlimit; 99130803Smarcel 100130803Smarcel u_int64_t ap_inserts; /* Records added. */ 101130803Smarcel u_int64_t ap_reads; /* Records read. */ 102130803Smarcel u_int64_t ap_drops; /* Records dropped. */ 103130803Smarcel u_int64_t ap_truncates; /* Records too long. */ 104130803Smarcel 105130803Smarcel TAILQ_HEAD(, audit_pipe_entry) ap_queue; 106130803Smarcel 107130803Smarcel TAILQ_ENTRY(audit_pipe) ap_list; 108130803Smarcel}; 109130803Smarcel 110130803Smarcel/* 111130803Smarcel * Global list of audit pipes, mutex to protect it and the pipes. Finder 112130803Smarcel * grained locking may be desirable at some point. 113130803Smarcel */ 114130803Smarcelstatic TAILQ_HEAD(, audit_pipe) audit_pipe_list; 115130803Smarcelstatic struct mtx audit_pipe_mtx; 116130803Smarcel 117130803Smarcel/* 118130803Smarcel * This CV is used to wakeup on an audit record write. Eventually, it should 119130803Smarcel * probably be per-pipe. 120130803Smarcel */ 121130803Smarcelstatic struct cv audit_pipe_cv; 122130803Smarcel 123130803Smarcel/* 124130803Smarcel * Cloning related variables and constants. 125130803Smarcel */ 126130803Smarcel#define AUDIT_PIPE_NAME "auditpipe" 127130803Smarcelstatic eventhandler_tag audit_pipe_eh_tag; 128130803Smarcelstatic struct clonedevs *audit_pipe_clones; 129130803Smarcel 130130803Smarcel/* 131130803Smarcel * Special device methods and definition. 132130803Smarcel */ 133130803Smarcelstatic d_open_t audit_pipe_open; 134130803Smarcelstatic d_close_t audit_pipe_close; 135130803Smarcelstatic d_read_t audit_pipe_read; 136130803Smarcelstatic d_ioctl_t audit_pipe_ioctl; 137130803Smarcelstatic d_poll_t audit_pipe_poll; 138130803Smarcel 139130803Smarcelstatic struct cdevsw audit_pipe_cdevsw = { 140130803Smarcel .d_version = D_VERSION, 141130803Smarcel .d_flags = D_PSEUDO, 142130803Smarcel .d_open = audit_pipe_open, 143130803Smarcel .d_close = audit_pipe_close, 144130803Smarcel .d_read = audit_pipe_read, 145130803Smarcel .d_ioctl = audit_pipe_ioctl, 146130803Smarcel .d_poll = audit_pipe_poll, 147130803Smarcel .d_name = AUDIT_PIPE_NAME, 148130803Smarcel}; 149130803Smarcel 150130803Smarcel/* 151130803Smarcel * Some global statistics on audit pipes. 152130803Smarcel */ 153130803Smarcelstatic int audit_pipe_count; /* Current number of pipes. */ 154130803Smarcelstatic u_int64_t audit_pipe_ever; /* Pipes ever allocated. */ 155130803Smarcelstatic u_int64_t audit_pipe_records; /* Records seen. */ 156130803Smarcelstatic u_int64_t audit_pipe_drops; /* Global record drop count. */ 157130803Smarcel 158130803Smarcel/* 159130803Smarcel * Free an audit pipe entry. 160130803Smarcel */ 161130803Smarcelstatic void 162130803Smarcelaudit_pipe_entry_free(struct audit_pipe_entry *ape) 163130803Smarcel{ 164130803Smarcel 165130803Smarcel free(ape->ape_record, M_AUDIT_PIPE_ENTRY); 166130803Smarcel free(ape, M_AUDIT_PIPE_ENTRY); 167130803Smarcel} 168130803Smarcel 169130803Smarcel/* 170130803Smarcel * Apparent individual record to a queue -- allocate queue-local buffer, and 171130803Smarcel * add to the queue. We try to drop from the head of the queue so that more 172130803Smarcel * recent events take precedence over older ones, but if allocation fails we 173130803Smarcel * do drop the new event. 174130803Smarcel */ 175130803Smarcelstatic void 176130803Smarcelaudit_pipe_append(struct audit_pipe *ap, void *record, u_int record_len) 177130803Smarcel{ 178130803Smarcel struct audit_pipe_entry *ape, *ape_remove; 179130803Smarcel 180130803Smarcel mtx_assert(&audit_pipe_mtx, MA_OWNED); 181130803Smarcel 182130803Smarcel ape = malloc(sizeof(*ape), M_AUDIT_PIPE_ENTRY, M_NOWAIT | M_ZERO); 183130803Smarcel if (ape == NULL) { 184130803Smarcel ap->ap_drops++; 185130803Smarcel audit_pipe_drops++; 186130803Smarcel return; 187130803Smarcel } 188130803Smarcel 189130803Smarcel ape->ape_record = malloc(record_len, M_AUDIT_PIPE_ENTRY, M_NOWAIT); 190130803Smarcel if (ape->ape_record == NULL) { 191130803Smarcel free(ape, M_AUDIT_PIPE_ENTRY); 192130803Smarcel ap->ap_drops++; 193130803Smarcel audit_pipe_drops++; 194130803Smarcel return; 195130803Smarcel } 196130803Smarcel 197130803Smarcel bcopy(record, ape->ape_record, record_len); 198130803Smarcel ape->ape_record_len = record_len; 199130803Smarcel 200130803Smarcel if (ap->ap_qlen >= ap->ap_qlimit) { 201130803Smarcel ape_remove = TAILQ_FIRST(&ap->ap_queue); 202130803Smarcel TAILQ_REMOVE(&ap->ap_queue, ape_remove, ape_queue); 203130803Smarcel audit_pipe_entry_free(ape_remove); 204130803Smarcel ap->ap_qlen--; 205130803Smarcel ap->ap_drops++; 206130803Smarcel audit_pipe_drops++; 207130803Smarcel } 208130803Smarcel 209130803Smarcel TAILQ_INSERT_TAIL(&ap->ap_queue, ape, ape_queue); 210130803Smarcel ap->ap_inserts++; 211130803Smarcel ap->ap_qlen++; 212130803Smarcel selwakeuppri(&ap->ap_selinfo, PSOCK); 213130803Smarcel if (ap->ap_flags & AUDIT_PIPE_ASYNC) 214130803Smarcel pgsigio(&ap->ap_sigio, SIGIO, 0); 215130803Smarcel} 216130803Smarcel 217130803Smarcel/* 218130803Smarcel * audit_pipe_submit(): audit_worker submits audit records via this 219130803Smarcel * interface, which arranges for them to be delivered to pipe queues. 220130803Smarcel */ 221130803Smarcelvoid 222130803Smarcelaudit_pipe_submit(void *record, u_int record_len) 223130803Smarcel{ 224130803Smarcel struct audit_pipe *ap; 225130803Smarcel 226130803Smarcel /* 227130803Smarcel * Lockless read to avoid mutex overhead if pipes are not in use. 228130803Smarcel */ 229130803Smarcel if (TAILQ_FIRST(&audit_pipe_list) == NULL) 230130803Smarcel return; 231130803Smarcel 232130803Smarcel mtx_lock(&audit_pipe_mtx); 233130803Smarcel TAILQ_FOREACH(ap, &audit_pipe_list, ap_list) 234130803Smarcel audit_pipe_append(ap, record, record_len); 235130803Smarcel audit_pipe_records++; 236130803Smarcel mtx_unlock(&audit_pipe_mtx); 237130803Smarcel cv_signal(&audit_pipe_cv); 238130803Smarcel} 239130803Smarcel 240130803Smarcel/* 241130803Smarcel * Read the next record off of an audit pipe. 242130803Smarcel */ 243130803Smarcelstatic struct audit_pipe_entry * 244130803Smarcelaudit_pipe_pop(struct audit_pipe *ap) 245130803Smarcel{ 246130803Smarcel struct audit_pipe_entry *ape; 247130803Smarcel 248130803Smarcel mtx_assert(&audit_pipe_mtx, MA_OWNED); 249130803Smarcel 250130803Smarcel ape = TAILQ_FIRST(&ap->ap_queue); 251130803Smarcel KASSERT((ape == NULL && ap->ap_qlen == 0) || 252130803Smarcel (ape != NULL && ap->ap_qlen != 0), ("audit_pipe_pop: qlen")); 253130803Smarcel if (ape == NULL) 254130803Smarcel return (NULL); 255130803Smarcel TAILQ_REMOVE(&ap->ap_queue, ape, ape_queue); 256130803Smarcel ap->ap_qlen--; 257130803Smarcel return (ape); 258130803Smarcel} 259130803Smarcel 260130803Smarcel/* 261130803Smarcel * Allocate a new audit pipe. Connects the pipe, on success, to the global 262130803Smarcel * list and updates statistics. 263130803Smarcel */ 264130803Smarcelstatic struct audit_pipe * 265130803Smarcelaudit_pipe_alloc(void) 266130803Smarcel{ 267130803Smarcel struct audit_pipe *ap; 268130803Smarcel 269130803Smarcel mtx_assert(&audit_pipe_mtx, MA_OWNED); 270130803Smarcel 271130803Smarcel ap = malloc(sizeof(*ap), M_AUDIT_PIPE, M_NOWAIT | M_ZERO); 272130803Smarcel if (ap == NULL) 273130803Smarcel return (NULL); 274130803Smarcel ap->ap_qlimit = AUDIT_PIPE_QLIMIT_DEFAULT; 275130803Smarcel TAILQ_INIT(&ap->ap_queue); 276130803Smarcel TAILQ_INSERT_HEAD(&audit_pipe_list, ap, ap_list); 277130803Smarcel audit_pipe_count++; 278130803Smarcel audit_pipe_ever++; 279130803Smarcel return (ap); 280130803Smarcel} 281130803Smarcel 282130803Smarcel/* 283130803Smarcel * Free an audit pipe. Assumes mutex is held, audit_pipe is still on the 284130803Smarcel * global list. Frees any audit pipe entries in the queue. 285130803Smarcel */ 286130803Smarcelstatic void 287130803Smarcelaudit_pipe_free(struct audit_pipe *ap) 288130803Smarcel{ 289130803Smarcel struct audit_pipe_entry *ape; 290130803Smarcel 291130803Smarcel mtx_assert(&audit_pipe_mtx, MA_OWNED); 292130803Smarcel 293130803Smarcel TAILQ_REMOVE(&audit_pipe_list, ap, ap_list); 294130803Smarcel while ((ape = TAILQ_FIRST(&ap->ap_queue)) != NULL) { 295130803Smarcel TAILQ_REMOVE(&ap->ap_queue, ape, ape_queue); 296130803Smarcel audit_pipe_entry_free(ape); 297130803Smarcel ap->ap_qlen--; 298130803Smarcel } 299130803Smarcel KASSERT(ap->ap_qlen == 0, ("audit_pipe_free: ap_qlen")); 300130803Smarcel free(ap, M_AUDIT_PIPE); 301130803Smarcel audit_pipe_count--; 302130803Smarcel} 303130803Smarcel 304130803Smarcel/* 305130803Smarcel * Audit pipe clone routine -- provide specific requested audit pipe, or a 306130803Smarcel * fresh one if a specific one is not requested. 307130803Smarcel */ 308130803Smarcelstatic void 309130803Smarcelaudit_pipe_clone(void *arg, struct ucred *cred, char *name, int namelen, 310130803Smarcel struct cdev **dev) 311130803Smarcel{ 312130803Smarcel int i, u; 313130803Smarcel 314130803Smarcel if (*dev != NULL) 315130803Smarcel return; 316130803Smarcel 317130803Smarcel if (strcmp(name, AUDIT_PIPE_NAME) == 0) 318130803Smarcel u = -1; 319130803Smarcel else if (dev_stdclone(name, NULL, AUDIT_PIPE_NAME, &u) != 1) 320130803Smarcel return; 321130803Smarcel 322130803Smarcel i = clone_create(&audit_pipe_clones, &audit_pipe_cdevsw, &u, dev, 0); 323130803Smarcel if (i) { 324130803Smarcel *dev = make_dev(&audit_pipe_cdevsw, unit2minor(u), UID_ROOT, 325130803Smarcel GID_WHEEL, 0600, "%s%d", AUDIT_PIPE_NAME, u); 326130803Smarcel if (*dev != NULL) { 327130803Smarcel dev_ref(*dev); 328130803Smarcel (*dev)->si_flags |= SI_CHEAPCLONE; 329130803Smarcel } 330130803Smarcel } 331130803Smarcel} 332130803Smarcel 333130803Smarcel/* 334130803Smarcel * Audit pipe open method. Explicit suser check isn't used as this allows 335130803Smarcel * file permissions on the special device to be used to grant audit review 336130803Smarcel * access. 337130803Smarcel */ 338130803Smarcelstatic int 339130803Smarcelaudit_pipe_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 340130803Smarcel{ 341130803Smarcel struct audit_pipe *ap; 342130803Smarcel 343130803Smarcel mtx_lock(&audit_pipe_mtx); 344130803Smarcel ap = dev->si_drv1; 345130803Smarcel if (ap == NULL) { 346130803Smarcel ap = audit_pipe_alloc(); 347130803Smarcel if (ap == NULL) { 348130803Smarcel mtx_unlock(&audit_pipe_mtx); 349130803Smarcel return (ENOMEM); 350130803Smarcel } 351130803Smarcel dev->si_drv1 = ap; 352130803Smarcel } else { 353130803Smarcel KASSERT(ap->ap_open, ("audit_pipe_open: ap && !ap_open")); 354130803Smarcel mtx_unlock(&audit_pipe_mtx); 355130803Smarcel return (EBUSY); 356130803Smarcel } 357130803Smarcel ap->ap_open = 1; 358130803Smarcel mtx_unlock(&audit_pipe_mtx); 359130803Smarcel fsetown(td->td_proc->p_pid, &ap->ap_sigio); 360130803Smarcel return (0); 361130803Smarcel} 362130803Smarcel 363130803Smarcel/* 364130803Smarcel * Close audit pipe, tear down all records, etc. 365130803Smarcel */ 366130803Smarcelstatic int 367130803Smarcelaudit_pipe_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 368130803Smarcel{ 369130803Smarcel struct audit_pipe *ap; 370130803Smarcel 371130803Smarcel ap = dev->si_drv1; 372130803Smarcel KASSERT(ap != NULL, ("audit_pipe_close: ap == NULL")); 373130803Smarcel KASSERT(ap->ap_open, ("audit_pipe_close: !ap_open")); 374130803Smarcel funsetown(&ap->ap_sigio); 375130803Smarcel mtx_lock(&audit_pipe_mtx); 376130803Smarcel ap->ap_open = 0; 377130803Smarcel audit_pipe_free(ap); 378130803Smarcel dev->si_drv1 = NULL; 379130803Smarcel mtx_unlock(&audit_pipe_mtx); 380130803Smarcel return (0); 381130803Smarcel} 382130803Smarcel 383130803Smarcel/* 384130803Smarcel * Audit pipe ioctl() routine. Handle file descriptor and audit pipe layer 385130803Smarcel * commands. 386130803Smarcel * 387130803Smarcel * Would be desirable to support filtering, although perhaps something simple 388130803Smarcel * like an event mask, as opposed to something complicated like BPF. 389130803Smarcel */ 390130803Smarcelstatic int 391130803Smarcelaudit_pipe_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, 392130803Smarcel struct thread *td) 393130803Smarcel{ 394130803Smarcel struct audit_pipe *ap; 395130803Smarcel int error; 396130803Smarcel 397130803Smarcel ap = dev->si_drv1; 398130803Smarcel KASSERT(ap != NULL, ("audit_pipe_ioctl: ap == NULL")); 399130803Smarcel switch (cmd) { 400130803Smarcel case FIONBIO: 401130803Smarcel mtx_lock(&audit_pipe_mtx); 402130803Smarcel if (*(int *)data) 403130803Smarcel ap->ap_flags |= AUDIT_PIPE_NBIO; 404130803Smarcel else 405130803Smarcel ap->ap_flags &= ~AUDIT_PIPE_NBIO; 406130803Smarcel mtx_unlock(&audit_pipe_mtx); 407130803Smarcel error = 0; 408130803Smarcel break; 409130803Smarcel 410130803Smarcel case FIONREAD: 411130803Smarcel mtx_lock(&audit_pipe_mtx); 412130803Smarcel if (TAILQ_FIRST(&ap->ap_queue) != NULL) 413130803Smarcel *(int *)data = 414130803Smarcel TAILQ_FIRST(&ap->ap_queue)->ape_record_len; 415130803Smarcel else 416130803Smarcel *(int *)data = 0; 417130803Smarcel mtx_unlock(&audit_pipe_mtx); 418130803Smarcel error = 0; 419130803Smarcel break; 420130803Smarcel 421130803Smarcel case FIOASYNC: 422130803Smarcel mtx_lock(&audit_pipe_mtx); 423130803Smarcel if (*(int *)data) 424130803Smarcel ap->ap_flags |= AUDIT_PIPE_ASYNC; 425130803Smarcel else 426130803Smarcel ap->ap_flags &= ~AUDIT_PIPE_ASYNC; 427130803Smarcel mtx_unlock(&audit_pipe_mtx); 428130803Smarcel error = 0; 429130803Smarcel break; 430130803Smarcel 431130803Smarcel case FIOSETOWN: 432130803Smarcel error = fsetown(*(int *)data, &ap->ap_sigio); 433130803Smarcel break; 434130803Smarcel 435130803Smarcel case FIOGETOWN: 436130803Smarcel *(int *)data = fgetown(&ap->ap_sigio); 437130803Smarcel error = 0; 438130803Smarcel break; 439130803Smarcel 440130803Smarcel case AUDITPIPE_GET_QLEN: 441130803Smarcel *(u_int *)data = ap->ap_qlen; 442130803Smarcel error = 0; 443130803Smarcel break; 444130803Smarcel 445130803Smarcel case AUDITPIPE_GET_QLIMIT: 446130803Smarcel *(u_int *)data = ap->ap_qlimit; 447130803Smarcel error = 0; 448130803Smarcel break; 449130803Smarcel 450130803Smarcel case AUDITPIPE_SET_QLIMIT: 451130803Smarcel /* Lockless integer write. */ 452130803Smarcel if (*(u_int *)data >= AUDIT_PIPE_QLIMIT_MIN || 453130803Smarcel *(u_int *)data <= AUDIT_PIPE_QLIMIT_MAX) { 454130803Smarcel ap->ap_qlimit = *(u_int *)data; 455130803Smarcel error = 0; 456130803Smarcel } else 457130803Smarcel error = EINVAL; 458130803Smarcel break; 459130803Smarcel 460130803Smarcel case AUDITPIPE_GET_INSERTS: 461130803Smarcel *(u_int *)data = ap->ap_inserts; 462130803Smarcel error = 0; 463130803Smarcel break; 464130803Smarcel 465130803Smarcel case AUDITPIPE_GET_READS: 466130803Smarcel *(u_int *)data = ap->ap_reads; 467130803Smarcel error = 0; 468130803Smarcel break; 469130803Smarcel 470130803Smarcel case AUDITPIPE_GET_DROPS: 471130803Smarcel *(u_int *)data = ap->ap_drops; 472130803Smarcel error = 0; 473130803Smarcel break; 474130803Smarcel 475130803Smarcel case AUDITPIPE_GET_TRUNCATES: 476130803Smarcel *(u_int *)data = ap->ap_truncates; 477130803Smarcel error = 0; 478130803Smarcel break; 479130803Smarcel 480130803Smarcel default: 481130803Smarcel error = ENOTTY; 482130803Smarcel } 483130803Smarcel return (error); 484130803Smarcel} 485130803Smarcel 486130803Smarcel/* 487130803Smarcel * Audit pipe read. Pull one record off the queue and copy to user space. 488130803Smarcel * On error, the record is dropped. 489130803Smarcel */ 490130803Smarcelstatic int 491130803Smarcelaudit_pipe_read(struct cdev *dev, struct uio *uio, int flag) 492130803Smarcel{ 493130803Smarcel struct audit_pipe_entry *ape; 494130803Smarcel struct audit_pipe *ap; 495130803Smarcel int error; 496130803Smarcel 497130803Smarcel ap = dev->si_drv1; 498130803Smarcel KASSERT(ap != NULL, ("audit_pipe_read: ap == NULL")); 499130803Smarcel mtx_lock(&audit_pipe_mtx); 500130803Smarcel do { 501130803Smarcel /* 502130803Smarcel * Wait for a record that fits into the read buffer, dropping 503130803Smarcel * records that would be truncated if actually passed to the 504130803Smarcel * process. This helps maintain the discreet record read 505130803Smarcel * interface. 506130803Smarcel */ 507130803Smarcel while ((ape = audit_pipe_pop(ap)) == NULL) { 508130803Smarcel if (ap->ap_flags & AUDIT_PIPE_NBIO) { 509130803Smarcel mtx_unlock(&audit_pipe_mtx); 510130803Smarcel return (EAGAIN); 511130803Smarcel } 512130803Smarcel error = cv_wait_sig(&audit_pipe_cv, &audit_pipe_mtx); 513130803Smarcel if (error) { 514130803Smarcel mtx_unlock(&audit_pipe_mtx); 515130803Smarcel return (error); 516130803Smarcel } 517130803Smarcel } 518130803Smarcel if (ape->ape_record_len <= uio->uio_resid) 519130803Smarcel break; 520130803Smarcel audit_pipe_entry_free(ape); 521130803Smarcel ap->ap_truncates++; 522130803Smarcel } while (1); 523130803Smarcel mtx_unlock(&audit_pipe_mtx); 524130803Smarcel 525130803Smarcel /* 526130803Smarcel * Now read record to user space memory. Even if the read is short, 527130803Smarcel * we abandon the remainder of the record, supporting only discreet 528130803Smarcel * record reads. 529130803Smarcel */ 530130803Smarcel error = uiomove(ape->ape_record, ape->ape_record_len, uio); 531130803Smarcel audit_pipe_entry_free(ape); 532130803Smarcel return (error); 533130803Smarcel} 534130803Smarcel 535130803Smarcel/* 536130803Smarcel * Audit pipe poll. 537130803Smarcel */ 538130803Smarcelstatic int 539130803Smarcelaudit_pipe_poll(struct cdev *dev, int events, struct thread *td) 540130803Smarcel{ 541130803Smarcel struct audit_pipe *ap; 542130803Smarcel int revents; 543130803Smarcel 544130803Smarcel revents = 0; 545130803Smarcel ap = dev->si_drv1; 546130803Smarcel KASSERT(ap != NULL, ("audit_pipe_poll: ap == NULL")); 547130803Smarcel if (events & (POLLIN | POLLRDNORM)) { 548130803Smarcel mtx_lock(&audit_pipe_mtx); 549130803Smarcel if (TAILQ_FIRST(&ap->ap_queue) != NULL) 550130803Smarcel revents |= events & (POLLIN | POLLRDNORM); 551130803Smarcel else 552130803Smarcel selrecord(td, &ap->ap_selinfo); 553130803Smarcel mtx_unlock(&audit_pipe_mtx); 554130803Smarcel } 555130803Smarcel return (revents); 556130803Smarcel} 557130803Smarcel 558130803Smarcel/* 559130803Smarcel * Initialize the audit pipe system. 560130803Smarcel */ 561130803Smarcelstatic void 562130803Smarcelaudit_pipe_init(void *unused) 563130803Smarcel{ 564130803Smarcel 565130803Smarcel TAILQ_INIT(&audit_pipe_list); 566130803Smarcel mtx_init(&audit_pipe_mtx, "audit_pipe_mtx", NULL, MTX_DEF); 567130803Smarcel cv_init(&audit_pipe_cv, "audit_pipe_cv"); 568130803Smarcel 569130803Smarcel clone_setup(&audit_pipe_clones); 570130803Smarcel audit_pipe_eh_tag = EVENTHANDLER_REGISTER(dev_clone, 571130803Smarcel audit_pipe_clone, 0, 1000); 572130803Smarcel if (audit_pipe_eh_tag == NULL) 573130803Smarcel panic("audit_pipe_init: EVENTHANDLER_REGISTER"); 574130803Smarcel} 575130803Smarcel 576130803SmarcelSYSINIT(audit_pipe_init, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, audit_pipe_init, 577130803Smarcel NULL); 578130803Smarcel