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