audit_pipe.c revision 161582
12973Sjlahoda/*- 22973Sjlahoda * Copyright (c) 2006 Robert N. M. Watson 32973Sjlahoda * All rights reserved. 42973Sjlahoda * 52973Sjlahoda * This software was developed by Robert Watson for the TrustedBSD Project. 62973Sjlahoda * 72973Sjlahoda * Redistribution and use in source and binary forms, with or without 82973Sjlahoda * modification, are permitted provided that the following conditions 92973Sjlahoda * are met: 102973Sjlahoda * 1. Redistributions of source code must retain the above copyright 112973Sjlahoda * notice, this list of conditions and the following disclaimer. 122973Sjlahoda * 2. Redistributions in binary form must reproduce the above copyright 132973Sjlahoda * notice, this list of conditions and the following disclaimer in the 142973Sjlahoda * documentation and/or other materials provided with the distribution. 152973Sjlahoda * 162973Sjlahoda * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 172973Sjlahoda * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 182973Sjlahoda * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 192973Sjlahoda * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 202973Sjlahoda * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 212973Sjlahoda * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 222973Sjlahoda * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 232973Sjlahoda * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 242973Sjlahoda * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 252973Sjlahoda * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 262973Sjlahoda * SUCH DAMAGE. 272973Sjlahoda * 282973Sjlahoda * $FreeBSD: head/sys/security/audit/audit_pipe.c 161582 2006-08-24 17:42:38Z rwatson $ 292973Sjlahoda */ 302973Sjlahoda 312973Sjlahoda#include <sys/param.h> 322973Sjlahoda#include <sys/condvar.h> 332973Sjlahoda#include <sys/conf.h> 342973Sjlahoda#include <sys/eventhandler.h> 352973Sjlahoda#include <sys/filio.h> 362973Sjlahoda#include <sys/kernel.h> 372973Sjlahoda#include <sys/lock.h> 382973Sjlahoda#include <sys/malloc.h> 392973Sjlahoda#include <sys/mutex.h> 402973Sjlahoda#include <sys/poll.h> 412973Sjlahoda#include <sys/proc.h> 422973Sjlahoda#include <sys/queue.h> 432973Sjlahoda#include <sys/selinfo.h> 442973Sjlahoda#include <sys/sigio.h> 452973Sjlahoda#include <sys/signal.h> 462973Sjlahoda#include <sys/signalvar.h> 472973Sjlahoda#include <sys/systm.h> 482973Sjlahoda#include <sys/uio.h> 492973Sjlahoda 502973Sjlahoda#include <security/audit/audit.h> 512973Sjlahoda#include <security/audit/audit_ioctl.h> 522973Sjlahoda#include <security/audit/audit_private.h> 532973Sjlahoda 542973Sjlahoda/* 552973Sjlahoda * Implementation of a clonable special device providing a live stream of BSM 562973Sjlahoda * audit data. This is a "tee" of the data going to the file. It provides 572973Sjlahoda * unreliable but timely access to audit events. Consumers of this interface 582973Sjlahoda * should be very careful to avoid introducing event cycles. Consumers may 592973Sjlahoda * express interest via a set of preselection ioctls. 602973Sjlahoda */ 612973Sjlahoda 622973Sjlahoda/* 632973Sjlahoda * Memory types. 642973Sjlahoda */ 652973Sjlahodastatic MALLOC_DEFINE(M_AUDIT_PIPE, "audit_pipe", "Audit pipes"); 662973Sjlahodastatic MALLOC_DEFINE(M_AUDIT_PIPE_ENTRY, "audit_pipeent", 672973Sjlahoda "Audit pipe entries and buffers"); 682973Sjlahodastatic MALLOC_DEFINE(M_AUDIT_PIPE_PRESELECT, "audit_pipe_preselect", 692973Sjlahoda "Audit pipe preselection structure"); 702973Sjlahoda 712973Sjlahoda/* 722973Sjlahoda * Audit pipe buffer parameters. 732973Sjlahoda */ 742973Sjlahoda#define AUDIT_PIPE_QLIMIT_DEFAULT (128) 752973Sjlahoda#define AUDIT_PIPE_QLIMIT_MIN (0) 762973Sjlahoda#define AUDIT_PIPE_QLIMIT_MAX (1024) 772973Sjlahoda 782973Sjlahoda/* 792973Sjlahoda * Description of an entry in an audit_pipe. 802973Sjlahoda */ 812973Sjlahodastruct audit_pipe_entry { 822973Sjlahoda void *ape_record; 832973Sjlahoda u_int ape_record_len; 842973Sjlahoda TAILQ_ENTRY(audit_pipe_entry) ape_queue; 852973Sjlahoda}; 862973Sjlahoda 872973Sjlahoda/* 882973Sjlahoda * Audit pipes allow processes to express "interest" in the set of records 892973Sjlahoda * that are delivered via the pipe. They do this in a similar manner to the 902973Sjlahoda * mechanism for audit trail configuration, by expressing two global masks, 912973Sjlahoda * and optionally expressing per-auid masks. The following data structure is 922973Sjlahoda * the per-auid mask description. The global state is stored in the audit 932973Sjlahoda * pipe data structure. 942973Sjlahoda * 952973Sjlahoda * We may want to consider a more space/time-efficient data structure once 962973Sjlahoda * usage patterns for per-auid specifications are clear. 972973Sjlahoda */ 982973Sjlahodastruct audit_pipe_preselect { 992973Sjlahoda au_id_t app_auid; 1002973Sjlahoda au_mask_t app_mask; 1012973Sjlahoda TAILQ_ENTRY(audit_pipe_preselect) app_list; 1022973Sjlahoda}; 1032973Sjlahoda 1042973Sjlahoda/* 1052973Sjlahoda * Description of an individual audit_pipe. Consists largely of a bounded 1062973Sjlahoda * length queue. 1072973Sjlahoda */ 1082973Sjlahoda#define AUDIT_PIPE_ASYNC 0x00000001 1092973Sjlahoda#define AUDIT_PIPE_NBIO 0x00000002 1102973Sjlahodastruct audit_pipe { 1112973Sjlahoda int ap_open; /* Device open? */ 1122973Sjlahoda u_int ap_flags; 1132973Sjlahoda 1142973Sjlahoda struct selinfo ap_selinfo; 1152973Sjlahoda struct sigio *ap_sigio; 1162973Sjlahoda 1172973Sjlahoda u_int ap_qlen; 1182973Sjlahoda u_int ap_qlimit; 1192973Sjlahoda 1202973Sjlahoda u_int64_t ap_inserts; /* Records added. */ 1212973Sjlahoda u_int64_t ap_reads; /* Records read. */ 1222973Sjlahoda u_int64_t ap_drops; /* Records dropped. */ 1232973Sjlahoda u_int64_t ap_truncates; /* Records too long. */ 1242973Sjlahoda 1252973Sjlahoda /* 1262973Sjlahoda * Fields relating to pipe interest: global masks for unmatched 1272973Sjlahoda * processes (attributable, non-attributable), and a list of specific 1282973Sjlahoda * interest specifications by auid. 1292973Sjlahoda */ 1302973Sjlahoda int ap_preselect_mode; 1312973Sjlahoda au_mask_t ap_preselect_flags; 1322973Sjlahoda au_mask_t ap_preselect_naflags; 1332973Sjlahoda TAILQ_HEAD(, audit_pipe_preselect) ap_preselect_list; 1342973Sjlahoda 1352973Sjlahoda /* 1362973Sjlahoda * Current pending record list. 1372973Sjlahoda */ 1382973Sjlahoda TAILQ_HEAD(, audit_pipe_entry) ap_queue; 1392973Sjlahoda 1402973Sjlahoda /* 1412973Sjlahoda * Global pipe list. 1422973Sjlahoda */ 1432973Sjlahoda TAILQ_ENTRY(audit_pipe) ap_list; 1442973Sjlahoda}; 1452973Sjlahoda 1462973Sjlahoda/* 1472973Sjlahoda * Global list of audit pipes, mutex to protect it and the pipes. Finer 1482973Sjlahoda * grained locking may be desirable at some point. 1492973Sjlahoda */ 1502973Sjlahodastatic TAILQ_HEAD(, audit_pipe) audit_pipe_list; 1512973Sjlahodastatic struct mtx audit_pipe_mtx; 1522973Sjlahoda 1532973Sjlahoda/* 1542973Sjlahoda * This CV is used to wakeup on an audit record write. Eventually, it might 1552973Sjlahoda * be per-pipe to avoid unnecessary wakeups when several pipes with different 1562973Sjlahoda * preselection masks are present. 1572973Sjlahoda */ 1582973Sjlahodastatic struct cv audit_pipe_cv; 1592973Sjlahoda 1602973Sjlahoda/* 1612973Sjlahoda * Cloning related variables and constants. 1622973Sjlahoda */ 1632973Sjlahoda#define AUDIT_PIPE_NAME "auditpipe" 1642973Sjlahodastatic eventhandler_tag audit_pipe_eh_tag; 1652973Sjlahodastatic struct clonedevs *audit_pipe_clones; 1662973Sjlahoda 1672973Sjlahoda/* 1682973Sjlahoda * Special device methods and definition. 1692973Sjlahoda */ 1702973Sjlahodastatic d_open_t audit_pipe_open; 1712973Sjlahodastatic d_close_t audit_pipe_close; 1722973Sjlahodastatic d_read_t audit_pipe_read; 1732973Sjlahodastatic d_ioctl_t audit_pipe_ioctl; 1742973Sjlahodastatic d_poll_t audit_pipe_poll; 1752973Sjlahodastatic d_kqfilter_t audit_pipe_kqfilter; 1762973Sjlahoda 1772973Sjlahodastatic struct cdevsw audit_pipe_cdevsw = { 1782973Sjlahoda .d_version = D_VERSION, 1792973Sjlahoda .d_flags = D_PSEUDO | D_NEEDGIANT, 1802973Sjlahoda .d_open = audit_pipe_open, 1812973Sjlahoda .d_close = audit_pipe_close, 1822973Sjlahoda .d_read = audit_pipe_read, 1832973Sjlahoda .d_ioctl = audit_pipe_ioctl, 1842973Sjlahoda .d_poll = audit_pipe_poll, 1852973Sjlahoda .d_kqfilter = audit_pipe_kqfilter, 1862973Sjlahoda .d_name = AUDIT_PIPE_NAME, 1872973Sjlahoda}; 1882973Sjlahoda 1892973Sjlahodastatic int audit_pipe_kqread(struct knote *note, long hint); 1902973Sjlahodastatic void audit_pipe_kqdetach(struct knote *note); 1912973Sjlahoda 1922973Sjlahodastatic struct filterops audit_pipe_read_filterops = { 1932973Sjlahoda .f_isfd = 1, 1942973Sjlahoda .f_attach = NULL, 1952973Sjlahoda .f_detach = audit_pipe_kqdetach, 1962973Sjlahoda .f_event = audit_pipe_kqread, 1972973Sjlahoda}; 1982973Sjlahoda 1992973Sjlahoda/* 2002973Sjlahoda * Some global statistics on audit pipes. 2012973Sjlahoda */ 2022973Sjlahodastatic int audit_pipe_count; /* Current number of pipes. */ 2032973Sjlahodastatic u_int64_t audit_pipe_ever; /* Pipes ever allocated. */ 2042973Sjlahodastatic u_int64_t audit_pipe_records; /* Records seen. */ 2052973Sjlahodastatic u_int64_t audit_pipe_drops; /* Global record drop count. */ 2062973Sjlahoda 2072973Sjlahoda/* 2082973Sjlahoda * Free an audit pipe entry. 2092973Sjlahoda */ 2102973Sjlahodastatic void 2112973Sjlahodaaudit_pipe_entry_free(struct audit_pipe_entry *ape) 2122973Sjlahoda{ 2132973Sjlahoda 2142973Sjlahoda free(ape->ape_record, M_AUDIT_PIPE_ENTRY); 2152973Sjlahoda free(ape, M_AUDIT_PIPE_ENTRY); 2162973Sjlahoda} 2172973Sjlahoda 2182973Sjlahoda/* 2192973Sjlahoda * Find an audit pipe preselection specification for an auid, if any. 2202973Sjlahoda */ 2212973Sjlahodastatic struct audit_pipe_preselect * 2222973Sjlahodaaudit_pipe_preselect_find(struct audit_pipe *ap, au_id_t auid) 2232973Sjlahoda{ 2242973Sjlahoda struct audit_pipe_preselect *app; 2252973Sjlahoda 2262973Sjlahoda mtx_assert(&audit_pipe_mtx, MA_OWNED); 2272973Sjlahoda 2282973Sjlahoda TAILQ_FOREACH(app, &ap->ap_preselect_list, app_list) { 2292973Sjlahoda if (app->app_auid == auid) 2302973Sjlahoda return (app); 2312973Sjlahoda } 2322973Sjlahoda return (NULL); 2332973Sjlahoda} 2342973Sjlahoda 2352973Sjlahoda/* 2362973Sjlahoda * Query the per-pipe mask for a specific auid. 2372973Sjlahoda */ 2382973Sjlahodastatic int 2392973Sjlahodaaudit_pipe_preselect_get(struct audit_pipe *ap, au_id_t auid, 2402973Sjlahoda au_mask_t *maskp) 2412973Sjlahoda{ 2422973Sjlahoda struct audit_pipe_preselect *app; 2432973Sjlahoda int error; 2442973Sjlahoda 2452973Sjlahoda mtx_lock(&audit_pipe_mtx); 2462973Sjlahoda app = audit_pipe_preselect_find(ap, auid); 2472973Sjlahoda if (app != NULL) { 2482973Sjlahoda *maskp = app->app_mask; 2492973Sjlahoda error = 0; 2502973Sjlahoda } else 2512973Sjlahoda error = ENOENT; 2522973Sjlahoda mtx_unlock(&audit_pipe_mtx); 2532973Sjlahoda return (error); 2542973Sjlahoda} 2552973Sjlahoda 2562973Sjlahoda/* 2572973Sjlahoda * Set the per-pipe mask for a specific auid. Add a new entry if needed; 2582973Sjlahoda * otherwise, update the current entry. 2592973Sjlahoda */ 2602973Sjlahodastatic void 2612973Sjlahodaaudit_pipe_preselect_set(struct audit_pipe *ap, au_id_t auid, au_mask_t mask) 2622973Sjlahoda{ 2632973Sjlahoda struct audit_pipe_preselect *app, *app_new; 2642973Sjlahoda 2652973Sjlahoda /* 2662973Sjlahoda * Pessimistically assume that the auid doesn't already have a mask 2672973Sjlahoda * set, and allocate. We will free it if it is unneeded. 2682973Sjlahoda */ 2692973Sjlahoda app_new = malloc(sizeof(*app_new), M_AUDIT_PIPE_PRESELECT, M_WAITOK); 2702973Sjlahoda mtx_lock(&audit_pipe_mtx); 2712973Sjlahoda app = audit_pipe_preselect_find(ap, auid); 2722973Sjlahoda if (app == NULL) { 2732973Sjlahoda app = app_new; 2742973Sjlahoda app_new = NULL; 2752973Sjlahoda app->app_auid = auid; 2762973Sjlahoda TAILQ_INSERT_TAIL(&ap->ap_preselect_list, app, app_list); 2772973Sjlahoda } 2782973Sjlahoda app->app_mask = mask; 2792973Sjlahoda mtx_unlock(&audit_pipe_mtx); 2802973Sjlahoda if (app_new != NULL) 2812973Sjlahoda free(app_new, M_AUDIT_PIPE_PRESELECT); 2822973Sjlahoda} 2832973Sjlahoda 2842973Sjlahoda/* 2852973Sjlahoda * Delete a per-auid mask on an audit pipe. 2862973Sjlahoda */ 2872973Sjlahodastatic int 2882973Sjlahodaaudit_pipe_preselect_delete(struct audit_pipe *ap, au_id_t auid) 2892973Sjlahoda{ 2902973Sjlahoda struct audit_pipe_preselect *app; 2912973Sjlahoda int error; 2922973Sjlahoda 2932973Sjlahoda mtx_lock(&audit_pipe_mtx); 2942973Sjlahoda app = audit_pipe_preselect_find(ap, auid); 2952973Sjlahoda if (app != NULL) { 2962973Sjlahoda TAILQ_REMOVE(&ap->ap_preselect_list, app, app_list); 2972973Sjlahoda error = 0; 2982973Sjlahoda } else 2992973Sjlahoda error = ENOENT; 3002973Sjlahoda mtx_unlock(&audit_pipe_mtx); 3012973Sjlahoda if (app != NULL) 3022973Sjlahoda free(app, M_AUDIT_PIPE_PRESELECT); 3032973Sjlahoda return (error); 3042973Sjlahoda} 3052973Sjlahoda 3062973Sjlahoda/* 3072973Sjlahoda * Delete all per-auid masks on an audit pipe. 3082973Sjlahoda */ 3092973Sjlahodastatic void 3102973Sjlahodaaudit_pipe_preselect_flush_locked(struct audit_pipe *ap) 3112973Sjlahoda{ 3122973Sjlahoda struct audit_pipe_preselect *app; 3132973Sjlahoda 3142973Sjlahoda mtx_assert(&audit_pipe_mtx, MA_OWNED); 3152973Sjlahoda 3162973Sjlahoda while ((app = TAILQ_FIRST(&ap->ap_preselect_list)) != NULL) { 3172973Sjlahoda TAILQ_REMOVE(&ap->ap_preselect_list, app, app_list); 3182973Sjlahoda free(app, M_AUDIT_PIPE_PRESELECT); 3192973Sjlahoda } 3202973Sjlahoda} 3212973Sjlahoda 3222973Sjlahodastatic void 3232973Sjlahodaaudit_pipe_preselect_flush(struct audit_pipe *ap) 3242973Sjlahoda{ 3252973Sjlahoda 3262973Sjlahoda mtx_lock(&audit_pipe_mtx); 3272973Sjlahoda audit_pipe_preselect_flush_locked(ap); 3282973Sjlahoda mtx_unlock(&audit_pipe_mtx); 3292973Sjlahoda} 3302973Sjlahoda 3312973Sjlahoda/* 3322973Sjlahoda * Determine whether a specific audit pipe matches a record with these 3332973Sjlahoda * properties. Algorithm is as follows: 3342973Sjlahoda * 3352973Sjlahoda * - If the pipe is configured to track the default trail configuration, then 3362973Sjlahoda * use the results of global preselection matching. 3372973Sjlahoda * - If not, search for a specifically configured auid entry matching the 3382973Sjlahoda * event. If an entry is found, use that. 3392973Sjlahoda * - Otherwise, use the default flags or naflags configured for the pipe. 3402973Sjlahoda */ 3412973Sjlahodastatic int 3422973Sjlahodaaudit_pipe_preselect_check(struct audit_pipe *ap, au_id_t auid, 3432973Sjlahoda au_event_t event, au_class_t class, int sorf, int trail_preselect) 3442973Sjlahoda{ 3452973Sjlahoda struct audit_pipe_preselect *app; 3462973Sjlahoda 3472973Sjlahoda mtx_assert(&audit_pipe_mtx, MA_OWNED); 3482973Sjlahoda 3492973Sjlahoda switch (ap->ap_preselect_mode) { 3502973Sjlahoda case AUDITPIPE_PRESELECT_MODE_TRAIL: 3512973Sjlahoda return (trail_preselect); 3522973Sjlahoda 3532973Sjlahoda case AUDITPIPE_PRESELECT_MODE_LOCAL: 3542973Sjlahoda app = audit_pipe_preselect_find(ap, auid); 3552973Sjlahoda if (app == NULL) { 3562973Sjlahoda if (auid == AU_DEFAUDITID) 3572973Sjlahoda return (au_preselect(event, class, 3582973Sjlahoda &ap->ap_preselect_naflags, sorf)); 3592973Sjlahoda else 3602973Sjlahoda return (au_preselect(event, class, 3612973Sjlahoda &ap->ap_preselect_flags, sorf)); 3622973Sjlahoda } else 3632973Sjlahoda return (au_preselect(event, class, &app->app_mask, 3642973Sjlahoda sorf)); 3652973Sjlahoda 3662973Sjlahoda default: 3672973Sjlahoda panic("audit_pipe_preselect_check: mode %d", 3682973Sjlahoda ap->ap_preselect_mode); 3692973Sjlahoda } 3702973Sjlahoda 3712973Sjlahoda return (0); 3722973Sjlahoda} 3732973Sjlahoda 3742973Sjlahoda/* 3752973Sjlahoda * Determine whether there exists a pipe interested in a record with specific 3762973Sjlahoda * properties. 3772973Sjlahoda */ 3782973Sjlahodaint 3792973Sjlahodaaudit_pipe_preselect(au_id_t auid, au_event_t event, au_class_t class, 3802973Sjlahoda int sorf, int trail_preselect) 3812973Sjlahoda{ 3822973Sjlahoda struct audit_pipe *ap; 3832973Sjlahoda 3842973Sjlahoda mtx_lock(&audit_pipe_mtx); 3852973Sjlahoda TAILQ_FOREACH(ap, &audit_pipe_list, ap_list) { 3862973Sjlahoda if (audit_pipe_preselect_check(ap, auid, event, class, sorf, 3872973Sjlahoda trail_preselect)) { 3882973Sjlahoda mtx_unlock(&audit_pipe_mtx); 3892973Sjlahoda return (1); 3902973Sjlahoda } 3912973Sjlahoda } 3922973Sjlahoda mtx_unlock(&audit_pipe_mtx); 3932973Sjlahoda return (0); 3942973Sjlahoda} 3952973Sjlahoda 3962973Sjlahoda/* 3972973Sjlahoda * Append individual record to a queue -- allocate queue-local buffer, and 3982973Sjlahoda * add to the queue. We try to drop from the head of the queue so that more 3992973Sjlahoda * recent events take precedence over older ones, but if allocation fails we 4002973Sjlahoda * do drop the new event. 4012973Sjlahoda */ 4022973Sjlahodastatic void 4032973Sjlahodaaudit_pipe_append(struct audit_pipe *ap, void *record, u_int record_len) 4042973Sjlahoda{ 4052973Sjlahoda struct audit_pipe_entry *ape, *ape_remove; 4062973Sjlahoda 4072973Sjlahoda mtx_assert(&audit_pipe_mtx, MA_OWNED); 4082973Sjlahoda 4092973Sjlahoda ape = malloc(sizeof(*ape), M_AUDIT_PIPE_ENTRY, M_NOWAIT | M_ZERO); 4102973Sjlahoda if (ape == NULL) { 4112973Sjlahoda ap->ap_drops++; 4122973Sjlahoda audit_pipe_drops++; 4132973Sjlahoda return; 4142973Sjlahoda } 4152973Sjlahoda 4162973Sjlahoda ape->ape_record = malloc(record_len, M_AUDIT_PIPE_ENTRY, M_NOWAIT); 4172973Sjlahoda if (ape->ape_record == NULL) { 4182973Sjlahoda free(ape, M_AUDIT_PIPE_ENTRY); 4192973Sjlahoda ap->ap_drops++; 4202973Sjlahoda audit_pipe_drops++; 4212973Sjlahoda return; 4222973Sjlahoda } 4232973Sjlahoda 4242973Sjlahoda bcopy(record, ape->ape_record, record_len); 4252973Sjlahoda ape->ape_record_len = record_len; 4262973Sjlahoda 4272973Sjlahoda if (ap->ap_qlen >= ap->ap_qlimit) { 4282973Sjlahoda ape_remove = TAILQ_FIRST(&ap->ap_queue); 4292973Sjlahoda TAILQ_REMOVE(&ap->ap_queue, ape_remove, ape_queue); 4302973Sjlahoda audit_pipe_entry_free(ape_remove); 4312973Sjlahoda ap->ap_qlen--; 4322973Sjlahoda ap->ap_drops++; 4332973Sjlahoda audit_pipe_drops++; 4342973Sjlahoda } 4352973Sjlahoda 4362973Sjlahoda TAILQ_INSERT_TAIL(&ap->ap_queue, ape, ape_queue); 4372973Sjlahoda ap->ap_inserts++; 4382973Sjlahoda ap->ap_qlen++; 4392973Sjlahoda selwakeuppri(&ap->ap_selinfo, PSOCK); 4402973Sjlahoda KNOTE_LOCKED(&ap->ap_selinfo.si_note, 0); 4412973Sjlahoda if (ap->ap_flags & AUDIT_PIPE_ASYNC) 4422973Sjlahoda pgsigio(&ap->ap_sigio, SIGIO, 0); 4432973Sjlahoda} 4442973Sjlahoda 4452973Sjlahoda/* 4462973Sjlahoda * audit_pipe_submit(): audit_worker submits audit records via this 4472973Sjlahoda * interface, which arranges for them to be delivered to pipe queues. 4482973Sjlahoda */ 4492973Sjlahodavoid 4502973Sjlahodaaudit_pipe_submit(au_id_t auid, au_event_t event, au_class_t class, int sorf, 4512973Sjlahoda int trail_select, void *record, u_int record_len) 4522973Sjlahoda{ 4532973Sjlahoda struct audit_pipe *ap; 4542973Sjlahoda 4552973Sjlahoda /* 4562973Sjlahoda * Lockless read to avoid mutex overhead if pipes are not in use. 4572973Sjlahoda */ 4582973Sjlahoda if (TAILQ_FIRST(&audit_pipe_list) == NULL) 4592973Sjlahoda return; 4602973Sjlahoda 4612973Sjlahoda mtx_lock(&audit_pipe_mtx); 4622973Sjlahoda TAILQ_FOREACH(ap, &audit_pipe_list, ap_list) { 4632973Sjlahoda if (audit_pipe_preselect_check(ap, auid, event, class, sorf, 4642973Sjlahoda trail_select)) 4652973Sjlahoda audit_pipe_append(ap, record, record_len); 4662973Sjlahoda } 4672973Sjlahoda audit_pipe_records++; 4682973Sjlahoda mtx_unlock(&audit_pipe_mtx); 4692973Sjlahoda cv_signal(&audit_pipe_cv); 4702973Sjlahoda} 4712973Sjlahoda 4722973Sjlahoda/* 4732973Sjlahoda * audit_pipe_submit_user(): the same as audit_pipe_submit(), except that 4742973Sjlahoda * since we don't currently have selection information available, it is 4752973Sjlahoda * delivered to the pipe unconditionally. 4762973Sjlahoda * 4772973Sjlahoda * XXXRW: This is a bug. The BSM check routine for submitting a user record 4782973Sjlahoda * should parse that information and return it. 4792973Sjlahoda */ 4802973Sjlahodavoid 4812973Sjlahodaaudit_pipe_submit_user(void *record, u_int record_len) 4822973Sjlahoda{ 4832973Sjlahoda struct audit_pipe *ap; 4842973Sjlahoda 4852973Sjlahoda /* 4862973Sjlahoda * Lockless read to avoid mutex overhead if pipes are not in use. 4872973Sjlahoda */ 4882973Sjlahoda if (TAILQ_FIRST(&audit_pipe_list) == NULL) 4892973Sjlahoda return; 4902973Sjlahoda 4912973Sjlahoda mtx_lock(&audit_pipe_mtx); 4922973Sjlahoda TAILQ_FOREACH(ap, &audit_pipe_list, ap_list) 4932973Sjlahoda audit_pipe_append(ap, record, record_len); 4942973Sjlahoda audit_pipe_records++; 4952973Sjlahoda mtx_unlock(&audit_pipe_mtx); 4962973Sjlahoda cv_signal(&audit_pipe_cv); 4972973Sjlahoda} 4982973Sjlahoda 4992973Sjlahoda 5002973Sjlahoda/* 5012973Sjlahoda * Pop the next record off of an audit pipe. 5022973Sjlahoda */ 5032973Sjlahodastatic struct audit_pipe_entry * 5042973Sjlahodaaudit_pipe_pop(struct audit_pipe *ap) 5052973Sjlahoda{ 5062973Sjlahoda struct audit_pipe_entry *ape; 5072973Sjlahoda 5082973Sjlahoda mtx_assert(&audit_pipe_mtx, MA_OWNED); 5092973Sjlahoda 5102973Sjlahoda ape = TAILQ_FIRST(&ap->ap_queue); 5112973Sjlahoda KASSERT((ape == NULL && ap->ap_qlen == 0) || 5122973Sjlahoda (ape != NULL && ap->ap_qlen != 0), ("audit_pipe_pop: qlen")); 5132973Sjlahoda if (ape == NULL) 5142973Sjlahoda return (NULL); 5152973Sjlahoda TAILQ_REMOVE(&ap->ap_queue, ape, ape_queue); 5162973Sjlahoda ap->ap_qlen--; 5172973Sjlahoda return (ape); 5182973Sjlahoda} 5192973Sjlahoda 5202973Sjlahoda/* 5212973Sjlahoda * Allocate a new audit pipe. Connects the pipe, on success, to the global 5222973Sjlahoda * list and updates statistics. 5232973Sjlahoda */ 5242973Sjlahodastatic struct audit_pipe * 5252973Sjlahodaaudit_pipe_alloc(void) 5262973Sjlahoda{ 5272973Sjlahoda struct audit_pipe *ap; 5282973Sjlahoda 5292973Sjlahoda mtx_assert(&audit_pipe_mtx, MA_OWNED); 5302973Sjlahoda 5312973Sjlahoda ap = malloc(sizeof(*ap), M_AUDIT_PIPE, M_NOWAIT | M_ZERO); 5322973Sjlahoda if (ap == NULL) 5332973Sjlahoda return (NULL); 5342973Sjlahoda ap->ap_qlimit = AUDIT_PIPE_QLIMIT_DEFAULT; 5352973Sjlahoda TAILQ_INIT(&ap->ap_queue); 5362973Sjlahoda knlist_init(&ap->ap_selinfo.si_note, &audit_pipe_mtx, NULL, NULL, 5372973Sjlahoda NULL); 5382973Sjlahoda 5392973Sjlahoda /* 5402973Sjlahoda * Default flags, naflags, and auid-specific preselection settings to 5412973Sjlahoda * 0. Initialize the mode to the global trail so that if praudit(1) 5422973Sjlahoda * is run on /dev/auditpipe, it sees events associated with the 5432973Sjlahoda * default trail. Pipe-aware application can clear the flag, set 5442973Sjlahoda * custom masks, and flush the pipe as needed. 5452973Sjlahoda */ 5462973Sjlahoda bzero(&ap->ap_preselect_flags, sizeof(ap->ap_preselect_flags)); 5472973Sjlahoda bzero(&ap->ap_preselect_naflags, sizeof(ap->ap_preselect_naflags)); 5482973Sjlahoda TAILQ_INIT(&ap->ap_preselect_list); 5492973Sjlahoda ap->ap_preselect_mode = AUDITPIPE_PRESELECT_MODE_TRAIL; 5502973Sjlahoda 5512973Sjlahoda /* 5522973Sjlahoda * Add to global list and update global statistics. 5532973Sjlahoda */ 5542973Sjlahoda TAILQ_INSERT_HEAD(&audit_pipe_list, ap, ap_list); 5552973Sjlahoda audit_pipe_count++; 5562973Sjlahoda audit_pipe_ever++; 5572973Sjlahoda 5582973Sjlahoda return (ap); 5592973Sjlahoda} 5602973Sjlahoda 5612973Sjlahoda/* 5622973Sjlahoda * Flush all records currently present in an audit pipe; assume mutex is held. 5632973Sjlahoda */ 5642973Sjlahodastatic void 5652973Sjlahodaaudit_pipe_flush(struct audit_pipe *ap) 5662973Sjlahoda{ 5672973Sjlahoda struct audit_pipe_entry *ape; 5682973Sjlahoda 5692973Sjlahoda mtx_assert(&audit_pipe_mtx, MA_OWNED); 5702973Sjlahoda 5712973Sjlahoda while ((ape = TAILQ_FIRST(&ap->ap_queue)) != NULL) { 5722973Sjlahoda TAILQ_REMOVE(&ap->ap_queue, ape, ape_queue); 5732973Sjlahoda audit_pipe_entry_free(ape); 5742973Sjlahoda ap->ap_qlen--; 5752973Sjlahoda } 5762973Sjlahoda KASSERT(ap->ap_qlen == 0, ("audit_pipe_free: ap_qlen")); 5772973Sjlahoda} 5782973Sjlahoda 5792973Sjlahoda/* 5802973Sjlahoda * Free an audit pipe; this means freeing all preselection state and all 5812973Sjlahoda * records in the pipe. Assumes mutex is held to prevent any new records 5822973Sjlahoda * from being inserted during the free, and that the audit pipe is still on 5832973Sjlahoda * the global list. 5842973Sjlahoda */ 5852973Sjlahodastatic void 5862973Sjlahodaaudit_pipe_free(struct audit_pipe *ap) 5872973Sjlahoda{ 5882973Sjlahoda 5892973Sjlahoda mtx_assert(&audit_pipe_mtx, MA_OWNED); 5902973Sjlahoda 5912973Sjlahoda audit_pipe_preselect_flush_locked(ap); 5922973Sjlahoda audit_pipe_flush(ap); 5932973Sjlahoda knlist_destroy(&ap->ap_selinfo.si_note); 5942973Sjlahoda TAILQ_REMOVE(&audit_pipe_list, ap, ap_list); 5952973Sjlahoda free(ap, M_AUDIT_PIPE); 5962973Sjlahoda audit_pipe_count--; 5972973Sjlahoda} 5982973Sjlahoda 5992973Sjlahoda/* 6002973Sjlahoda * Audit pipe clone routine -- provide specific requested audit pipe, or a 6012973Sjlahoda * fresh one if a specific one is not requested. 6022973Sjlahoda */ 6032973Sjlahodastatic void 6042973Sjlahodaaudit_pipe_clone(void *arg, struct ucred *cred, char *name, int namelen, 6052973Sjlahoda struct cdev **dev) 6062973Sjlahoda{ 6072973Sjlahoda int i, u; 6082973Sjlahoda 6092973Sjlahoda if (*dev != NULL) 6102973Sjlahoda return; 6112973Sjlahoda 6122973Sjlahoda if (strcmp(name, AUDIT_PIPE_NAME) == 0) 6132973Sjlahoda u = -1; 6142973Sjlahoda else if (dev_stdclone(name, NULL, AUDIT_PIPE_NAME, &u) != 1) 6152973Sjlahoda return; 6162973Sjlahoda 6172973Sjlahoda i = clone_create(&audit_pipe_clones, &audit_pipe_cdevsw, &u, dev, 0); 6182973Sjlahoda if (i) { 6192973Sjlahoda *dev = make_dev(&audit_pipe_cdevsw, unit2minor(u), UID_ROOT, 6202973Sjlahoda GID_WHEEL, 0600, "%s%d", AUDIT_PIPE_NAME, u); 6212973Sjlahoda if (*dev != NULL) { 6222973Sjlahoda dev_ref(*dev); 6232973Sjlahoda (*dev)->si_flags |= SI_CHEAPCLONE; 6242973Sjlahoda } 6252973Sjlahoda } 6262973Sjlahoda} 6272973Sjlahoda 6282973Sjlahoda/* 6292973Sjlahoda * Audit pipe open method. Explicit suser check isn't used as this allows 6302973Sjlahoda * file permissions on the special device to be used to grant audit review 6312973Sjlahoda * access. 6322973Sjlahoda */ 6332973Sjlahodastatic int 6342973Sjlahodaaudit_pipe_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 6352973Sjlahoda{ 6362973Sjlahoda struct audit_pipe *ap; 6372973Sjlahoda 6382973Sjlahoda mtx_lock(&audit_pipe_mtx); 6392973Sjlahoda ap = dev->si_drv1; 6402973Sjlahoda if (ap == NULL) { 6412973Sjlahoda ap = audit_pipe_alloc(); 6422973Sjlahoda if (ap == NULL) { 6432973Sjlahoda mtx_unlock(&audit_pipe_mtx); 6442973Sjlahoda return (ENOMEM); 6452973Sjlahoda } 6462973Sjlahoda dev->si_drv1 = ap; 6472973Sjlahoda } else { 6482973Sjlahoda KASSERT(ap->ap_open, ("audit_pipe_open: ap && !ap_open")); 6492973Sjlahoda mtx_unlock(&audit_pipe_mtx); 6502973Sjlahoda return (EBUSY); 6512973Sjlahoda } 6522973Sjlahoda ap->ap_open = 1; 6532973Sjlahoda mtx_unlock(&audit_pipe_mtx); 6542973Sjlahoda fsetown(td->td_proc->p_pid, &ap->ap_sigio); 6552973Sjlahoda return (0); 6562973Sjlahoda} 6572973Sjlahoda 6582973Sjlahoda/* 6592973Sjlahoda * Close audit pipe, tear down all records, etc. 6602973Sjlahoda */ 6612973Sjlahodastatic int 6622973Sjlahodaaudit_pipe_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 6632973Sjlahoda{ 6642973Sjlahoda struct audit_pipe *ap; 6652973Sjlahoda 6662973Sjlahoda ap = dev->si_drv1; 6672973Sjlahoda KASSERT(ap != NULL, ("audit_pipe_close: ap == NULL")); 6682973Sjlahoda KASSERT(ap->ap_open, ("audit_pipe_close: !ap_open")); 6692973Sjlahoda funsetown(&ap->ap_sigio); 6702973Sjlahoda mtx_lock(&audit_pipe_mtx); 6712973Sjlahoda ap->ap_open = 0; 6722973Sjlahoda audit_pipe_free(ap); 6732973Sjlahoda dev->si_drv1 = NULL; 6742973Sjlahoda mtx_unlock(&audit_pipe_mtx); 6752973Sjlahoda return (0); 6762973Sjlahoda} 6772973Sjlahoda 6782973Sjlahoda/* 6792973Sjlahoda * Audit pipe ioctl() routine. Handle file descriptor and audit pipe layer 6802973Sjlahoda * commands. 6812973Sjlahoda * 6822973Sjlahoda * Would be desirable to support filtering, although perhaps something simple 6832973Sjlahoda * like an event mask, as opposed to something complicated like BPF. 6842973Sjlahoda */ 6852973Sjlahodastatic int 6862973Sjlahodaaudit_pipe_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, 6872973Sjlahoda struct thread *td) 6882973Sjlahoda{ 6892973Sjlahoda struct auditpipe_ioctl_preselect *aip; 6902973Sjlahoda struct audit_pipe *ap; 6912973Sjlahoda au_mask_t *maskp; 6922973Sjlahoda int error, mode; 6932973Sjlahoda au_id_t auid; 6942973Sjlahoda 6952973Sjlahoda ap = dev->si_drv1; 6962973Sjlahoda KASSERT(ap != NULL, ("audit_pipe_ioctl: ap == NULL")); 6972973Sjlahoda 6982973Sjlahoda /* 6992973Sjlahoda * Audit pipe ioctls: first come standard device node ioctls, then 7002973Sjlahoda * manipulation of pipe settings, and finally, statistics query 7012973Sjlahoda * ioctls. 7022973Sjlahoda */ 7032973Sjlahoda switch (cmd) { 7042973Sjlahoda case FIONBIO: 7052973Sjlahoda mtx_lock(&audit_pipe_mtx); 7062973Sjlahoda if (*(int *)data) 7072973Sjlahoda ap->ap_flags |= AUDIT_PIPE_NBIO; 7082973Sjlahoda else 7092973Sjlahoda ap->ap_flags &= ~AUDIT_PIPE_NBIO; 7102973Sjlahoda mtx_unlock(&audit_pipe_mtx); 7112973Sjlahoda error = 0; 7122973Sjlahoda break; 7132973Sjlahoda 7142973Sjlahoda case FIONREAD: 7152973Sjlahoda mtx_lock(&audit_pipe_mtx); 7162973Sjlahoda if (TAILQ_FIRST(&ap->ap_queue) != NULL) 7172973Sjlahoda *(int *)data = 7182973Sjlahoda TAILQ_FIRST(&ap->ap_queue)->ape_record_len; 7192973Sjlahoda else 7202973Sjlahoda *(int *)data = 0; 7212973Sjlahoda mtx_unlock(&audit_pipe_mtx); 7222973Sjlahoda error = 0; 7232973Sjlahoda break; 7242973Sjlahoda 7252973Sjlahoda case FIOASYNC: 7262973Sjlahoda mtx_lock(&audit_pipe_mtx); 7272973Sjlahoda if (*(int *)data) 7282973Sjlahoda ap->ap_flags |= AUDIT_PIPE_ASYNC; 7292973Sjlahoda else 7302973Sjlahoda ap->ap_flags &= ~AUDIT_PIPE_ASYNC; 7312973Sjlahoda mtx_unlock(&audit_pipe_mtx); 7322973Sjlahoda error = 0; 7332973Sjlahoda break; 7342973Sjlahoda 7352973Sjlahoda case FIOSETOWN: 7362973Sjlahoda error = fsetown(*(int *)data, &ap->ap_sigio); 7372973Sjlahoda break; 7382973Sjlahoda 7392973Sjlahoda case FIOGETOWN: 7402973Sjlahoda *(int *)data = fgetown(&ap->ap_sigio); 7412973Sjlahoda error = 0; 7422973Sjlahoda break; 7432973Sjlahoda 7442973Sjlahoda case AUDITPIPE_GET_QLEN: 7452973Sjlahoda *(u_int *)data = ap->ap_qlen; 7462973Sjlahoda error = 0; 7472973Sjlahoda break; 7482973Sjlahoda 7492973Sjlahoda case AUDITPIPE_GET_QLIMIT: 7502973Sjlahoda *(u_int *)data = ap->ap_qlimit; 7512973Sjlahoda error = 0; 7522973Sjlahoda break; 7532973Sjlahoda 7542973Sjlahoda case AUDITPIPE_SET_QLIMIT: 7552973Sjlahoda /* Lockless integer write. */ 7562973Sjlahoda if (*(u_int *)data >= AUDIT_PIPE_QLIMIT_MIN || 7572973Sjlahoda *(u_int *)data <= AUDIT_PIPE_QLIMIT_MAX) { 7582973Sjlahoda ap->ap_qlimit = *(u_int *)data; 7592973Sjlahoda error = 0; 7602973Sjlahoda } else 7612973Sjlahoda error = EINVAL; 7622973Sjlahoda break; 7632973Sjlahoda 7642973Sjlahoda case AUDITPIPE_GET_QLIMIT_MIN: 7652973Sjlahoda *(u_int *)data = AUDIT_PIPE_QLIMIT_MIN; 7662973Sjlahoda error = 0; 7672973Sjlahoda break; 7682973Sjlahoda 7692973Sjlahoda case AUDITPIPE_GET_QLIMIT_MAX: 7702973Sjlahoda *(u_int *)data = AUDIT_PIPE_QLIMIT_MAX; 7712973Sjlahoda error = 0; 7722973Sjlahoda break; 7732973Sjlahoda 7742973Sjlahoda case AUDITPIPE_GET_PRESELECT_FLAGS: 7752973Sjlahoda mtx_lock(&audit_pipe_mtx); 7762973Sjlahoda maskp = (au_mask_t *)data; 7772973Sjlahoda *maskp = ap->ap_preselect_flags; 7782973Sjlahoda mtx_unlock(&audit_pipe_mtx); 7792973Sjlahoda error = 0; 7802973Sjlahoda break; 7812973Sjlahoda 7822973Sjlahoda case AUDITPIPE_SET_PRESELECT_FLAGS: 7832973Sjlahoda mtx_lock(&audit_pipe_mtx); 7842973Sjlahoda maskp = (au_mask_t *)data; 7852973Sjlahoda ap->ap_preselect_flags = *maskp; 7862973Sjlahoda mtx_unlock(&audit_pipe_mtx); 7872973Sjlahoda error = 0; 7882973Sjlahoda break; 7892973Sjlahoda 7902973Sjlahoda case AUDITPIPE_GET_PRESELECT_NAFLAGS: 7912973Sjlahoda mtx_lock(&audit_pipe_mtx); 7922973Sjlahoda maskp = (au_mask_t *)data; 7932973Sjlahoda *maskp = ap->ap_preselect_naflags; 7942973Sjlahoda mtx_unlock(&audit_pipe_mtx); 7952973Sjlahoda error = 0; 7962973Sjlahoda break; 7972973Sjlahoda 7982973Sjlahoda case AUDITPIPE_SET_PRESELECT_NAFLAGS: 7992973Sjlahoda mtx_lock(&audit_pipe_mtx); 8002973Sjlahoda maskp = (au_mask_t *)data; 8012973Sjlahoda ap->ap_preselect_naflags = *maskp; 8022973Sjlahoda mtx_unlock(&audit_pipe_mtx); 8032973Sjlahoda error = 0; 8042973Sjlahoda break; 8052973Sjlahoda 8062973Sjlahoda case AUDITPIPE_GET_PRESELECT_AUID: 8072973Sjlahoda aip = (struct auditpipe_ioctl_preselect *)data; 8082973Sjlahoda error = audit_pipe_preselect_get(ap, aip->aip_auid, 8092973Sjlahoda &aip->aip_mask); 8102973Sjlahoda break; 8112973Sjlahoda 8122973Sjlahoda case AUDITPIPE_SET_PRESELECT_AUID: 8132973Sjlahoda aip = (struct auditpipe_ioctl_preselect *)data; 8142973Sjlahoda audit_pipe_preselect_set(ap, aip->aip_auid, aip->aip_mask); 8152973Sjlahoda error = 0; 8162973Sjlahoda break; 8172973Sjlahoda 8182973Sjlahoda case AUDITPIPE_DELETE_PRESELECT_AUID: 8192973Sjlahoda auid = *(au_id_t *)data; 8202973Sjlahoda error = audit_pipe_preselect_delete(ap, auid); 8212973Sjlahoda break; 8222973Sjlahoda 8232973Sjlahoda case AUDITPIPE_FLUSH_PRESELECT_AUID: 8242973Sjlahoda audit_pipe_preselect_flush(ap); 8252973Sjlahoda error = 0; 8262973Sjlahoda break; 8272973Sjlahoda 8282973Sjlahoda case AUDITPIPE_GET_PRESELECT_MODE: 8292973Sjlahoda mtx_lock(&audit_pipe_mtx); 8302973Sjlahoda *(int *)data = ap->ap_preselect_mode; 8312973Sjlahoda mtx_unlock(&audit_pipe_mtx); 8322973Sjlahoda error = 0; 8332973Sjlahoda break; 8342973Sjlahoda 8352973Sjlahoda case AUDITPIPE_SET_PRESELECT_MODE: 8362973Sjlahoda mode = *(int *)data; 8372973Sjlahoda switch (mode) { 8382973Sjlahoda case AUDITPIPE_PRESELECT_MODE_TRAIL: 8392973Sjlahoda case AUDITPIPE_PRESELECT_MODE_LOCAL: 8402973Sjlahoda mtx_lock(&audit_pipe_mtx); 8412973Sjlahoda ap->ap_preselect_mode = mode; 8422973Sjlahoda mtx_unlock(&audit_pipe_mtx); 8432973Sjlahoda error = 0; 8442973Sjlahoda break; 8452973Sjlahoda 8462973Sjlahoda default: 8472973Sjlahoda error = EINVAL; 8482973Sjlahoda } 8492973Sjlahoda break; 8502973Sjlahoda 8512973Sjlahoda case AUDITPIPE_FLUSH: 8522973Sjlahoda mtx_lock(&audit_pipe_mtx); 8532973Sjlahoda audit_pipe_flush(ap); 8542973Sjlahoda mtx_unlock(&audit_pipe_mtx); 8552973Sjlahoda error = 0; 8562973Sjlahoda break; 8572973Sjlahoda 8582973Sjlahoda case AUDITPIPE_GET_INSERTS: 8592973Sjlahoda *(u_int *)data = ap->ap_inserts; 8602973Sjlahoda error = 0; 8612973Sjlahoda break; 8622973Sjlahoda 8632973Sjlahoda case AUDITPIPE_GET_READS: 8642973Sjlahoda *(u_int *)data = ap->ap_reads; 8652973Sjlahoda error = 0; 8662973Sjlahoda break; 8672973Sjlahoda 8682973Sjlahoda case AUDITPIPE_GET_DROPS: 8692973Sjlahoda *(u_int *)data = ap->ap_drops; 8702973Sjlahoda error = 0; 8712973Sjlahoda break; 8722973Sjlahoda 8732973Sjlahoda case AUDITPIPE_GET_TRUNCATES: 8742973Sjlahoda *(u_int *)data = ap->ap_truncates; 8752973Sjlahoda error = 0; 8762973Sjlahoda break; 8772973Sjlahoda 8782973Sjlahoda default: 8792973Sjlahoda error = ENOTTY; 8802973Sjlahoda } 8812973Sjlahoda return (error); 8822973Sjlahoda} 8832973Sjlahoda 8842973Sjlahoda/* 8852973Sjlahoda * Audit pipe read. Pull one record off the queue and copy to user space. 8862973Sjlahoda * On error, the record is dropped. 8872973Sjlahoda * 8882973Sjlahoda * Providing more sophisticated behavior, such as partial reads, is tricky 8892973Sjlahoda * due to the potential for parallel I/O. If partial read support is 8902973Sjlahoda * required, it will require a per-pipe "current record being read" along 8912973Sjlahoda * with an offset into that trecord which has already been read. Threads 8922973Sjlahoda * performing partial reads will need to allocate per-thread copies of the 8932973Sjlahoda * data so that if another thread completes the read of the record, it can be 8942973Sjlahoda * freed without adding reference count logic. If this is added, a flag to 8952973Sjlahoda * indicate that only atomic record reads are desired would be useful, as if 8962973Sjlahoda * different threads are all waiting for records on the pipe, they will want 8972973Sjlahoda * independent record reads, which is currently the behavior. 8982973Sjlahoda */ 8992973Sjlahodastatic int 9002973Sjlahodaaudit_pipe_read(struct cdev *dev, struct uio *uio, int flag) 9012973Sjlahoda{ 9022973Sjlahoda struct audit_pipe_entry *ape; 9032973Sjlahoda struct audit_pipe *ap; 9042973Sjlahoda int error; 9052973Sjlahoda 9062973Sjlahoda ap = dev->si_drv1; 9072973Sjlahoda KASSERT(ap != NULL, ("audit_pipe_read: ap == NULL")); 9082973Sjlahoda mtx_lock(&audit_pipe_mtx); 9092973Sjlahoda do { 9102973Sjlahoda /* 9112973Sjlahoda * Wait for a record that fits into the read buffer, dropping 9122973Sjlahoda * records that would be truncated if actually passed to the 9132973Sjlahoda * process. This helps maintain the discreet record read 9142973Sjlahoda * interface. 9152973Sjlahoda */ 9162973Sjlahoda while ((ape = audit_pipe_pop(ap)) == NULL) { 9172973Sjlahoda if (ap->ap_flags & AUDIT_PIPE_NBIO) { 9182973Sjlahoda mtx_unlock(&audit_pipe_mtx); 9192973Sjlahoda return (EAGAIN); 9202973Sjlahoda } 9212973Sjlahoda error = cv_wait_sig(&audit_pipe_cv, &audit_pipe_mtx); 9222973Sjlahoda if (error) { 9232973Sjlahoda mtx_unlock(&audit_pipe_mtx); 9242973Sjlahoda return (error); 9252973Sjlahoda } 9262973Sjlahoda } 9272973Sjlahoda if (ape->ape_record_len <= uio->uio_resid) 9282973Sjlahoda break; 9292973Sjlahoda audit_pipe_entry_free(ape); 9302973Sjlahoda ap->ap_truncates++; 9312973Sjlahoda } while (1); 9322973Sjlahoda mtx_unlock(&audit_pipe_mtx); 9332973Sjlahoda 9342973Sjlahoda /* 9352973Sjlahoda * Now read record to user space memory. Even if the read is short, 9362973Sjlahoda * we abandon the remainder of the record, supporting only discreet 9372973Sjlahoda * record reads. 9382973Sjlahoda */ 9392973Sjlahoda error = uiomove(ape->ape_record, ape->ape_record_len, uio); 9402973Sjlahoda audit_pipe_entry_free(ape); 9412973Sjlahoda return (error); 9422973Sjlahoda} 9432973Sjlahoda 9442973Sjlahoda/* 9452973Sjlahoda * Audit pipe poll. 9462973Sjlahoda */ 9472973Sjlahodastatic int 9482973Sjlahodaaudit_pipe_poll(struct cdev *dev, int events, struct thread *td) 9492973Sjlahoda{ 9502973Sjlahoda struct audit_pipe *ap; 9512973Sjlahoda int revents; 9522973Sjlahoda 9532973Sjlahoda revents = 0; 9542973Sjlahoda ap = dev->si_drv1; 9552973Sjlahoda KASSERT(ap != NULL, ("audit_pipe_poll: ap == NULL")); 9562973Sjlahoda if (events & (POLLIN | POLLRDNORM)) { 9572973Sjlahoda mtx_lock(&audit_pipe_mtx); 9582973Sjlahoda if (TAILQ_FIRST(&ap->ap_queue) != NULL) 9592973Sjlahoda revents |= events & (POLLIN | POLLRDNORM); 9602973Sjlahoda else 9612973Sjlahoda selrecord(td, &ap->ap_selinfo); 9622973Sjlahoda mtx_unlock(&audit_pipe_mtx); 9632973Sjlahoda } 9642973Sjlahoda return (revents); 9652973Sjlahoda} 9662973Sjlahoda 9672973Sjlahoda/* 9682973Sjlahoda * Audit pipe kqfilter. 9692973Sjlahoda */ 9702973Sjlahodastatic int 9712973Sjlahodaaudit_pipe_kqfilter(struct cdev *dev, struct knote *kn) 9722973Sjlahoda{ 9732973Sjlahoda struct audit_pipe *ap; 9742973Sjlahoda 9752973Sjlahoda ap = dev->si_drv1; 9762973Sjlahoda KASSERT(ap != NULL, ("audit_pipe_kqfilter: ap == NULL")); 9772973Sjlahoda 9782973Sjlahoda if (kn->kn_filter != EVFILT_READ) 9792973Sjlahoda return (EINVAL); 9802973Sjlahoda 9812973Sjlahoda kn->kn_fop = &audit_pipe_read_filterops; 9822973Sjlahoda kn->kn_hook = ap; 9832973Sjlahoda 9842973Sjlahoda mtx_lock(&audit_pipe_mtx); 9852973Sjlahoda knlist_add(&ap->ap_selinfo.si_note, kn, 1); 9862973Sjlahoda mtx_unlock(&audit_pipe_mtx); 9872973Sjlahoda return (0); 9882973Sjlahoda} 9892973Sjlahoda 9902973Sjlahoda/* 9912973Sjlahoda * Return true if there are records available for reading on the pipe. 9922973Sjlahoda */ 9932973Sjlahodastatic int 9942973Sjlahodaaudit_pipe_kqread(struct knote *kn, long hint) 9952973Sjlahoda{ 9962973Sjlahoda struct audit_pipe_entry *ape; 9972973Sjlahoda struct audit_pipe *ap; 9982973Sjlahoda 9992973Sjlahoda mtx_assert(&audit_pipe_mtx, MA_OWNED); 10002973Sjlahoda 10012973Sjlahoda ap = (struct audit_pipe *)kn->kn_hook; 10022973Sjlahoda KASSERT(ap != NULL, ("audit_pipe_kqread: ap == NULL")); 10032973Sjlahoda 10042973Sjlahoda if (ap->ap_qlen != 0) { 10052973Sjlahoda ape = TAILQ_FIRST(&ap->ap_queue); 10062973Sjlahoda KASSERT(ape != NULL, ("audit_pipe_kqread: ape == NULL")); 10072973Sjlahoda 10082973Sjlahoda kn->kn_data = ape->ape_record_len; 10092973Sjlahoda return (1); 10102973Sjlahoda } else { 10112973Sjlahoda kn->kn_data = 0; 10122973Sjlahoda return (0); 10132973Sjlahoda } 10142973Sjlahoda} 10152973Sjlahoda 10162973Sjlahoda/* 10172973Sjlahoda * Detach kqueue state from audit pipe. 10182973Sjlahoda */ 10192973Sjlahodastatic void 10202973Sjlahodaaudit_pipe_kqdetach(struct knote *kn) 10212973Sjlahoda{ 10222973Sjlahoda struct audit_pipe *ap; 10232973Sjlahoda 10242973Sjlahoda ap = (struct audit_pipe *)kn->kn_hook; 10252973Sjlahoda KASSERT(ap != NULL, ("audit_pipe_kqdetach: ap == NULL")); 10262973Sjlahoda 10272973Sjlahoda mtx_lock(&audit_pipe_mtx); 10282973Sjlahoda knlist_remove(&ap->ap_selinfo.si_note, kn, 1); 10292973Sjlahoda mtx_unlock(&audit_pipe_mtx); 10302973Sjlahoda} 10312973Sjlahoda 10322973Sjlahoda/* 10332973Sjlahoda * Initialize the audit pipe system. 10342973Sjlahoda */ 10352973Sjlahodastatic void 10362973Sjlahodaaudit_pipe_init(void *unused) 10372973Sjlahoda{ 10382973Sjlahoda 10392973Sjlahoda TAILQ_INIT(&audit_pipe_list); 10402973Sjlahoda mtx_init(&audit_pipe_mtx, "audit_pipe_mtx", NULL, MTX_DEF); 10412973Sjlahoda cv_init(&audit_pipe_cv, "audit_pipe_cv"); 10422973Sjlahoda 10432973Sjlahoda clone_setup(&audit_pipe_clones); 10442973Sjlahoda audit_pipe_eh_tag = EVENTHANDLER_REGISTER(dev_clone, 10452973Sjlahoda audit_pipe_clone, 0, 1000); 10462973Sjlahoda if (audit_pipe_eh_tag == NULL) 10472973Sjlahoda panic("audit_pipe_init: EVENTHANDLER_REGISTER"); 10482973Sjlahoda} 10492973Sjlahoda 10502973SjlahodaSYSINIT(audit_pipe_init, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, audit_pipe_init, 10512973Sjlahoda NULL); 10522973Sjlahoda