[Midnightbsd-cvs] src: security/audit: add openbsm files

laffer1 at midnightbsd.org laffer1 at midnightbsd.org
Tue Sep 2 22:13:48 EDT 2008


Log Message:
-----------
add openbsm files

Modified Files:
--------------
    src/sys/security/audit:
        audit_syscalls.c (r1.1 -> r1.2)

Added Files:
-----------
    src/sys/security/audit:
        audit.c (r1.1)
        audit.h (r1.1)
        audit_arg.c (r1.1)
        audit_bsm.c (r1.1)
        audit_bsm_klib.c (r1.1)
        audit_bsm_token.c (r1.1)
        audit_ioctl.h (r1.1)
        audit_pipe.c (r1.1)
        audit_private.h (r1.1)
        audit_trigger.c (r1.1)
        audit_worker.c (r1.1)

-------------- next part --------------
--- /dev/null
+++ sys/security/audit/audit_trigger.c
@@ -0,0 +1,177 @@
+/*-
+ * Copyright (c) 2005 Wayne J. Salamon
+ * All rights reserved.
+ *
+ * This software was developed by Wayne Salamon for the TrustedBSD Project.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/security/audit/audit_trigger.c,v 1.5 2007/06/13 21:17:23 rwatson Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/systm.h>
+#include <sys/uio.h>
+
+#include <security/audit/audit.h>
+#include <security/audit/audit_private.h>
+
+/*
+ * Structures and operations to support the basic character special device
+ * used to communicate with userland.  /dev/audit reliably delivers one-byte
+ * messages to a listening application (or discards them if there is no
+ * listening application).
+ *
+ * Currently, select/poll are not supported on the trigger device.
+ */
+struct trigger_info {
+	unsigned int			trigger;
+	TAILQ_ENTRY(trigger_info)	list;
+};
+
+static MALLOC_DEFINE(M_AUDITTRIGGER, "audit_trigger", "Audit trigger events");
+static struct cdev *audit_dev;
+static int audit_isopen = 0;
+static TAILQ_HEAD(, trigger_info) trigger_list;
+static struct mtx audit_trigger_mtx;
+
+static int
+audit_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
+{
+	int error;
+
+	/* Only one process may open the device at a time. */
+	mtx_lock(&audit_trigger_mtx);
+	if (!audit_isopen) {
+		error = 0;
+		audit_isopen = 1;
+	} else
+		error = EBUSY;
+	mtx_unlock(&audit_trigger_mtx);
+
+	return (error);
+}
+
+static int
+audit_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
+{
+	struct trigger_info *ti;
+
+	/* Flush the queue of pending trigger events. */
+	mtx_lock(&audit_trigger_mtx);
+	audit_isopen = 0;
+	while (!TAILQ_EMPTY(&trigger_list)) {
+		ti = TAILQ_FIRST(&trigger_list);
+		TAILQ_REMOVE(&trigger_list, ti, list);
+		free(ti, M_AUDITTRIGGER);
+	}
+	mtx_unlock(&audit_trigger_mtx);
+
+	return (0);
+}
+
+static int
+audit_read(struct cdev *dev, struct uio *uio, int ioflag)
+{
+	int error = 0;
+	struct trigger_info *ti = NULL;
+
+	mtx_lock(&audit_trigger_mtx);
+	while (TAILQ_EMPTY(&trigger_list)) {
+		error = msleep(&trigger_list, &audit_trigger_mtx,
+		    PSOCK | PCATCH, "auditd", 0);
+		if (error)
+			break;
+	}
+	if (!error) {
+		ti = TAILQ_FIRST(&trigger_list);
+		TAILQ_REMOVE(&trigger_list, ti, list);
+	}
+	mtx_unlock(&audit_trigger_mtx);
+	if (!error) {
+		error = uiomove(ti, sizeof *ti, uio);
+		free(ti, M_AUDITTRIGGER);
+	}
+	return (error);
+}
+
+static int
+audit_write(struct cdev *dev, struct uio *uio, int ioflag)
+{
+
+	/* Communication is kernel->userspace only. */
+	return (EOPNOTSUPP);
+}
+
+int
+send_trigger(unsigned int trigger)
+{
+	struct trigger_info *ti;
+
+	ti = malloc(sizeof *ti, M_AUDITTRIGGER, M_WAITOK);
+	mtx_lock(&audit_trigger_mtx);
+	if (!audit_isopen) {
+		/* If nobody's listening, we ain't talking. */
+		mtx_unlock(&audit_trigger_mtx);
+		free(ti, M_AUDITTRIGGER);
+		return (ENODEV);
+	}
+	ti->trigger = trigger;
+	TAILQ_INSERT_TAIL(&trigger_list, ti, list);
+	wakeup(&trigger_list);
+	mtx_unlock(&audit_trigger_mtx);
+	return (0);
+}
+
+static struct cdevsw audit_cdevsw = {
+	.d_version =	D_VERSION,
+	.d_open = 	audit_open,
+	.d_close = 	audit_close,
+	.d_read = 	audit_read,
+	.d_write = 	audit_write,
+	.d_name = 	"audit"
+};
+
+void
+audit_trigger_init(void)
+{
+
+	TAILQ_INIT(&trigger_list);
+	mtx_init(&audit_trigger_mtx, "audit_trigger_mtx", NULL, MTX_DEF);
+}
+
+static void
+audit_trigger_cdev_init(void *unused)
+{
+
+	/* Create the special device file. */
+	audit_dev = make_dev(&audit_cdevsw, 0, UID_ROOT, GID_KMEM, 0600,
+	    AUDITDEV_FILENAME);
+}
+
+SYSINIT(audit_trigger_cdev_init, SI_SUB_DRIVERS, SI_ORDER_MIDDLE,
+    audit_trigger_cdev_init, NULL);
Index: audit_syscalls.c
===================================================================
RCS file: /home/cvs/src/sys/security/audit/audit_syscalls.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -L sys/security/audit/audit_syscalls.c -L sys/security/audit/audit_syscalls.c -u -r1.1 -r1.2
--- sys/security/audit/audit_syscalls.c
+++ sys/security/audit/audit_syscalls.c
@@ -26,11 +26,667 @@
  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  * POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/sys/security/audit/audit_syscalls.c,v 1.1.2.1 2006/03/17 01:47:06 rwatson Exp $
+ * $FreeBSD: src/sys/security/audit/audit_syscalls.c,v 1.21 2007/06/27 17:01:15 csjp Exp $
  */
 
+#include "opt_mac.h"
+
 #include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/priv.h>
+#include <sys/proc.h>
 #include <sys/sysproto.h>
+#include <sys/systm.h>
+#include <sys/vnode.h>
+#include <sys/jail.h>
+
+#include <bsm/audit.h>
+#include <bsm/audit_kevents.h>
+
+#include <security/audit/audit.h>
+#include <security/audit/audit_private.h>
+#include <security/mac/mac_framework.h>
+
+#ifdef AUDIT
+
+/*
+ * System call to allow a user space application to submit a BSM audit record
+ * to the kernel for inclusion in the audit log.  This function does little
+ * verification on the audit record that is submitted.
+ *
+ * XXXAUDIT: Audit preselection for user records does not currently work,
+ * since we pre-select only based on the AUE_audit event type, not the event
+ * type submitted as part of the user audit data.
+ */
+/* ARGSUSED */
+int
+audit(struct thread *td, struct audit_args *uap)
+{
+	int error;
+	void * rec;
+	struct kaudit_record *ar;
+
+	if (jailed(td->td_ucred))
+		return (ENOSYS);
+	error = priv_check(td, PRIV_AUDIT_SUBMIT);
+	if (error)
+		return (error);
+
+	if ((uap->length <= 0) || (uap->length > audit_qctrl.aq_bufsz))
+		return (EINVAL);
+
+	ar = currecord();
+
+	/*
+	 * If there's no current audit record (audit() itself not audited)
+	 * commit the user audit record.
+	 */
+	if (ar == NULL) {
+
+		/*
+		 * This is not very efficient; we're required to allocate a
+		 * complete kernel audit record just so the user record can
+		 * tag along.
+		 *
+		 * XXXAUDIT: Maybe AUE_AUDIT in the system call context and
+		 * special pre-select handling?
+		 */
+		td->td_ar = audit_new(AUE_NULL, td);
+		if (td->td_ar == NULL)
+			return (ENOTSUP);
+		ar = td->td_ar;
+	}
+
+	if (uap->length > MAX_AUDIT_RECORD_SIZE)
+		return (EINVAL);
+
+	rec = malloc(uap->length, M_AUDITDATA, M_WAITOK);
+
+	error = copyin(uap->record, rec, uap->length);
+	if (error)
+		goto free_out;
+
+	/* Verify the record. */
+	if (bsm_rec_verify(rec) == 0) {
+		error = EINVAL;
+		goto free_out;
+	}
+
+#ifdef MAC
+	error = mac_check_system_audit(td->td_ucred, rec, uap->length);
+	if (error)
+		goto free_out;
+#endif
+
+	/*
+	 * Attach the user audit record to the kernel audit record.  Because
+	 * this system call is an auditable event, we will write the user
+	 * record along with the record for this audit event.
+	 *
+	 * XXXAUDIT: KASSERT appropriate starting values of k_udata, k_ulen,
+	 * k_ar_commit & AR_COMMIT_USER?
+	 */
+	ar->k_udata = rec;
+	ar->k_ulen  = uap->length;
+	ar->k_ar_commit |= AR_COMMIT_USER;
+
+	/*
+	 * Currently we assume that all preselection has been performed in
+	 * userspace.  We unconditionally set these masks so that the records
+	 * get committed both to the trail and pipe.  In the future we will
+	 * want to setup kernel based preselection.
+	 */
+	ar->k_ar_commit |= (AR_PRESELECT_USER_TRAIL | AR_PRESELECT_USER_PIPE);
+	return (0);
+
+free_out:
+	/*
+	 * audit_syscall_exit() will free the audit record on the thread even
+	 * if we allocated it above.
+	 */
+	free(rec, M_AUDITDATA);
+	return (error);
+}
+
+/*
+ *  System call to manipulate auditing.
+ */
+/* ARGSUSED */
+int
+auditon(struct thread *td, struct auditon_args *uap)
+{
+	struct ucred *newcred, *oldcred;
+	int error;
+	union auditon_udata udata;
+	struct proc *tp;
+
+	if (jailed(td->td_ucred))
+		return (ENOSYS);
+	AUDIT_ARG(cmd, uap->cmd);
+
+#ifdef MAC
+	error = mac_check_system_auditon(td->td_ucred, uap->cmd);
+	if (error)
+		return (error);
+#endif
+
+	error = priv_check(td, PRIV_AUDIT_CONTROL);
+	if (error)
+		return (error);
+
+	if ((uap->length <= 0) || (uap->length > sizeof(union auditon_udata)))
+		return (EINVAL);
+
+	memset((void *)&udata, 0, sizeof(udata));
+
+	/*
+	 * Some of the GET commands use the arguments too.
+	 */
+	switch (uap->cmd) {
+	case A_SETPOLICY:
+	case A_SETKMASK:
+	case A_SETQCTRL:
+	case A_SETSTAT:
+	case A_SETUMASK:
+	case A_SETSMASK:
+	case A_SETCOND:
+	case A_SETCLASS:
+	case A_SETPMASK:
+	case A_SETFSIZE:
+	case A_SETKAUDIT:
+	case A_GETCLASS:
+	case A_GETPINFO:
+	case A_GETPINFO_ADDR:
+	case A_SENDTRIGGER:
+		error = copyin(uap->data, (void *)&udata, uap->length);
+		if (error)
+			return (error);
+		AUDIT_ARG(auditon, &udata);
+		break;
+	}
+
+	/*
+	 * XXXAUDIT: Locking?
+	 */
+	switch (uap->cmd) {
+	case A_GETPOLICY:
+		if (!audit_fail_stop)
+			udata.au_policy |= AUDIT_CNT;
+		if (audit_panic_on_write_fail)
+			udata.au_policy |= AUDIT_AHLT;
+		if (audit_argv)
+			udata.au_policy |= AUDIT_ARGV;
+		if (audit_arge)
+			udata.au_policy |= AUDIT_ARGE;
+		break;
+
+	case A_SETPOLICY:
+		if (udata.au_policy & ~(AUDIT_CNT|AUDIT_AHLT|AUDIT_ARGV|
+		    AUDIT_ARGE))
+			return (EINVAL);
+		/*
+		 * XXX - Need to wake up waiters if the policy relaxes?
+		 */
+		audit_fail_stop = ((udata.au_policy & AUDIT_CNT) == 0);
+		audit_panic_on_write_fail = (udata.au_policy & AUDIT_AHLT);
+		audit_argv = (udata.au_policy & AUDIT_ARGV);
+		audit_arge = (udata.au_policy & AUDIT_ARGE);
+		break;
+
+	case A_GETKMASK:
+		udata.au_mask = audit_nae_mask;
+		break;
+
+	case A_SETKMASK:
+		audit_nae_mask = udata.au_mask;
+		break;
+
+	case A_GETQCTRL:
+		udata.au_qctrl = audit_qctrl;
+		break;
+
+	case A_SETQCTRL:
+		if ((udata.au_qctrl.aq_hiwater > AQ_MAXHIGH) ||
+		    (udata.au_qctrl.aq_lowater >= udata.au_qctrl.aq_hiwater) ||
+		    (udata.au_qctrl.aq_bufsz > AQ_MAXBUFSZ) ||
+		    (udata.au_qctrl.aq_minfree < 0) ||
+		    (udata.au_qctrl.aq_minfree > 100))
+			return (EINVAL);
+
+		audit_qctrl = udata.au_qctrl;
+		/* XXX The queue delay value isn't used with the kernel. */
+		audit_qctrl.aq_delay = -1;
+		break;
+
+	case A_GETCWD:
+		return (ENOSYS);
+		break;
+
+	case A_GETCAR:
+		return (ENOSYS);
+		break;
+
+	case A_GETSTAT:
+		return (ENOSYS);
+		break;
+
+	case A_SETSTAT:
+		return (ENOSYS);
+		break;
+
+	case A_SETUMASK:
+		return (ENOSYS);
+		break;
+
+	case A_SETSMASK:
+		return (ENOSYS);
+		break;
+
+	case A_GETCOND:
+		if (audit_enabled && !audit_suspended)
+			udata.au_cond = AUC_AUDITING;
+		else
+			udata.au_cond = AUC_NOAUDIT;
+		break;
+
+	case A_SETCOND:
+		if (udata.au_cond == AUC_NOAUDIT)
+			audit_suspended = 1;
+		if (udata.au_cond == AUC_AUDITING)
+			audit_suspended = 0;
+		if (udata.au_cond == AUC_DISABLED) {
+			audit_suspended = 1;
+			audit_shutdown(NULL, 0);
+		}
+		break;
+
+	case A_GETCLASS:
+		udata.au_evclass.ec_class = au_event_class(
+		    udata.au_evclass.ec_number);
+		break;
+
+	case A_SETCLASS:
+		au_evclassmap_insert(udata.au_evclass.ec_number,
+		    udata.au_evclass.ec_class);
+		break;
+
+	case A_GETPINFO:
+		if (udata.au_aupinfo.ap_pid < 1)
+			return (EINVAL);
+		if ((tp = pfind(udata.au_aupinfo.ap_pid)) == NULL)
+			return (EINVAL);
+		if (p_cansee(td, tp) != 0) {
+			PROC_UNLOCK(tp);
+			return (EINVAL);
+		}
+		if (tp->p_ucred->cr_audit.ai_termid.at_type == AU_IPv6) {
+			PROC_UNLOCK(tp);
+			return (EINVAL);
+		}
+		udata.au_aupinfo.ap_auid =
+		    tp->p_ucred->cr_audit.ai_auid;
+		udata.au_aupinfo.ap_mask.am_success =
+		    tp->p_ucred->cr_audit.ai_mask.am_success;
+		udata.au_aupinfo.ap_mask.am_failure =
+		    tp->p_ucred->cr_audit.ai_mask.am_failure;
+		udata.au_aupinfo.ap_termid.machine =
+		    tp->p_ucred->cr_audit.ai_termid.at_addr[0];
+		udata.au_aupinfo.ap_termid.port =
+		    (dev_t)tp->p_ucred->cr_audit.ai_termid.at_port;
+		udata.au_aupinfo.ap_asid =
+		    tp->p_ucred->cr_audit.ai_asid;
+		PROC_UNLOCK(tp);
+		break;
+
+	case A_SETPMASK:
+		if (udata.au_aupinfo.ap_pid < 1)
+			return (EINVAL);
+		newcred = crget();
+		if ((tp = pfind(udata.au_aupinfo.ap_pid)) == NULL) {
+			crfree(newcred);
+			return (EINVAL);
+		}
+		if (p_cansee(td, tp) != 0) {
+			PROC_UNLOCK(tp);
+			crfree(newcred);
+			return (EINVAL);
+		}
+		oldcred = tp->p_ucred;
+		crcopy(newcred, oldcred);
+		newcred->cr_audit.ai_mask.am_success =
+		    udata.au_aupinfo.ap_mask.am_success;
+		newcred->cr_audit.ai_mask.am_failure =
+		    udata.au_aupinfo.ap_mask.am_failure;
+		td->td_proc->p_ucred = newcred;
+		PROC_UNLOCK(tp);
+		crfree(oldcred);
+		break;
+
+	case A_SETFSIZE:
+		if ((udata.au_fstat.af_filesz != 0) &&
+		   (udata.au_fstat.af_filesz < MIN_AUDIT_FILE_SIZE))
+			return (EINVAL);
+		audit_fstat.af_filesz = udata.au_fstat.af_filesz;
+		break;
+
+	case A_GETFSIZE:
+		udata.au_fstat.af_filesz = audit_fstat.af_filesz;
+		udata.au_fstat.af_currsz = audit_fstat.af_currsz;
+		break;
+
+	case A_GETPINFO_ADDR:
+		if (udata.au_aupinfo_addr.ap_pid < 1)
+			return (EINVAL);
+		if ((tp = pfind(udata.au_aupinfo_addr.ap_pid)) == NULL)
+			return (EINVAL);
+		udata.au_aupinfo_addr.ap_auid =
+		    tp->p_ucred->cr_audit.ai_auid;
+		udata.au_aupinfo_addr.ap_mask.am_success =
+		    tp->p_ucred->cr_audit.ai_mask.am_success;
+		udata.au_aupinfo_addr.ap_mask.am_failure =
+		    tp->p_ucred->cr_audit.ai_mask.am_failure;
+		udata.au_aupinfo_addr.ap_termid =
+		    tp->p_ucred->cr_audit.ai_termid;
+		udata.au_aupinfo_addr.ap_asid =
+		    tp->p_ucred->cr_audit.ai_asid;
+		PROC_UNLOCK(tp);
+		break;
+
+	case A_GETKAUDIT:
+		return (ENOSYS);
+		break;
+
+	case A_SETKAUDIT:
+		return (ENOSYS);
+		break;
+
+	case A_SENDTRIGGER:
+		if ((udata.au_trigger < AUDIT_TRIGGER_MIN) ||
+		    (udata.au_trigger > AUDIT_TRIGGER_MAX))
+			return (EINVAL);
+		return (send_trigger(udata.au_trigger));
+	}
+
+	/*
+	 * Copy data back to userspace for the GET comands.
+	 */
+	switch (uap->cmd) {
+	case A_GETPOLICY:
+	case A_GETKMASK:
+	case A_GETQCTRL:
+	case A_GETCWD:
+	case A_GETCAR:
+	case A_GETSTAT:
+	case A_GETCOND:
+	case A_GETCLASS:
+	case A_GETPINFO:
+	case A_GETFSIZE:
+	case A_GETPINFO_ADDR:
+	case A_GETKAUDIT:
+		error = copyout((void *)&udata, uap->data, uap->length);
+		if (error)
+			return (error);
+		break;
+	}
+
+	return (0);
+}
+
+/*
+ * System calls to manage the user audit information.
+ */
+/* ARGSUSED */
+int
+getauid(struct thread *td, struct getauid_args *uap)
+{
+	int error;
+
+	if (jailed(td->td_ucred))
+		return (ENOSYS);
+	error = priv_check(td, PRIV_AUDIT_GETAUDIT);
+	if (error)
+		return (error);
+	return (copyout(&td->td_ucred->cr_audit.ai_auid, uap->auid,
+	    sizeof(td->td_ucred->cr_audit.ai_auid)));
+}
+
+/* ARGSUSED */
+int
+setauid(struct thread *td, struct setauid_args *uap)
+{
+	struct ucred *newcred, *oldcred;
+	au_id_t id;
+	int error;
+
+	if (jailed(td->td_ucred))
+		return (ENOSYS);
+	error = copyin(uap->auid, &id, sizeof(id));
+	if (error)
+		return (error);
+	audit_arg_auid(id);
+	newcred = crget();
+	PROC_LOCK(td->td_proc);
+	oldcred = td->td_proc->p_ucred;
+	crcopy(newcred, oldcred);
+#ifdef MAC
+	error = mac_check_proc_setauid(oldcred, id);
+	if (error)
+		goto fail;
+#endif
+	error = priv_check_cred(oldcred, PRIV_AUDIT_SETAUDIT, 0);
+	if (error)
+		goto fail;
+	newcred->cr_audit.ai_auid = id;
+	td->td_proc->p_ucred = newcred;
+	PROC_UNLOCK(td->td_proc);
+	crfree(oldcred);
+	return (0);
+fail:
+	PROC_UNLOCK(td->td_proc);
+	crfree(newcred);
+	return (error);
+}
+
+/*
+ * System calls to get and set process audit information.
+ */
+/* ARGSUSED */
+int
+getaudit(struct thread *td, struct getaudit_args *uap)
+{
+	struct auditinfo ai;
+	int error;
+
+	if (jailed(td->td_ucred))
+		return (ENOSYS);
+	error = priv_check(td, PRIV_AUDIT_GETAUDIT);
+	if (error)
+		return (error);
+	if (td->td_ucred->cr_audit.ai_termid.at_type == AU_IPv6)
+		return (E2BIG);
+	bzero(&ai, sizeof(ai));
+	ai.ai_auid = td->td_ucred->cr_audit.ai_auid;
+	ai.ai_mask = td->td_ucred->cr_audit.ai_mask;
+	ai.ai_asid = td->td_ucred->cr_audit.ai_asid;
+	ai.ai_termid.machine = td->td_ucred->cr_audit.ai_termid.at_addr[0];
+	ai.ai_termid.port = td->td_ucred->cr_audit.ai_termid.at_port;
+	return (copyout(&ai, uap->auditinfo, sizeof(ai)));
+}
+
+/* ARGSUSED */
+int
+setaudit(struct thread *td, struct setaudit_args *uap)
+{
+	struct ucred *newcred, *oldcred;
+	struct auditinfo ai;
+	int error;
+
+	if (jailed(td->td_ucred))
+		return (ENOSYS);
+	error = copyin(uap->auditinfo, &ai, sizeof(ai));
+	if (error)
+		return (error);
+	audit_arg_auditinfo(&ai);
+	newcred = crget();
+	PROC_LOCK(td->td_proc);
+	oldcred = td->td_proc->p_ucred;
+	crcopy(newcred, oldcred);
+#ifdef MAC
+	error = mac_check_proc_setaudit(oldcred, &ai);
+	if (error)
+		goto fail;
+#endif
+	error = priv_check_cred(oldcred, PRIV_AUDIT_SETAUDIT, 0);
+	if (error)
+		goto fail;
+	bzero(&newcred->cr_audit, sizeof(newcred->cr_audit));
+	newcred->cr_audit.ai_auid = ai.ai_auid;
+	newcred->cr_audit.ai_mask = ai.ai_mask;
+	newcred->cr_audit.ai_asid = ai.ai_asid;
+	newcred->cr_audit.ai_termid.at_addr[0] = ai.ai_termid.machine;
+	newcred->cr_audit.ai_termid.at_port = ai.ai_termid.port;
+	newcred->cr_audit.ai_termid.at_type = AU_IPv4;
+	td->td_proc->p_ucred = newcred;
+	PROC_UNLOCK(td->td_proc);
+	crfree(oldcred);
+	return (0);
+fail:
+	PROC_UNLOCK(td->td_proc);
+	crfree(newcred);
+	return (error);
+}
+
+/* ARGSUSED */
+int
+getaudit_addr(struct thread *td, struct getaudit_addr_args *uap)
+{
+	int error;
+
+	if (jailed(td->td_ucred))
+		return (ENOSYS);
+	if (uap->length < sizeof(*uap->auditinfo_addr))
+		return (EOVERFLOW);
+	error = priv_check(td, PRIV_AUDIT_GETAUDIT);
+	if (error)
+		return (error);
+	return (copyout(&td->td_ucred->cr_audit, uap->auditinfo_addr,
+	    sizeof(*uap->auditinfo_addr)));
+}
+
+/* ARGSUSED */
+int
+setaudit_addr(struct thread *td, struct setaudit_addr_args *uap)
+{
+	struct ucred *newcred, *oldcred;
+	struct auditinfo_addr aia;
+	int error;
+
+	if (jailed(td->td_ucred))
+		return (ENOSYS);
+	error = copyin(uap->auditinfo_addr, &aia, sizeof(aia));
+	if (error)
+		return (error);
+	audit_arg_auditinfo_addr(&aia);
+	if (aia.ai_termid.at_type != AU_IPv6 &&
+	    aia.ai_termid.at_type != AU_IPv4)
+		return (EINVAL);
+	newcred = crget();
+	PROC_LOCK(td->td_proc);	
+	oldcred = td->td_proc->p_ucred;
+	crcopy(newcred, oldcred);
+#ifdef MAC
+	error = mac_check_proc_setaudit_addr(oldcred, &aia);
+	if (error)
+		goto fail;
+#endif
+	error = priv_check_cred(oldcred, PRIV_AUDIT_SETAUDIT, 0);
+	if (error)
+		goto fail;
+	newcred->cr_audit = aia;
+	td->td_proc->p_ucred = newcred;
+	PROC_UNLOCK(td->td_proc);
+	crfree(oldcred);
+	return (0);
+fail:
+	PROC_UNLOCK(td->td_proc);
+	crfree(newcred);
+	return (error);
+}
+
+/*
+ * Syscall to manage audit files.
+ */
+/* ARGSUSED */
+int
+auditctl(struct thread *td, struct auditctl_args *uap)
+{
+	struct nameidata nd;
+	struct ucred *cred;
+	struct vnode *vp;
+	int error = 0;
+	int flags, vfslocked;
+
+	if (jailed(td->td_ucred))
+		return (ENOSYS);
+	error = priv_check(td, PRIV_AUDIT_CONTROL);
+	if (error)
+		return (error);
+
+	vp = NULL;
+	cred = NULL;
+
+	/*
+	 * If a path is specified, open the replacement vnode, perform
+	 * validity checks, and grab another reference to the current
+	 * credential.
+	 *
+	 * On Darwin, a NULL path argument is also used to disable audit.
+	 */
+	if (uap->path == NULL)
+		return (EINVAL);
+
+	NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | MPSAFE | AUDITVNODE1,
+	    UIO_USERSPACE, uap->path, td);
+	flags = AUDIT_OPEN_FLAGS;
+	error = vn_open(&nd, &flags, 0, NULL);
+	if (error)
+		return (error);
+	vfslocked = NDHASGIANT(&nd);
+	vp = nd.ni_vp;
+#ifdef MAC
+	error = mac_check_system_auditctl(td->td_ucred, vp);
+	VOP_UNLOCK(vp, 0, td);
+	if (error) {
+		vn_close(vp, AUDIT_CLOSE_FLAGS, td->td_ucred, td);
+		VFS_UNLOCK_GIANT(vfslocked);
+		return (error);
+	}
+#else
+	VOP_UNLOCK(vp, 0, td);
+#endif
+	NDFREE(&nd, NDF_ONLY_PNBUF);
+	if (vp->v_type != VREG) {
+		vn_close(vp, AUDIT_CLOSE_FLAGS, td->td_ucred, td);
+		VFS_UNLOCK_GIANT(vfslocked);
+		return (EINVAL);
+	}
+	VFS_UNLOCK_GIANT(vfslocked);
+	cred = td->td_ucred;
+	crhold(cred);
+
+	/*
+	 * XXXAUDIT: Should audit_suspended actually be cleared by
+	 * audit_worker?
+	 */
+	audit_suspended = 0;
+
+	audit_rotate_vnode(cred, vp);
+
+	return (error);
+}
+
+#else /* !AUDIT */
 
 int
 audit(struct thread *td, struct audit_args *uap)
@@ -94,3 +750,4 @@
 
 	return (ENOSYS);
 }
+#endif /* AUDIT */
--- /dev/null
+++ sys/security/audit/audit_worker.c
@@ -0,0 +1,551 @@
+/*
+ * Copyright (c) 1999-2005 Apple Computer, Inc.
+ * Copyright (c) 2006 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/security/audit/audit_worker.c,v 1.16 2007/06/01 21:58:59 rwatson Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/condvar.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/filedesc.h>
+#include <sys/fcntl.h>
+#include <sys/ipc.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/domain.h>
+#include <sys/sysproto.h>
+#include <sys/sysent.h>
+#include <sys/systm.h>
+#include <sys/ucred.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+#include <sys/unistd.h>
+#include <sys/vnode.h>
+
+#include <bsm/audit.h>
+#include <bsm/audit_internal.h>
+#include <bsm/audit_kevents.h>
+
+#include <netinet/in.h>
+#include <netinet/in_pcb.h>
+
+#include <security/audit/audit.h>
+#include <security/audit/audit_private.h>
+
+#include <vm/uma.h>
+
+/*
+ * Worker thread that will schedule disk I/O, etc.
+ */
+static struct proc		*audit_thread;
+
+/*
+ * When an audit log is rotated, the actual rotation must be performed by the
+ * audit worker thread, as it may have outstanding writes on the current
+ * audit log.  audit_replacement_vp holds the vnode replacing the current
+ * vnode.  We can't let more than one replacement occur at a time, so if more
+ * than one thread requests a replacement, only one can have the replacement
+ * "in progress" at any given moment.  If a thread tries to replace the audit
+ * vnode and discovers a replacement is already in progress (i.e.,
+ * audit_replacement_flag != 0), then it will sleep on audit_replacement_cv
+ * waiting its turn to perform a replacement.  When a replacement is
+ * completed, this cv is signalled by the worker thread so a waiting thread
+ * can start another replacement.  We also store a credential to perform
+ * audit log write operations with.
+ *
+ * The current credential and vnode are thread-local to audit_worker.
+ */
+static struct cv		audit_replacement_cv;
+
+static int			audit_replacement_flag;
+static struct vnode		*audit_replacement_vp;
+static struct ucred		*audit_replacement_cred;
+
+/*
+ * Flags related to Kernel->user-space communication.
+ */
+static int			audit_file_rotate_wait;
+
+/*
+ * Write an audit record to a file, performed as the last stage after both
+ * preselection and BSM conversion.  Both space management and write failures
+ * are handled in this function.
+ *
+ * No attempt is made to deal with possible failure to deliver a trigger to
+ * the audit daemon, since the message is asynchronous anyway.
+ */
+static void
+audit_record_write(struct vnode *vp, struct ucred *cred, struct thread *td,
+    void *data, size_t len)
+{
+	static struct timeval last_lowspace_trigger;
+	static struct timeval last_fail;
+	static int cur_lowspace_trigger;
+	struct statfs *mnt_stat;
+	int error, vfslocked;
+	static int cur_fail;
+	struct vattr vattr;
+	long temp;
+
+	if (vp == NULL)
+		return;
+
+ 	mnt_stat = &vp->v_mount->mnt_stat;
+	vfslocked = VFS_LOCK_GIANT(vp->v_mount);
+
+	/*
+	 * First, gather statistics on the audit log file and file system so
+	 * that we know how we're doing on space.  Consider failure of these
+	 * operations to indicate a future inability to write to the file.
+	 */
+	error = VFS_STATFS(vp->v_mount, mnt_stat, td);
+	if (error)
+		goto fail;
+	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
+	error = VOP_GETATTR(vp, &vattr, cred, td);
+	VOP_UNLOCK(vp, 0, td);
+	if (error)
+		goto fail;
+	audit_fstat.af_currsz = vattr.va_size;
+
+	/*
+	 * We handle four different space-related limits:
+	 *
+	 * - A fixed (hard) limit on the minimum free blocks we require on
+	 *   the file system, and results in record loss, a trigger, and
+	 *   possible fail stop due to violating invariants.
+	 *
+	 * - An administrative (soft) limit, which when fallen below, results
+	 *   in the kernel notifying the audit daemon of low space.
+	 *
+	 * - An audit trail size limit, which when gone above, results in the
+	 *   kernel notifying the audit daemon that rotation is desired.
+	 *
+	 * - The total depth of the kernel audit record exceeding free space,
+	 *   which can lead to possible fail stop (with drain), in order to
+	 *   prevent violating invariants.  Failure here doesn't halt
+	 *   immediately, but prevents new records from being generated.
+	 *
+	 * Possibly, the last of these should be handled differently, always
+	 * allowing a full queue to be lost, rather than trying to prevent
+	 * loss.
+	 *
+	 * First, handle the hard limit, which generates a trigger and may
+	 * fail stop.  This is handled in the same manner as ENOSPC from
+	 * VOP_WRITE, and results in record loss.
+	 */
+	if (mnt_stat->f_bfree < AUDIT_HARD_LIMIT_FREE_BLOCKS) {
+		error = ENOSPC;
+		goto fail_enospc;
+	}
+
+	/*
+	 * Second, handle falling below the soft limit, if defined; we send
+	 * the daemon a trigger and continue processing the record.  Triggers
+	 * are limited to 1/sec.
+	 */
+	if (audit_qctrl.aq_minfree != 0) {
+		/*
+		 * XXXAUDIT: Check math and block size calculations here.
+		 */
+		temp = mnt_stat->f_blocks / (100 / audit_qctrl.aq_minfree);
+		if (mnt_stat->f_bfree < temp) {
+			if (ppsratecheck(&last_lowspace_trigger,
+			    &cur_lowspace_trigger, 1)) {
+				(void)send_trigger(AUDIT_TRIGGER_LOW_SPACE);
+				printf("Warning: audit space low\n");
+			}
+		}
+	}
+
+	/*
+	 * If the current file is getting full, generate a rotation trigger
+	 * to the daemon.  This is only approximate, which is fine as more
+	 * records may be generated before the daemon rotates the file.
+	 */
+	if ((audit_fstat.af_filesz != 0) && (audit_file_rotate_wait == 0) &&
+	    (vattr.va_size >= audit_fstat.af_filesz)) {
+		audit_file_rotate_wait = 1;
+		(void)send_trigger(AUDIT_TRIGGER_ROTATE_KERNEL);
+	}
+
+	/*
+	 * If the estimated amount of audit data in the audit event queue
+	 * (plus records allocated but not yet queued) has reached the amount
+	 * of free space on the disk, then we need to go into an audit fail
+	 * stop state, in which we do not permit the allocation/committing of
+	 * any new audit records.  We continue to process records but don't
+	 * allow any activities that might generate new records.  In the
+	 * future, we might want to detect when space is available again and
+	 * allow operation to continue, but this behavior is sufficient to
+	 * meet fail stop requirements in CAPP.
+	 */
+	if (audit_fail_stop) {
+		if ((unsigned long)((audit_q_len + audit_pre_q_len + 1) *
+		    MAX_AUDIT_RECORD_SIZE) / mnt_stat->f_bsize >=
+		    (unsigned long)(mnt_stat->f_bfree)) {
+			if (ppsratecheck(&last_fail, &cur_fail, 1))
+				printf("audit_record_write: free space "
+				    "below size of audit queue, failing "
+				    "stop\n");
+			audit_in_failure = 1;
+		} else if (audit_in_failure) {
+			/*
+			 * Note: if we want to handle recovery, this is the
+			 * spot to do it: unset audit_in_failure, and issue a
+			 * wakeup on the cv.
+			 */
+		}
+	}
+
+	error = vn_rdwr(UIO_WRITE, vp, data, len, (off_t)0, UIO_SYSSPACE,
+	    IO_APPEND|IO_UNIT, cred, NULL, NULL, td);
+	if (error == ENOSPC)
+		goto fail_enospc;
+	else if (error)
+		goto fail;
+
+	/*
+	 * Catch completion of a queue drain here; if we're draining and the
+	 * queue is now empty, fail stop.  That audit_fail_stop is implicitly
+	 * true, since audit_in_failure can only be set of audit_fail_stop is
+	 * set.
+	 *
+	 * Note: if we handle recovery from audit_in_failure, then we need to
+	 * make panic here conditional.
+	 */
+	if (audit_in_failure) {
+		if (audit_q_len == 0 && audit_pre_q_len == 0) {
+			VOP_LOCK(vp, LK_DRAIN | LK_INTERLOCK, td);
+			(void)VOP_FSYNC(vp, MNT_WAIT, td);
+			VOP_UNLOCK(vp, 0, td);
+			panic("Audit store overflow; record queue drained.");
+		}
+	}
+
+	VFS_UNLOCK_GIANT(vfslocked);
+	return;
+
+fail_enospc:
+	/*
+	 * ENOSPC is considered a special case with respect to failures, as
+	 * this can reflect either our preemptive detection of insufficient
+	 * space, or ENOSPC returned by the vnode write call.
+	 */
+	if (audit_fail_stop) {
+		VOP_LOCK(vp, LK_DRAIN | LK_INTERLOCK, td);
+		(void)VOP_FSYNC(vp, MNT_WAIT, td);
+		VOP_UNLOCK(vp, 0, td);
+		panic("Audit log space exhausted and fail-stop set.");
+	}
+	(void)send_trigger(AUDIT_TRIGGER_NO_SPACE);
+	audit_suspended = 1;
+
+	/* FALLTHROUGH */
+fail:
+	/*
+	 * We have failed to write to the file, so the current record is
+	 * lost, which may require an immediate system halt.
+	 */
+	if (audit_panic_on_write_fail) {
+		VOP_LOCK(vp, LK_DRAIN | LK_INTERLOCK, td);
+		(void)VOP_FSYNC(vp, MNT_WAIT, td);
+		VOP_UNLOCK(vp, 0, td);
+		panic("audit_worker: write error %d\n", error);
+	} else if (ppsratecheck(&last_fail, &cur_fail, 1))
+		printf("audit_worker: write error %d\n", error);
+	VFS_UNLOCK_GIANT(vfslocked);
+}
+
+/*
+ * If an appropriate signal has been received rotate the audit log based on
+ * the global replacement variables.  Signal consumers as needed that the
+ * rotation has taken place.
+ *
+ * The global variables and CVs used to signal the audit_worker to perform a
+ * rotation are essentially a message queue of depth 1.  It would be much
+ * nicer to actually use a message queue.
+ */
+static void
+audit_worker_rotate(struct ucred **audit_credp, struct vnode **audit_vpp,
+    struct thread *audit_td)
+{
+	int do_replacement_signal, vfslocked;
+	struct ucred *old_cred;
+	struct vnode *old_vp;
+
+	mtx_assert(&audit_mtx, MA_OWNED);
+
+	do_replacement_signal = 0;
+	while (audit_replacement_flag != 0) {
+		old_cred = *audit_credp;
+		old_vp = *audit_vpp;
+		*audit_credp = audit_replacement_cred;
+		*audit_vpp = audit_replacement_vp;
+		audit_replacement_cred = NULL;
+		audit_replacement_vp = NULL;
+		audit_replacement_flag = 0;
+
+		audit_enabled = (*audit_vpp != NULL);
+
+		if (old_vp != NULL) {
+			mtx_unlock(&audit_mtx);
+			vfslocked = VFS_LOCK_GIANT(old_vp->v_mount);
+			vn_close(old_vp, AUDIT_CLOSE_FLAGS, old_cred,
+			    audit_td);
+			VFS_UNLOCK_GIANT(vfslocked);
+			crfree(old_cred);
+			mtx_lock(&audit_mtx);
+			old_cred = NULL;
+			old_vp = NULL;
+		}
+		do_replacement_signal = 1;
+	}
+
+	/*
+	 * Signal that replacement have occurred to wake up and start any
+	 * other replacements started in parallel.  We can continue about our
+	 * business in the mean time.  We broadcast so that both new
+	 * replacements can be inserted, but also so that the source(s) of
+	 * replacement can return successfully.
+	 */
+	if (do_replacement_signal)
+		cv_broadcast(&audit_replacement_cv);
+}
+
+/*
+ * Given a kernel audit record, process as required.  Kernel audit records
+ * are converted to one, or possibly two, BSM records, depending on whether
+ * there is a user audit record present also.  Kernel records need be
+ * converted to BSM before they can be written out.  Both types will be
+ * written to disk, and audit pipes.
+ */
+static void
+audit_worker_process_record(struct vnode *audit_vp, struct ucred *audit_cred,
+    struct thread *audit_td, struct kaudit_record *ar)
+{
+	struct au_record *bsm;
+	au_class_t class;
+	au_event_t event;
+	au_id_t auid;
+	int error, sorf;
+
+	/*
+	 * First, handle the user record, if any: commit to the system trail
+	 * and audit pipes as selected.
+	 */
+	if ((ar->k_ar_commit & AR_COMMIT_USER) &&
+	    (ar->k_ar_commit & AR_PRESELECT_USER_TRAIL))
+		audit_record_write(audit_vp, audit_cred, audit_td,
+		    ar->k_udata, ar->k_ulen);
+
+	if ((ar->k_ar_commit & AR_COMMIT_USER) &&
+	    (ar->k_ar_commit & AR_PRESELECT_USER_PIPE))
+		audit_pipe_submit_user(ar->k_udata, ar->k_ulen);
+
+	if (!(ar->k_ar_commit & AR_COMMIT_KERNEL) ||
+	    ((ar->k_ar_commit & AR_PRESELECT_PIPE) == 0 &&
+	    (ar->k_ar_commit & AR_PRESELECT_TRAIL) == 0))
+		return;
+
+	auid = ar->k_ar.ar_subj_auid;
+	event = ar->k_ar.ar_event;
+	class = au_event_class(event);
+	if (ar->k_ar.ar_errno == 0)
+		sorf = AU_PRS_SUCCESS;
+	else
+		sorf = AU_PRS_FAILURE;
+
+	error = kaudit_to_bsm(ar, &bsm);
+	switch (error) {
+	case BSM_NOAUDIT:
+		return;
+
+	case BSM_FAILURE:
+		printf("audit_worker_process_record: BSM_FAILURE\n");
+		return;
+
+	case BSM_SUCCESS:
+		break;
+
+	default:
+		panic("kaudit_to_bsm returned %d", error);
+	}
+
+	if (ar->k_ar_commit & AR_PRESELECT_TRAIL)
+		audit_record_write(audit_vp, audit_cred, audit_td, bsm->data,
+		    bsm->len);
+
+	if (ar->k_ar_commit & AR_PRESELECT_PIPE)
+		audit_pipe_submit(auid, event, class, sorf,
+		    ar->k_ar_commit & AR_PRESELECT_TRAIL, bsm->data,
+		    bsm->len);
+
+	kau_free(bsm);
+}
+
+/*
+ * The audit_worker thread is responsible for watching the event queue,
+ * dequeueing records, converting them to BSM format, and committing them to
+ * disk.  In order to minimize lock thrashing, records are dequeued in sets
+ * to a thread-local work queue.  In addition, the audit_work performs the
+ * actual exchange of audit log vnode pointer, as audit_vp is a thread-local
+ * variable.
+ */
+static void
+audit_worker(void *arg)
+{
+	struct kaudit_queue ar_worklist;
+	struct kaudit_record *ar;
+	struct ucred *audit_cred;
+	struct thread *audit_td;
+	struct vnode *audit_vp;
+	int lowater_signal;
+
+	/*
+	 * These are thread-local variables requiring no synchronization.
+	 */
+	TAILQ_INIT(&ar_worklist);
+	audit_cred = NULL;
+	audit_td = curthread;
+	audit_vp = NULL;
+
+	mtx_lock(&audit_mtx);
+	while (1) {
+		mtx_assert(&audit_mtx, MA_OWNED);
+
+		/*
+		 * Wait for record or rotation events.
+		 */
+		while (!audit_replacement_flag && TAILQ_EMPTY(&audit_q))
+			cv_wait(&audit_worker_cv, &audit_mtx);
+
+		/*
+		 * First priority: replace the audit log target if requested.
+		 */
+		audit_worker_rotate(&audit_cred, &audit_vp, audit_td);
+
+		/*
+		 * If there are records in the global audit record queue,
+		 * transfer them to a thread-local queue and process them
+		 * one by one.  If we cross the low watermark threshold,
+		 * signal any waiting processes that they may wake up and
+		 * continue generating records.
+		 */
+		lowater_signal = 0;
+		while ((ar = TAILQ_FIRST(&audit_q))) {
+			TAILQ_REMOVE(&audit_q, ar, k_q);
+			audit_q_len--;
+			if (audit_q_len == audit_qctrl.aq_lowater)
+				lowater_signal++;
+			TAILQ_INSERT_TAIL(&ar_worklist, ar, k_q);
+		}
+		if (lowater_signal)
+			cv_broadcast(&audit_watermark_cv);
+
+		mtx_unlock(&audit_mtx);
+		while ((ar = TAILQ_FIRST(&ar_worklist))) {
+			TAILQ_REMOVE(&ar_worklist, ar, k_q);
+			audit_worker_process_record(audit_vp, audit_cred,
+			    audit_td, ar);
+			audit_free(ar);
+		}
+		mtx_lock(&audit_mtx);
+	}
+}
+
+/*
+ * audit_rotate_vnode() is called by a user or kernel thread to configure or
+ * de-configure auditing on a vnode.  The arguments are the replacement
+ * credential and vnode to substitute for the current credential and vnode,
+ * if any.  If either is set to NULL, both should be NULL, and this is used
+ * to indicate that audit is being disabled.  The real work is done in the
+ * audit_worker thread, but audit_rotate_vnode() waits synchronously for that
+ * to complete.
+ *
+ * The vnode should be referenced and opened by the caller.  The credential
+ * should be referenced.  audit_rotate_vnode() will own both references as of
+ * this call, so the caller should not release either.
+ *
+ * XXXAUDIT: Review synchronize communication logic.  Really, this is a
+ * message queue of depth 1.  We are essentially acquiring ownership of the
+ * communications queue, inserting our message, and waiting for an
+ * acknowledgement.
+ */
+void
+audit_rotate_vnode(struct ucred *cred, struct vnode *vp)
+{
+
+	/*
+	 * If other parallel log replacements have been requested, we wait
+	 * until they've finished before continuing.
+	 */
+	mtx_lock(&audit_mtx);
+	while (audit_replacement_flag != 0)
+		cv_wait(&audit_replacement_cv, &audit_mtx);
+	audit_replacement_cred = cred;
+	audit_replacement_flag = 1;
+	audit_replacement_vp = vp;
+
+	/*
+	 * Wake up the audit worker to perform the exchange once we release
+	 * the mutex.
+	 */
+	cv_signal(&audit_worker_cv);
+
+	/*
+	 * Wait for the audit_worker to broadcast that a replacement has
+	 * taken place; we know that once this has happened, our vnode has
+	 * been replaced in, so we can return successfully.
+	 */
+	cv_wait(&audit_replacement_cv, &audit_mtx);
+	audit_file_rotate_wait = 0; /* We can now request another rotation */
+	mtx_unlock(&audit_mtx);
+}
+
+void
+audit_worker_init(void)
+{
+	int error;
+
+	cv_init(&audit_replacement_cv, "audit_replacement_cv");
+	error = kthread_create(audit_worker, NULL, &audit_thread, RFHIGHPID,
+	    0, "audit");
+	if (error)
+		panic("audit_worker_init: kthread_create returned %d", error);
+}
--- /dev/null
+++ sys/security/audit/audit_bsm.c
@@ -0,0 +1,1447 @@
+/*
+ * Copyright (c) 1999-2005 Apple Computer, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/security/audit/audit_bsm.c,v 1.20.2.1.2.1 2008/02/05 14:36:41 csjp Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/vnode.h>
+#include <sys/ipc.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/socket.h>
+#include <sys/extattr.h>
+#include <sys/fcntl.h>
+#include <sys/user.h>
+#include <sys/systm.h>
+
+#include <bsm/audit.h>
+#include <bsm/audit_internal.h>
+#include <bsm/audit_record.h>
+#include <bsm/audit_kevents.h>
+
+#include <security/audit/audit.h>
+#include <security/audit/audit_private.h>
+
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+
+MALLOC_DEFINE(M_AUDITBSM, "audit_bsm", "Audit BSM data");
+
+static void	audit_sys_auditon(struct audit_record *ar,
+		    struct au_record *rec);
+
+/*
+ * Initialize the BSM auditing subsystem.
+ */
+void
+kau_init(void)
+{
+
+	au_evclassmap_init();
+}
+
+/*
+ * This call reserves memory for the audit record.  Memory must be guaranteed
+ * before any auditable event can be generated.  The au_record structure
+ * maintains a reference to the memory allocated above and also the list of
+ * tokens associated with this record
+ */
+static struct au_record *
+kau_open(void)
+{
+	struct au_record *rec;
+
+	rec = malloc(sizeof(*rec), M_AUDITBSM, M_WAITOK);
+	rec->data = NULL;
+	TAILQ_INIT(&rec->token_q);
+	rec->len = 0;
+	rec->used = 1;
+
+	return (rec);
+}
+
+/*
+ * Store the token with the record descriptor.
+ */
+static void
+kau_write(struct au_record *rec, struct au_token *tok)
+{
+
+	KASSERT(tok != NULL, ("kau_write: tok == NULL"));
+
+	TAILQ_INSERT_TAIL(&rec->token_q, tok, tokens);
+	rec->len += tok->len;
+}
+
+/*
+ * Close out the audit record by adding the header token, identifying any
+ * missing tokens.  Write out the tokens to the record memory.
+ */
+static void
+kau_close(struct au_record *rec, struct timespec *ctime, short event)
+{
+	u_char *dptr;
+	size_t tot_rec_size;
+	token_t *cur, *hdr, *trail;
+	struct timeval tm;
+
+	tot_rec_size = rec->len + AUDIT_HEADER_SIZE + AUDIT_TRAILER_SIZE;
+	rec->data = malloc(tot_rec_size, M_AUDITBSM, M_WAITOK | M_ZERO);
+
+	tm.tv_usec = ctime->tv_nsec / 1000;
+	tm.tv_sec = ctime->tv_sec;
+	hdr = au_to_header32_tm(tot_rec_size, event, 0, tm);
+	TAILQ_INSERT_HEAD(&rec->token_q, hdr, tokens);
+
+	trail = au_to_trailer(tot_rec_size);
+	TAILQ_INSERT_TAIL(&rec->token_q, trail, tokens);
+
+	rec->len = tot_rec_size;
+	dptr = rec->data;
+	TAILQ_FOREACH(cur, &rec->token_q, tokens) {
+		memcpy(dptr, cur->t_data, cur->len);
+		dptr += cur->len;
+	}
+}
+
+/*
+ * Free a BSM audit record by releasing all the tokens and clearing the audit
+ * record information.
+ */
+void
+kau_free(struct au_record *rec)
+{
+	struct au_token *tok;
+
+	/* Free the token list. */
+	while ((tok = TAILQ_FIRST(&rec->token_q))) {
+		TAILQ_REMOVE(&rec->token_q, tok, tokens);
+		free(tok->t_data, M_AUDITBSM);
+		free(tok, M_AUDITBSM);
+	}
+
+	rec->used = 0;
+	rec->len = 0;
+	free(rec->data, M_AUDITBSM);
+	free(rec, M_AUDITBSM);
+}
+
+/*
+ * XXX: May want turn some (or all) of these macros into functions in order
+ * to reduce the generated code sized.
+ *
+ * XXXAUDIT: These macros assume that 'kar', 'ar', 'rec', and 'tok' in the
+ * caller are OK with this.
+ */
+#define UPATH1_TOKENS do {						\
+	if (ARG_IS_VALID(kar, ARG_UPATH1)) {				\
+		tok = au_to_path(ar->ar_arg_upath1);			\
+		kau_write(rec, tok);					\
+	}								\
+} while (0)
+
+#define UPATH2_TOKENS do {						\
+	if (ARG_IS_VALID(kar, ARG_UPATH2)) {				\
+		tok = au_to_path(ar->ar_arg_upath2);			\
+		kau_write(rec, tok);					\
+	}								\
+} while (0)
+
+#define VNODE1_TOKENS do {						\
+	if (ARG_IS_VALID(kar, ARG_VNODE1)) {  				\
+		tok = au_to_attr32(&ar->ar_arg_vnode1);			\
+		kau_write(rec, tok);					\
+	}								\
+} while (0)
+
+#define UPATH1_VNODE1_TOKENS do {					\
+	if (ARG_IS_VALID(kar, ARG_UPATH1)) {  				\
+		UPATH1_TOKENS;						\
+	}								\
+	if (ARG_IS_VALID(kar, ARG_VNODE1)) {  				\
+		tok = au_to_attr32(&ar->ar_arg_vnode1);			\
+		kau_write(rec, tok);					\
+	}								\
+} while (0)
+
+#define VNODE2_TOKENS do {						\
+	if (ARG_IS_VALID(kar, ARG_VNODE2)) {  				\
+		tok = au_to_attr32(&ar->ar_arg_vnode2);			\
+		kau_write(rec, tok);					\
+	}								\
+} while (0)
+
+#define FD_VNODE1_TOKENS	do {					\
+	if (ARG_IS_VALID(kar, ARG_VNODE1)) {				\
+		if (ARG_IS_VALID(kar, ARG_FD)) {			\
+			tok = au_to_arg32(1, "fd", ar->ar_arg_fd);	\
+			kau_write(rec, tok);				\
+		}							\
+		tok = au_to_attr32(&ar->ar_arg_vnode1);			\
+		kau_write(rec, tok);					\
+	} else {							\
+		if (ARG_IS_VALID(kar, ARG_FD)) {			\
+			tok = au_to_arg32(1, "non-file: fd",		\
+			    ar->ar_arg_fd);				\
+			kau_write(rec, tok);				\
+		}							\
+	}								\
+} while (0)
+
+#define PROCESS_PID_TOKENS(argn) do {					\
+	if ((ar->ar_arg_pid > 0) /* Reference a single process */	\
+	    && (ARG_IS_VALID(kar, ARG_PROCESS))) {			\
+		tok = au_to_process32_ex(ar->ar_arg_auid,		\
+		    ar->ar_arg_euid, ar->ar_arg_egid,			\
+		    ar->ar_arg_ruid, ar->ar_arg_rgid,			\
+		    ar->ar_arg_pid, ar->ar_arg_asid,			\
+		    &ar->ar_arg_termid_addr);				\
+		kau_write(rec, tok);					\
+	} else if (ARG_IS_VALID(kar, ARG_PID)) {			\
+		tok = au_to_arg32(argn, "process", ar->ar_arg_pid);	\
+		kau_write(rec, tok);					\
+	}								\
+} while (0)								\
+
+#define EXTATTR_TOKENS	do {						\
+	if (ARG_IS_VALID(kar, ARG_VALUE)) {				\
+		switch (ar->ar_arg_value) {				\
+		case EXTATTR_NAMESPACE_USER:				\
+			tok = au_to_text(EXTATTR_NAMESPACE_USER_STRING);\
+			break;						\
+		case EXTATTR_NAMESPACE_SYSTEM:				\
+			tok = au_to_text(EXTATTR_NAMESPACE_SYSTEM_STRING);\
+			break;						\
+		default:						\
+			tok = au_to_arg32(3, "attrnamespace",		\
+			    ar->ar_arg_value);				\
+			break;						\
+		}							\
+		kau_write(rec, tok);					\
+	}								\
+	/* attrname is in the text field */				\
+	if (ARG_IS_VALID(kar, ARG_TEXT)) {				\
+		tok = au_to_text(ar->ar_arg_text);			\
+		kau_write(rec, tok);					\
+	}								\
+} while (0)
+
+/*
+ * Implement auditing for the auditon() system call. The audit tokens that
+ * are generated depend on the command that was sent into the auditon()
+ * system call.
+ */
+static void
+audit_sys_auditon(struct audit_record *ar, struct au_record *rec)
+{
+	struct au_token *tok;
+
+	switch (ar->ar_arg_cmd) {
+	case A_SETPOLICY:
+		if (sizeof(ar->ar_arg_auditon.au_flags) > 4)
+			tok = au_to_arg64(1, "policy",
+			    ar->ar_arg_auditon.au_flags);
+		else
+			tok = au_to_arg32(1, "policy",
+			    ar->ar_arg_auditon.au_flags);
+		kau_write(rec, tok);
+		break;
+
+	case A_SETKMASK:
+		tok = au_to_arg32(2, "setkmask:as_success",
+		    ar->ar_arg_auditon.au_mask.am_success);
+		kau_write(rec, tok);
+		tok = au_to_arg32(2, "setkmask:as_failure",
+		    ar->ar_arg_auditon.au_mask.am_failure);
+		kau_write(rec, tok);
+		break;
+
+	case A_SETQCTRL:
+		tok = au_to_arg32(3, "setqctrl:aq_hiwater",
+		    ar->ar_arg_auditon.au_qctrl.aq_hiwater);
+		kau_write(rec, tok);
+		tok = au_to_arg32(3, "setqctrl:aq_lowater",
+		    ar->ar_arg_auditon.au_qctrl.aq_lowater);
+		kau_write(rec, tok);
+		tok = au_to_arg32(3, "setqctrl:aq_bufsz",
+		    ar->ar_arg_auditon.au_qctrl.aq_bufsz);
+		kau_write(rec, tok);
+		tok = au_to_arg32(3, "setqctrl:aq_delay",
+		    ar->ar_arg_auditon.au_qctrl.aq_delay);
+		kau_write(rec, tok);
+		tok = au_to_arg32(3, "setqctrl:aq_minfree",
+		    ar->ar_arg_auditon.au_qctrl.aq_minfree);
+		kau_write(rec, tok);
+		break;
+
+	case A_SETUMASK:
+		tok = au_to_arg32(3, "setumask:as_success",
+		    ar->ar_arg_auditon.au_auinfo.ai_mask.am_success);
+		kau_write(rec, tok);
+		tok = au_to_arg32(3, "setumask:as_failure",
+		    ar->ar_arg_auditon.au_auinfo.ai_mask.am_failure);
+		kau_write(rec, tok);
+		break;
+
+	case A_SETSMASK:
+		tok = au_to_arg32(3, "setsmask:as_success",
+		    ar->ar_arg_auditon.au_auinfo.ai_mask.am_success);
+		kau_write(rec, tok);
+		tok = au_to_arg32(3, "setsmask:as_failure",
+		    ar->ar_arg_auditon.au_auinfo.ai_mask.am_failure);
+		kau_write(rec, tok);
+		break;
+
+	case A_SETCOND:
+		if (sizeof(ar->ar_arg_auditon.au_cond) > 4)
+			tok = au_to_arg64(3, "setcond",
+			    ar->ar_arg_auditon.au_cond);
+		else
+			tok = au_to_arg32(3, "setcond",
+			    ar->ar_arg_auditon.au_cond);
+		kau_write(rec, tok);
+		break;
+
+	case A_SETCLASS:
+		tok = au_to_arg32(2, "setclass:ec_event",
+		    ar->ar_arg_auditon.au_evclass.ec_number);
+		kau_write(rec, tok);
+		tok = au_to_arg32(3, "setclass:ec_class",
+		    ar->ar_arg_auditon.au_evclass.ec_class);
+		kau_write(rec, tok);
+		break;
+
+	case A_SETPMASK:
+		tok = au_to_arg32(2, "setpmask:as_success",
+		    ar->ar_arg_auditon.au_aupinfo.ap_mask.am_success);
+		kau_write(rec, tok);
+		tok = au_to_arg32(2, "setpmask:as_failure",
+		    ar->ar_arg_auditon.au_aupinfo.ap_mask.am_failure);
+		kau_write(rec, tok);
+		break;
+
+	case A_SETFSIZE:
+		tok = au_to_arg32(2, "setfsize:filesize",
+		    ar->ar_arg_auditon.au_fstat.af_filesz);
+		kau_write(rec, tok);
+		break;
+
+	default:
+		break;
+	}
+}
+
+/*
+ * Convert an internal kernel audit record to a BSM record and return a
+ * success/failure indicator. The BSM record is passed as an out parameter to
+ * this function.
+ *
+ * Return conditions:
+ *   BSM_SUCCESS: The BSM record is valid
+ *   BSM_FAILURE: Failure; the BSM record is NULL.
+ *   BSM_NOAUDIT: The event is not auditable for BSM; the BSM record is NULL.
+ */
+int
+kaudit_to_bsm(struct kaudit_record *kar, struct au_record **pau)
+{
+	struct au_token *tok, *subj_tok;
+	struct au_record *rec;
+	au_tid_t tid;
+	struct audit_record *ar;
+	int ctr;
+
+	KASSERT(kar != NULL, ("kaudit_to_bsm: kar == NULL"));
+
+	*pau = NULL;
+	ar = &kar->k_ar;
+	rec = kau_open();
+
+	/* Create the subject token */
+	switch (ar->ar_subj_term_addr.at_type) {
+	case AU_IPv4:
+		tid.port = ar->ar_subj_term_addr.at_port;
+		tid.machine = ar->ar_subj_term_addr.at_addr[0];
+		subj_tok = au_to_subject32(ar->ar_subj_auid,  /* audit ID */
+		    ar->ar_subj_cred.cr_uid, /* eff uid */
+		    ar->ar_subj_egid,	/* eff group id */
+		    ar->ar_subj_ruid, 	/* real uid */
+		    ar->ar_subj_rgid, 	/* real group id */
+		    ar->ar_subj_pid,	/* process id */
+		    ar->ar_subj_asid,	/* session ID */
+		    &tid);
+		break;
+	case AU_IPv6:
+		subj_tok = au_to_subject32_ex(ar->ar_subj_auid,
+		    ar->ar_subj_cred.cr_uid,
+		    ar->ar_subj_egid,
+		    ar->ar_subj_ruid,
+		    ar->ar_subj_rgid,
+		    ar->ar_subj_pid,
+		    ar->ar_subj_asid,
+		    &ar->ar_subj_term_addr);
+		break;
+	default:
+		bzero(&tid, sizeof(tid));
+		subj_tok = au_to_subject32(ar->ar_subj_auid,
+		    ar->ar_subj_cred.cr_uid,
+		    ar->ar_subj_egid,
+		    ar->ar_subj_ruid,
+		    ar->ar_subj_rgid,
+		    ar->ar_subj_pid,
+		    ar->ar_subj_asid,
+		    &tid);
+	}
+
+	/*
+	 * The logic inside each case fills in the tokens required for the
+	 * event, except for the header, trailer, and return tokens.  The
+	 * header and trailer tokens are added by the kau_close() function.
+	 * The return token is added outside of the switch statement.
+	 */
+	switch(ar->ar_event) {
+	case AUE_ACCEPT:
+	case AUE_BIND:
+	case AUE_LISTEN:
+	case AUE_CONNECT:
+	case AUE_RECV:
+	case AUE_RECVFROM:
+	case AUE_RECVMSG:
+	case AUE_SEND:
+	case AUE_SENDFILE:
+	case AUE_SENDMSG:
+	case AUE_SENDTO:
+		/*
+		 * Socket-related events.
+		 */
+		if (ARG_IS_VALID(kar, ARG_FD)) {
+			tok = au_to_arg32(1, "fd", ar->ar_arg_fd);
+			kau_write(rec, tok);
+		}
+		if (ARG_IS_VALID(kar, ARG_SADDRINET)) {
+			tok = au_to_sock_inet((struct sockaddr_in *)
+			    &ar->ar_arg_sockaddr);
+			kau_write(rec, tok);
+		}
+		if (ARG_IS_VALID(kar, ARG_SADDRUNIX)) {
+			tok = au_to_sock_unix((struct sockaddr_un *)
+			    &ar->ar_arg_sockaddr);
+			kau_write(rec, tok);
+			UPATH1_TOKENS;
+		}
+		/* XXX Need to handle ARG_SADDRINET6 */
+		break;
+
+	case AUE_SOCKET:
+	case AUE_SOCKETPAIR:
+		if (ARG_IS_VALID(kar, ARG_SOCKINFO)) {
+			tok = au_to_arg32(1,"domain",
+			    ar->ar_arg_sockinfo.so_domain);
+			kau_write(rec, tok);
+			tok = au_to_arg32(2,"type",
+			    ar->ar_arg_sockinfo.so_type);
+			kau_write(rec, tok);
+			tok = au_to_arg32(3,"protocol",
+			    ar->ar_arg_sockinfo.so_protocol);
+			kau_write(rec, tok);
+		}
+		break;
+
+	case AUE_SETSOCKOPT:
+	case AUE_SHUTDOWN:
+		if (ARG_IS_VALID(kar, ARG_FD)) {
+			tok = au_to_arg32(1, "fd", ar->ar_arg_fd);
+			kau_write(rec, tok);
+		}
+		break;
+
+	case AUE_ACCT:
+		if (ARG_IS_VALID(kar, ARG_UPATH1)) {
+			UPATH1_VNODE1_TOKENS;
+		} else {
+			tok = au_to_arg32(1, "accounting off", 0);
+			kau_write(rec, tok);
+		}
+		break;
+
+	case AUE_SETAUID:
+		if (ARG_IS_VALID(kar, ARG_AUID)) {
+			tok = au_to_arg32(2, "setauid", ar->ar_arg_auid);
+			kau_write(rec, tok);
+		}
+		break;
+
+	case AUE_SETAUDIT:
+		if (ARG_IS_VALID(kar, ARG_AUID) &&
+		    ARG_IS_VALID(kar, ARG_ASID) &&
+		    ARG_IS_VALID(kar, ARG_AMASK) &&
+		    ARG_IS_VALID(kar, ARG_TERMID)) {
+			tok = au_to_arg32(1, "setaudit:auid",
+			    ar->ar_arg_auid);
+			kau_write(rec, tok);
+			tok = au_to_arg32(1, "setaudit:port",
+			    ar->ar_arg_termid.port);
+			kau_write(rec, tok);
+			tok = au_to_arg32(1, "setaudit:machine",
+			    ar->ar_arg_termid.machine);
+			kau_write(rec, tok);
+			tok = au_to_arg32(1, "setaudit:as_success",
+			    ar->ar_arg_amask.am_success);
+			kau_write(rec, tok);
+			tok = au_to_arg32(1, "setaudit:as_failure",
+			    ar->ar_arg_amask.am_failure);
+			kau_write(rec, tok);
+			tok = au_to_arg32(1, "setaudit:asid",
+			    ar->ar_arg_asid);
+			kau_write(rec, tok);
+		}
+		break;
+
+	case AUE_SETAUDIT_ADDR:
+		if (ARG_IS_VALID(kar, ARG_AUID) &&
+		    ARG_IS_VALID(kar, ARG_ASID) &&
+		    ARG_IS_VALID(kar, ARG_AMASK) &&
+		    ARG_IS_VALID(kar, ARG_TERMID_ADDR)) {
+			tok = au_to_arg32(1, "setaudit_addr:auid",
+			    ar->ar_arg_auid);
+			kau_write(rec, tok);
+			tok = au_to_arg32(1, "setaudit_addr:as_success",
+			    ar->ar_arg_amask.am_success);
+			kau_write(rec, tok);
+			tok = au_to_arg32(1, "setaudit_addr:as_failure",
+			    ar->ar_arg_amask.am_failure);
+			kau_write(rec, tok);
+			tok = au_to_arg32(1, "setaudit_addr:asid",
+			    ar->ar_arg_asid);
+			kau_write(rec, tok);
+			tok = au_to_arg32(1, "setaudit_addr:type",
+			    ar->ar_arg_termid_addr.at_type);
+			kau_write(rec, tok);
+			tok = au_to_arg32(1, "setaudit_addr:port",
+			    ar->ar_arg_termid_addr.at_port);
+			kau_write(rec, tok);
+			if (ar->ar_arg_termid_addr.at_type == AU_IPv6)
+				tok = au_to_in_addr_ex((struct in6_addr *)
+				    &ar->ar_arg_termid_addr.at_addr[0]);
+			if (ar->ar_arg_termid_addr.at_type == AU_IPv4)
+				tok = au_to_in_addr((struct in_addr *)
+				    &ar->ar_arg_termid_addr.at_addr[0]);
+			kau_write(rec, tok);
+		}
+		break;
+
+	case AUE_AUDITON:
+		/*
+		 * For AUDITON commands without own event, audit the cmd.
+		 */
+		if (ARG_IS_VALID(kar, ARG_CMD)) {
+			tok = au_to_arg32(1, "cmd", ar->ar_arg_cmd);
+			kau_write(rec, tok);
+		}
+		/* fall thru */
+
+	case AUE_AUDITON_GETCAR:
+	case AUE_AUDITON_GETCLASS:
+	case AUE_AUDITON_GETCOND:
+	case AUE_AUDITON_GETCWD:
+	case AUE_AUDITON_GETKMASK:
+	case AUE_AUDITON_GETSTAT:
+	case AUE_AUDITON_GPOLICY:
+	case AUE_AUDITON_GQCTRL:
+	case AUE_AUDITON_SETCLASS:
+	case AUE_AUDITON_SETCOND:
+	case AUE_AUDITON_SETKMASK:
+	case AUE_AUDITON_SETSMASK:
+	case AUE_AUDITON_SETSTAT:
+	case AUE_AUDITON_SETUMASK:
+	case AUE_AUDITON_SPOLICY:
+	case AUE_AUDITON_SQCTRL:
+		if (ARG_IS_VALID(kar, ARG_AUDITON))
+			audit_sys_auditon(ar, rec);
+		break;
+
+	case AUE_AUDITCTL:
+		UPATH1_VNODE1_TOKENS;
+		break;
+
+	case AUE_EXIT:
+		if (ARG_IS_VALID(kar, ARG_EXIT)) {
+			tok = au_to_exit(ar->ar_arg_exitretval,
+			    ar->ar_arg_exitstatus);
+			kau_write(rec, tok);
+		}
+		break;
+
+	case AUE_ADJTIME:
+	case AUE_CLOCK_SETTIME:
+	case AUE_AUDIT:
+	case AUE_DUP2:
+	case AUE_GETAUDIT:
+	case AUE_GETAUDIT_ADDR:
+	case AUE_GETAUID:
+	case AUE_GETCWD:
+	case AUE_GETFSSTAT:
+	case AUE_GETRESUID:
+	case AUE_GETRESGID:
+	case AUE_KQUEUE:
+	case AUE_LSEEK:
+	case AUE_MODLOAD:
+	case AUE_MODUNLOAD:
+	case AUE_MSGSYS:
+	case AUE_NFS_SVC:
+	case AUE_NTP_ADJTIME:
+	case AUE_PIPE:
+	case AUE_PROFILE:
+	case AUE_RTPRIO:
+	case AUE_SEMSYS:
+	case AUE_SHMSYS:
+	case AUE_SETPGRP:
+	case AUE_SETRLIMIT:
+	case AUE_SETSID:
+	case AUE_SETTIMEOFDAY:
+	case AUE_SYSARCH:
+
+		/*
+		 * Header, subject, and return tokens added at end.
+		 */
+		break;
+
+	case AUE_MKFIFO:
+		if (ARG_IS_VALID(kar, ARG_MODE)) {
+			tok = au_to_arg32(2, "mode", ar->ar_arg_mode);
+			kau_write(rec, tok);
+		}
+		/* fall through */
+	case AUE_ACCESS:
+	case AUE_CHDIR:
+	case AUE_CHROOT:
+	case AUE_EACCESS:
+	case AUE_GETATTRLIST:
+	case AUE_JAIL:
+	case AUE_LUTIMES:
+	case AUE_NFS_GETFH:
+	case AUE_LSTAT:
+	case AUE_PATHCONF:
+	case AUE_READLINK:
+	case AUE_REVOKE:
+	case AUE_RMDIR:
+	case AUE_SEARCHFS:
+	case AUE_SETATTRLIST:
+	case AUE_STAT:
+	case AUE_STATFS:
+	case AUE_SWAPON:
+	case AUE_SWAPOFF:
+	case AUE_TRUNCATE:
+	case AUE_UNDELETE:
+	case AUE_UNLINK:
+	case AUE_UTIMES:
+		UPATH1_VNODE1_TOKENS;
+		break;
+
+	case AUE_FHSTATFS:
+	case AUE_FHOPEN:
+	case AUE_FHSTAT:
+		/* XXXRW: Need to audit vnode argument. */
+		break;
+
+	case AUE_CHFLAGS:
+	case AUE_LCHFLAGS:
+		if (ARG_IS_VALID(kar, ARG_FFLAGS)) {
+			tok = au_to_arg32(2, "flags", ar->ar_arg_fflags);
+			kau_write(rec, tok);
+		}
+		UPATH1_VNODE1_TOKENS;
+		break;
+
+	case AUE_CHMOD:
+	case AUE_LCHMOD:
+		if (ARG_IS_VALID(kar, ARG_MODE)) {
+			tok = au_to_arg32(2, "new file mode",
+			    ar->ar_arg_mode);
+			kau_write(rec, tok);
+		}
+		UPATH1_VNODE1_TOKENS;
+		break;
+
+	case AUE_CHOWN:
+	case AUE_LCHOWN:
+		if (ARG_IS_VALID(kar, ARG_UID)) {
+			tok = au_to_arg32(2, "new file uid", ar->ar_arg_uid);
+			kau_write(rec, tok);
+		}
+		if (ARG_IS_VALID(kar, ARG_GID)) {
+			tok = au_to_arg32(3, "new file gid", ar->ar_arg_gid);
+			kau_write(rec, tok);
+		}
+		UPATH1_VNODE1_TOKENS;
+		break;
+
+	case AUE_EXCHANGEDATA:
+		UPATH1_VNODE1_TOKENS;
+		UPATH2_TOKENS;
+		break;
+
+	case AUE_CLOSE:
+		if (ARG_IS_VALID(kar, ARG_FD)) {
+			tok = au_to_arg32(2, "fd", ar->ar_arg_fd);
+			kau_write(rec, tok);
+		}
+		UPATH1_VNODE1_TOKENS;
+		break;
+
+	case AUE_EXTATTRCTL:
+		UPATH1_VNODE1_TOKENS;
+		if (ARG_IS_VALID(kar, ARG_CMD)) {
+			tok = au_to_arg32(2, "cmd", ar->ar_arg_cmd);
+			kau_write(rec, tok);
+		}
+		/* extattrctl(2) filename parameter is in upath2/vnode2 */
+		UPATH2_TOKENS;
+		VNODE2_TOKENS;
+		EXTATTR_TOKENS;
+		break;
+
+	case AUE_EXTATTR_GET_FILE:
+	case AUE_EXTATTR_SET_FILE:
+	case AUE_EXTATTR_LIST_FILE:
+	case AUE_EXTATTR_DELETE_FILE:
+	case AUE_EXTATTR_GET_LINK:
+	case AUE_EXTATTR_SET_LINK:
+	case AUE_EXTATTR_LIST_LINK:
+	case AUE_EXTATTR_DELETE_LINK:
+		UPATH1_VNODE1_TOKENS;
+		EXTATTR_TOKENS;
+		break;
+
+	case AUE_EXTATTR_GET_FD:
+	case AUE_EXTATTR_SET_FD:
+	case AUE_EXTATTR_LIST_FD:
+	case AUE_EXTATTR_DELETE_FD:
+		if (ARG_IS_VALID(kar, ARG_FD)) {
+			tok = au_to_arg32(2, "fd", ar->ar_arg_fd);
+			kau_write(rec, tok);
+		}
+		EXTATTR_TOKENS;
+		break;
+
+	case AUE_EXECVE:
+		if (ARG_IS_VALID(kar, ARG_ARGV)) {
+			tok = au_to_exec_args(ar->ar_arg_argv,
+			    ar->ar_arg_argc);
+			kau_write(rec, tok);
+		}
+		if (ARG_IS_VALID(kar, ARG_ENVV)) {
+			tok = au_to_exec_env(ar->ar_arg_envv,
+			    ar->ar_arg_envc);
+			kau_write(rec, tok);
+		}
+		UPATH1_VNODE1_TOKENS;
+		break;
+
+	case AUE_FCHMOD:
+		if (ARG_IS_VALID(kar, ARG_MODE)) {
+			tok = au_to_arg32(2, "new file mode",
+			    ar->ar_arg_mode);
+			kau_write(rec, tok);
+		}
+		FD_VNODE1_TOKENS;
+		break;
+
+	/*
+	 * XXXRW: Some of these need to handle non-vnode cases as well.
+	 */
+	case AUE_FCHDIR:
+	case AUE_FPATHCONF:
+	case AUE_FSTAT:
+	case AUE_FSTATFS:
+	case AUE_FSYNC:
+	case AUE_FTRUNCATE:
+	case AUE_FUTIMES:
+	case AUE_GETDIRENTRIES:
+	case AUE_GETDIRENTRIESATTR:
+	case AUE_POLL:
+	case AUE_READ:
+	case AUE_READV:
+	case AUE_WRITE:
+	case AUE_WRITEV:
+		FD_VNODE1_TOKENS;
+		break;
+
+	case AUE_FCHOWN:
+		if (ARG_IS_VALID(kar, ARG_UID)) {
+			tok = au_to_arg32(2, "new file uid", ar->ar_arg_uid);
+			kau_write(rec, tok);
+		}
+		if (ARG_IS_VALID(kar, ARG_GID)) {
+			tok = au_to_arg32(3, "new file gid", ar->ar_arg_gid);
+			kau_write(rec, tok);
+		}
+		FD_VNODE1_TOKENS;
+		break;
+
+	case AUE_FCNTL:
+		if (ar->ar_arg_cmd == F_GETLK || ar->ar_arg_cmd == F_SETLK ||
+			ar->ar_arg_cmd == F_SETLKW) {
+			if (ARG_IS_VALID(kar, ARG_CMD)) {
+				tok = au_to_arg32(2, "cmd", ar->ar_arg_cmd);
+				kau_write(rec, tok);
+			}
+			FD_VNODE1_TOKENS;
+		}
+		break;
+
+	case AUE_FCHFLAGS:
+		if (ARG_IS_VALID(kar, ARG_FFLAGS)) {
+			tok = au_to_arg32(2, "flags", ar->ar_arg_fflags);
+			kau_write(rec, tok);
+		}
+		FD_VNODE1_TOKENS;
+		break;
+
+	case AUE_FLOCK:
+		if (ARG_IS_VALID(kar, ARG_CMD)) {
+			tok = au_to_arg32(2, "operation", ar->ar_arg_cmd);
+			kau_write(rec, tok);
+		}
+		FD_VNODE1_TOKENS;
+		break;
+
+	case AUE_RFORK:
+		if (ARG_IS_VALID(kar, ARG_FFLAGS)) {
+			tok = au_to_arg32(1, "flags", ar->ar_arg_fflags);
+			kau_write(rec, tok);
+		}
+		/* fall through */
+	case AUE_FORK:
+	case AUE_VFORK:
+		if (ARG_IS_VALID(kar, ARG_PID)) {
+			tok = au_to_arg32(0, "child PID", ar->ar_arg_pid);
+			kau_write(rec, tok);
+		}
+		break;
+
+	case AUE_IOCTL:
+		if (ARG_IS_VALID(kar, ARG_CMD)) {
+			tok = au_to_arg32(2, "cmd", ar->ar_arg_cmd);
+			kau_write(rec, tok);
+		}
+		if (ARG_IS_VALID(kar, ARG_ADDR)) {
+			tok = au_to_arg32(1, "arg",
+			    (u_int32_t)(uintptr_t)ar->ar_arg_addr);
+			kau_write(rec, tok);
+		}
+		if (ARG_IS_VALID(kar, ARG_VNODE1))
+			FD_VNODE1_TOKENS;
+		else {
+			if (ARG_IS_VALID(kar, ARG_SOCKINFO)) {
+				tok = kau_to_socket(&ar->ar_arg_sockinfo);
+				kau_write(rec, tok);
+			} else {
+				if (ARG_IS_VALID(kar, ARG_FD)) {
+					tok = au_to_arg32(1, "fd",
+					    ar->ar_arg_fd);
+			    		kau_write(rec, tok);
+				}
+			}
+		}
+		break;
+
+	case AUE_KILL:
+	case AUE_KILLPG:
+		if (ARG_IS_VALID(kar, ARG_SIGNUM)) {
+			tok = au_to_arg32(2, "signal", ar->ar_arg_signum);
+			kau_write(rec, tok);
+		}
+		PROCESS_PID_TOKENS(1);
+		break;
+
+	case AUE_KTRACE:
+		if (ARG_IS_VALID(kar, ARG_CMD)) {
+			tok = au_to_arg32(2, "ops", ar->ar_arg_cmd);
+			kau_write(rec, tok);
+		}
+		if (ARG_IS_VALID(kar, ARG_VALUE)) {
+			tok = au_to_arg32(3, "trpoints", ar->ar_arg_value);
+			kau_write(rec, tok);
+		}
+		PROCESS_PID_TOKENS(4);
+		UPATH1_VNODE1_TOKENS;
+		break;
+
+	case AUE_LINK:
+	case AUE_RENAME:
+		UPATH1_VNODE1_TOKENS;
+		UPATH2_TOKENS;
+		break;
+
+	case AUE_LOADSHFILE:
+		if (ARG_IS_VALID(kar, ARG_ADDR)) {
+			tok = au_to_arg32(4, "base addr",
+			    (u_int32_t)(uintptr_t)ar->ar_arg_addr);
+			kau_write(rec, tok);
+		}
+		UPATH1_VNODE1_TOKENS;
+		break;
+
+	case AUE_MKDIR:
+		if (ARG_IS_VALID(kar, ARG_MODE)) {
+			tok = au_to_arg32(2, "mode", ar->ar_arg_mode);
+			kau_write(rec, tok);
+		}
+		UPATH1_VNODE1_TOKENS;
+		break;
+
+	case AUE_MKNOD:
+		if (ARG_IS_VALID(kar, ARG_MODE)) {
+			tok = au_to_arg32(2, "mode", ar->ar_arg_mode);
+			kau_write(rec, tok);
+		}
+		if (ARG_IS_VALID(kar, ARG_DEV)) {
+			tok = au_to_arg32(3, "dev", ar->ar_arg_dev);
+			kau_write(rec, tok);
+		}
+		UPATH1_VNODE1_TOKENS;
+		break;
+
+	case AUE_MMAP:
+	case AUE_MUNMAP:
+	case AUE_MPROTECT:
+	case AUE_MLOCK:
+	case AUE_MUNLOCK:
+	case AUE_MINHERIT:
+		if (ARG_IS_VALID(kar, ARG_ADDR)) {
+			tok = au_to_arg32(1, "addr",
+			    (u_int32_t)(uintptr_t)ar->ar_arg_addr);
+			kau_write(rec, tok);
+		}
+		if (ARG_IS_VALID(kar, ARG_LEN)) {
+			tok = au_to_arg32(2, "len", ar->ar_arg_len);
+			kau_write(rec, tok);
+		}
+		if (ar->ar_event == AUE_MMAP)
+			FD_VNODE1_TOKENS;
+		if (ar->ar_event == AUE_MPROTECT) {
+			if (ARG_IS_VALID(kar, ARG_VALUE)) {
+				tok = au_to_arg32(3, "protection",
+				    ar->ar_arg_value);
+				kau_write(rec, tok);
+			}
+		}
+		if (ar->ar_event == AUE_MINHERIT) {
+			if (ARG_IS_VALID(kar, ARG_VALUE)) {
+				tok = au_to_arg32(3, "inherit",
+				    ar->ar_arg_value);
+				kau_write(rec, tok);
+			}
+		}
+		break;
+
+	case AUE_MOUNT:
+	case AUE_NMOUNT:
+		/* XXX Need to handle NFS mounts */
+		if (ARG_IS_VALID(kar, ARG_FFLAGS)) {
+			tok = au_to_arg32(3, "flags", ar->ar_arg_fflags);
+			kau_write(rec, tok);
+		}
+		if (ARG_IS_VALID(kar, ARG_TEXT)) {
+			tok = au_to_text(ar->ar_arg_text);
+			kau_write(rec, tok);
+		}
+		/* fall through */
+
+	case AUE_UMOUNT:
+		UPATH1_VNODE1_TOKENS;
+		break;
+
+	case AUE_MSGCTL:
+		ar->ar_event = msgctl_to_event(ar->ar_arg_svipc_cmd);
+		/* Fall through */
+
+	case AUE_MSGRCV:
+	case AUE_MSGSND:
+		tok = au_to_arg32(1, "msg ID", ar->ar_arg_svipc_id);
+		kau_write(rec, tok);
+		if (ar->ar_errno != EINVAL) {
+			tok = au_to_ipc(AT_IPC_MSG, ar->ar_arg_svipc_id);
+			kau_write(rec, tok);
+		}
+		break;
+
+	case AUE_MSGGET:
+		if (ar->ar_errno == 0) {
+			if (ARG_IS_VALID(kar, ARG_SVIPC_ID)) {
+				tok = au_to_ipc(AT_IPC_MSG,
+				    ar->ar_arg_svipc_id);
+				kau_write(rec, tok);
+			}
+		}
+		break;
+
+	case AUE_RESETSHFILE:
+		if (ARG_IS_VALID(kar, ARG_ADDR)) {
+			tok = au_to_arg32(1, "base addr",
+			    (u_int32_t)(uintptr_t)ar->ar_arg_addr);
+			kau_write(rec, tok);
+		}
+		break;
+
+	case AUE_OPEN_RC:
+	case AUE_OPEN_RTC:
+	case AUE_OPEN_RWC:
+	case AUE_OPEN_RWTC:
+	case AUE_OPEN_WC:
+	case AUE_OPEN_WTC:
+	case AUE_CREAT:
+		if (ARG_IS_VALID(kar, ARG_MODE)) {
+			tok = au_to_arg32(3, "mode", ar->ar_arg_mode);
+			kau_write(rec, tok);
+		}
+		/* fall through */
+
+	case AUE_OPEN_R:
+	case AUE_OPEN_RT:
+	case AUE_OPEN_RW:
+	case AUE_OPEN_RWT:
+	case AUE_OPEN_W:
+	case AUE_OPEN_WT:
+		if (ARG_IS_VALID(kar, ARG_FFLAGS)) {
+			tok = au_to_arg32(2, "flags", ar->ar_arg_fflags);
+			kau_write(rec, tok);
+		}
+		UPATH1_VNODE1_TOKENS;
+		break;
+
+	case AUE_PTRACE:
+		if (ARG_IS_VALID(kar, ARG_CMD)) {
+			tok = au_to_arg32(1, "request", ar->ar_arg_cmd);
+			kau_write(rec, tok);
+		}
+		if (ARG_IS_VALID(kar, ARG_ADDR)) {
+			tok = au_to_arg32(3, "addr",
+			    (u_int32_t)(uintptr_t)ar->ar_arg_addr);
+			kau_write(rec, tok);
+		}
+		if (ARG_IS_VALID(kar, ARG_VALUE)) {
+			tok = au_to_arg32(4, "data", ar->ar_arg_value);
+			kau_write(rec, tok);
+		}
+		PROCESS_PID_TOKENS(2);
+		break;
+
+	case AUE_QUOTACTL:
+		if (ARG_IS_VALID(kar, ARG_CMD)) {
+			tok = au_to_arg32(2, "command", ar->ar_arg_cmd);
+			kau_write(rec, tok);
+		}
+		if (ARG_IS_VALID(kar, ARG_UID)) {
+			tok = au_to_arg32(3, "uid", ar->ar_arg_uid);
+			kau_write(rec, tok);
+		}
+		UPATH1_VNODE1_TOKENS;
+		break;
+
+	case AUE_REBOOT:
+		if (ARG_IS_VALID(kar, ARG_CMD)) {
+			tok = au_to_arg32(1, "howto", ar->ar_arg_cmd);
+			kau_write(rec, tok);
+		}
+		break;
+
+	case AUE_SEMCTL:
+		ar->ar_event = semctl_to_event(ar->ar_arg_svipc_cmd);
+		/* Fall through */
+
+	case AUE_SEMOP:
+		if (ARG_IS_VALID(kar, ARG_SVIPC_ID)) {
+			tok = au_to_arg32(1, "sem ID", ar->ar_arg_svipc_id);
+			kau_write(rec, tok);
+			if (ar->ar_errno != EINVAL) {
+				tok = au_to_ipc(AT_IPC_SEM,
+				    ar->ar_arg_svipc_id);
+				kau_write(rec, tok);
+			}
+		}
+		break;
+
+	case AUE_SEMGET:
+		if (ar->ar_errno == 0) {
+			if (ARG_IS_VALID(kar, ARG_SVIPC_ID)) {
+				tok = au_to_ipc(AT_IPC_SEM,
+				    ar->ar_arg_svipc_id);
+				kau_write(rec, tok);
+			}
+		}
+		break;
+
+	case AUE_SETEGID:
+		if (ARG_IS_VALID(kar, ARG_EGID)) {
+			tok = au_to_arg32(1, "gid", ar->ar_arg_egid);
+			kau_write(rec, tok);
+		}
+		break;
+
+	case AUE_SETEUID:
+		if (ARG_IS_VALID(kar, ARG_EUID)) {
+			tok = au_to_arg32(1, "uid", ar->ar_arg_euid);
+			kau_write(rec, tok);
+		}
+		break;
+
+	case AUE_SETREGID:
+		if (ARG_IS_VALID(kar, ARG_RGID)) {
+			tok = au_to_arg32(1, "rgid", ar->ar_arg_rgid);
+			kau_write(rec, tok);
+		}
+		if (ARG_IS_VALID(kar, ARG_EGID)) {
+			tok = au_to_arg32(2, "egid", ar->ar_arg_egid);
+			kau_write(rec, tok);
+		}
+		break;
+
+	case AUE_SETREUID:
+		if (ARG_IS_VALID(kar, ARG_RUID)) {
+			tok = au_to_arg32(1, "ruid", ar->ar_arg_ruid);
+			kau_write(rec, tok);
+		}
+		if (ARG_IS_VALID(kar, ARG_EUID)) {
+			tok = au_to_arg32(2, "euid", ar->ar_arg_euid);
+			kau_write(rec, tok);
+		}
+		break;
+
+	case AUE_SETRESGID:
+		if (ARG_IS_VALID(kar, ARG_RGID)) {
+			tok = au_to_arg32(1, "rgid", ar->ar_arg_rgid);
+			kau_write(rec, tok);
+		}
+		if (ARG_IS_VALID(kar, ARG_EGID)) {
+			tok = au_to_arg32(2, "egid", ar->ar_arg_egid);
+			kau_write(rec, tok);
+		}
+		if (ARG_IS_VALID(kar, ARG_SGID)) {
+			tok = au_to_arg32(3, "sgid", ar->ar_arg_sgid);
+			kau_write(rec, tok);
+		}
+		break;
+
+	case AUE_SETRESUID:
+		if (ARG_IS_VALID(kar, ARG_RUID)) {
+			tok = au_to_arg32(1, "ruid", ar->ar_arg_ruid);
+			kau_write(rec, tok);
+		}
+		if (ARG_IS_VALID(kar, ARG_EUID)) {
+			tok = au_to_arg32(2, "euid", ar->ar_arg_euid);
+			kau_write(rec, tok);
+		}
+		if (ARG_IS_VALID(kar, ARG_SUID)) {
+			tok = au_to_arg32(3, "suid", ar->ar_arg_suid);
+			kau_write(rec, tok);
+		}
+		break;
+
+	case AUE_SETGID:
+		if (ARG_IS_VALID(kar, ARG_GID)) {
+			tok = au_to_arg32(1, "gid", ar->ar_arg_gid);
+			kau_write(rec, tok);
+		}
+		break;
+
+	case AUE_SETUID:
+		if (ARG_IS_VALID(kar, ARG_UID)) {
+			tok = au_to_arg32(1, "uid", ar->ar_arg_uid);
+			kau_write(rec, tok);
+		}
+		break;
+
+	case AUE_SETGROUPS:
+		if (ARG_IS_VALID(kar, ARG_GROUPSET)) {
+			for(ctr = 0; ctr < ar->ar_arg_groups.gidset_size; ctr++)
+			{
+				tok = au_to_arg32(1, "setgroups", 							ar->ar_arg_groups.gidset[ctr]);
+				kau_write(rec, tok);
+			}
+		}
+		break;
+
+	case AUE_SETLOGIN:
+		if (ARG_IS_VALID(kar, ARG_TEXT)) {
+			tok = au_to_text(ar->ar_arg_text);
+			kau_write(rec, tok);
+		}
+		break;
+
+	case AUE_SETPRIORITY:
+		if (ARG_IS_VALID(kar, ARG_CMD)) {
+			tok = au_to_arg32(1, "which", ar->ar_arg_cmd);
+			kau_write(rec, tok);
+		}
+		if (ARG_IS_VALID(kar, ARG_UID)) {
+			tok = au_to_arg32(2, "who", ar->ar_arg_uid);
+			kau_write(rec, tok);
+		}
+		if (ARG_IS_VALID(kar, ARG_VALUE)) {
+			tok = au_to_arg32(2, "priority", ar->ar_arg_value);
+			kau_write(rec, tok);
+		}
+		break;
+
+	case AUE_SETPRIVEXEC:
+		if (ARG_IS_VALID(kar, ARG_VALUE)) {
+			tok = au_to_arg32(1, "flag", ar->ar_arg_value);
+			kau_write(rec, tok);
+		}
+		break;
+
+	/* AUE_SHMAT, AUE_SHMCTL, AUE_SHMDT and AUE_SHMGET are SysV IPC */
+	case AUE_SHMAT:
+		if (ARG_IS_VALID(kar, ARG_SVIPC_ID)) {
+			tok = au_to_arg32(1, "shmid", ar->ar_arg_svipc_id);
+			kau_write(rec, tok);
+			/* XXXAUDIT: Does having the ipc token make sense? */
+			tok = au_to_ipc(AT_IPC_SHM, ar->ar_arg_svipc_id);
+			kau_write(rec, tok);
+		}
+		if (ARG_IS_VALID(kar, ARG_SVIPC_ADDR)) {
+			tok = au_to_arg32(2, "shmaddr",
+			    (int)(uintptr_t)ar->ar_arg_svipc_addr);
+			kau_write(rec, tok);
+		}
+		if (ARG_IS_VALID(kar, ARG_SVIPC_PERM)) {
+			tok = au_to_ipc_perm(&ar->ar_arg_svipc_perm);
+			kau_write(rec, tok);
+		}
+		break;
+
+	case AUE_SHMCTL:
+		if (ARG_IS_VALID(kar, ARG_SVIPC_ID)) {
+			tok = au_to_arg32(1, "shmid", ar->ar_arg_svipc_id);
+			kau_write(rec, tok);
+			/* XXXAUDIT: Does having the ipc token make sense? */
+			tok = au_to_ipc(AT_IPC_SHM, ar->ar_arg_svipc_id);
+			kau_write(rec, tok);
+		}
+		switch (ar->ar_arg_svipc_cmd) {
+		case IPC_STAT:
+			ar->ar_event = AUE_SHMCTL_STAT;
+			break;
+		case IPC_RMID:
+			ar->ar_event = AUE_SHMCTL_RMID;
+			break;
+		case IPC_SET:
+			ar->ar_event = AUE_SHMCTL_SET;
+			if (ARG_IS_VALID(kar, ARG_SVIPC_PERM)) {
+				tok = au_to_ipc_perm(&ar->ar_arg_svipc_perm);
+				kau_write(rec, tok);
+			}
+			break;
+		default:
+			break;	/* We will audit a bad command */
+		}
+		break;
+
+	case AUE_SHMDT:
+		if (ARG_IS_VALID(kar, ARG_SVIPC_ADDR)) {
+			tok = au_to_arg32(1, "shmaddr",
+			    (int)(uintptr_t)ar->ar_arg_svipc_addr);
+			kau_write(rec, tok);
+		}
+		break;
+
+	case AUE_SHMGET:
+		/* This is unusual; the return value is in an argument token */
+		if (ARG_IS_VALID(kar, ARG_SVIPC_ID)) {
+			tok = au_to_arg32(0, "shmid", ar->ar_arg_svipc_id);
+			kau_write(rec, tok);
+			tok = au_to_ipc(AT_IPC_SHM, ar->ar_arg_svipc_id);
+			kau_write(rec, tok);
+		}
+		if (ARG_IS_VALID(kar, ARG_SVIPC_PERM)) {
+			tok = au_to_ipc_perm(&ar->ar_arg_svipc_perm);
+			kau_write(rec, tok);
+		}
+		break;
+
+	/* AUE_SHMOPEN, AUE_SHMUNLINK, AUE_SEMOPEN, AUE_SEMCLOSE
+	 * and AUE_SEMUNLINK are Posix IPC */
+	case AUE_SHMOPEN:
+		if (ARG_IS_VALID(kar, ARG_SVIPC_ADDR)) {
+			tok = au_to_arg32(2, "flags", ar->ar_arg_fflags);
+			kau_write(rec, tok);
+		}
+		if (ARG_IS_VALID(kar, ARG_MODE)) {
+			tok = au_to_arg32(3, "mode", ar->ar_arg_mode);
+			kau_write(rec, tok);
+		}
+	case AUE_SHMUNLINK:
+		if (ARG_IS_VALID(kar, ARG_TEXT)) {
+			tok = au_to_text(ar->ar_arg_text);
+			kau_write(rec, tok);
+		}
+		if (ARG_IS_VALID(kar, ARG_POSIX_IPC_PERM)) {
+		/* Create an ipc_perm token */
+			struct ipc_perm perm;
+			perm.uid = ar->ar_arg_pipc_perm.pipc_uid;
+			perm.gid = ar->ar_arg_pipc_perm.pipc_gid;
+			perm.cuid = ar->ar_arg_pipc_perm.pipc_uid;
+			perm.cgid = ar->ar_arg_pipc_perm.pipc_gid;
+			perm.mode = ar->ar_arg_pipc_perm.pipc_mode;
+			perm.seq = 0;
+			perm.key = 0;
+			tok = au_to_ipc_perm(&perm);
+			kau_write(rec, tok);
+		}
+		break;
+
+	case AUE_SEMOPEN:
+		if (ARG_IS_VALID(kar, ARG_FFLAGS)) {
+			tok = au_to_arg32(2, "flags", ar->ar_arg_fflags);
+			kau_write(rec, tok);
+		}
+		if (ARG_IS_VALID(kar, ARG_MODE)) {
+			tok = au_to_arg32(3, "mode", ar->ar_arg_mode);
+			kau_write(rec, tok);
+		}
+		if (ARG_IS_VALID(kar, ARG_VALUE)) {
+			tok = au_to_arg32(4, "value", ar->ar_arg_value);
+			kau_write(rec, tok);
+		}
+		/* fall through */
+
+	case AUE_SEMUNLINK:
+		if (ARG_IS_VALID(kar, ARG_TEXT)) {
+			tok = au_to_text(ar->ar_arg_text);
+			kau_write(rec, tok);
+		}
+		if (ARG_IS_VALID(kar, ARG_POSIX_IPC_PERM)) {
+		/* Create an ipc_perm token */
+			struct ipc_perm perm;
+			perm.uid = ar->ar_arg_pipc_perm.pipc_uid;
+			perm.gid = ar->ar_arg_pipc_perm.pipc_gid;
+			perm.cuid = ar->ar_arg_pipc_perm.pipc_uid;
+			perm.cgid = ar->ar_arg_pipc_perm.pipc_gid;
+			perm.mode = ar->ar_arg_pipc_perm.pipc_mode;
+			perm.seq = 0;
+			perm.key = 0;
+			tok = au_to_ipc_perm(&perm);
+			kau_write(rec, tok);
+		}
+		break;
+
+	case AUE_SEMCLOSE:
+		if (ARG_IS_VALID(kar, ARG_FD)) {
+			tok = au_to_arg32(1, "sem", ar->ar_arg_fd);
+			kau_write(rec, tok);
+		}
+		break;
+
+	case AUE_SYMLINK:
+		if (ARG_IS_VALID(kar, ARG_TEXT)) {
+			tok = au_to_text(ar->ar_arg_text);
+			kau_write(rec, tok);
+		}
+		UPATH1_VNODE1_TOKENS;
+		break;
+
+	case AUE_SYSCTL:
+		if (ARG_IS_VALID(kar, ARG_CTLNAME | ARG_LEN)) {
+			for (ctr = 0; ctr < ar->ar_arg_len; ctr++) {
+				tok = au_to_arg32(1, "name",
+				    ar->ar_arg_ctlname[ctr]);
+				kau_write(rec, tok);
+			}
+		}
+		if (ARG_IS_VALID(kar, ARG_VALUE)) {
+			tok = au_to_arg32(5, "newval", ar->ar_arg_value);
+			kau_write(rec, tok);
+		}
+		if (ARG_IS_VALID(kar, ARG_TEXT)) {
+			tok = au_to_text(ar->ar_arg_text);
+			kau_write(rec, tok);
+		}
+		break;
+
+	case AUE_UMASK:
+		if (ARG_IS_VALID(kar, ARG_MASK)) {
+			tok = au_to_arg32(1, "new mask", ar->ar_arg_mask);
+			kau_write(rec, tok);
+		}
+		tok = au_to_arg32(0, "prev mask", ar->ar_retval);
+		kau_write(rec, tok);
+		break;
+
+	case AUE_WAIT4:
+		if (ARG_IS_VALID(kar, ARG_PID)) {
+			tok = au_to_arg32(0, "pid", ar->ar_arg_pid);
+			kau_write(rec, tok);
+		}
+		break;
+
+	case AUE_NULL:
+	default:
+		printf("BSM conversion requested for unknown event %d\n",
+		    ar->ar_event);
+		/* Write the subject token so it is properly freed here. */
+		kau_write(rec, subj_tok);
+		kau_free(rec);
+		return (BSM_NOAUDIT);
+	}
+
+	kau_write(rec, subj_tok);
+	tok = au_to_return32((char)ar->ar_errno, ar->ar_retval);
+	kau_write(rec, tok);  /* Every record gets a return token */
+
+	kau_close(rec, &ar->ar_endtime, ar->ar_event);
+
+	*pau = rec;
+	return (BSM_SUCCESS);
+}
+
+/*
+ * Verify that a record is a valid BSM record. This verification is simple
+ * now, but may be expanded on sometime in the future.  Return 1 if the
+ * record is good, 0 otherwise.
+ */
+int
+bsm_rec_verify(void *rec)
+{
+	char c = *(char *)rec;
+
+	/*
+	 * Check the token ID of the first token; it has to be a header
+	 * token.
+	 *
+	 * XXXAUDIT There needs to be a token structure to map a token.
+	 * XXXAUDIT 'Shouldn't be simply looking at the first char.
+	 */
+	if ((c != AUT_HEADER32) && (c != AUT_HEADER32_EX) &&
+	    (c != AUT_HEADER64) && (c != AUT_HEADER64_EX))
+		return (0);
+	return (1);
+}
--- /dev/null
+++ sys/security/audit/audit_arg.c
@@ -0,0 +1,856 @@
+/*
+ * Copyright (c) 1999-2005 Apple Computer, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/security/audit/audit_arg.c,v 1.15 2007/06/27 17:01:14 csjp Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/filedesc.h>
+#include <sys/ipc.h>
+#include <sys/mount.h>
+#include <sys/proc.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/domain.h>
+#include <sys/sbuf.h>
+#include <sys/systm.h>
+#include <sys/un.h>
+#include <sys/vnode.h>
+
+#include <netinet/in.h>
+#include <netinet/in_pcb.h>
+
+#include <security/audit/audit.h>
+#include <security/audit/audit_private.h>
+
+/*
+ * Calls to manipulate elements of the audit record structure from system
+ * call code.  Macro wrappers will prevent this functions from being entered
+ * if auditing is disabled, avoiding the function call cost.  We check the
+ * thread audit record pointer anyway, as the audit condition could change,
+ * and pre-selection may not have allocated an audit record for this event.
+ *
+ * XXXAUDIT: Should we assert, in each case, that this field of the record
+ * hasn't already been filled in?
+ */
+void
+audit_arg_addr(void *addr)
+{
+	struct kaudit_record *ar;
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	ar->k_ar.ar_arg_addr = addr;
+	ARG_SET_VALID(ar, ARG_ADDR);
+}
+
+void
+audit_arg_exit(int status, int retval)
+{
+	struct kaudit_record *ar;
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	ar->k_ar.ar_arg_exitstatus = status;
+	ar->k_ar.ar_arg_exitretval = retval;
+	ARG_SET_VALID(ar, ARG_EXIT);
+}
+
+void
+audit_arg_len(int len)
+{
+	struct kaudit_record *ar;
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	ar->k_ar.ar_arg_len = len;
+	ARG_SET_VALID(ar, ARG_LEN);
+}
+
+void
+audit_arg_fd(int fd)
+{
+	struct kaudit_record *ar;
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	ar->k_ar.ar_arg_fd = fd;
+	ARG_SET_VALID(ar, ARG_FD);
+}
+
+void
+audit_arg_fflags(int fflags)
+{
+	struct kaudit_record *ar;
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	ar->k_ar.ar_arg_fflags = fflags;
+	ARG_SET_VALID(ar, ARG_FFLAGS);
+}
+
+void
+audit_arg_gid(gid_t gid)
+{
+	struct kaudit_record *ar;
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	ar->k_ar.ar_arg_gid = gid;
+	ARG_SET_VALID(ar, ARG_GID);
+}
+
+void
+audit_arg_uid(uid_t uid)
+{
+	struct kaudit_record *ar;
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	ar->k_ar.ar_arg_uid = uid;
+	ARG_SET_VALID(ar, ARG_UID);
+}
+
+void
+audit_arg_egid(gid_t egid)
+{
+	struct kaudit_record *ar;
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	ar->k_ar.ar_arg_egid = egid;
+	ARG_SET_VALID(ar, ARG_EGID);
+}
+
+void
+audit_arg_euid(uid_t euid)
+{
+	struct kaudit_record *ar;
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	ar->k_ar.ar_arg_euid = euid;
+	ARG_SET_VALID(ar, ARG_EUID);
+}
+
+void
+audit_arg_rgid(gid_t rgid)
+{
+	struct kaudit_record *ar;
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	ar->k_ar.ar_arg_rgid = rgid;
+	ARG_SET_VALID(ar, ARG_RGID);
+}
+
+void
+audit_arg_ruid(uid_t ruid)
+{
+	struct kaudit_record *ar;
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	ar->k_ar.ar_arg_ruid = ruid;
+	ARG_SET_VALID(ar, ARG_RUID);
+}
+
+void
+audit_arg_sgid(gid_t sgid)
+{
+	struct kaudit_record *ar;
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	ar->k_ar.ar_arg_sgid = sgid;
+	ARG_SET_VALID(ar, ARG_SGID);
+}
+
+void
+audit_arg_suid(uid_t suid)
+{
+	struct kaudit_record *ar;
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	ar->k_ar.ar_arg_suid = suid;
+	ARG_SET_VALID(ar, ARG_SUID);
+}
+
+void
+audit_arg_groupset(gid_t *gidset, u_int gidset_size)
+{
+	int i;
+	struct kaudit_record *ar;
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	for (i = 0; i < gidset_size; i++)
+		ar->k_ar.ar_arg_groups.gidset[i] = gidset[i];
+	ar->k_ar.ar_arg_groups.gidset_size = gidset_size;
+	ARG_SET_VALID(ar, ARG_GROUPSET);
+}
+
+void
+audit_arg_login(char *login)
+{
+	struct kaudit_record *ar;
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	strlcpy(ar->k_ar.ar_arg_login, login, MAXLOGNAME);
+	ARG_SET_VALID(ar, ARG_LOGIN);
+}
+
+void
+audit_arg_ctlname(int *name, int namelen)
+{
+	struct kaudit_record *ar;
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	bcopy(name, &ar->k_ar.ar_arg_ctlname, namelen * sizeof(int));
+	ar->k_ar.ar_arg_len = namelen;
+	ARG_SET_VALID(ar, ARG_CTLNAME | ARG_LEN);
+}
+
+void
+audit_arg_mask(int mask)
+{
+	struct kaudit_record *ar;
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	ar->k_ar.ar_arg_mask = mask;
+	ARG_SET_VALID(ar, ARG_MASK);
+}
+
+void
+audit_arg_mode(mode_t mode)
+{
+	struct kaudit_record *ar;
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	ar->k_ar.ar_arg_mode = mode;
+	ARG_SET_VALID(ar, ARG_MODE);
+}
+
+void
+audit_arg_dev(int dev)
+{
+	struct kaudit_record *ar;
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	ar->k_ar.ar_arg_dev = dev;
+	ARG_SET_VALID(ar, ARG_DEV);
+}
+
+void
+audit_arg_value(long value)
+{
+	struct kaudit_record *ar;
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	ar->k_ar.ar_arg_value = value;
+	ARG_SET_VALID(ar, ARG_VALUE);
+}
+
+void
+audit_arg_owner(uid_t uid, gid_t gid)
+{
+	struct kaudit_record *ar;
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	ar->k_ar.ar_arg_uid = uid;
+	ar->k_ar.ar_arg_gid = gid;
+	ARG_SET_VALID(ar, ARG_UID | ARG_GID);
+}
+
+void
+audit_arg_pid(pid_t pid)
+{
+	struct kaudit_record *ar;
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	ar->k_ar.ar_arg_pid = pid;
+	ARG_SET_VALID(ar, ARG_PID);
+}
+
+void
+audit_arg_process(struct proc *p)
+{
+	struct kaudit_record *ar;
+
+	KASSERT(p != NULL, ("audit_arg_process: p == NULL"));
+
+	PROC_LOCK_ASSERT(p, MA_OWNED);
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	ar->k_ar.ar_arg_auid = p->p_ucred->cr_audit.ai_auid;
+	ar->k_ar.ar_arg_euid = p->p_ucred->cr_uid;
+	ar->k_ar.ar_arg_egid = p->p_ucred->cr_groups[0];
+	ar->k_ar.ar_arg_ruid = p->p_ucred->cr_ruid;
+	ar->k_ar.ar_arg_rgid = p->p_ucred->cr_rgid;
+	ar->k_ar.ar_arg_asid = p->p_ucred->cr_audit.ai_asid;
+	ar->k_ar.ar_arg_termid_addr = p->p_ucred->cr_audit.ai_termid;
+	ar->k_ar.ar_arg_pid = p->p_pid;
+	ARG_SET_VALID(ar, ARG_AUID | ARG_EUID | ARG_EGID | ARG_RUID |
+	    ARG_RGID | ARG_ASID | ARG_TERMID_ADDR | ARG_PID | ARG_PROCESS);
+}
+
+void
+audit_arg_signum(u_int signum)
+{
+	struct kaudit_record *ar;
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	ar->k_ar.ar_arg_signum = signum;
+	ARG_SET_VALID(ar, ARG_SIGNUM);
+}
+
+void
+audit_arg_socket(int sodomain, int sotype, int soprotocol)
+{
+	struct kaudit_record *ar;
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	ar->k_ar.ar_arg_sockinfo.so_domain = sodomain;
+	ar->k_ar.ar_arg_sockinfo.so_type = sotype;
+	ar->k_ar.ar_arg_sockinfo.so_protocol = soprotocol;
+	ARG_SET_VALID(ar, ARG_SOCKINFO);
+}
+
+void
+audit_arg_sockaddr(struct thread *td, struct sockaddr *sa)
+{
+	struct kaudit_record *ar;
+
+	KASSERT(td != NULL, ("audit_arg_sockaddr: td == NULL"));
+	KASSERT(sa != NULL, ("audit_arg_sockaddr: sa == NULL"));
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	bcopy(sa, &ar->k_ar.ar_arg_sockaddr, sa->sa_len);
+	switch (sa->sa_family) {
+	case AF_INET:
+		ARG_SET_VALID(ar, ARG_SADDRINET);
+		break;
+
+	case AF_INET6:
+		ARG_SET_VALID(ar, ARG_SADDRINET6);
+		break;
+
+	case AF_UNIX:
+		audit_arg_upath(td, ((struct sockaddr_un *)sa)->sun_path,
+				ARG_UPATH1);
+		ARG_SET_VALID(ar, ARG_SADDRUNIX);
+		break;
+	/* XXXAUDIT: default:? */
+	}
+}
+
+void
+audit_arg_auid(uid_t auid)
+{
+	struct kaudit_record *ar;
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	ar->k_ar.ar_arg_auid = auid;
+	ARG_SET_VALID(ar, ARG_AUID);
+}
+
+void
+audit_arg_auditinfo(struct auditinfo *au_info)
+{
+	struct kaudit_record *ar;
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	ar->k_ar.ar_arg_auid = au_info->ai_auid;
+	ar->k_ar.ar_arg_asid = au_info->ai_asid;
+	ar->k_ar.ar_arg_amask.am_success = au_info->ai_mask.am_success;
+	ar->k_ar.ar_arg_amask.am_failure = au_info->ai_mask.am_failure;
+	ar->k_ar.ar_arg_termid.port = au_info->ai_termid.port;
+	ar->k_ar.ar_arg_termid.machine = au_info->ai_termid.machine;
+	ARG_SET_VALID(ar, ARG_AUID | ARG_ASID | ARG_AMASK | ARG_TERMID);
+}
+
+void
+audit_arg_auditinfo_addr(struct auditinfo_addr *au_info)
+{
+	struct kaudit_record *ar;
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	ar->k_ar.ar_arg_auid = au_info->ai_auid;
+	ar->k_ar.ar_arg_asid = au_info->ai_asid;
+	ar->k_ar.ar_arg_amask.am_success = au_info->ai_mask.am_success;
+	ar->k_ar.ar_arg_amask.am_failure = au_info->ai_mask.am_failure;
+	ar->k_ar.ar_arg_termid_addr.at_type = au_info->ai_termid.at_type;
+	ar->k_ar.ar_arg_termid_addr.at_port = au_info->ai_termid.at_port;
+	ar->k_ar.ar_arg_termid_addr.at_addr[0] = au_info->ai_termid.at_addr[0];
+	ar->k_ar.ar_arg_termid_addr.at_addr[1] = au_info->ai_termid.at_addr[1];
+	ar->k_ar.ar_arg_termid_addr.at_addr[2] = au_info->ai_termid.at_addr[2];
+	ar->k_ar.ar_arg_termid_addr.at_addr[3] = au_info->ai_termid.at_addr[3];
+	ARG_SET_VALID(ar, ARG_AUID | ARG_ASID | ARG_AMASK | ARG_TERMID_ADDR);
+}
+
+void
+audit_arg_text(char *text)
+{
+	struct kaudit_record *ar;
+
+	KASSERT(text != NULL, ("audit_arg_text: text == NULL"));
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	/* Invalidate the text string */
+	ar->k_ar.ar_valid_arg &= (ARG_ALL ^ ARG_TEXT);
+
+	if (ar->k_ar.ar_arg_text == NULL)
+		ar->k_ar.ar_arg_text = malloc(MAXPATHLEN, M_AUDITTEXT,
+		    M_WAITOK);
+
+	strncpy(ar->k_ar.ar_arg_text, text, MAXPATHLEN);
+	ARG_SET_VALID(ar, ARG_TEXT);
+}
+
+void
+audit_arg_cmd(int cmd)
+{
+	struct kaudit_record *ar;
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	ar->k_ar.ar_arg_cmd = cmd;
+	ARG_SET_VALID(ar, ARG_CMD);
+}
+
+void
+audit_arg_svipc_cmd(int cmd)
+{
+	struct kaudit_record *ar;
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	ar->k_ar.ar_arg_svipc_cmd = cmd;
+	ARG_SET_VALID(ar, ARG_SVIPC_CMD);
+}
+
+void
+audit_arg_svipc_perm(struct ipc_perm *perm)
+{
+	struct kaudit_record *ar;
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	bcopy(perm, &ar->k_ar.ar_arg_svipc_perm,
+	    sizeof(ar->k_ar.ar_arg_svipc_perm));
+	ARG_SET_VALID(ar, ARG_SVIPC_PERM);
+}
+
+void
+audit_arg_svipc_id(int id)
+{
+	struct kaudit_record *ar;
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	ar->k_ar.ar_arg_svipc_id = id;
+	ARG_SET_VALID(ar, ARG_SVIPC_ID);
+}
+
+void
+audit_arg_svipc_addr(void * addr)
+{
+	struct kaudit_record *ar;
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	ar->k_ar.ar_arg_svipc_addr = addr;
+	ARG_SET_VALID(ar, ARG_SVIPC_ADDR);
+}
+
+void
+audit_arg_posix_ipc_perm(uid_t uid, gid_t gid, mode_t mode)
+{
+	struct kaudit_record *ar;
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	ar->k_ar.ar_arg_pipc_perm.pipc_uid = uid;
+	ar->k_ar.ar_arg_pipc_perm.pipc_gid = gid;
+	ar->k_ar.ar_arg_pipc_perm.pipc_mode = mode;
+	ARG_SET_VALID(ar, ARG_POSIX_IPC_PERM);
+}
+
+void
+audit_arg_auditon(union auditon_udata *udata)
+{
+	struct kaudit_record *ar;
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	bcopy((void *)udata, &ar->k_ar.ar_arg_auditon,
+	    sizeof(ar->k_ar.ar_arg_auditon));
+	ARG_SET_VALID(ar, ARG_AUDITON);
+}
+
+/*
+ * Audit information about a file, either the file's vnode info, or its
+ * socket address info.
+ */
+void
+audit_arg_file(struct proc *p, struct file *fp)
+{
+	struct kaudit_record *ar;
+	struct socket *so;
+	struct inpcb *pcb;
+	struct vnode *vp;
+	int vfslocked;
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	switch (fp->f_type) {
+	case DTYPE_VNODE:
+	case DTYPE_FIFO:
+		/*
+		 * XXXAUDIT: Only possibly to record as first vnode?
+		 */
+		vp = fp->f_vnode;
+		vfslocked = VFS_LOCK_GIANT(vp->v_mount);
+		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, curthread);
+		audit_arg_vnode(vp, ARG_VNODE1);
+		VOP_UNLOCK(vp, 0, curthread);
+		VFS_UNLOCK_GIANT(vfslocked);
+		break;
+
+	case DTYPE_SOCKET:
+		so = (struct socket *)fp->f_data;
+		if (INP_CHECK_SOCKAF(so, PF_INET)) {
+			SOCK_LOCK(so);
+			ar->k_ar.ar_arg_sockinfo.so_type =
+			    so->so_type;
+			ar->k_ar.ar_arg_sockinfo.so_domain =
+			    INP_SOCKAF(so);
+			ar->k_ar.ar_arg_sockinfo.so_protocol =
+			    so->so_proto->pr_protocol;
+			SOCK_UNLOCK(so);
+			pcb = (struct inpcb *)so->so_pcb;
+			INP_LOCK(pcb);
+			ar->k_ar.ar_arg_sockinfo.so_raddr =
+			    pcb->inp_faddr.s_addr;
+			ar->k_ar.ar_arg_sockinfo.so_laddr =
+			    pcb->inp_laddr.s_addr;
+			ar->k_ar.ar_arg_sockinfo.so_rport =
+			    pcb->inp_fport;
+			ar->k_ar.ar_arg_sockinfo.so_lport =
+			    pcb->inp_lport;
+			INP_UNLOCK(pcb);
+			ARG_SET_VALID(ar, ARG_SOCKINFO);
+		}
+		break;
+
+	default:
+		/* XXXAUDIT: else? */
+		break;
+	}
+}
+
+/*
+ * Store a path as given by the user process for auditing into the audit
+ * record stored on the user thread. This function will allocate the memory
+ * to store the path info if not already available. This memory will be freed
+ * when the audit record is freed.
+ *
+ * XXXAUDIT: Possibly assert that the memory isn't already allocated?
+ */
+void
+audit_arg_upath(struct thread *td, char *upath, u_int64_t flag)
+{
+	struct kaudit_record *ar;
+	char **pathp;
+
+	KASSERT(td != NULL, ("audit_arg_upath: td == NULL"));
+	KASSERT(upath != NULL, ("audit_arg_upath: upath == NULL"));
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	KASSERT((flag == ARG_UPATH1) || (flag == ARG_UPATH2),
+	    ("audit_arg_upath: flag %llu", (unsigned long long)flag));
+	KASSERT((flag != ARG_UPATH1) || (flag != ARG_UPATH2),
+	    ("audit_arg_upath: flag %llu", (unsigned long long)flag));
+
+	if (flag == ARG_UPATH1)
+		pathp = &ar->k_ar.ar_arg_upath1;
+	else
+		pathp = &ar->k_ar.ar_arg_upath2;
+
+	if (*pathp == NULL)
+		*pathp = malloc(MAXPATHLEN, M_AUDITPATH, M_WAITOK);
+
+	canon_path(td, upath, *pathp);
+
+	ARG_SET_VALID(ar, flag);
+}
+
+/*
+ * Function to save the path and vnode attr information into the audit
+ * record.
+ *
+ * It is assumed that the caller will hold any vnode locks necessary to
+ * perform a VOP_GETATTR() on the passed vnode.
+ *
+ * XXX: The attr code is very similar to vfs_vnops.c:vn_stat(), but always
+ * provides access to the generation number as we need that to construct the
+ * BSM file ID.
+ *
+ * XXX: We should accept the process argument from the caller, since it's
+ * very likely they already have a reference.
+ *
+ * XXX: Error handling in this function is poor.
+ *
+ * XXXAUDIT: Possibly KASSERT the path pointer is NULL?
+ */
+void
+audit_arg_vnode(struct vnode *vp, u_int64_t flags)
+{
+	struct kaudit_record *ar;
+	struct vattr vattr;
+	int error;
+	struct vnode_au_info *vnp;
+
+	KASSERT(vp != NULL, ("audit_arg_vnode: vp == NULL"));
+	KASSERT((flags == ARG_VNODE1) || (flags == ARG_VNODE2),
+	    ("audit_arg_vnode: flags %jd", (intmax_t)flags));
+
+	/*
+	 * Assume that if the caller is calling audit_arg_vnode() on a
+	 * non-MPSAFE vnode, then it will have acquired Giant.
+	 */
+	VFS_ASSERT_GIANT(vp->v_mount);
+	ASSERT_VOP_LOCKED(vp, "audit_arg_vnode");
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	/*
+	 * XXXAUDIT: The below clears, and then resets the flags for valid
+	 * arguments.  Ideally, either the new vnode is used, or the old one
+	 * would be.
+	 */
+	if (flags & ARG_VNODE1) {
+		ar->k_ar.ar_valid_arg &= (ARG_ALL ^ ARG_VNODE1);
+		vnp = &ar->k_ar.ar_arg_vnode1;
+	} else {
+		ar->k_ar.ar_valid_arg &= (ARG_ALL ^ ARG_VNODE2);
+		vnp = &ar->k_ar.ar_arg_vnode2;
+	}
+
+	error = VOP_GETATTR(vp, &vattr, curthread->td_ucred, curthread);
+	if (error) {
+		/* XXX: How to handle this case? */
+		return;
+	}
+
+	vnp->vn_mode = vattr.va_mode;
+	vnp->vn_uid = vattr.va_uid;
+	vnp->vn_gid = vattr.va_gid;
+	vnp->vn_dev = vattr.va_rdev;
+	vnp->vn_fsid = vattr.va_fsid;
+	vnp->vn_fileid = vattr.va_fileid;
+	vnp->vn_gen = vattr.va_gen;
+	if (flags & ARG_VNODE1)
+		ARG_SET_VALID(ar, ARG_VNODE1);
+	else
+		ARG_SET_VALID(ar, ARG_VNODE2);
+}
+
+/*
+ * Audit the argument strings passed to exec.
+ */
+void
+audit_arg_argv(char *argv, int argc, int length)
+{
+	struct kaudit_record *ar;
+
+	if (audit_argv == 0)
+		return;
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	ar->k_ar.ar_arg_argv = malloc(length, M_AUDITTEXT, M_WAITOK);
+	bcopy(argv, ar->k_ar.ar_arg_argv, length);
+	ar->k_ar.ar_arg_argc = argc;
+	ARG_SET_VALID(ar, ARG_ARGV);
+}
+
+/*
+ * Audit the environment strings passed to exec.
+ */
+void
+audit_arg_envv(char *envv, int envc, int length)
+{
+	struct kaudit_record *ar;
+
+	if (audit_arge == 0)
+		return;
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	ar->k_ar.ar_arg_envv = malloc(length, M_AUDITTEXT, M_WAITOK);
+	bcopy(envv, ar->k_ar.ar_arg_envv, length);
+	ar->k_ar.ar_arg_envc = envc;
+	ARG_SET_VALID(ar, ARG_ENVV);
+}
+
+/*
+ * The close() system call uses it's own audit call to capture the path/vnode
+ * information because those pieces are not easily obtained within the system
+ * call itself.
+ */
+void
+audit_sysclose(struct thread *td, int fd)
+{
+	struct kaudit_record *ar;
+	struct vnode *vp;
+	struct file *fp;
+	int vfslocked;
+
+	KASSERT(td != NULL, ("audit_sysclose: td == NULL"));
+
+	ar = currecord();
+	if (ar == NULL)
+		return;
+
+	audit_arg_fd(fd);
+
+	if (getvnode(td->td_proc->p_fd, fd, &fp) != 0)
+		return;
+
+	vp = fp->f_vnode;
+	vfslocked = VFS_LOCK_GIANT(vp->v_mount);
+	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
+	audit_arg_vnode(vp, ARG_VNODE1);
+	VOP_UNLOCK(vp, 0, td);
+	VFS_UNLOCK_GIANT(vfslocked);
+	fdrop(fp, td);
+}
--- /dev/null
+++ sys/security/audit/audit_pipe.c
@@ -0,0 +1,1057 @@
+/*-
+ * Copyright (c) 2006 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * This software was developed by Robert Watson for the TrustedBSD Project.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/security/audit/audit_pipe.c,v 1.11.2.2 2007/11/04 16:44:48 csjp Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/condvar.h>
+#include <sys/conf.h>
+#include <sys/eventhandler.h>
+#include <sys/filio.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/poll.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/selinfo.h>
+#include <sys/sigio.h>
+#include <sys/signal.h>
+#include <sys/signalvar.h>
+#include <sys/systm.h>
+#include <sys/uio.h>
+
+#include <security/audit/audit.h>
+#include <security/audit/audit_ioctl.h>
+#include <security/audit/audit_private.h>
+
+/*
+ * Implementation of a clonable special device providing a live stream of BSM
+ * audit data.  This is a "tee" of the data going to the file.  It provides
+ * unreliable but timely access to audit events.  Consumers of this interface
+ * should be very careful to avoid introducing event cycles.  Consumers may
+ * express interest via a set of preselection ioctls.
+ */
+
+/*
+ * Memory types.
+ */
+static MALLOC_DEFINE(M_AUDIT_PIPE, "audit_pipe", "Audit pipes");
+static MALLOC_DEFINE(M_AUDIT_PIPE_ENTRY, "audit_pipeent",
+    "Audit pipe entries and buffers");
+static MALLOC_DEFINE(M_AUDIT_PIPE_PRESELECT, "audit_pipe_preselect",
+    "Audit pipe preselection structure");
+
+/*
+ * Audit pipe buffer parameters.
+ */
+#define	AUDIT_PIPE_QLIMIT_DEFAULT	(128)
+#define	AUDIT_PIPE_QLIMIT_MIN		(0)
+#define	AUDIT_PIPE_QLIMIT_MAX		(1024)
+
+/*
+ * Description of an entry in an audit_pipe.
+ */
+struct audit_pipe_entry {
+	void				*ape_record;
+	u_int				 ape_record_len;
+	TAILQ_ENTRY(audit_pipe_entry)	 ape_queue;
+};
+
+/*
+ * Audit pipes allow processes to express "interest" in the set of records
+ * that are delivered via the pipe.  They do this in a similar manner to the
+ * mechanism for audit trail configuration, by expressing two global masks,
+ * and optionally expressing per-auid masks.  The following data structure is
+ * the per-auid mask description.  The global state is stored in the audit
+ * pipe data structure.
+ *
+ * We may want to consider a more space/time-efficient data structure once
+ * usage patterns for per-auid specifications are clear.
+ */
+struct audit_pipe_preselect {
+	au_id_t					 app_auid;
+	au_mask_t				 app_mask;
+	TAILQ_ENTRY(audit_pipe_preselect)	 app_list;
+};
+
+/*
+ * Description of an individual audit_pipe.  Consists largely of a bounded
+ * length queue.
+ */
+#define	AUDIT_PIPE_ASYNC	0x00000001
+#define	AUDIT_PIPE_NBIO		0x00000002
+struct audit_pipe {
+	int				 ap_open;	/* Device open? */
+	u_int				 ap_flags;
+
+	struct selinfo			 ap_selinfo;
+	struct sigio			*ap_sigio;
+
+	u_int				 ap_qlen;
+	u_int				 ap_qlimit;
+
+	u_int64_t			 ap_inserts;	/* Records added. */
+	u_int64_t			 ap_reads;	/* Records read. */
+	u_int64_t			 ap_drops;	/* Records dropped. */
+	u_int64_t			 ap_truncates;	/* Records too long. */
+
+	/*
+	 * Fields relating to pipe interest: global masks for unmatched
+	 * processes (attributable, non-attributable), and a list of specific
+	 * interest specifications by auid.
+	 */
+	int				 ap_preselect_mode;
+	au_mask_t			 ap_preselect_flags;
+	au_mask_t			 ap_preselect_naflags;
+	TAILQ_HEAD(, audit_pipe_preselect)	ap_preselect_list;
+
+	/*
+	 * Current pending record list.
+	 */
+	TAILQ_HEAD(, audit_pipe_entry)	 ap_queue;
+
+	/*
+	 * Global pipe list.
+	 */
+	TAILQ_ENTRY(audit_pipe)		 ap_list;
+};
+
+/*
+ * Global list of audit pipes, mutex to protect it and the pipes.  Finer
+ * grained locking may be desirable at some point.
+ */
+static TAILQ_HEAD(, audit_pipe)	 audit_pipe_list;
+static struct mtx		 audit_pipe_mtx;
+
+/*
+ * This CV is used to wakeup on an audit record write.  Eventually, it might
+ * be per-pipe to avoid unnecessary wakeups when several pipes with different
+ * preselection masks are present.
+ */
+static struct cv		 audit_pipe_cv;
+
+/*
+ * Cloning related variables and constants.
+ */
+#define	AUDIT_PIPE_NAME		"auditpipe"
+static eventhandler_tag		 audit_pipe_eh_tag;
+static struct clonedevs		*audit_pipe_clones;
+
+/*
+ * Special device methods and definition.
+ */
+static d_open_t		audit_pipe_open;
+static d_close_t	audit_pipe_close;
+static d_read_t		audit_pipe_read;
+static d_ioctl_t	audit_pipe_ioctl;
+static d_poll_t		audit_pipe_poll;
+static d_kqfilter_t	audit_pipe_kqfilter;
+
+static struct cdevsw	audit_pipe_cdevsw = {
+	.d_version =	D_VERSION,
+	.d_flags =	D_PSEUDO | D_NEEDGIANT,
+	.d_open =	audit_pipe_open,
+	.d_close =	audit_pipe_close,
+	.d_read =	audit_pipe_read,
+	.d_ioctl =	audit_pipe_ioctl,
+	.d_poll =	audit_pipe_poll,
+	.d_kqfilter =	audit_pipe_kqfilter,
+	.d_name =	AUDIT_PIPE_NAME,
+};
+
+static int	audit_pipe_kqread(struct knote *note, long hint);
+static void	audit_pipe_kqdetach(struct knote *note);
+
+static struct filterops audit_pipe_read_filterops = {
+	.f_isfd =	1,
+	.f_attach =	NULL,
+	.f_detach =	audit_pipe_kqdetach,
+	.f_event =	audit_pipe_kqread,
+};
+
+/*
+ * Some global statistics on audit pipes.
+ */
+static int		audit_pipe_count;	/* Current number of pipes. */
+static u_int64_t	audit_pipe_ever;	/* Pipes ever allocated. */
+static u_int64_t	audit_pipe_records;	/* Records seen. */
+static u_int64_t	audit_pipe_drops;	/* Global record drop count. */
+
+/*
+ * Free an audit pipe entry.
+ */
+static void
+audit_pipe_entry_free(struct audit_pipe_entry *ape)
+{
+
+	free(ape->ape_record, M_AUDIT_PIPE_ENTRY);
+	free(ape, M_AUDIT_PIPE_ENTRY);
+}
+
+/*
+ * Find an audit pipe preselection specification for an auid, if any.
+ */
+static struct audit_pipe_preselect *
+audit_pipe_preselect_find(struct audit_pipe *ap, au_id_t auid)
+{
+	struct audit_pipe_preselect *app;
+
+	mtx_assert(&audit_pipe_mtx, MA_OWNED);
+
+	TAILQ_FOREACH(app, &ap->ap_preselect_list, app_list) {
+		if (app->app_auid == auid)
+			return (app);
+	}
+	return (NULL);
+}
+
+/*
+ * Query the per-pipe mask for a specific auid.
+ */
+static int
+audit_pipe_preselect_get(struct audit_pipe *ap, au_id_t auid,
+    au_mask_t *maskp)
+{
+	struct audit_pipe_preselect *app;
+	int error;
+
+	mtx_lock(&audit_pipe_mtx);
+	app = audit_pipe_preselect_find(ap, auid);
+	if (app != NULL) {
+		*maskp = app->app_mask;
+		error = 0;
+	} else
+		error = ENOENT;
+	mtx_unlock(&audit_pipe_mtx);
+	return (error);
+}
+
+/*
+ * Set the per-pipe mask for a specific auid.  Add a new entry if needed;
+ * otherwise, update the current entry.
+ */
+static void
+audit_pipe_preselect_set(struct audit_pipe *ap, au_id_t auid, au_mask_t mask)
+{
+	struct audit_pipe_preselect *app, *app_new;
+
+	/*
+	 * Pessimistically assume that the auid doesn't already have a mask
+	 * set, and allocate.  We will free it if it is unneeded.
+	 */
+	app_new = malloc(sizeof(*app_new), M_AUDIT_PIPE_PRESELECT, M_WAITOK);
+	mtx_lock(&audit_pipe_mtx);
+	app = audit_pipe_preselect_find(ap, auid);
+	if (app == NULL) {
+		app = app_new;
+		app_new = NULL;
+		app->app_auid = auid;
+		TAILQ_INSERT_TAIL(&ap->ap_preselect_list, app, app_list);
+	}
+	app->app_mask = mask;
+	mtx_unlock(&audit_pipe_mtx);
+	if (app_new != NULL)
+		free(app_new, M_AUDIT_PIPE_PRESELECT);
+}
+
+/*
+ * Delete a per-auid mask on an audit pipe.
+ */
+static int
+audit_pipe_preselect_delete(struct audit_pipe *ap, au_id_t auid)
+{
+	struct audit_pipe_preselect *app;
+	int error;
+
+	mtx_lock(&audit_pipe_mtx);
+	app = audit_pipe_preselect_find(ap, auid);
+	if (app != NULL) {
+		TAILQ_REMOVE(&ap->ap_preselect_list, app, app_list);
+		error = 0;
+	} else
+		error = ENOENT;
+	mtx_unlock(&audit_pipe_mtx);
+	if (app != NULL)
+		free(app, M_AUDIT_PIPE_PRESELECT);
+	return (error);
+}
+
+/*
+ * Delete all per-auid masks on an audit pipe.
+ */
+static void
+audit_pipe_preselect_flush_locked(struct audit_pipe *ap)
+{
+	struct audit_pipe_preselect *app;
+
+	mtx_assert(&audit_pipe_mtx, MA_OWNED);
+
+	while ((app = TAILQ_FIRST(&ap->ap_preselect_list)) != NULL) {
+		TAILQ_REMOVE(&ap->ap_preselect_list, app, app_list);
+		free(app, M_AUDIT_PIPE_PRESELECT);
+	}
+}
+
+static void
+audit_pipe_preselect_flush(struct audit_pipe *ap)
+{
+
+	mtx_lock(&audit_pipe_mtx);
+	audit_pipe_preselect_flush_locked(ap);
+	mtx_unlock(&audit_pipe_mtx);
+}
+
+/*-
+ * Determine whether a specific audit pipe matches a record with these
+ * properties.  Algorithm is as follows:
+ *
+ * - If the pipe is configured to track the default trail configuration, then
+ *   use the results of global preselection matching.
+ * - If not, search for a specifically configured auid entry matching the
+ *   event.  If an entry is found, use that.
+ * - Otherwise, use the default flags or naflags configured for the pipe.
+ */
+static int
+audit_pipe_preselect_check(struct audit_pipe *ap, au_id_t auid,
+    au_event_t event, au_class_t class, int sorf, int trail_preselect)
+{
+	struct audit_pipe_preselect *app;
+
+	mtx_assert(&audit_pipe_mtx, MA_OWNED);
+
+	switch (ap->ap_preselect_mode) {
+	case AUDITPIPE_PRESELECT_MODE_TRAIL:
+		return (trail_preselect);
+
+	case AUDITPIPE_PRESELECT_MODE_LOCAL:
+		app = audit_pipe_preselect_find(ap, auid);
+		if (app == NULL) {
+			if (auid == AU_DEFAUDITID)
+				return (au_preselect(event, class,
+				    &ap->ap_preselect_naflags, sorf));
+			else
+				return (au_preselect(event, class,
+				    &ap->ap_preselect_flags, sorf));
+		} else
+			return (au_preselect(event, class, &app->app_mask,
+			    sorf));
+
+	default:
+		panic("audit_pipe_preselect_check: mode %d",
+		    ap->ap_preselect_mode);
+	}
+
+	return (0);
+}
+
+/*
+ * Determine whether there exists a pipe interested in a record with specific
+ * properties.
+ */
+int
+audit_pipe_preselect(au_id_t auid, au_event_t event, au_class_t class,
+    int sorf, int trail_preselect)
+{
+	struct audit_pipe *ap;
+
+	mtx_lock(&audit_pipe_mtx);
+	TAILQ_FOREACH(ap, &audit_pipe_list, ap_list) {
+		if (audit_pipe_preselect_check(ap, auid, event, class, sorf,
+		    trail_preselect)) {
+			mtx_unlock(&audit_pipe_mtx);
+			return (1);
+		}
+	}
+	mtx_unlock(&audit_pipe_mtx);
+	return (0);
+}
+
+/*
+ * Append individual record to a queue -- allocate queue-local buffer, and
+ * add to the queue.  We try to drop from the head of the queue so that more
+ * recent events take precedence over older ones, but if allocation fails we
+ * do drop the new event.
+ */
+static void
+audit_pipe_append(struct audit_pipe *ap, void *record, u_int record_len)
+{
+	struct audit_pipe_entry *ape, *ape_remove;
+
+	mtx_assert(&audit_pipe_mtx, MA_OWNED);
+
+	ape = malloc(sizeof(*ape), M_AUDIT_PIPE_ENTRY, M_NOWAIT | M_ZERO);
+	if (ape == NULL) {
+		ap->ap_drops++;
+		audit_pipe_drops++;
+		return;
+	}
+
+	ape->ape_record = malloc(record_len, M_AUDIT_PIPE_ENTRY, M_NOWAIT);
+	if (ape->ape_record == NULL) {
+		free(ape, M_AUDIT_PIPE_ENTRY);
+		ap->ap_drops++;
+		audit_pipe_drops++;
+		return;
+	}
+
+	bcopy(record, ape->ape_record, record_len);
+	ape->ape_record_len = record_len;
+
+	if (ap->ap_qlen >= ap->ap_qlimit) {
+		ape_remove = TAILQ_FIRST(&ap->ap_queue);
+		TAILQ_REMOVE(&ap->ap_queue, ape_remove, ape_queue);
+		audit_pipe_entry_free(ape_remove);
+		ap->ap_qlen--;
+		ap->ap_drops++;
+		audit_pipe_drops++;
+	}
+
+	TAILQ_INSERT_TAIL(&ap->ap_queue, ape, ape_queue);
+	ap->ap_inserts++;
+	ap->ap_qlen++;
+	selwakeuppri(&ap->ap_selinfo, PSOCK);
+	KNOTE_LOCKED(&ap->ap_selinfo.si_note, 0);
+	if (ap->ap_flags & AUDIT_PIPE_ASYNC)
+		pgsigio(&ap->ap_sigio, SIGIO, 0);
+}
+
+/*
+ * audit_pipe_submit(): audit_worker submits audit records via this
+ * interface, which arranges for them to be delivered to pipe queues.
+ */
+void
+audit_pipe_submit(au_id_t auid, au_event_t event, au_class_t class, int sorf,
+    int trail_select, void *record, u_int record_len)
+{
+	struct audit_pipe *ap;
+
+	/*
+	 * Lockless read to avoid mutex overhead if pipes are not in use.
+	 */
+	if (TAILQ_FIRST(&audit_pipe_list) == NULL)
+		return;
+
+	mtx_lock(&audit_pipe_mtx);
+	TAILQ_FOREACH(ap, &audit_pipe_list, ap_list) {
+		if (audit_pipe_preselect_check(ap, auid, event, class, sorf,
+		    trail_select))
+			audit_pipe_append(ap, record, record_len);
+	}
+	audit_pipe_records++;
+	mtx_unlock(&audit_pipe_mtx);
+	cv_broadcastpri(&audit_pipe_cv, PSOCK);
+}
+
+/*
+ * audit_pipe_submit_user(): the same as audit_pipe_submit(), except that
+ * since we don't currently have selection information available, it is
+ * delivered to the pipe unconditionally.
+ *
+ * XXXRW: This is a bug.  The BSM check routine for submitting a user record
+ * should parse that information and return it.
+ */
+void
+audit_pipe_submit_user(void *record, u_int record_len)
+{
+	struct audit_pipe *ap;
+
+	/*
+	 * Lockless read to avoid mutex overhead if pipes are not in use.
+	 */
+	if (TAILQ_FIRST(&audit_pipe_list) == NULL)
+		return;
+
+	mtx_lock(&audit_pipe_mtx);
+	TAILQ_FOREACH(ap, &audit_pipe_list, ap_list)
+		audit_pipe_append(ap, record, record_len);
+	audit_pipe_records++;
+	mtx_unlock(&audit_pipe_mtx);
+	cv_broadcastpri(&audit_pipe_cv, PSOCK);
+}
+
+
+/*
+ * Pop the next record off of an audit pipe.
+ */
+static struct audit_pipe_entry *
+audit_pipe_pop(struct audit_pipe *ap)
+{
+	struct audit_pipe_entry *ape;
+
+	mtx_assert(&audit_pipe_mtx, MA_OWNED);
+
+	ape = TAILQ_FIRST(&ap->ap_queue);
+	KASSERT((ape == NULL && ap->ap_qlen == 0) ||
+	    (ape != NULL && ap->ap_qlen != 0), ("audit_pipe_pop: qlen"));
+	if (ape == NULL)
+		return (NULL);
+	TAILQ_REMOVE(&ap->ap_queue, ape, ape_queue);
+	ap->ap_qlen--;
+	return (ape);
+}
+
+/*
+ * Allocate a new audit pipe.  Connects the pipe, on success, to the global
+ * list and updates statistics.
+ */
+static struct audit_pipe *
+audit_pipe_alloc(void)
+{
+	struct audit_pipe *ap;
+
+	mtx_assert(&audit_pipe_mtx, MA_OWNED);
+
+	ap = malloc(sizeof(*ap), M_AUDIT_PIPE, M_NOWAIT | M_ZERO);
+	if (ap == NULL)
+		return (NULL);
+	ap->ap_qlimit = AUDIT_PIPE_QLIMIT_DEFAULT;
+	TAILQ_INIT(&ap->ap_queue);
+	knlist_init(&ap->ap_selinfo.si_note, &audit_pipe_mtx, NULL, NULL,
+	    NULL);
+
+	/*
+	 * Default flags, naflags, and auid-specific preselection settings to
+	 * 0.  Initialize the mode to the global trail so that if praudit(1)
+	 * is run on /dev/auditpipe, it sees events associated with the
+	 * default trail.  Pipe-aware application can clear the flag, set
+	 * custom masks, and flush the pipe as needed.
+	 */
+	bzero(&ap->ap_preselect_flags, sizeof(ap->ap_preselect_flags));
+	bzero(&ap->ap_preselect_naflags, sizeof(ap->ap_preselect_naflags));
+	TAILQ_INIT(&ap->ap_preselect_list);
+	ap->ap_preselect_mode = AUDITPIPE_PRESELECT_MODE_TRAIL;
+
+	/*
+	 * Add to global list and update global statistics.
+	 */
+	TAILQ_INSERT_HEAD(&audit_pipe_list, ap, ap_list);
+	audit_pipe_count++;
+	audit_pipe_ever++;
+
+	return (ap);
+}
+
+/*
+ * Flush all records currently present in an audit pipe; assume mutex is held.
+ */
+static void
+audit_pipe_flush(struct audit_pipe *ap)
+{
+	struct audit_pipe_entry *ape;
+
+	mtx_assert(&audit_pipe_mtx, MA_OWNED);
+
+	while ((ape = TAILQ_FIRST(&ap->ap_queue)) != NULL) {
+		TAILQ_REMOVE(&ap->ap_queue, ape, ape_queue);
+		audit_pipe_entry_free(ape);
+		ap->ap_qlen--;
+	}
+	KASSERT(ap->ap_qlen == 0, ("audit_pipe_free: ap_qlen"));
+}
+
+/*
+ * Free an audit pipe; this means freeing all preselection state and all
+ * records in the pipe.  Assumes mutex is held to prevent any new records
+ * from being inserted during the free, and that the audit pipe is still on
+ * the global list.
+ */
+static void
+audit_pipe_free(struct audit_pipe *ap)
+{
+
+	mtx_assert(&audit_pipe_mtx, MA_OWNED);
+
+	audit_pipe_preselect_flush_locked(ap);
+	audit_pipe_flush(ap);
+	knlist_destroy(&ap->ap_selinfo.si_note);
+	TAILQ_REMOVE(&audit_pipe_list, ap, ap_list);
+	free(ap, M_AUDIT_PIPE);
+	audit_pipe_count--;
+}
+
+/*
+ * Audit pipe clone routine -- provide specific requested audit pipe, or a
+ * fresh one if a specific one is not requested.
+ */
+static void
+audit_pipe_clone(void *arg, struct ucred *cred, char *name, int namelen,
+    struct cdev **dev)
+{
+	int i, u;
+
+	if (*dev != NULL)
+		return;
+
+	if (strcmp(name, AUDIT_PIPE_NAME) == 0)
+		u = -1;
+	else if (dev_stdclone(name, NULL, AUDIT_PIPE_NAME, &u) != 1)
+		return;
+
+	i = clone_create(&audit_pipe_clones, &audit_pipe_cdevsw, &u, dev, 0);
+	if (i) {
+		*dev = make_dev(&audit_pipe_cdevsw, unit2minor(u), UID_ROOT,
+		    GID_WHEEL, 0600, "%s%d", AUDIT_PIPE_NAME, u);
+		if (*dev != NULL) {
+			dev_ref(*dev);
+			(*dev)->si_flags |= SI_CHEAPCLONE;
+		}
+	}
+}
+
+/*
+ * Audit pipe open method.  Explicit privilege check isn't used as this
+ * allows file permissions on the special device to be used to grant audit
+ * review access.  Those file permissions should be managed carefully.
+ */
+static int
+audit_pipe_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
+{
+	struct audit_pipe *ap;
+
+	mtx_lock(&audit_pipe_mtx);
+	ap = dev->si_drv1;
+	if (ap == NULL) {
+		ap = audit_pipe_alloc();
+		if (ap == NULL) {
+			mtx_unlock(&audit_pipe_mtx);
+			return (ENOMEM);
+		}
+		dev->si_drv1 = ap;
+	} else {
+		KASSERT(ap->ap_open, ("audit_pipe_open: ap && !ap_open"));
+		mtx_unlock(&audit_pipe_mtx);
+		return (EBUSY);
+	}
+	ap->ap_open = 1;
+	mtx_unlock(&audit_pipe_mtx);
+	fsetown(td->td_proc->p_pid, &ap->ap_sigio);
+	return (0);
+}
+
+/*
+ * Close audit pipe, tear down all records, etc.
+ */
+static int
+audit_pipe_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
+{
+	struct audit_pipe *ap;
+
+	ap = dev->si_drv1;
+	KASSERT(ap != NULL, ("audit_pipe_close: ap == NULL"));
+	KASSERT(ap->ap_open, ("audit_pipe_close: !ap_open"));
+	funsetown(&ap->ap_sigio);
+	mtx_lock(&audit_pipe_mtx);
+	ap->ap_open = 0;
+	audit_pipe_free(ap);
+	dev->si_drv1 = NULL;
+	mtx_unlock(&audit_pipe_mtx);
+	return (0);
+}
+
+/*
+ * Audit pipe ioctl() routine.  Handle file descriptor and audit pipe layer
+ * commands.
+ *
+ * Would be desirable to support filtering, although perhaps something simple
+ * like an event mask, as opposed to something complicated like BPF.
+ */
+static int
+audit_pipe_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag,
+    struct thread *td)
+{
+	struct auditpipe_ioctl_preselect *aip;
+	struct audit_pipe *ap;
+	au_mask_t *maskp;
+	int error, mode;
+	au_id_t auid;
+
+	ap = dev->si_drv1;
+	KASSERT(ap != NULL, ("audit_pipe_ioctl: ap == NULL"));
+
+	/*
+	 * Audit pipe ioctls: first come standard device node ioctls, then
+	 * manipulation of pipe settings, and finally, statistics query
+	 * ioctls.
+	 */
+	switch (cmd) {
+	case FIONBIO:
+		mtx_lock(&audit_pipe_mtx);
+		if (*(int *)data)
+			ap->ap_flags |= AUDIT_PIPE_NBIO;
+		else
+			ap->ap_flags &= ~AUDIT_PIPE_NBIO;
+		mtx_unlock(&audit_pipe_mtx);
+		error = 0;
+		break;
+
+	case FIONREAD:
+		mtx_lock(&audit_pipe_mtx);
+		if (TAILQ_FIRST(&ap->ap_queue) != NULL)
+			*(int *)data =
+			    TAILQ_FIRST(&ap->ap_queue)->ape_record_len;
+		else
+			*(int *)data = 0;
+		mtx_unlock(&audit_pipe_mtx);
+		error = 0;
+		break;
+
+	case FIOASYNC:
+		mtx_lock(&audit_pipe_mtx);
+		if (*(int *)data)
+			ap->ap_flags |= AUDIT_PIPE_ASYNC;
+		else
+			ap->ap_flags &= ~AUDIT_PIPE_ASYNC;
+		mtx_unlock(&audit_pipe_mtx);
+		error = 0;
+		break;
+
+	case FIOSETOWN:
+		error = fsetown(*(int *)data, &ap->ap_sigio);
+		break;
+
+	case FIOGETOWN:
+		*(int *)data = fgetown(&ap->ap_sigio);
+		error = 0;
+		break;
+
+	case AUDITPIPE_GET_QLEN:
+		*(u_int *)data = ap->ap_qlen;
+		error = 0;
+		break;
+
+	case AUDITPIPE_GET_QLIMIT:
+		*(u_int *)data = ap->ap_qlimit;
+		error = 0;
+		break;
+
+	case AUDITPIPE_SET_QLIMIT:
+		/* Lockless integer write. */
+		if (*(u_int *)data >= AUDIT_PIPE_QLIMIT_MIN ||
+		    *(u_int *)data <= AUDIT_PIPE_QLIMIT_MAX) {
+			ap->ap_qlimit = *(u_int *)data;
+			error = 0;
+		} else
+			error = EINVAL;
+		break;
+
+	case AUDITPIPE_GET_QLIMIT_MIN:
+		*(u_int *)data = AUDIT_PIPE_QLIMIT_MIN;
+		error = 0;
+		break;
+
+	case AUDITPIPE_GET_QLIMIT_MAX:
+		*(u_int *)data = AUDIT_PIPE_QLIMIT_MAX;
+		error = 0;
+		break;
+
+	case AUDITPIPE_GET_PRESELECT_FLAGS:
+		mtx_lock(&audit_pipe_mtx);
+		maskp = (au_mask_t *)data;
+		*maskp = ap->ap_preselect_flags;
+		mtx_unlock(&audit_pipe_mtx);
+		error = 0;
+		break;
+
+	case AUDITPIPE_SET_PRESELECT_FLAGS:
+		mtx_lock(&audit_pipe_mtx);
+		maskp = (au_mask_t *)data;
+		ap->ap_preselect_flags = *maskp;
+		mtx_unlock(&audit_pipe_mtx);
+		error = 0;
+		break;
+
+	case AUDITPIPE_GET_PRESELECT_NAFLAGS:
+		mtx_lock(&audit_pipe_mtx);
+		maskp = (au_mask_t *)data;
+		*maskp = ap->ap_preselect_naflags;
+		mtx_unlock(&audit_pipe_mtx);
+		error = 0;
+		break;
+
+	case AUDITPIPE_SET_PRESELECT_NAFLAGS:
+		mtx_lock(&audit_pipe_mtx);
+		maskp = (au_mask_t *)data;
+		ap->ap_preselect_naflags = *maskp;
+		mtx_unlock(&audit_pipe_mtx);
+		error = 0;
+		break;
+
+	case AUDITPIPE_GET_PRESELECT_AUID:
+		aip = (struct auditpipe_ioctl_preselect *)data;
+		error = audit_pipe_preselect_get(ap, aip->aip_auid,
+		    &aip->aip_mask);
+		break;
+
+	case AUDITPIPE_SET_PRESELECT_AUID:
+		aip = (struct auditpipe_ioctl_preselect *)data;
+		audit_pipe_preselect_set(ap, aip->aip_auid, aip->aip_mask);
+		error = 0;
+		break;
+
+	case AUDITPIPE_DELETE_PRESELECT_AUID:
+		auid = *(au_id_t *)data;
+		error = audit_pipe_preselect_delete(ap, auid);
+		break;
+
+	case AUDITPIPE_FLUSH_PRESELECT_AUID:
+		audit_pipe_preselect_flush(ap);
+		error = 0;
+		break;
+
+	case AUDITPIPE_GET_PRESELECT_MODE:
+		mtx_lock(&audit_pipe_mtx);
+		*(int *)data = ap->ap_preselect_mode;
+		mtx_unlock(&audit_pipe_mtx);
+		error = 0;
+		break;
+
+	case AUDITPIPE_SET_PRESELECT_MODE:
+		mode = *(int *)data;
+		switch (mode) {
+		case AUDITPIPE_PRESELECT_MODE_TRAIL:
+		case AUDITPIPE_PRESELECT_MODE_LOCAL:
+			mtx_lock(&audit_pipe_mtx);
+			ap->ap_preselect_mode = mode;
+			mtx_unlock(&audit_pipe_mtx);
+			error = 0;
+			break;
+
+		default:
+			error = EINVAL;
+		}
+		break;
+
+	case AUDITPIPE_FLUSH:
+		mtx_lock(&audit_pipe_mtx);
+		audit_pipe_flush(ap);
+		mtx_unlock(&audit_pipe_mtx);
+		error = 0;
+		break;
+
+	case AUDITPIPE_GET_MAXAUDITDATA:
+		*(u_int *)data = MAXAUDITDATA;
+		error = 0;
+		break;
+
+	case AUDITPIPE_GET_INSERTS:
+		*(u_int *)data = ap->ap_inserts;
+		error = 0;
+		break;
+
+	case AUDITPIPE_GET_READS:
+		*(u_int *)data = ap->ap_reads;
+		error = 0;
+		break;
+
+	case AUDITPIPE_GET_DROPS:
+		*(u_int *)data = ap->ap_drops;
+		error = 0;
+		break;
+
+	case AUDITPIPE_GET_TRUNCATES:
+		*(u_int *)data = ap->ap_truncates;
+		error = 0;
+		break;
+
+	default:
+		error = ENOTTY;
+	}
+	return (error);
+}
+
+/*
+ * Audit pipe read.  Pull one record off the queue and copy to user space.
+ * On error, the record is dropped.
+ *
+ * Providing more sophisticated behavior, such as partial reads, is tricky
+ * due to the potential for parallel I/O.  If partial read support is
+ * required, it will require a per-pipe "current record being read" along
+ * with an offset into that trecord which has already been read.  Threads
+ * performing partial reads will need to allocate per-thread copies of the
+ * data so that if another thread completes the read of the record, it can be
+ * freed without adding reference count logic.  If this is added, a flag to
+ * indicate that only atomic record reads are desired would be useful, as if
+ * different threads are all waiting for records on the pipe, they will want
+ * independent record reads, which is currently the behavior.
+ */
+static int
+audit_pipe_read(struct cdev *dev, struct uio *uio, int flag)
+{
+	struct audit_pipe_entry *ape;
+	struct audit_pipe *ap;
+	int error;
+
+	ap = dev->si_drv1;
+	KASSERT(ap != NULL, ("audit_pipe_read: ap == NULL"));
+	mtx_lock(&audit_pipe_mtx);
+	do {
+		/*
+		 * Wait for a record that fits into the read buffer, dropping
+		 * records that would be truncated if actually passed to the
+		 * process.  This helps maintain the discreet record read
+		 * interface.
+		 */
+		while ((ape = audit_pipe_pop(ap)) == NULL) {
+			if (ap->ap_flags & AUDIT_PIPE_NBIO) {
+				mtx_unlock(&audit_pipe_mtx);
+				return (EAGAIN);
+			}
+			error = cv_wait_sig(&audit_pipe_cv, &audit_pipe_mtx);
+			if (error) {
+				mtx_unlock(&audit_pipe_mtx);
+				return (error);
+			}
+		}
+		if (ape->ape_record_len <= uio->uio_resid)
+			break;
+		audit_pipe_entry_free(ape);
+		ap->ap_truncates++;
+	} while (1);
+	ap->ap_reads++;
+	mtx_unlock(&audit_pipe_mtx);
+
+	/*
+	 * Now read record to user space memory.  Even if the read is short,
+	 * we abandon the remainder of the record, supporting only discreet
+	 * record reads.
+	 */
+	error = uiomove(ape->ape_record, ape->ape_record_len, uio);
+	audit_pipe_entry_free(ape);
+	return (error);
+}
+
+/*
+ * Audit pipe poll.
+ */
+static int
+audit_pipe_poll(struct cdev *dev, int events, struct thread *td)
+{
+	struct audit_pipe *ap;
+	int revents;
+
+	revents = 0;
+	ap = dev->si_drv1;
+	KASSERT(ap != NULL, ("audit_pipe_poll: ap == NULL"));
+	if (events & (POLLIN | POLLRDNORM)) {
+		mtx_lock(&audit_pipe_mtx);
+		if (TAILQ_FIRST(&ap->ap_queue) != NULL)
+			revents |= events & (POLLIN | POLLRDNORM);
+		else
+			selrecord(td, &ap->ap_selinfo);
+		mtx_unlock(&audit_pipe_mtx);
+	}
+	return (revents);
+}
+
+/*
+ * Audit pipe kqfilter.
+ */
+static int
+audit_pipe_kqfilter(struct cdev *dev, struct knote *kn)
+{
+	struct audit_pipe *ap;
+
+	ap = dev->si_drv1;
+	KASSERT(ap != NULL, ("audit_pipe_kqfilter: ap == NULL"));
+
+	if (kn->kn_filter != EVFILT_READ)
+		return (EINVAL);
+
+	kn->kn_fop = &audit_pipe_read_filterops;
+	kn->kn_hook = ap;
+
+	mtx_lock(&audit_pipe_mtx);
+	knlist_add(&ap->ap_selinfo.si_note, kn, 1);
+	mtx_unlock(&audit_pipe_mtx);
+	return (0);
+}
+
+/*
+ * Return true if there are records available for reading on the pipe.
+ */
+static int
+audit_pipe_kqread(struct knote *kn, long hint)
+{
+	struct audit_pipe_entry *ape;
+	struct audit_pipe *ap;
+
+	mtx_assert(&audit_pipe_mtx, MA_OWNED);
+
+	ap = (struct audit_pipe *)kn->kn_hook;
+	KASSERT(ap != NULL, ("audit_pipe_kqread: ap == NULL"));
+
+	if (ap->ap_qlen != 0) {
+		ape = TAILQ_FIRST(&ap->ap_queue);
+		KASSERT(ape != NULL, ("audit_pipe_kqread: ape == NULL"));
+
+		kn->kn_data = ape->ape_record_len;
+		return (1);
+	} else {
+		kn->kn_data = 0;
+		return (0);
+	}
+}
+
+/*
+ * Detach kqueue state from audit pipe.
+ */
+static void
+audit_pipe_kqdetach(struct knote *kn)
+{
+	struct audit_pipe *ap;
+
+	ap = (struct audit_pipe *)kn->kn_hook;
+	KASSERT(ap != NULL, ("audit_pipe_kqdetach: ap == NULL"));
+
+	mtx_lock(&audit_pipe_mtx);
+	knlist_remove(&ap->ap_selinfo.si_note, kn, 1);
+	mtx_unlock(&audit_pipe_mtx);
+}
+
+/*
+ * Initialize the audit pipe system.
+ */
+static void
+audit_pipe_init(void *unused)
+{
+
+	TAILQ_INIT(&audit_pipe_list);
+	mtx_init(&audit_pipe_mtx, "audit_pipe_mtx", NULL, MTX_DEF);
+	cv_init(&audit_pipe_cv, "audit_pipe_cv");
+
+	clone_setup(&audit_pipe_clones);
+	audit_pipe_eh_tag = EVENTHANDLER_REGISTER(dev_clone,
+	    audit_pipe_clone, 0, 1000);
+	if (audit_pipe_eh_tag == NULL)
+		panic("audit_pipe_init: EVENTHANDLER_REGISTER");
+}
+
+SYSINIT(audit_pipe_init, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, audit_pipe_init,
+    NULL);
--- /dev/null
+++ sys/security/audit/audit.c
@@ -0,0 +1,575 @@
+/*
+ * Copyright (c) 1999-2005 Apple Computer, Inc.
+ * Copyright (c) 2006-2007 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/security/audit/audit.c,v 1.33.2.1 2007/11/02 09:53:32 rwatson Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/condvar.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/filedesc.h>
+#include <sys/fcntl.h>
+#include <sys/ipc.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/priv.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/domain.h>
+#include <sys/sysctl.h>
+#include <sys/sysproto.h>
+#include <sys/sysent.h>
+#include <sys/systm.h>
+#include <sys/ucred.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+#include <sys/unistd.h>
+#include <sys/vnode.h>
+
+#include <bsm/audit.h>
+#include <bsm/audit_internal.h>
+#include <bsm/audit_kevents.h>
+
+#include <netinet/in.h>
+#include <netinet/in_pcb.h>
+
+#include <security/audit/audit.h>
+#include <security/audit/audit_private.h>
+
+#include <vm/uma.h>
+
+static uma_zone_t	audit_record_zone;
+static MALLOC_DEFINE(M_AUDITCRED, "audit_cred", "Audit cred storage");
+MALLOC_DEFINE(M_AUDITDATA, "audit_data", "Audit data storage");
+MALLOC_DEFINE(M_AUDITPATH, "audit_path", "Audit path storage");
+MALLOC_DEFINE(M_AUDITTEXT, "audit_text", "Audit text storage");
+
+SYSCTL_NODE(_security, OID_AUTO, audit, CTLFLAG_RW, 0,
+    "TrustedBSD audit controls");
+
+/*
+ * Audit control settings that are set/read by system calls and are hence
+ * non-static.
+ *
+ * Define the audit control flags.
+ */
+int			audit_enabled;
+int			audit_suspended;
+
+/*
+ * Flags controlling behavior in low storage situations.  Should we panic if
+ * a write fails?  Should we fail stop if we're out of disk space?
+ */
+int			audit_panic_on_write_fail;
+int			audit_fail_stop;
+int			audit_argv;
+int			audit_arge;
+
+/*
+ * Are we currently "failing stop" due to out of disk space?
+ */
+int			audit_in_failure;
+
+/*
+ * Global audit statistics.
+ */
+struct audit_fstat	audit_fstat;
+
+/*
+ * Preselection mask for non-attributable events.
+ */
+struct au_mask		audit_nae_mask;
+
+/*
+ * Mutex to protect global variables shared between various threads and
+ * processes.
+ */
+struct mtx		audit_mtx;
+
+/*
+ * Queue of audit records ready for delivery to disk.  We insert new records
+ * at the tail, and remove records from the head.  Also, a count of the
+ * number of records used for checking queue depth.  In addition, a counter
+ * of records that we have allocated but are not yet in the queue, which is
+ * needed to estimate the total size of the combined set of records
+ * outstanding in the system.
+ */
+struct kaudit_queue	audit_q;
+int			audit_q_len;
+int			audit_pre_q_len;
+
+/*
+ * Audit queue control settings (minimum free, low/high water marks, etc.)
+ */
+struct au_qctrl		audit_qctrl;
+
+/*
+ * Condition variable to signal to the worker that it has work to do: either
+ * new records are in the queue, or a log replacement is taking place.
+ */
+struct cv		audit_worker_cv;
+
+/*
+ * Condition variable to flag when crossing the low watermark, meaning that
+ * threads blocked due to hitting the high watermark can wake up and continue
+ * to commit records.
+ */
+struct cv		audit_watermark_cv;
+
+/*
+ * Condition variable for  auditing threads wait on when in fail-stop mode.
+ * Threads wait on this CV forever (and ever), never seeing the light of day
+ * again.
+ */
+static struct cv	audit_fail_cv;
+
+/*
+ * Construct an audit record for the passed thread.
+ */
+static int
+audit_record_ctor(void *mem, int size, void *arg, int flags)
+{
+	struct kaudit_record *ar;
+	struct thread *td;
+
+	KASSERT(sizeof(*ar) == size, ("audit_record_ctor: wrong size"));
+
+	td = arg;
+	ar = mem;
+	bzero(ar, sizeof(*ar));
+	ar->k_ar.ar_magic = AUDIT_RECORD_MAGIC;
+	nanotime(&ar->k_ar.ar_starttime);
+
+	/*
+	 * Export the subject credential.
+	 */
+	cru2x(td->td_ucred, &ar->k_ar.ar_subj_cred);
+	ar->k_ar.ar_subj_ruid = td->td_ucred->cr_ruid;
+	ar->k_ar.ar_subj_rgid = td->td_ucred->cr_rgid;
+	ar->k_ar.ar_subj_egid = td->td_ucred->cr_groups[0];
+	ar->k_ar.ar_subj_auid = td->td_ucred->cr_audit.ai_auid;
+	ar->k_ar.ar_subj_asid = td->td_ucred->cr_audit.ai_asid;
+	ar->k_ar.ar_subj_pid = td->td_proc->p_pid;
+	ar->k_ar.ar_subj_amask = td->td_ucred->cr_audit.ai_mask;
+	ar->k_ar.ar_subj_term_addr = td->td_ucred->cr_audit.ai_termid;
+	return (0);
+}
+
+static void
+audit_record_dtor(void *mem, int size, void *arg)
+{
+	struct kaudit_record *ar;
+
+	KASSERT(sizeof(*ar) == size, ("audit_record_dtor: wrong size"));
+
+	ar = mem;
+	if (ar->k_ar.ar_arg_upath1 != NULL)
+		free(ar->k_ar.ar_arg_upath1, M_AUDITPATH);
+	if (ar->k_ar.ar_arg_upath2 != NULL)
+		free(ar->k_ar.ar_arg_upath2, M_AUDITPATH);
+	if (ar->k_ar.ar_arg_text != NULL)
+		free(ar->k_ar.ar_arg_text, M_AUDITTEXT);
+	if (ar->k_udata != NULL)
+		free(ar->k_udata, M_AUDITDATA);
+	if (ar->k_ar.ar_arg_argv != NULL)
+		free(ar->k_ar.ar_arg_argv, M_AUDITTEXT);
+	if (ar->k_ar.ar_arg_envv != NULL)
+		free(ar->k_ar.ar_arg_envv, M_AUDITTEXT);
+}
+
+/*
+ * Initialize the Audit subsystem: configuration state, work queue,
+ * synchronization primitives, worker thread, and trigger device node.  Also
+ * call into the BSM assembly code to initialize it.
+ */
+static void
+audit_init(void)
+{
+
+	audit_enabled = 0;
+	audit_suspended = 0;
+	audit_panic_on_write_fail = 0;
+	audit_fail_stop = 0;
+	audit_in_failure = 0;
+	audit_argv = 0;
+	audit_arge = 0;
+
+	audit_fstat.af_filesz = 0;	/* '0' means unset, unbounded. */
+	audit_fstat.af_currsz = 0;
+	audit_nae_mask.am_success = 0;
+	audit_nae_mask.am_failure = 0;
+
+	TAILQ_INIT(&audit_q);
+	audit_q_len = 0;
+	audit_pre_q_len = 0;
+	audit_qctrl.aq_hiwater = AQ_HIWATER;
+	audit_qctrl.aq_lowater = AQ_LOWATER;
+	audit_qctrl.aq_bufsz = AQ_BUFSZ;
+	audit_qctrl.aq_minfree = AU_FS_MINFREE;
+
+	mtx_init(&audit_mtx, "audit_mtx", NULL, MTX_DEF);
+	cv_init(&audit_worker_cv, "audit_worker_cv");
+	cv_init(&audit_watermark_cv, "audit_watermark_cv");
+	cv_init(&audit_fail_cv, "audit_fail_cv");
+
+	audit_record_zone = uma_zcreate("audit_record",
+	    sizeof(struct kaudit_record), audit_record_ctor,
+	    audit_record_dtor, NULL, NULL, UMA_ALIGN_PTR, 0);
+
+	/* Initialize the BSM audit subsystem. */
+	kau_init();
+
+	audit_trigger_init();
+
+	/* Register shutdown handler. */
+	EVENTHANDLER_REGISTER(shutdown_pre_sync, audit_shutdown, NULL,
+	    SHUTDOWN_PRI_FIRST);
+
+	/* Start audit worker thread. */
+	audit_worker_init();
+}
+
+SYSINIT(audit_init, SI_SUB_AUDIT, SI_ORDER_FIRST, audit_init, NULL)
+
+/*
+ * Drain the audit queue and close the log at shutdown.  Note that this can
+ * be called both from the system shutdown path and also from audit
+ * configuration syscalls, so 'arg' and 'howto' are ignored.
+ */
+void
+audit_shutdown(void *arg, int howto)
+{
+
+	audit_rotate_vnode(NULL, NULL);
+}
+
+/*
+ * Return the current thread's audit record, if any.
+ */
+struct kaudit_record *
+currecord(void)
+{
+
+	return (curthread->td_ar);
+}
+
+/*
+ * XXXAUDIT: There are a number of races present in the code below due to
+ * release and re-grab of the mutex.  The code should be revised to become
+ * slightly less racy.
+ *
+ * XXXAUDIT: Shouldn't there be logic here to sleep waiting on available
+ * pre_q space, suspending the system call until there is room?
+ */
+struct kaudit_record *
+audit_new(int event, struct thread *td)
+{
+	struct kaudit_record *ar;
+	int no_record;
+
+	mtx_lock(&audit_mtx);
+	no_record = (audit_suspended || !audit_enabled);
+	mtx_unlock(&audit_mtx);
+	if (no_record)
+		return (NULL);
+
+	/*
+	 * Note: the number of outstanding uncommitted audit records is
+	 * limited to the number of concurrent threads servicing system calls
+	 * in the kernel.
+	 */
+	ar = uma_zalloc_arg(audit_record_zone, td, M_WAITOK);
+	ar->k_ar.ar_event = event;
+
+	mtx_lock(&audit_mtx);
+	audit_pre_q_len++;
+	mtx_unlock(&audit_mtx);
+
+	return (ar);
+}
+
+void
+audit_free(struct kaudit_record *ar)
+{
+
+	uma_zfree(audit_record_zone, ar);
+}
+
+void
+audit_commit(struct kaudit_record *ar, int error, int retval)
+{
+	au_event_t event;
+	au_class_t class;
+	au_id_t auid;
+	int sorf;
+	struct au_mask *aumask;
+
+	if (ar == NULL)
+		return;
+
+	/*
+	 * Decide whether to commit the audit record by checking the error
+	 * value from the system call and using the appropriate audit mask.
+	 */
+	if (ar->k_ar.ar_subj_auid == AU_DEFAUDITID)
+		aumask = &audit_nae_mask;
+	else
+		aumask = &ar->k_ar.ar_subj_amask;
+
+	if (error)
+		sorf = AU_PRS_FAILURE;
+	else
+		sorf = AU_PRS_SUCCESS;
+
+	switch(ar->k_ar.ar_event) {
+	case AUE_OPEN_RWTC:
+		/*
+		 * The open syscall always writes a AUE_OPEN_RWTC event;
+		 * change it to the proper type of event based on the flags
+		 * and the error value.
+		 */
+		ar->k_ar.ar_event = flags_and_error_to_openevent(
+		    ar->k_ar.ar_arg_fflags, error);
+		break;
+
+	case AUE_SYSCTL:
+		ar->k_ar.ar_event = ctlname_to_sysctlevent(
+		    ar->k_ar.ar_arg_ctlname, ar->k_ar.ar_valid_arg);
+		break;
+
+	case AUE_AUDITON:
+		/* Convert the auditon() command to an event. */
+		ar->k_ar.ar_event = auditon_command_event(ar->k_ar.ar_arg_cmd);
+		break;
+	}
+
+	auid = ar->k_ar.ar_subj_auid;
+	event = ar->k_ar.ar_event;
+	class = au_event_class(event);
+
+	ar->k_ar_commit |= AR_COMMIT_KERNEL;
+	if (au_preselect(event, class, aumask, sorf) != 0)
+		ar->k_ar_commit |= AR_PRESELECT_TRAIL;
+	if (audit_pipe_preselect(auid, event, class, sorf,
+	    ar->k_ar_commit & AR_PRESELECT_TRAIL) != 0)
+		ar->k_ar_commit |= AR_PRESELECT_PIPE;
+	if ((ar->k_ar_commit & (AR_PRESELECT_TRAIL | AR_PRESELECT_PIPE |
+	    AR_PRESELECT_USER_TRAIL | AR_PRESELECT_USER_PIPE)) == 0) {
+		mtx_lock(&audit_mtx);
+		audit_pre_q_len--;
+		mtx_unlock(&audit_mtx);
+		audit_free(ar);
+		return;
+	}
+
+	ar->k_ar.ar_errno = error;
+	ar->k_ar.ar_retval = retval;
+	nanotime(&ar->k_ar.ar_endtime);
+
+	/*
+	 * Note: it could be that some records initiated while audit was
+	 * enabled should still be committed?
+	 */
+	mtx_lock(&audit_mtx);
+	if (audit_suspended || !audit_enabled) {
+		audit_pre_q_len--;
+		mtx_unlock(&audit_mtx);
+		audit_free(ar);
+		return;
+	}
+
+	/*
+	 * Constrain the number of committed audit records based on the
+	 * configurable parameter.
+	 */
+	while (audit_q_len >= audit_qctrl.aq_hiwater)
+		cv_wait(&audit_watermark_cv, &audit_mtx);
+
+	TAILQ_INSERT_TAIL(&audit_q, ar, k_q);
+	audit_q_len++;
+	audit_pre_q_len--;
+	cv_signal(&audit_worker_cv);
+	mtx_unlock(&audit_mtx);
+}
+
+/*
+ * audit_syscall_enter() is called on entry to each system call.  It is
+ * responsible for deciding whether or not to audit the call (preselection),
+ * and if so, allocating a per-thread audit record.  audit_new() will fill in
+ * basic thread/credential properties.
+ */
+void
+audit_syscall_enter(unsigned short code, struct thread *td)
+{
+	struct au_mask *aumask;
+	au_class_t class;
+	au_event_t event;
+	au_id_t auid;
+
+	KASSERT(td->td_ar == NULL, ("audit_syscall_enter: td->td_ar != NULL"));
+
+	/*
+	 * In FreeBSD, each ABI has its own system call table, and hence
+	 * mapping of system call codes to audit events.  Convert the code to
+	 * an audit event identifier using the process system call table
+	 * reference.  In Darwin, there's only one, so we use the global
+	 * symbol for the system call table.  No audit record is generated
+	 * for bad system calls, as no operation has been performed.
+	 */
+	if (code >= td->td_proc->p_sysent->sv_size)
+		return;
+
+	event = td->td_proc->p_sysent->sv_table[code].sy_auevent;
+	if (event == AUE_NULL)
+		return;
+
+	/*
+	 * Check which audit mask to use; either the kernel non-attributable
+	 * event mask or the process audit mask.
+	 */
+	auid = td->td_ucred->cr_audit.ai_auid;
+	if (auid == AU_DEFAUDITID)
+		aumask = &audit_nae_mask;
+	else
+		aumask = &td->td_ucred->cr_audit.ai_mask;
+
+	/*
+	 * Allocate an audit record, if preselection allows it, and store in
+	 * the thread for later use.
+	 */
+	class = au_event_class(event);
+	if (au_preselect(event, class, aumask, AU_PRS_BOTH)) {
+		/*
+		 * If we're out of space and need to suspend unprivileged
+		 * processes, do that here rather than trying to allocate
+		 * another audit record.
+		 *
+		 * Note: we might wish to be able to continue here in the
+		 * future, if the system recovers.  That should be possible
+		 * by means of checking the condition in a loop around
+		 * cv_wait().  It might be desirable to reevaluate whether an
+		 * audit record is still required for this event by
+		 * re-calling au_preselect().
+		 */
+		if (audit_in_failure &&
+		    priv_check(td, PRIV_AUDIT_FAILSTOP) != 0) {
+			cv_wait(&audit_fail_cv, &audit_mtx);
+			panic("audit_failing_stop: thread continued");
+		}
+		td->td_ar = audit_new(event, td);
+	} else if (audit_pipe_preselect(auid, event, class, AU_PRS_BOTH, 0))
+		td->td_ar = audit_new(event, td);
+	else
+		td->td_ar = NULL;
+}
+
+/*
+ * audit_syscall_exit() is called from the return of every system call, or in
+ * the event of exit1(), during the execution of exit1().  It is responsible
+ * for committing the audit record, if any, along with return condition.
+ */
+void
+audit_syscall_exit(int error, struct thread *td)
+{
+	int retval;
+
+	/*
+	 * Commit the audit record as desired; once we pass the record into
+	 * audit_commit(), the memory is owned by the audit subsystem.  The
+	 * return value from the system call is stored on the user thread.
+	 * If there was an error, the return value is set to -1, imitating
+	 * the behavior of the cerror routine.
+	 */
+	if (error)
+		retval = -1;
+	else
+		retval = td->td_retval[0];
+
+	audit_commit(td->td_ar, error, retval);
+	td->td_ar = NULL;
+}
+
+void
+audit_cred_copy(struct ucred *src, struct ucred *dest)
+{
+
+	bcopy(&src->cr_audit, &dest->cr_audit, sizeof(dest->cr_audit));
+}
+
+void
+audit_cred_destroy(struct ucred *cred)
+{
+
+}
+
+void
+audit_cred_init(struct ucred *cred)
+{
+
+	bzero(&cred->cr_audit, sizeof(cred->cr_audit));
+}
+
+/*
+ * Initialize audit information for the first kernel process (proc 0) and for
+ * the first user process (init).
+ */
+void
+audit_cred_kproc0(struct ucred *cred)
+{
+
+	cred->cr_audit.ai_auid = AU_DEFAUDITID;
+}
+
+void
+audit_cred_proc1(struct ucred *cred)
+{
+
+	cred->cr_audit.ai_auid = AU_DEFAUDITID;
+}
+
+void
+audit_thread_alloc(struct thread *td)
+{
+
+	td->td_ar = NULL;
+}
+
+void
+audit_thread_free(struct thread *td)
+{
+
+	KASSERT(td->td_ar == NULL, ("audit_thread_free: td_ar != NULL"));
+}
--- /dev/null
+++ sys/security/audit/audit_ioctl.h
@@ -0,0 +1,83 @@
+/*-
+ * Copyright (c) 2006 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * This software was developed by Robert Watson for the TrustedBSD Project.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/security/audit/audit_ioctl.h,v 1.5 2007/04/29 16:20:32 rwatson Exp $
+ */
+
+#ifndef _SECURITY_AUDIT_AUDIT_IOCTL_H_
+#define	_SECURITY_AUDIT_AUDIT_IOCTL_H_
+
+#define	AUDITPIPE_IOBASE	'A'
+
+/*
+ * Data structures used for complex ioctl arguments.  Do not change existing
+ * structures, add new revised ones to be used by new ioctls, and keep the
+ * old structures and ioctls for backwards compatibility.
+ */
+struct auditpipe_ioctl_preselect {
+	au_id_t		aip_auid;
+	au_mask_t	aip_mask;
+};
+
+/*
+ * Possible modes of operation for audit pipe preselection.
+ */
+#define	AUDITPIPE_PRESELECT_MODE_TRAIL	1	/* Global audit trail. */
+#define	AUDITPIPE_PRESELECT_MODE_LOCAL	2	/* Local audit trail. */
+
+/*
+ * Ioctls to read and control the behavior of individual audit pipe devices.
+ */
+#define	AUDITPIPE_GET_QLEN		_IOR(AUDITPIPE_IOBASE, 1, u_int)
+#define	AUDITPIPE_GET_QLIMIT		_IOR(AUDITPIPE_IOBASE, 2, u_int)
+#define	AUDITPIPE_SET_QLIMIT		_IOW(AUDITPIPE_IOBASE, 3, u_int)
+#define	AUDITPIPE_GET_QLIMIT_MIN	_IOR(AUDITPIPE_IOBASE, 4, u_int)
+#define	AUDITPIPE_GET_QLIMIT_MAX	_IOR(AUDITPIPE_IOBASE, 5, u_int)
+#define	AUDITPIPE_GET_PRESELECT_FLAGS	_IOR(AUDITPIPE_IOBASE, 6, au_mask_t)
+#define	AUDITPIPE_SET_PRESELECT_FLAGS	_IOW(AUDITPIPE_IOBASE, 7, au_mask_t)
+#define	AUDITPIPE_GET_PRESELECT_NAFLAGS	_IOR(AUDITPIPE_IOBASE, 8, au_mask_t)
+#define	AUDITPIPE_SET_PRESELECT_NAFLAGS	_IOW(AUDITPIPE_IOBASE, 9, au_mask_t)
+#define	AUDITPIPE_GET_PRESELECT_AUID	_IOR(AUDITPIPE_IOBASE, 10,	\
+					    struct auditpipe_ioctl_preselect)
+#define	AUDITPIPE_SET_PRESELECT_AUID	_IOW(AUDITPIPE_IOBASE, 11,	\
+					    struct auditpipe_ioctl_preselect)
+#define	AUDITPIPE_DELETE_PRESELECT_AUID	_IOW(AUDITPIPE_IOBASE, 12, au_id_t)
+#define	AUDITPIPE_FLUSH_PRESELECT_AUID	_IO(AUDITPIPE_IOBASE, 13)
+#define	AUDITPIPE_GET_PRESELECT_MODE	_IOR(AUDITPIPE_IOBASE, 14, int)
+#define	AUDITPIPE_SET_PRESELECT_MODE	_IOW(AUDITPIPE_IOBASE, 15, int)
+#define	AUDITPIPE_FLUSH			_IO(AUDITPIPE_IOBASE, 16)
+#define	AUDITPIPE_GET_MAXAUDITDATA	_IOR(AUDITPIPE_IOBASE, 17, u_int)
+
+/*
+ * Ioctls to retrieve audit pipe statistics.
+ */
+#define	AUDITPIPE_GET_INSERTS		_IOR(AUDITPIPE_IOBASE, 100, u_int64_t)
+#define	AUDITPIPE_GET_READS		_IOR(AUDITPIPE_IOBASE, 101, u_int64_t)
+#define	AUDITPIPE_GET_DROPS		_IOR(AUDITPIPE_IOBASE, 102, u_int64_t)
+#define	AUDITPIPE_GET_TRUNCATES		_IOR(AUDITPIPE_IOBASE, 103, u_int64_t)
+
+#endif /* _SECURITY_AUDIT_AUDIT_IOCTL_H_ */
--- /dev/null
+++ sys/security/audit/audit_bsm_token.c
@@ -0,0 +1,1370 @@
+/*
+ * Copyright (c) 2004 Apple Computer, Inc.
+ * Copyright (c) 2005 SPARTA, Inc.
+ * All rights reserved.
+ *
+ * This code was developed in part by Robert N. M. Watson, Senior Principal
+ * Scientist, SPARTA, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/security/audit/audit_bsm_token.c,v 1.14 2007/06/27 17:01:14 csjp Exp $
+ */
+
+#include <sys/types.h>
+#include <sys/endian.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <sys/ipc.h>
+#include <sys/libkern.h>
+#include <sys/malloc.h>
+#include <sys/un.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+
+
+#include <bsm/audit.h>
+#include <bsm/audit_internal.h>
+#include <bsm/audit_record.h>
+#include <security/audit/audit.h>
+#include <security/audit/audit_private.h>
+
+#define	GET_TOKEN_AREA(t, dptr, length) do {				\
+	t = malloc(sizeof(token_t), M_AUDITBSM, M_WAITOK);		\
+	t->t_data = malloc(length, M_AUDITBSM, M_WAITOK | M_ZERO);	\
+	t->len = length;						\
+	dptr = t->t_data;						\
+} while (0)
+
+/*
+ * token ID                1 byte
+ * argument #              1 byte
+ * argument value          4 bytes/8 bytes (32-bit/64-bit value)
+ * text length             2 bytes
+ * text                    N bytes + 1 terminating NULL byte
+ */
+token_t *
+au_to_arg32(char n, char *text, u_int32_t v)
+{
+	token_t *t;
+	u_char *dptr = NULL;
+	u_int16_t textlen;
+
+	textlen = strlen(text);
+	textlen += 1;
+
+	GET_TOKEN_AREA(t, dptr, 2 * sizeof(u_char) + sizeof(u_int32_t) +
+	    sizeof(u_int16_t) + textlen);
+
+	ADD_U_CHAR(dptr, AUT_ARG32);
+	ADD_U_CHAR(dptr, n);
+	ADD_U_INT32(dptr, v);
+	ADD_U_INT16(dptr, textlen);
+	ADD_STRING(dptr, text, textlen);
+
+	return (t);
+
+}
+
+token_t *
+au_to_arg64(char n, char *text, u_int64_t v)
+{
+	token_t *t;
+	u_char *dptr = NULL;
+	u_int16_t textlen;
+
+	textlen = strlen(text);
+	textlen += 1;
+
+	GET_TOKEN_AREA(t, dptr, 2 * sizeof(u_char) + sizeof(u_int64_t) +
+	    sizeof(u_int16_t) + textlen);
+
+	ADD_U_CHAR(dptr, AUT_ARG64);
+	ADD_U_CHAR(dptr, n);
+	ADD_U_INT64(dptr, v);
+	ADD_U_INT16(dptr, textlen);
+	ADD_STRING(dptr, text, textlen);
+
+	return (t);
+
+}
+
+token_t *
+au_to_arg(char n, char *text, u_int32_t v)
+{
+
+	return (au_to_arg32(n, text, v));
+}
+
+#if defined(_KERNEL) || defined(KERNEL)
+/*
+ * token ID                1 byte
+ * file access mode        4 bytes
+ * owner user ID           4 bytes
+ * owner group ID          4 bytes
+ * file system ID          4 bytes
+ * node ID                 8 bytes
+ * device                  4 bytes/8 bytes (32-bit/64-bit)
+ */
+token_t *
+au_to_attr32(struct vnode_au_info *vni)
+{
+	token_t *t;
+	u_char *dptr = NULL;
+	u_int16_t pad0_16 = 0;
+	u_int16_t pad0_32 = 0;
+
+	GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 2 * sizeof(u_int16_t) +
+	    3 * sizeof(u_int32_t) + sizeof(u_int64_t) + sizeof(u_int32_t));
+
+	ADD_U_CHAR(dptr, AUT_ATTR32);
+
+	/*
+	 * Darwin defines the size for the file mode
+	 * as 2 bytes; BSM defines 4 so pad with 0
+	 */
+	ADD_U_INT16(dptr, pad0_16);
+	ADD_U_INT16(dptr, vni->vn_mode);
+
+	ADD_U_INT32(dptr, vni->vn_uid);
+	ADD_U_INT32(dptr, vni->vn_gid);
+	ADD_U_INT32(dptr, vni->vn_fsid);
+
+	/*
+	 * Some systems use 32-bit file ID's, other's use 64-bit file IDs.
+	 * Attempt to handle both, and let the compiler sort it out.  If we
+	 * could pick this out at compile-time, it would be better, so as to
+	 * avoid the else case below.
+	 */
+	if (sizeof(vni->vn_fileid) == sizeof(uint32_t)) {
+		ADD_U_INT32(dptr, pad0_32);
+		ADD_U_INT32(dptr, vni->vn_fileid);
+	} else if (sizeof(vni->vn_fileid) == sizeof(uint64_t))
+		ADD_U_INT64(dptr, vni->vn_fileid);
+	else
+		ADD_U_INT64(dptr, 0LL);
+
+	ADD_U_INT32(dptr, vni->vn_dev);
+
+	return (t);
+}
+
+token_t *
+au_to_attr64(struct vnode_au_info *vni)
+{
+	token_t *t;
+	u_char *dptr = NULL;
+	u_int16_t pad0_16 = 0;
+	u_int16_t pad0_32 = 0;
+
+	GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 2 * sizeof(u_int16_t) +
+	    3 * sizeof(u_int32_t) + sizeof(u_int64_t) * 2);
+
+	ADD_U_CHAR(dptr, AUT_ATTR64);
+
+	/*
+	 * Darwin defines the size for the file mode
+	 * as 2 bytes; BSM defines 4 so pad with 0
+	 */
+	ADD_U_INT16(dptr, pad0_16);
+	ADD_U_INT16(dptr, vni->vn_mode);
+
+	ADD_U_INT32(dptr, vni->vn_uid);
+	ADD_U_INT32(dptr, vni->vn_gid);
+	ADD_U_INT32(dptr, vni->vn_fsid);
+
+	/*
+	 * Some systems use 32-bit file ID's, other's use 64-bit file IDs.
+	 * Attempt to handle both, and let the compiler sort it out.  If we
+	 * could pick this out at compile-time, it would be better, so as to
+	 * avoid the else case below.
+	 */
+	if (sizeof(vni->vn_fileid) == sizeof(uint32_t)) {
+		ADD_U_INT32(dptr, pad0_32);
+		ADD_U_INT32(dptr, vni->vn_fileid);
+	} else if (sizeof(vni->vn_fileid) == sizeof(uint64_t))
+		ADD_U_INT64(dptr, vni->vn_fileid);
+	else
+		ADD_U_INT64(dptr, 0LL);
+
+	ADD_U_INT64(dptr, vni->vn_dev);
+
+	return (t);
+}
+
+token_t *
+au_to_attr(struct vnode_au_info *vni)
+{
+
+	return (au_to_attr32(vni));
+}
+#endif /* !(defined(_KERNEL) || defined(KERNEL) */
+
+/*
+ * token ID                1 byte
+ * how to print            1 byte
+ * basic unit              1 byte
+ * unit count              1 byte
+ * data items              (depends on basic unit)
+ */
+token_t *
+au_to_data(char unit_print, char unit_type, char unit_count, char *p)
+{
+	token_t *t;
+	u_char *dptr = NULL;
+	size_t datasize, totdata;
+
+	/* Determine the size of the basic unit. */
+	switch (unit_type) {
+	case AUR_BYTE:
+	/* case AUR_CHAR: */
+		datasize = AUR_BYTE_SIZE;
+		break;
+
+	case AUR_SHORT:
+		datasize = AUR_SHORT_SIZE;
+		break;
+
+	case AUR_INT32:
+	/* case AUR_INT: */
+		datasize = AUR_INT32_SIZE;
+		break;
+
+	case AUR_INT64:
+		datasize = AUR_INT64_SIZE;
+		break;
+
+	default:
+ 		return (NULL);
+	}
+
+	totdata = datasize * unit_count;
+
+	GET_TOKEN_AREA(t, dptr, 4 * sizeof(u_char) + totdata);
+
+	ADD_U_CHAR(dptr, AUT_DATA);
+	ADD_U_CHAR(dptr, unit_print);
+	ADD_U_CHAR(dptr, unit_type);
+	ADD_U_CHAR(dptr, unit_count);
+	ADD_MEM(dptr, p, totdata);
+
+	return (t);
+}
+
+
+/*
+ * token ID                1 byte
+ * status		   4 bytes
+ * return value            4 bytes
+ */
+token_t *
+au_to_exit(int retval, int err)
+{
+	token_t *t;
+	u_char *dptr = NULL;
+
+	GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 2 * sizeof(u_int32_t));
+
+	ADD_U_CHAR(dptr, AUT_EXIT);
+	ADD_U_INT32(dptr, err);
+	ADD_U_INT32(dptr, retval);
+
+	return (t);
+}
+
+/*
+ */
+token_t *
+au_to_groups(int *groups)
+{
+
+	return (au_to_newgroups(AUDIT_MAX_GROUPS, (gid_t*)groups));
+}
+
+/*
+ * token ID                1 byte
+ * number groups           2 bytes
+ * group list              count * 4 bytes
+ */
+token_t *
+au_to_newgroups(u_int16_t n, gid_t *groups)
+{
+	token_t *t;
+	u_char *dptr = NULL;
+	int i;
+
+	GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int16_t) +
+	    n * sizeof(u_int32_t));
+
+	ADD_U_CHAR(dptr, AUT_NEWGROUPS);
+	ADD_U_INT16(dptr, n);
+	for (i = 0; i < n; i++)
+		ADD_U_INT32(dptr, groups[i]);
+
+	return (t);
+}
+
+/*
+ * token ID                1 byte
+ * internet address        4 bytes
+ */
+token_t *
+au_to_in_addr(struct in_addr *internet_addr)
+{
+	token_t *t;
+	u_char *dptr = NULL;
+
+	GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(uint32_t));
+
+	ADD_U_CHAR(dptr, AUT_IN_ADDR);
+	ADD_MEM(dptr, &internet_addr->s_addr, sizeof(uint32_t));
+
+	return (t);
+}
+
+/*
+ * token ID                1 byte
+ * address type/length     4 bytes
+ * Address                16 bytes
+ */
+token_t *
+au_to_in_addr_ex(struct in6_addr *internet_addr)
+{
+	token_t *t;
+	u_char *dptr = NULL;
+	u_int32_t type = AU_IPv6;
+
+	GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 5 * sizeof(uint32_t));
+
+	ADD_U_CHAR(dptr, AUT_IN_ADDR_EX);
+	ADD_U_INT32(dptr, type);
+	ADD_MEM(dptr, internet_addr, 4 * sizeof(uint32_t));
+
+	return (t);
+}
+
+/*
+ * token ID                1 byte
+ * ip header		   20 bytes
+ *
+ * The IP header should be submitted in network byte order.
+ */
+token_t *
+au_to_ip(struct ip *ip)
+{
+	token_t *t;
+	u_char *dptr = NULL;
+
+	GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(struct ip));
+
+	ADD_U_CHAR(dptr, AUT_IP);
+	ADD_MEM(dptr, ip, sizeof(struct ip));
+
+	return (t);
+}
+
+/*
+ * token ID                1 byte
+ * object ID type          1 byte
+ * object ID               4 bytes
+ */
+token_t *
+au_to_ipc(char type, int id)
+{
+	token_t *t;
+	u_char *dptr = NULL;
+
+	GET_TOKEN_AREA(t, dptr, 2 * sizeof(u_char) + sizeof(u_int32_t));
+
+	ADD_U_CHAR(dptr, AUT_IPC);
+	ADD_U_CHAR(dptr, type);
+	ADD_U_INT32(dptr, id);
+
+	return (t);
+}
+
+/*
+ * token ID                1 byte
+ * owner user ID           4 bytes
+ * owner group ID          4 bytes
+ * creator user ID         4 bytes
+ * creator group ID        4 bytes
+ * access mode             4 bytes
+ * slot sequence #         4 bytes
+ * key                     4 bytes
+ */
+token_t *
+au_to_ipc_perm(struct ipc_perm *perm)
+{
+	token_t *t;
+	u_char *dptr = NULL;
+	u_int16_t pad0 = 0;
+
+	GET_TOKEN_AREA(t, dptr, 12 * sizeof(u_int16_t) + sizeof(u_int32_t));
+
+	ADD_U_CHAR(dptr, AUT_IPC_PERM);
+
+	/*
+	 * Darwin defines the sizes for ipc_perm members
+	 * as 2 bytes; BSM defines 4 so pad with 0
+	 */
+	ADD_U_INT16(dptr, pad0);
+	ADD_U_INT16(dptr, perm->uid);
+
+	ADD_U_INT16(dptr, pad0);
+	ADD_U_INT16(dptr, perm->gid);
+
+	ADD_U_INT16(dptr, pad0);
+	ADD_U_INT16(dptr, perm->cuid);
+
+	ADD_U_INT16(dptr, pad0);
+	ADD_U_INT16(dptr, perm->cgid);
+
+	ADD_U_INT16(dptr, pad0);
+	ADD_U_INT16(dptr, perm->mode);
+
+	ADD_U_INT16(dptr, pad0);
+	ADD_U_INT16(dptr, perm->seq);
+
+	ADD_U_INT32(dptr, perm->key);
+
+	return (t);
+}
+
+/*
+ * token ID                1 byte
+ * port IP address         2 bytes
+ */
+token_t *
+au_to_iport(u_int16_t iport)
+{
+	token_t *t;
+	u_char *dptr = NULL;
+
+	GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int16_t));
+
+	ADD_U_CHAR(dptr, AUT_IPORT);
+	ADD_U_INT16(dptr, iport);
+
+	return (t);
+}
+
+/*
+ * token ID                1 byte
+ * size                    2 bytes
+ * data                    size bytes
+ */
+token_t *
+au_to_opaque(char *data, u_int16_t bytes)
+{
+	token_t *t;
+	u_char *dptr = NULL;
+
+	GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int16_t) + bytes);
+
+	ADD_U_CHAR(dptr, AUT_OPAQUE);
+	ADD_U_INT16(dptr, bytes);
+	ADD_MEM(dptr, data, bytes);
+
+	return (t);
+}
+
+/*
+ * token ID                1 byte
+ * seconds of time         4 bytes
+ * milliseconds of time    4 bytes
+ * file name len           2 bytes
+ * file pathname           N bytes + 1 terminating NULL byte
+ */
+token_t *
+au_to_file(char *file, struct timeval tm)
+{
+	token_t *t;
+	u_char *dptr = NULL;
+	u_int16_t filelen;
+	u_int32_t timems;
+
+	filelen = strlen(file);
+	filelen += 1;
+
+	GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 2 * sizeof(u_int32_t) +
+	    sizeof(u_int16_t) + filelen);
+
+	timems = tm.tv_usec/1000;
+
+	ADD_U_CHAR(dptr, AUT_OTHER_FILE32);
+	ADD_U_INT32(dptr, tm.tv_sec);
+	ADD_U_INT32(dptr, timems);	/* We need time in ms. */
+	ADD_U_INT16(dptr, filelen);
+	ADD_STRING(dptr, file, filelen);
+
+	return (t);
+}
+
+/*
+ * token ID                1 byte
+ * text length             2 bytes
+ * text                    N bytes + 1 terminating NULL byte
+ */
+token_t *
+au_to_text(char *text)
+{
+	token_t *t;
+	u_char *dptr = NULL;
+	u_int16_t textlen;
+
+	textlen = strlen(text);
+	textlen += 1;
+
+	GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int16_t) + textlen);
+
+	ADD_U_CHAR(dptr, AUT_TEXT);
+	ADD_U_INT16(dptr, textlen);
+	ADD_STRING(dptr, text, textlen);
+
+	return (t);
+}
+
+/*
+ * token ID                1 byte
+ * path length             2 bytes
+ * path                    N bytes + 1 terminating NULL byte
+ */
+token_t *
+au_to_path(char *text)
+{
+	token_t *t;
+	u_char *dptr = NULL;
+	u_int16_t textlen;
+
+	textlen = strlen(text);
+	textlen += 1;
+
+	GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int16_t) + textlen);
+
+	ADD_U_CHAR(dptr, AUT_PATH);
+	ADD_U_INT16(dptr, textlen);
+	ADD_STRING(dptr, text, textlen);
+
+	return (t);
+}
+
+/*
+ * token ID                1 byte
+ * audit ID                4 bytes
+ * effective user ID       4 bytes
+ * effective group ID      4 bytes
+ * real user ID            4 bytes
+ * real group ID           4 bytes
+ * process ID              4 bytes
+ * session ID              4 bytes
+ * terminal ID
+ *   port ID               4 bytes/8 bytes (32-bit/64-bit value)
+ *   machine address       4 bytes
+ */
+token_t *
+au_to_process32(au_id_t auid, uid_t euid, gid_t egid, uid_t ruid, gid_t rgid,
+    pid_t pid, au_asid_t sid, au_tid_t *tid)
+{
+	token_t *t;
+	u_char *dptr = NULL;
+
+	GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 9 * sizeof(u_int32_t));
+
+	ADD_U_CHAR(dptr, AUT_PROCESS32);
+	ADD_U_INT32(dptr, auid);
+	ADD_U_INT32(dptr, euid);
+	ADD_U_INT32(dptr, egid);
+	ADD_U_INT32(dptr, ruid);
+	ADD_U_INT32(dptr, rgid);
+	ADD_U_INT32(dptr, pid);
+	ADD_U_INT32(dptr, sid);
+	ADD_U_INT32(dptr, tid->port);
+	ADD_MEM(dptr, &tid->machine, sizeof(u_int32_t));
+
+	return (t);
+}
+
+token_t *
+au_to_process64(au_id_t auid, uid_t euid, gid_t egid, uid_t ruid, gid_t rgid,
+    pid_t pid, au_asid_t sid, au_tid_t *tid)
+{
+	token_t *t;
+	u_char *dptr = NULL;
+
+	GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 8 * sizeof(u_int32_t) +
+	    sizeof(u_int64_t));
+
+	ADD_U_CHAR(dptr, AUT_PROCESS64);
+	ADD_U_INT32(dptr, auid);
+	ADD_U_INT32(dptr, euid);
+	ADD_U_INT32(dptr, egid);
+	ADD_U_INT32(dptr, ruid);
+	ADD_U_INT32(dptr, rgid);
+	ADD_U_INT32(dptr, pid);
+	ADD_U_INT32(dptr, sid);
+	ADD_U_INT64(dptr, tid->port);
+	ADD_MEM(dptr, &tid->machine, sizeof(u_int32_t));
+
+	return (t);
+}
+
+token_t *
+au_to_process(au_id_t auid, uid_t euid, gid_t egid, uid_t ruid, gid_t rgid,
+    pid_t pid, au_asid_t sid, au_tid_t *tid)
+{
+
+	return (au_to_process32(auid, euid, egid, ruid, rgid, pid, sid,
+	    tid));
+}
+
+/*
+ * token ID                1 byte
+ * audit ID                4 bytes
+ * effective user ID       4 bytes
+ * effective group ID      4 bytes
+ * real user ID            4 bytes
+ * real group ID           4 bytes
+ * process ID              4 bytes
+ * session ID              4 bytes
+ * terminal ID
+ *   port ID               4 bytes/8 bytes (32-bit/64-bit value)
+ *   address type-len      4 bytes
+ *   machine address    4/16 bytes
+ */
+token_t *
+au_to_process32_ex(au_id_t auid, uid_t euid, gid_t egid, uid_t ruid,
+    gid_t rgid, pid_t pid, au_asid_t sid, au_tid_addr_t *tid)
+{
+	token_t *t;
+	u_char *dptr = NULL;
+
+	KASSERT((tid->at_type == AU_IPv4) || (tid->at_type == AU_IPv6),
+	    ("au_to_process32_ex: type %u", (unsigned int)tid->at_type));
+	if (tid->at_type == AU_IPv6)
+		GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 13 *
+		    sizeof(u_int32_t));
+	else
+		GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 10 *
+		    sizeof(u_int32_t));
+
+	ADD_U_CHAR(dptr, AUT_PROCESS32_EX);
+	ADD_U_INT32(dptr, auid);
+	ADD_U_INT32(dptr, euid);
+	ADD_U_INT32(dptr, egid);
+	ADD_U_INT32(dptr, ruid);
+	ADD_U_INT32(dptr, rgid);
+	ADD_U_INT32(dptr, pid);
+	ADD_U_INT32(dptr, sid);
+	ADD_U_INT32(dptr, tid->at_port);
+	ADD_U_INT32(dptr, tid->at_type);
+        if (tid->at_type == AU_IPv6)
+                ADD_MEM(dptr, &tid->at_addr[0], 4 * sizeof(u_int32_t));
+        else
+                ADD_MEM(dptr, &tid->at_addr[0], sizeof(u_int32_t));
+
+	return (t);
+}
+
+token_t *
+au_to_process64_ex(au_id_t auid, uid_t euid, gid_t egid, uid_t ruid,
+    gid_t rgid, pid_t pid, au_asid_t sid, au_tid_addr_t *tid)
+{
+	token_t *t;
+	u_char *dptr = NULL;
+
+	if (tid->at_type == AU_IPv4)
+		GET_TOKEN_AREA(t, dptr, sizeof(u_char) +
+		    7 * sizeof(u_int32_t) + sizeof(u_int64_t) +
+		    2 * sizeof(u_int32_t));
+	else if (tid->at_type == AU_IPv6)
+		GET_TOKEN_AREA(t, dptr, sizeof(u_char) +
+		    7 * sizeof(u_int32_t) + sizeof(u_int64_t) +
+		    5 * sizeof(u_int32_t));
+	else
+		panic("au_to_process64_ex: invalidate at_type (%d)",
+		    tid->at_type);
+
+	ADD_U_CHAR(dptr, AUT_PROCESS64_EX);
+	ADD_U_INT32(dptr, auid);
+	ADD_U_INT32(dptr, euid);
+	ADD_U_INT32(dptr, egid);
+	ADD_U_INT32(dptr, ruid);
+	ADD_U_INT32(dptr, rgid);
+	ADD_U_INT32(dptr, pid);
+	ADD_U_INT32(dptr, sid);
+	ADD_U_INT64(dptr, tid->at_port);
+	ADD_U_INT32(dptr, tid->at_type);
+	ADD_MEM(dptr, &tid->at_addr[0], sizeof(u_int32_t));
+	if (tid->at_type == AU_IPv6) {
+		ADD_MEM(dptr, &tid->at_addr[1], sizeof(u_int32_t));
+		ADD_MEM(dptr, &tid->at_addr[2], sizeof(u_int32_t));
+		ADD_MEM(dptr, &tid->at_addr[3], sizeof(u_int32_t));
+	}
+
+	return (t);
+}
+
+token_t *
+au_to_process_ex(au_id_t auid, uid_t euid, gid_t egid, uid_t ruid,
+    gid_t rgid, pid_t pid, au_asid_t sid, au_tid_addr_t *tid)
+{
+
+	return (au_to_process32_ex(auid, euid, egid, ruid, rgid, pid, sid,
+	    tid));
+}
+
+/*
+ * token ID                1 byte
+ * error status            1 byte
+ * return value            4 bytes/8 bytes (32-bit/64-bit value)
+ */
+token_t *
+au_to_return32(char status, u_int32_t ret)
+{
+	token_t *t;
+	u_char *dptr = NULL;
+
+	GET_TOKEN_AREA(t, dptr, 2 * sizeof(u_char) + sizeof(u_int32_t));
+
+	ADD_U_CHAR(dptr, AUT_RETURN32);
+	ADD_U_CHAR(dptr, status);
+	ADD_U_INT32(dptr, ret);
+
+	return (t);
+}
+
+token_t *
+au_to_return64(char status, u_int64_t ret)
+{
+	token_t *t;
+	u_char *dptr = NULL;
+
+	GET_TOKEN_AREA(t, dptr, 2 * sizeof(u_char) + sizeof(u_int64_t));
+
+	ADD_U_CHAR(dptr, AUT_RETURN64);
+	ADD_U_CHAR(dptr, status);
+	ADD_U_INT64(dptr, ret);
+
+	return (t);
+}
+
+token_t *
+au_to_return(char status, u_int32_t ret)
+{
+
+	return (au_to_return32(status, ret));
+}
+
+/*
+ * token ID                1 byte
+ * sequence number         4 bytes
+ */
+token_t *
+au_to_seq(long audit_count)
+{
+	token_t *t;
+	u_char *dptr = NULL;
+
+	GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int32_t));
+
+	ADD_U_CHAR(dptr, AUT_SEQ);
+	ADD_U_INT32(dptr, audit_count);
+
+	return (t);
+}
+
+/*
+ * token ID                1 byte
+ * socket type             2 bytes
+ * local port              2 bytes
+ * local Internet address  4 bytes
+ * remote port             2 bytes
+ * remote Internet address 4 bytes
+ */
+token_t *
+au_to_socket(struct socket *so)
+{
+
+	/* XXXRW ... */
+	return (NULL);
+}
+
+/*
+ * Kernel-specific version of the above function.
+ */
+#ifdef _KERNEL
+token_t *
+kau_to_socket(struct socket_au_info *soi)
+{
+	token_t *t;
+	u_char *dptr;
+	u_int16_t so_type;
+
+	GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 2 * sizeof(u_int16_t) +
+	    sizeof(u_int32_t) + sizeof(u_int16_t) + sizeof(u_int32_t));
+						 
+	ADD_U_CHAR(dptr, AU_SOCK_TOKEN);
+	/* Coerce the socket type into a short value */
+	so_type = soi->so_type;
+	ADD_U_INT16(dptr, so_type);
+	ADD_U_INT16(dptr, soi->so_lport);
+	ADD_U_INT32(dptr, soi->so_laddr);
+	ADD_U_INT16(dptr, soi->so_rport);
+	ADD_U_INT32(dptr, soi->so_raddr);
+
+	return (t);
+}
+#endif
+
+/*
+ * token ID                1 byte
+ * socket type             2 bytes
+ * local port              2 bytes
+ * address type/length     4 bytes
+ * local Internet address  4 bytes/16 bytes (IPv4/IPv6 address)
+ * remote port             4 bytes
+ * address type/length     4 bytes
+ * remote Internet address 4 bytes/16 bytes (IPv4/IPv6 address)
+ */
+token_t *
+au_to_socket_ex_32(u_int16_t lp, u_int16_t rp, struct sockaddr *la,
+    struct sockaddr *ra)
+{
+
+	return (NULL);
+}
+
+token_t *
+au_to_socket_ex_128(u_int16_t lp, u_int16_t rp, struct sockaddr *la,
+    struct sockaddr *ra)
+{
+
+	return (NULL);
+}
+
+/*
+ * token ID                1 byte
+ * socket family           2 bytes
+ * path                    104 bytes
+ */
+token_t *
+au_to_sock_unix(struct sockaddr_un *so)
+{
+	token_t *t;
+	u_char *dptr;
+
+	GET_TOKEN_AREA(t, dptr, 3 * sizeof(u_char) + strlen(so->sun_path) + 1);
+
+	ADD_U_CHAR(dptr, AU_SOCK_UNIX_TOKEN);
+	/* BSM token has two bytes for family */
+	ADD_U_CHAR(dptr, 0);
+	ADD_U_CHAR(dptr, so->sun_family);
+	ADD_STRING(dptr, so->sun_path, strlen(so->sun_path) + 1);
+
+	return (t);
+}
+
+/*
+ * token ID                1 byte
+ * socket family           2 bytes
+ * local port              2 bytes
+ * socket address          4 bytes
+ */
+token_t *
+au_to_sock_inet32(struct sockaddr_in *so)
+{
+	token_t *t;
+	u_char *dptr = NULL;
+	uint16_t family;
+
+	GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 2 * sizeof(uint16_t) +
+	    sizeof(uint32_t));
+
+	ADD_U_CHAR(dptr, AUT_SOCKINET32);
+	/*
+	 * BSM defines the family field as 16 bits, but many operating
+	 * systems have an 8-bit sin_family field.  Extend to 16 bits before
+	 * writing into the token.  Assume that both the port and the address
+	 * in the sockaddr_in are already in network byte order, but family
+	 * is in local byte order.
+	 *
+	 * XXXRW: Should a name space conversion be taking place on the value
+	 * of sin_family?
+ 	 */
+	family = so->sin_family;
+	ADD_U_INT16(dptr, family);
+	ADD_MEM(dptr, &so->sin_port, sizeof(uint16_t));
+	ADD_MEM(dptr, &so->sin_addr.s_addr, sizeof(uint32_t));
+
+	return (t);
+
+}
+
+token_t *
+au_to_sock_inet128(struct sockaddr_in6 *so)
+{
+	token_t *t;
+	u_char *dptr = NULL;
+
+	GET_TOKEN_AREA(t, dptr, 3 * sizeof(u_char) + sizeof(u_int16_t) +
+	    4 * sizeof(u_int32_t));
+
+	ADD_U_CHAR(dptr, AUT_SOCKINET128);
+	/*
+	 * In Darwin, sin6_family is one octet, but BSM defines the token
+ 	 * to store two. So we copy in a 0 first.
+ 	 */
+	ADD_U_CHAR(dptr, 0);
+	ADD_U_CHAR(dptr, so->sin6_family);
+
+	ADD_U_INT16(dptr, so->sin6_port);
+	ADD_MEM(dptr, &so->sin6_addr, 4 * sizeof(uint32_t));
+
+	return (t);
+
+}
+
+token_t *
+au_to_sock_inet(struct sockaddr_in *so)
+{
+
+	return (au_to_sock_inet32(so));
+}
+
+/*
+ * token ID                1 byte
+ * audit ID                4 bytes
+ * effective user ID       4 bytes
+ * effective group ID      4 bytes
+ * real user ID            4 bytes
+ * real group ID           4 bytes
+ * process ID              4 bytes
+ * session ID              4 bytes
+ * terminal ID
+ *   port ID               4 bytes/8 bytes (32-bit/64-bit value)
+ *   machine address       4 bytes
+ */
+token_t *
+au_to_subject32(au_id_t auid, uid_t euid, gid_t egid, uid_t ruid, gid_t rgid,
+    pid_t pid, au_asid_t sid, au_tid_t *tid)
+{
+	token_t *t;
+	u_char *dptr = NULL;
+
+	GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 9 * sizeof(u_int32_t));
+
+	ADD_U_CHAR(dptr, AUT_SUBJECT32);
+	ADD_U_INT32(dptr, auid);
+	ADD_U_INT32(dptr, euid);
+	ADD_U_INT32(dptr, egid);
+	ADD_U_INT32(dptr, ruid);
+	ADD_U_INT32(dptr, rgid);
+	ADD_U_INT32(dptr, pid);
+	ADD_U_INT32(dptr, sid);
+	ADD_U_INT32(dptr, tid->port);
+	ADD_MEM(dptr, &tid->machine, sizeof(u_int32_t));
+
+	return (t);
+}
+
+token_t *
+au_to_subject64(au_id_t auid, uid_t euid, gid_t egid, uid_t ruid, gid_t rgid,
+    pid_t pid, au_asid_t sid, au_tid_t *tid)
+{
+	token_t *t;
+	u_char *dptr = NULL;
+
+	GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 7 * sizeof(u_int32_t) +
+	    sizeof(u_int64_t) + sizeof(u_int32_t));
+
+	ADD_U_CHAR(dptr, AUT_SUBJECT64);
+	ADD_U_INT32(dptr, auid);
+	ADD_U_INT32(dptr, euid);
+	ADD_U_INT32(dptr, egid);
+	ADD_U_INT32(dptr, ruid);
+	ADD_U_INT32(dptr, rgid);
+	ADD_U_INT32(dptr, pid);
+	ADD_U_INT32(dptr, sid);
+	ADD_U_INT64(dptr, tid->port);
+	ADD_MEM(dptr, &tid->machine, sizeof(u_int32_t));
+
+	return (t);
+}
+
+token_t *
+au_to_subject(au_id_t auid, uid_t euid, gid_t egid, uid_t ruid, gid_t rgid,
+    pid_t pid, au_asid_t sid, au_tid_t *tid)
+{
+
+	return (au_to_subject32(auid, euid, egid, ruid, rgid, pid, sid,
+	    tid));
+}
+
+/*
+ * token ID                1 byte
+ * audit ID                4 bytes
+ * effective user ID       4 bytes
+ * effective group ID      4 bytes
+ * real user ID            4 bytes
+ * real group ID           4 bytes
+ * process ID              4 bytes
+ * session ID              4 bytes
+ * terminal ID
+ *   port ID               4 bytes/8 bytes (32-bit/64-bit value)
+ *   address type/length   4 bytes
+ *   machine address    4/16 bytes
+ */
+token_t *
+au_to_subject32_ex(au_id_t auid, uid_t euid, gid_t egid, uid_t ruid,
+    gid_t rgid, pid_t pid, au_asid_t sid, au_tid_addr_t *tid)
+{
+	token_t *t;
+	u_char *dptr = NULL;
+
+	KASSERT((tid->at_type == AU_IPv4) || (tid->at_type == AU_IPv6),
+	    ("au_to_subject32_ex: type %u", (unsigned int)tid->at_type));
+	if (tid->at_type == AU_IPv6)
+		GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 13 *
+		    sizeof(u_int32_t));
+	else
+		GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 10 *
+		    sizeof(u_int32_t));
+
+	ADD_U_CHAR(dptr, AUT_SUBJECT32_EX);
+	ADD_U_INT32(dptr, auid);
+	ADD_U_INT32(dptr, euid);
+	ADD_U_INT32(dptr, egid);
+	ADD_U_INT32(dptr, ruid);
+	ADD_U_INT32(dptr, rgid);
+	ADD_U_INT32(dptr, pid);
+	ADD_U_INT32(dptr, sid);
+	ADD_U_INT32(dptr, tid->at_port);
+	ADD_U_INT32(dptr, tid->at_type);
+	if (tid->at_type == AU_IPv6)  
+		ADD_MEM(dptr, &tid->at_addr[0], 4 * sizeof(u_int32_t));
+	else    
+		ADD_MEM(dptr, &tid->at_addr[0], sizeof(u_int32_t));
+
+	return (t);
+}
+
+token_t *
+au_to_subject64_ex(au_id_t auid, uid_t euid, gid_t egid, uid_t ruid,
+    gid_t rgid, pid_t pid, au_asid_t sid, au_tid_addr_t *tid)
+{
+	token_t *t;
+	u_char *dptr = NULL;
+
+	if (tid->at_type == AU_IPv4)
+		GET_TOKEN_AREA(t, dptr, sizeof(u_char) +
+		    7 * sizeof(u_int32_t) + sizeof(u_int64_t) +
+		    2 * sizeof(u_int32_t));
+	else if (tid->at_type == AU_IPv6)
+		GET_TOKEN_AREA(t, dptr, sizeof(u_char) +
+		    7 * sizeof(u_int32_t) + sizeof(u_int64_t) +
+		    5 * sizeof(u_int32_t));
+	else
+		panic("au_to_subject64_ex: invalid at_type (%d)",
+		    tid->at_type);
+
+	ADD_U_CHAR(dptr, AUT_SUBJECT64_EX);
+	ADD_U_INT32(dptr, auid);
+	ADD_U_INT32(dptr, euid);
+	ADD_U_INT32(dptr, egid);
+	ADD_U_INT32(dptr, ruid);
+	ADD_U_INT32(dptr, rgid);
+	ADD_U_INT32(dptr, pid);
+	ADD_U_INT32(dptr, sid);
+	ADD_U_INT64(dptr, tid->at_port);
+	ADD_U_INT32(dptr, tid->at_type);
+	if (tid->at_type == AU_IPv6)
+		ADD_MEM(dptr, &tid->at_addr[0], 4 * sizeof(u_int32_t));
+	else
+		ADD_MEM(dptr, &tid->at_addr[0], sizeof(u_int32_t));
+
+	return (t);
+}
+
+token_t *
+au_to_subject_ex(au_id_t auid, uid_t euid, gid_t egid, uid_t ruid,
+    gid_t rgid, pid_t pid, au_asid_t sid, au_tid_addr_t *tid)
+{
+
+	return (au_to_subject32_ex(auid, euid, egid, ruid, rgid, pid, sid,
+	    tid));
+}
+
+#if !defined(_KERNEL) && !defined(KERNEL) && defined(HAVE_AUDIT_SYSCALLS)
+/*
+ * Collects audit information for the current process
+ * and creates a subject token from it
+ */
+token_t *
+au_to_me(void)
+{
+	auditinfo_t auinfo;
+
+	if (getaudit(&auinfo) != 0)
+		return (NULL);
+
+	return (au_to_subject32(auinfo.ai_auid, geteuid(), getegid(),
+	    getuid(), getgid(), getpid(), auinfo.ai_asid, &auinfo.ai_termid));
+}
+#endif
+
+#if defined(_KERNEL) || defined(KERNEL)
+static token_t *
+au_to_exec_strings(char *strs, int count, u_char type)
+{
+	token_t *t;
+	u_char *dptr = NULL;
+	u_int32_t totlen;
+	int ctr;
+	char *p;
+
+	totlen = 0;
+	ctr = count;
+	p = strs;
+	while (ctr-- > 0) {
+		totlen += strlen(p) + 1;
+		p = strs + totlen;
+	}
+	GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int32_t) + totlen);
+	ADD_U_CHAR(dptr, type);
+	ADD_U_INT32(dptr, count);
+	ADD_STRING(dptr, strs, totlen);
+
+	return (t);
+}
+
+/*
+ * token ID				1 byte
+ * count				4 bytes
+ * text					count null-terminated strings
+ */
+token_t *
+au_to_exec_args(char *args, int argc)
+{
+
+	return (au_to_exec_strings(args, argc, AUT_EXEC_ARGS));
+}
+
+/*
+ * token ID				1 byte
+ * count				4 bytes
+ * text					count null-terminated strings
+ */
+token_t *
+au_to_exec_env(char *envs, int envc)
+{
+
+	return (au_to_exec_strings(envs, envc, AUT_EXEC_ENV));
+}
+#else
+/*
+ * token ID				1 byte
+ * count				4 bytes
+ * text					count null-terminated strings
+ */
+token_t *
+au_to_exec_args(char **argv)
+{
+	token_t *t;
+	u_char *dptr = NULL;
+	const char *nextarg;
+	int i, count = 0;
+	size_t totlen = 0;
+
+	nextarg = *argv;
+
+	while (nextarg != NULL) {
+		int nextlen;
+
+		nextlen = strlen(nextarg);
+		totlen += nextlen + 1;
+		count++;
+		nextarg = *(argv + count);
+	}
+
+	totlen += count * sizeof(char);	/* nul terminations. */
+	GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int32_t) + totlen);
+
+	ADD_U_CHAR(dptr, AUT_EXEC_ARGS);
+	ADD_U_INT32(dptr, count);
+
+	for (i = 0; i < count; i++) {
+		nextarg = *(argv + i);
+		ADD_MEM(dptr, nextarg, strlen(nextarg) + 1);
+	}
+
+	return (t);
+}
+
+/*
+ * token ID                1 byte
+ * zonename length         2 bytes
+ * zonename                N bytes + 1 terminating NULL byte
+ */
+token_t *
+au_to_zonename(char *zonename)
+{
+	u_char *dptr = NULL;
+	u_int16_t textlen;
+	token_t *t;
+
+	textlen = strlen(zonename);
+	textlen += 1;
+	GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int16_t) + textlen);
+	ADD_U_CHAR(dptr, AUT_ZONENAME);
+	ADD_U_INT16(dptr, textlen);
+	ADD_STRING(dptr, zonename, textlen);
+	return (t);
+}
+
+/*
+ * token ID				1 byte
+ * count				4 bytes
+ * text					count null-terminated strings
+ */
+token_t *
+au_to_exec_env(char **envp)
+{
+	token_t *t;
+	u_char *dptr = NULL;
+	int i, count = 0;
+	size_t totlen = 0;
+	const char *nextenv;
+
+	nextenv = *envp;
+
+	while (nextenv != NULL) {
+		int nextlen;
+
+		nextlen = strlen(nextenv);
+		totlen += nextlen + 1;
+		count++;
+		nextenv = *(envp + count);
+	}
+
+	totlen += sizeof(char) * count;
+	GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int32_t) + totlen);
+
+	ADD_U_CHAR(dptr, AUT_EXEC_ENV);
+	ADD_U_INT32(dptr, count);
+
+	for (i = 0; i < count; i++) {
+		nextenv = *(envp + i);
+		ADD_MEM(dptr, nextenv, strlen(nextenv) + 1);
+	}
+
+	return (t);
+}
+#endif
+
+/*
+ * token ID                1 byte
+ * record byte count       4 bytes
+ * version #               1 byte    [2]
+ * event type              2 bytes
+ * event modifier          2 bytes
+ * seconds of time         4 bytes/8 bytes (32-bit/64-bit value)
+ * milliseconds of time    4 bytes/8 bytes (32-bit/64-bit value)
+ */
+token_t *
+au_to_header32_tm(int rec_size, au_event_t e_type, au_emod_t e_mod,
+    struct timeval tm)
+{
+	token_t *t;
+	u_char *dptr = NULL;
+	u_int32_t timems;
+
+	GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int32_t) +
+	    sizeof(u_char) + 2 * sizeof(u_int16_t) + 2 * sizeof(u_int32_t));
+
+	ADD_U_CHAR(dptr, AUT_HEADER32);
+	ADD_U_INT32(dptr, rec_size);
+	ADD_U_CHAR(dptr, AUDIT_HEADER_VERSION_OPENBSM);
+	ADD_U_INT16(dptr, e_type);
+	ADD_U_INT16(dptr, e_mod);
+
+	timems = tm.tv_usec/1000;
+	/* Add the timestamp */
+	ADD_U_INT32(dptr, tm.tv_sec);
+	ADD_U_INT32(dptr, timems);	/* We need time in ms. */
+
+	return (t);
+}
+
+token_t *
+au_to_header64_tm(int rec_size, au_event_t e_type, au_emod_t e_mod,
+    struct timeval tm)
+{
+	token_t *t;
+	u_char *dptr = NULL;
+	u_int32_t timems;
+
+	GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int32_t) +
+	    sizeof(u_char) + 2 * sizeof(u_int16_t) + 2 * sizeof(u_int64_t));
+
+	ADD_U_CHAR(dptr, AUT_HEADER64);
+	ADD_U_INT32(dptr, rec_size);
+	ADD_U_CHAR(dptr, AUDIT_HEADER_VERSION_OPENBSM);
+	ADD_U_INT16(dptr, e_type);
+	ADD_U_INT16(dptr, e_mod);
+
+	timems = tm.tv_usec/1000;
+	/* Add the timestamp */
+	ADD_U_INT64(dptr, tm.tv_sec);
+	ADD_U_INT64(dptr, timems);	/* We need time in ms. */
+
+	return (t);
+}
+
+/*
+ * token ID                1 byte
+ * trailer magic number    2 bytes
+ * record byte count       4 bytes
+ */
+token_t *
+au_to_trailer(int rec_size)
+{
+	token_t *t;
+	u_char *dptr = NULL;
+	u_int16_t magic = TRAILER_PAD_MAGIC;
+
+	GET_TOKEN_AREA(t, dptr, sizeof(u_char) + sizeof(u_int16_t) +
+	    sizeof(u_int32_t));
+
+	ADD_U_CHAR(dptr, AUT_TRAILER);
+	ADD_U_INT16(dptr, magic);
+	ADD_U_INT32(dptr, rec_size);
+
+	return (t);
+}
--- /dev/null
+++ sys/security/audit/audit.h
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 1999-2005 Apple Computer, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/security/audit/audit.h,v 1.14 2007/06/27 17:01:14 csjp Exp $
+ */
+
+/*
+ * This header includes function prototypes and type definitions that are
+ * necessary for the kernel as a whole to interact with the audit subsystem.
+ */
+
+#ifndef _SECURITY_AUDIT_KERNEL_H_
+#define	_SEUCRITY_AUDIT_KERNEL_H_
+
+#ifndef _KERNEL
+#error "no user-serviceable parts inside"
+#endif
+
+#include <bsm/audit.h>
+
+#include <sys/file.h>
+#include <sys/sysctl.h>
+
+/*
+ * Audit subsystem condition flags.  The audit_enabled flag is set and
+ * removed automatically as a result of configuring log files, and can be
+ * observed but should not be directly manipulated.  The audit suspension
+ * flag permits audit to be temporarily disabled without reconfiguring the
+ * audit target.
+ */
+extern int	audit_enabled;
+extern int	audit_suspended;
+
+/*
+ * Define the masks for the audited arguments.
+ *
+ * XXXRW: These need to remain in audit.h for now because our vnode and name
+ * lookup audit calls rely on passing in flags to indicate which name or
+ * vnode is being logged.  These should move to audit_private.h when that is
+ * fixed.
+ */
+#define	ARG_EUID		0x0000000000000001ULL
+#define	ARG_RUID		0x0000000000000002ULL
+#define	ARG_SUID		0x0000000000000004ULL
+#define	ARG_EGID		0x0000000000000008ULL
+#define	ARG_RGID		0x0000000000000010ULL
+#define	ARG_SGID		0x0000000000000020ULL
+#define	ARG_PID			0x0000000000000040ULL
+#define	ARG_UID			0x0000000000000080ULL
+#define	ARG_AUID		0x0000000000000100ULL
+#define	ARG_GID			0x0000000000000200ULL
+#define	ARG_FD			0x0000000000000400ULL
+#define	ARG_POSIX_IPC_PERM	0x0000000000000800ULL
+#define	ARG_FFLAGS		0x0000000000001000ULL
+#define	ARG_MODE		0x0000000000002000ULL
+#define	ARG_DEV			0x0000000000004000ULL
+#define	ARG_ADDR		0x0000000000008000ULL
+#define	ARG_LEN			0x0000000000010000ULL
+#define	ARG_MASK		0x0000000000020000ULL
+#define	ARG_SIGNUM		0x0000000000040000ULL
+#define	ARG_LOGIN		0x0000000000080000ULL
+#define	ARG_SADDRINET		0x0000000000100000ULL
+#define	ARG_SADDRINET6		0x0000000000200000ULL
+#define	ARG_SADDRUNIX		0x0000000000400000ULL
+#define	ARG_TERMID_ADDR		0x0000000000400000ULL
+#define	ARG_UNUSED2		0x0000000001000000ULL
+#define	ARG_UPATH1		0x0000000002000000ULL
+#define	ARG_UPATH2		0x0000000004000000ULL
+#define	ARG_TEXT		0x0000000008000000ULL
+#define	ARG_VNODE1		0x0000000010000000ULL
+#define	ARG_VNODE2		0x0000000020000000ULL
+#define	ARG_SVIPC_CMD		0x0000000040000000ULL
+#define	ARG_SVIPC_PERM		0x0000000080000000ULL
+#define	ARG_SVIPC_ID		0x0000000100000000ULL
+#define	ARG_SVIPC_ADDR		0x0000000200000000ULL
+#define	ARG_GROUPSET		0x0000000400000000ULL
+#define	ARG_CMD			0x0000000800000000ULL
+#define	ARG_SOCKINFO		0x0000001000000000ULL
+#define	ARG_ASID		0x0000002000000000ULL
+#define	ARG_TERMID		0x0000004000000000ULL
+#define	ARG_AUDITON		0x0000008000000000ULL
+#define	ARG_VALUE		0x0000010000000000ULL
+#define	ARG_AMASK		0x0000020000000000ULL
+#define	ARG_CTLNAME		0x0000040000000000ULL
+#define	ARG_PROCESS		0x0000080000000000ULL
+#define	ARG_MACHPORT1		0x0000100000000000ULL
+#define	ARG_MACHPORT2		0x0000200000000000ULL
+#define	ARG_EXIT		0x0000400000000000ULL
+#define	ARG_IOVECSTR		0x0000800000000000ULL
+#define	ARG_ARGV		0x0001000000000000ULL
+#define	ARG_ENVV		0x0002000000000000ULL
+#define	ARG_NONE		0x0000000000000000ULL
+#define	ARG_ALL			0xFFFFFFFFFFFFFFFFULL
+
+void	 audit_syscall_enter(unsigned short code, struct thread *td);
+void	 audit_syscall_exit(int error, struct thread *td);
+
+/*
+ * The remaining kernel functions are conditionally compiled in as they are
+ * wrapped by a macro, and the macro should be the only place in the source
+ * tree where these functions are referenced.
+ */
+#ifdef AUDIT
+struct ipc_perm;
+struct sockaddr;
+union auditon_udata;
+void	 audit_arg_addr(void * addr);
+void	 audit_arg_exit(int status, int retval);
+void	 audit_arg_len(int len);
+void	 audit_arg_fd(int fd);
+void	 audit_arg_fflags(int fflags);
+void	 audit_arg_gid(gid_t gid);
+void	 audit_arg_uid(uid_t uid);
+void	 audit_arg_egid(gid_t egid);
+void	 audit_arg_euid(uid_t euid);
+void	 audit_arg_rgid(gid_t rgid);
+void	 audit_arg_ruid(uid_t ruid);
+void	 audit_arg_sgid(gid_t sgid);
+void	 audit_arg_suid(uid_t suid);
+void	 audit_arg_groupset(gid_t *gidset, u_int gidset_size);
+void	 audit_arg_login(char *login);
+void	 audit_arg_ctlname(int *name, int namelen);
+void	 audit_arg_mask(int mask);
+void	 audit_arg_mode(mode_t mode);
+void	 audit_arg_dev(int dev);
+void	 audit_arg_value(long value);
+void	 audit_arg_owner(uid_t uid, gid_t gid);
+void	 audit_arg_pid(pid_t pid);
+void	 audit_arg_process(struct proc *p);
+void	 audit_arg_signum(u_int signum);
+void	 audit_arg_socket(int sodomain, int sotype, int soprotocol);
+void	 audit_arg_sockaddr(struct thread *td, struct sockaddr *sa);
+void	 audit_arg_auid(uid_t auid);
+void	 audit_arg_auditinfo(struct auditinfo *au_info);
+void	 audit_arg_auditinfo_addr(struct auditinfo_addr *au_info);
+void	 audit_arg_upath(struct thread *td, char *upath, u_int64_t flags);
+void	 audit_arg_vnode(struct vnode *vp, u_int64_t flags);
+void	 audit_arg_text(char *text);
+void	 audit_arg_cmd(int cmd);
+void	 audit_arg_svipc_cmd(int cmd);
+void	 audit_arg_svipc_perm(struct ipc_perm *perm);
+void	 audit_arg_svipc_id(int id);
+void	 audit_arg_svipc_addr(void *addr);
+void	 audit_arg_posix_ipc_perm(uid_t uid, gid_t gid, mode_t mode);
+void	 audit_arg_auditon(union auditon_udata *udata);
+void	 audit_arg_file(struct proc *p, struct file *fp);
+void	 audit_arg_argv(char *argv, int argc, int length);
+void	 audit_arg_envv(char *envv, int envc, int length);
+void	 audit_sysclose(struct thread *td, int fd);
+void	 audit_cred_copy(struct ucred *src, struct ucred *dest);
+void	 audit_cred_destroy(struct ucred *cred);
+void	 audit_cred_init(struct ucred *cred);
+void	 audit_cred_kproc0(struct ucred *cred);
+void	 audit_cred_proc1(struct ucred *cred);
+void	 audit_thread_alloc(struct thread *td);
+void	 audit_thread_free(struct thread *td);
+
+/*
+ * Define a macro to wrap the audit_arg_* calls by checking the global
+ * audit_enabled flag before performing the actual call.
+ */
+#define	AUDIT_ARG(op, args...)	do {					\
+	if (audit_enabled)						\
+		audit_arg_ ## op (args);				\
+} while (0)
+
+#define	AUDIT_SYSCALL_ENTER(code, td)	do {				\
+	if (audit_enabled) {						\
+		audit_syscall_enter(code, td);				\
+	}								\
+} while (0)
+
+/*
+ * Wrap the audit_syscall_exit() function so that it is called only when
+ * auditing is enabled, or we have a audit record on the thread.  It is
+ * possible that an audit record was begun before auditing was turned off.
+ */
+#define	AUDIT_SYSCALL_EXIT(error, td)	do {				\
+	if (audit_enabled | (td->td_ar != NULL))			\
+		audit_syscall_exit(error, td);				\
+} while (0)
+
+/*
+ * A Macro to wrap the audit_sysclose() function.
+ */
+#define	AUDIT_SYSCLOSE(td, fd)	do {					\
+	if (audit_enabled)						\
+		audit_sysclose(td, fd);					\
+} while (0)
+
+#else /* !AUDIT */
+
+#define	AUDIT_ARG(op, args...)	do {					\
+} while (0)
+
+#define	AUDIT_SYSCALL_ENTER(code, td)	do {				\
+} while (0)
+
+#define	AUDIT_SYSCALL_EXIT(error, td)	do {				\
+} while (0)
+
+#define	AUDIT_SYSCLOSE(p, fd)	do {					\
+} while (0)
+
+#endif /* AUDIT */
+
+#endif /* !_SECURITY_AUDIT_KERNEL_H_ */
--- /dev/null
+++ sys/security/audit/audit_bsm_klib.c
@@ -0,0 +1,547 @@
+/*
+ * Copyright (c) 1999-2005 Apple Computer, Inc.
+ * Copyright (c) 2005 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/security/audit/audit_bsm_klib.c,v 1.7.2.1 2007/11/02 09:53:32 rwatson Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/fcntl.h>
+#include <sys/filedesc.h>
+#include <sys/libkern.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/proc.h>
+#include <sys/sem.h>
+#include <sys/syscall.h>
+#include <sys/sysctl.h>
+#include <sys/sysent.h>
+#include <sys/vnode.h>
+
+#include <bsm/audit.h>
+#include <bsm/audit_kevents.h>
+#include <security/audit/audit.h>
+#include <security/audit/audit_private.h>
+
+/*
+ * Hash table functions for the audit event number to event class mask
+ * mapping.
+ */
+#define EVCLASSMAP_HASH_TABLE_SIZE 251
+struct evclass_elem {
+	au_event_t event;
+	au_class_t class;
+	LIST_ENTRY(evclass_elem) entry;
+};
+struct evclass_list {
+	LIST_HEAD(, evclass_elem) head;
+};
+
+static MALLOC_DEFINE(M_AUDITEVCLASS, "audit_evclass", "Audit event class");
+static struct mtx		evclass_mtx;
+static struct evclass_list	evclass_hash[EVCLASSMAP_HASH_TABLE_SIZE];
+
+/*
+ * Look up the class for an audit event in the class mapping table.
+ */
+au_class_t
+au_event_class(au_event_t event)
+{
+	struct evclass_list *evcl;
+	struct evclass_elem *evc;
+	au_class_t class;
+
+	mtx_lock(&evclass_mtx);
+	evcl = &evclass_hash[event % EVCLASSMAP_HASH_TABLE_SIZE];
+	class = 0;
+	LIST_FOREACH(evc, &evcl->head, entry) {
+		if (evc->event == event) {
+			class = evc->class;
+			goto out;
+		}
+	}
+out:
+	mtx_unlock(&evclass_mtx);
+	return (class);
+}
+
+/*
+ * Insert a event to class mapping. If the event already exists in the
+ * mapping, then replace the mapping with the new one.
+ *
+ * XXX There is currently no constraints placed on the number of mappings.
+ * May want to either limit to a number, or in terms of memory usage.
+ */
+void
+au_evclassmap_insert(au_event_t event, au_class_t class)
+{
+	struct evclass_list *evcl;
+	struct evclass_elem *evc, *evc_new;
+
+	/*
+	 * Pessimistically, always allocate storage before acquiring mutex.
+	 * Free if there is already a mapping for this event.
+	 */
+	evc_new = malloc(sizeof(*evc), M_AUDITEVCLASS, M_WAITOK);
+
+	mtx_lock(&evclass_mtx);
+	evcl = &evclass_hash[event % EVCLASSMAP_HASH_TABLE_SIZE];
+	LIST_FOREACH(evc, &evcl->head, entry) {
+		if (evc->event == event) {
+			evc->class = class;
+			mtx_unlock(&evclass_mtx);
+			free(evc_new, M_AUDITEVCLASS);
+			return;
+		}
+	}
+	evc = evc_new;
+	evc->event = event;
+	evc->class = class;
+	LIST_INSERT_HEAD(&evcl->head, evc, entry);
+	mtx_unlock(&evclass_mtx);
+}
+
+void
+au_evclassmap_init(void)
+{
+	int i;
+
+	mtx_init(&evclass_mtx, "evclass_mtx", NULL, MTX_DEF);
+	for (i = 0; i < EVCLASSMAP_HASH_TABLE_SIZE; i++)
+		LIST_INIT(&evclass_hash[i].head);
+
+	/*
+	 * Set up the initial event to class mapping for system calls.
+	 *
+	 * XXXRW: Really, this should walk all possible audit events, not all
+	 * native ABI system calls, as there may be audit events reachable
+	 * only through non-native system calls.  It also seems a shame to
+	 * frob the mutex this early.
+	 */
+	for (i = 0; i < SYS_MAXSYSCALL; i++) {
+		if (sysent[i].sy_auevent != AUE_NULL)
+			au_evclassmap_insert(sysent[i].sy_auevent, 0);
+	}
+}
+
+/*
+ * Check whether an event is aditable by comparing the mask of classes this
+ * event is part of against the given mask.
+ */
+int
+au_preselect(au_event_t event, au_class_t class, au_mask_t *mask_p, int sorf)
+{
+	au_class_t effmask = 0;
+
+	if (mask_p == NULL)
+		return (-1);
+
+	/*
+	 * Perform the actual check of the masks against the event.
+	 */
+	if (sorf & AU_PRS_SUCCESS)
+		effmask |= (mask_p->am_success & class);
+
+	if (sorf & AU_PRS_FAILURE)
+		effmask |= (mask_p->am_failure & class);
+
+	if (effmask)
+		return (1);
+	else
+		return (0);
+}
+
+/*
+ * Convert sysctl names and present arguments to events.
+ */
+au_event_t
+ctlname_to_sysctlevent(int name[], uint64_t valid_arg)
+{
+
+	/* can't parse it - so return the worst case */
+	if ((valid_arg & (ARG_CTLNAME | ARG_LEN)) != (ARG_CTLNAME | ARG_LEN))
+		return (AUE_SYSCTL);
+
+	switch (name[0]) {
+	/* non-admin "lookups" treat them special */
+	case KERN_OSTYPE:
+	case KERN_OSRELEASE:
+	case KERN_OSREV:
+	case KERN_VERSION:
+	case KERN_ARGMAX:
+	case KERN_CLOCKRATE:
+	case KERN_BOOTTIME:
+	case KERN_POSIX1:
+	case KERN_NGROUPS:
+	case KERN_JOB_CONTROL:
+	case KERN_SAVED_IDS:
+	case KERN_OSRELDATE:
+	case KERN_DUMMY:
+		return (AUE_SYSCTL_NONADMIN);
+
+	/* only treat the changeable controls as admin */
+	case KERN_MAXVNODES:
+	case KERN_MAXPROC:
+	case KERN_MAXFILES:
+	case KERN_MAXPROCPERUID:
+	case KERN_MAXFILESPERPROC:
+	case KERN_HOSTID:
+	case KERN_SECURELVL:
+	case KERN_HOSTNAME:
+	case KERN_VNODE:
+	case KERN_PROC:
+	case KERN_FILE:
+	case KERN_PROF:
+	case KERN_NISDOMAINNAME:
+	case KERN_UPDATEINTERVAL:
+	case KERN_NTP_PLL:
+	case KERN_BOOTFILE:
+	case KERN_DUMPDEV:
+	case KERN_IPC:
+	case KERN_PS_STRINGS:
+	case KERN_USRSTACK:
+	case KERN_LOGSIGEXIT:
+	case KERN_IOV_MAX:
+	case KERN_MAXID:
+		return ((valid_arg & ARG_VALUE) ?
+			AUE_SYSCTL : AUE_SYSCTL_NONADMIN);
+
+	default:
+		return (AUE_SYSCTL);
+	}
+	/* NOTREACHED */
+}
+
+/*
+ * Convert an open flags specifier into a specific type of open event for
+ * auditing purposes.
+ */
+au_event_t
+flags_and_error_to_openevent(int oflags, int error)
+{
+	au_event_t aevent;
+
+	/*
+	 * Need to check only those flags we care about.
+	 */
+	oflags = oflags & (O_RDONLY | O_CREAT | O_TRUNC | O_RDWR | O_WRONLY);
+
+	/*
+	 * These checks determine what flags are on with the condition that
+	 * ONLY that combination is on, and no other flags are on.
+	 */
+	switch (oflags) {
+	case O_RDONLY:
+		aevent = AUE_OPEN_R;
+		break;
+
+	case (O_RDONLY | O_CREAT):
+		aevent = AUE_OPEN_RC;
+		break;
+
+	case (O_RDONLY | O_CREAT | O_TRUNC):
+		aevent = AUE_OPEN_RTC;
+		break;
+
+	case (O_RDONLY | O_TRUNC):
+		aevent = AUE_OPEN_RT;
+		break;
+
+	case O_RDWR:
+		aevent = AUE_OPEN_RW;
+		break;
+
+	case (O_RDWR | O_CREAT):
+		aevent = AUE_OPEN_RWC;
+		break;
+
+	case (O_RDWR | O_CREAT | O_TRUNC):
+		aevent = AUE_OPEN_RWTC;
+		break;
+
+	case (O_RDWR | O_TRUNC):
+		aevent = AUE_OPEN_RWT;
+		break;
+
+	case O_WRONLY:
+		aevent = AUE_OPEN_W;
+		break;
+
+	case (O_WRONLY | O_CREAT):
+		aevent = AUE_OPEN_WC;
+		break;
+
+	case (O_WRONLY | O_CREAT | O_TRUNC):
+		aevent = AUE_OPEN_WTC;
+		break;
+
+	case (O_WRONLY | O_TRUNC):
+		aevent = AUE_OPEN_WT;
+		break;
+
+	default:
+		aevent = AUE_OPEN;
+		break;
+	}
+
+#if 0
+	/*
+	 * Convert chatty errors to better matching events.  Failures to
+	 * find a file are really just attribute events -- so recast them as
+	 * such.
+	 *
+	 * XXXAUDIT: Solaris defines that AUE_OPEN will never be returned, it
+	 * is just a placeholder.  However, in Darwin we return that in
+	 * preference to other events.  For now, comment this out as we don't
+	 * have a BSM conversion routine for AUE_OPEN.
+	 */
+	switch (aevent) {
+	case AUE_OPEN_R:
+	case AUE_OPEN_RT:
+	case AUE_OPEN_RW:
+	case AUE_OPEN_RWT:
+	case AUE_OPEN_W:
+	case AUE_OPEN_WT:
+		if (error == ENOENT)
+			aevent = AUE_OPEN;
+	}
+#endif
+	return (aevent);
+}
+
+/*
+ * Convert a MSGCTL command to a specific event.
+ */
+int
+msgctl_to_event(int cmd)
+{
+
+	switch (cmd) {
+	case IPC_RMID:
+		return (AUE_MSGCTL_RMID);
+
+	case IPC_SET:
+		return (AUE_MSGCTL_SET);
+
+	case IPC_STAT:
+		return (AUE_MSGCTL_STAT);
+
+	default:
+		/* We will audit a bad command. */
+		return (AUE_MSGCTL);
+	}
+}
+
+/*
+ * Convert a SEMCTL command to a specific event.
+ */
+int
+semctl_to_event(int cmd)
+{
+
+	switch (cmd) {
+	case GETALL:
+		return (AUE_SEMCTL_GETALL);
+
+	case GETNCNT:
+		return (AUE_SEMCTL_GETNCNT);
+
+	case GETPID:
+		return (AUE_SEMCTL_GETPID);
+
+	case GETVAL:
+		return (AUE_SEMCTL_GETVAL);
+
+	case GETZCNT:
+		return (AUE_SEMCTL_GETZCNT);
+
+	case IPC_RMID:
+		return (AUE_SEMCTL_RMID);
+
+	case IPC_SET:
+		return (AUE_SEMCTL_SET);
+
+	case SETALL:
+		return (AUE_SEMCTL_SETALL);
+
+	case SETVAL:
+		return (AUE_SEMCTL_SETVAL);
+
+	case IPC_STAT:
+		return (AUE_SEMCTL_STAT);
+
+	default:
+		/* We will audit a bad command */
+		return (AUE_SEMCTL);
+	}
+}
+
+/*
+ * Convert a command for the auditon() system call to a audit event.
+ */
+int
+auditon_command_event(int cmd)
+{
+
+	switch(cmd) {
+	case A_GETPOLICY:
+		return (AUE_AUDITON_GPOLICY);
+
+	case A_SETPOLICY:
+		return (AUE_AUDITON_SPOLICY);
+
+	case A_GETKMASK:
+		return (AUE_AUDITON_GETKMASK);
+
+	case A_SETKMASK:
+		return (AUE_AUDITON_SETKMASK);
+
+	case A_GETQCTRL:
+		return (AUE_AUDITON_GQCTRL);
+
+	case A_SETQCTRL:
+		return (AUE_AUDITON_SQCTRL);
+
+	case A_GETCWD:
+		return (AUE_AUDITON_GETCWD);
+
+	case A_GETCAR:
+		return (AUE_AUDITON_GETCAR);
+
+	case A_GETSTAT:
+		return (AUE_AUDITON_GETSTAT);
+
+	case A_SETSTAT:
+		return (AUE_AUDITON_SETSTAT);
+
+	case A_SETUMASK:
+		return (AUE_AUDITON_SETUMASK);
+
+	case A_SETSMASK:
+		return (AUE_AUDITON_SETSMASK);
+
+	case A_GETCOND:
+		return (AUE_AUDITON_GETCOND);
+
+	case A_SETCOND:
+		return (AUE_AUDITON_SETCOND);
+
+	case A_GETCLASS:
+		return (AUE_AUDITON_GETCLASS);
+
+	case A_SETCLASS:
+		return (AUE_AUDITON_SETCLASS);
+
+	case A_GETPINFO:
+	case A_SETPMASK:
+	case A_SETFSIZE:
+	case A_GETFSIZE:
+	case A_GETPINFO_ADDR:
+	case A_GETKAUDIT:
+	case A_SETKAUDIT:
+	default:
+		return (AUE_AUDITON);	/* No special record */
+	}
+}
+
+/*
+ * Create a canonical path from given path by prefixing either the root
+ * directory, or the current working directory.  If the process working
+ * directory is NULL, we could use 'rootvnode' to obtain the root directory,
+ * but this results in a volfs name written to the audit log. So we will
+ * leave the filename starting with '/' in the audit log in this case.
+ *
+ * XXXRW: Since we combine two paths here, ideally a buffer of size
+ * MAXPATHLEN * 2 would be passed in.
+ */
+void
+canon_path(struct thread *td, char *path, char *cpath)
+{
+	char *bufp;
+	char *retbuf, *freebuf;
+	struct vnode *vnp;
+	struct filedesc *fdp;
+	int cisr, error, vfslocked;
+
+	WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL,
+	    "canon_path() at %s:%d", __FILE__, __LINE__);
+
+	fdp = td->td_proc->p_fd;
+	bufp = path;
+	cisr = 0;
+	FILEDESC_SLOCK(fdp);
+	if (*(path) == '/') {
+		while (*(bufp) == '/')
+			bufp++;			/* Skip leading '/'s. */
+		/*
+		 * If no process root, or it is the same as the system root,
+		 * audit the path as passed in with a single '/'.
+		 */
+		if ((fdp->fd_rdir == NULL) ||
+		    (fdp->fd_rdir == rootvnode)) {
+			vnp = NULL;
+			bufp--;			/* Restore one '/'. */
+		} else {
+			vnp = fdp->fd_rdir;	/* Use process root. */
+			vref(vnp);
+		}
+	} else {
+		vnp = fdp->fd_cdir;	/* Prepend the current dir. */
+		cisr = (fdp->fd_rdir == fdp->fd_cdir);
+		vref(vnp);
+		bufp = path;
+	}
+	FILEDESC_SUNLOCK(fdp);
+	if (vnp != NULL) {
+		/*
+		 * XXX: vn_fullpath() on FreeBSD is "less reliable" than
+		 * vn_getpath() on Darwin, so this will need more attention
+		 * in the future.  Also, the question and string bounding
+		 * here seems a bit questionable and will also require
+		 * attention.
+		 */
+		vfslocked = VFS_LOCK_GIANT(vnp->v_mount);
+		vn_lock(vnp, LK_EXCLUSIVE | LK_RETRY, td);
+		error = vn_fullpath(td, vnp, &retbuf, &freebuf);
+		if (error == 0) {
+			/* Copy and free buffer allocated by vn_fullpath().
+			 * If the current working directory was the same as
+			 * the root directory, and the path was a relative
+			 * pathname, do not separate the two components with
+			 * the '/' character.
+			 */
+			snprintf(cpath, MAXPATHLEN, "%s%s%s", retbuf,
+			    cisr ? "" : "/", bufp);
+			free(freebuf, M_TEMP);
+		} else
+			cpath[0] = '\0';
+		vput(vnp);
+		VFS_UNLOCK_GIANT(vfslocked);
+	} else
+		strlcpy(cpath, bufp, MAXPATHLEN);
+}
--- /dev/null
+++ sys/security/audit/audit_private.h
@@ -0,0 +1,339 @@
+/*
+ * Copyright (c) 1999-2005 Apple Computer, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/security/audit/audit_private.h,v 1.16 2007/06/01 21:58:58 rwatson Exp $
+ */
+
+/*
+ * This include file contains function prototypes and type definitions used
+ * within the audit implementation.
+ */
+
+#ifndef _SECURITY_AUDIT_PRIVATE_H_
+#define	_SECURITY_AUDIT_PRIVATE_H_
+
+#ifndef _KERNEL
+#error "no user-serviceable parts inside"
+#endif
+
+#include <sys/ipc.h>
+#include <sys/socket.h>
+#include <sys/ucred.h>
+
+#ifdef MALLOC_DECLARE
+MALLOC_DECLARE(M_AUDITBSM);
+MALLOC_DECLARE(M_AUDITDATA);
+MALLOC_DECLARE(M_AUDITPATH);
+MALLOC_DECLARE(M_AUDITTEXT);
+#endif
+
+/*
+ * Audit control variables that are usually set/read via system calls and
+ * used to control various aspects of auditing.
+ */
+extern struct au_qctrl		audit_qctrl;
+extern struct audit_fstat	audit_fstat;
+extern struct au_mask		audit_nae_mask;
+extern int			audit_panic_on_write_fail;
+extern int			audit_fail_stop;
+extern int			audit_argv;
+extern int			audit_arge;
+
+/*
+ * Success/failure conditions for the conversion of a kernel audit record to
+ * BSM format.
+ */
+#define	BSM_SUCCESS	0
+#define	BSM_FAILURE	1
+#define	BSM_NOAUDIT	2
+
+/*
+ * Defines for the kernel audit record k_ar_commit field.  Flags are set to
+ * indicate what sort of record it is, and which preselection mechanism
+ * selected it.
+ */
+#define	AR_COMMIT_KERNEL	0x00000001U
+#define	AR_COMMIT_USER		0x00000010U
+
+#define	AR_PRESELECT_TRAIL	0x00001000U
+#define	AR_PRESELECT_PIPE	0x00002000U
+
+#define	AR_PRESELECT_USER_TRAIL	0x00004000U
+#define	AR_PRESELECT_USER_PIPE	0x00008000U
+
+/*
+ * Audit data is generated as a stream of struct audit_record structures,
+ * linked by struct kaudit_record, and contain storage for possible audit so
+ * that it will not need to be allocated during the processing of a system
+ * call, both improving efficiency and avoiding sleeping at untimely moments.
+ * This structure is converted to BSM format before being written to disk.
+ */
+struct vnode_au_info {
+	mode_t	vn_mode;
+	uid_t	vn_uid;
+	gid_t	vn_gid;
+	dev_t	vn_dev;
+	long	vn_fsid;
+	long	vn_fileid;
+	long	vn_gen;
+};
+
+struct groupset {
+	gid_t	gidset[NGROUPS];
+	u_int	gidset_size;
+};
+
+struct socket_au_info {
+	int 		so_domain;
+	int		so_type;
+	int		so_protocol;
+	in_addr_t	so_raddr;	/* Remote address if INET socket. */
+	in_addr_t	so_laddr;	/* Local address if INET socket. */
+	u_short		so_rport;	/* Remote port. */
+	u_short		so_lport;	/* Local port. */
+};
+
+union auditon_udata {
+	char			*au_path;
+	long			au_cond;
+	long			au_flags;
+	long			au_policy;
+	int			au_trigger;
+	au_evclass_map_t	au_evclass;
+	au_mask_t		au_mask;
+	auditinfo_t		au_auinfo;
+	auditpinfo_t		au_aupinfo;
+	auditpinfo_addr_t	au_aupinfo_addr;
+	au_qctrl_t		au_qctrl;
+	au_stat_t		au_stat;
+	au_fstat_t		au_fstat;
+};
+
+struct posix_ipc_perm {
+	uid_t	pipc_uid;
+	gid_t	pipc_gid;
+	mode_t	pipc_mode;
+};
+
+struct audit_record {
+	/* Audit record header. */
+	u_int32_t		ar_magic;
+	int			ar_event;
+	int			ar_retval; /* value returned to the process */
+	int			ar_errno;  /* return status of system call */
+	struct timespec		ar_starttime;
+	struct timespec		ar_endtime;
+	u_int64_t		ar_valid_arg;  /* Bitmask of valid arguments */
+
+	/* Audit subject information. */
+	struct xucred		ar_subj_cred;
+	uid_t			ar_subj_ruid;
+	gid_t			ar_subj_rgid;
+	gid_t			ar_subj_egid;
+	uid_t			ar_subj_auid; /* Audit user ID */
+	pid_t			ar_subj_asid; /* Audit session ID */
+	pid_t			ar_subj_pid;
+	struct au_tid		ar_subj_term;
+	struct au_tid_addr	ar_subj_term_addr;
+	struct au_mask		ar_subj_amask;
+
+	/* Operation arguments. */
+	uid_t			ar_arg_euid;
+	uid_t			ar_arg_ruid;
+	uid_t			ar_arg_suid;
+	gid_t			ar_arg_egid;
+	gid_t			ar_arg_rgid;
+	gid_t			ar_arg_sgid;
+	pid_t			ar_arg_pid;
+	pid_t			ar_arg_asid;
+	struct au_tid		ar_arg_termid;
+	struct au_tid_addr	ar_arg_termid_addr;
+	uid_t			ar_arg_uid;
+	uid_t			ar_arg_auid;
+	gid_t			ar_arg_gid;
+	struct groupset		ar_arg_groups;
+	int			ar_arg_fd;
+	int			ar_arg_fflags;
+	mode_t			ar_arg_mode;
+	int			ar_arg_dev;
+	long			ar_arg_value;
+	void *			ar_arg_addr;
+	int			ar_arg_len;
+	int			ar_arg_mask;
+	u_int			ar_arg_signum;
+	char			ar_arg_login[MAXLOGNAME];
+	int			ar_arg_ctlname[CTL_MAXNAME];
+	struct socket_au_info	ar_arg_sockinfo;
+	char			*ar_arg_upath1;
+	char			*ar_arg_upath2;
+	char			*ar_arg_text;
+	struct au_mask		ar_arg_amask;
+	struct vnode_au_info	ar_arg_vnode1;
+	struct vnode_au_info	ar_arg_vnode2;
+	int			ar_arg_cmd;
+	int			ar_arg_svipc_cmd;
+	struct ipc_perm		ar_arg_svipc_perm;
+	int			ar_arg_svipc_id;
+	void *			ar_arg_svipc_addr;
+	struct posix_ipc_perm	ar_arg_pipc_perm;
+	union auditon_udata	ar_arg_auditon;
+	char			*ar_arg_argv;
+	int			ar_arg_argc;
+	char			*ar_arg_envv;
+	int			ar_arg_envc;
+	int			ar_arg_exitstatus;
+	int			ar_arg_exitretval;
+	struct sockaddr_storage ar_arg_sockaddr;
+};
+
+/*
+ * Arguments in the audit record are initially not defined; flags are set to
+ * indicate if they are present so they can be included in the audit log
+ * stream only if defined.
+ */
+#define	ARG_IS_VALID(kar, arg)	((kar)->k_ar.ar_valid_arg & (arg))
+#define	ARG_SET_VALID(kar, arg) do {					\
+	(kar)->k_ar.ar_valid_arg |= (arg);				\
+} while (0)
+
+/*
+ * In-kernel version of audit record; the basic record plus queue meta-data.
+ * This record can also have a pointer set to some opaque data that will be
+ * passed through to the audit writing mechanism.
+ */
+struct kaudit_record {
+	struct audit_record		 k_ar;
+	u_int32_t			 k_ar_commit;
+	void				*k_udata;	/* User data. */
+	u_int				 k_ulen;	/* User data length. */
+	struct uthread			*k_uthread;	/* Audited thread. */
+	TAILQ_ENTRY(kaudit_record)	 k_q;
+};
+TAILQ_HEAD(kaudit_queue, kaudit_record);
+
+/*
+ * Functions to manage the allocation, release, and commit of kernel audit
+ * records.
+ */
+void			 audit_abort(struct kaudit_record *ar);
+void			 audit_commit(struct kaudit_record *ar, int error,
+			    int retval);
+struct kaudit_record	*audit_new(int event, struct thread *td);
+
+/*
+ * Functions relating to the conversion of internal kernel audit records to
+ * the BSM file format.
+ */
+struct au_record;
+int	 kaudit_to_bsm(struct kaudit_record *kar, struct au_record **pau);
+int	 bsm_rec_verify(void *rec);
+
+/*
+ * Kernel versions of the libbsm audit record functions.
+ */
+void	 kau_free(struct au_record *rec);
+void	 kau_init(void);
+
+/*
+ * Return values for pre-selection and post-selection decisions.
+ */
+#define	AU_PRS_SUCCESS	1
+#define	AU_PRS_FAILURE	2
+#define	AU_PRS_BOTH	(AU_PRS_SUCCESS|AU_PRS_FAILURE)
+
+/*
+ * Data structures relating to the kernel audit queue.  Ideally, these might
+ * be abstracted so that only accessor methods are exposed.
+ */
+extern struct mtx		audit_mtx;
+extern struct cv		audit_watermark_cv;
+extern struct cv		audit_worker_cv;
+extern struct kaudit_queue	audit_q;
+extern int			audit_q_len;
+extern int			audit_pre_q_len;
+extern int			audit_in_failure;
+
+/*
+ * Flags to use on audit files when opening and closing.
+ */
+#define	AUDIT_OPEN_FLAGS	(FWRITE | O_APPEND)
+#define	AUDIT_CLOSE_FLAGS	(FWRITE | O_APPEND)
+
+#include <sys/fcntl.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+
+/*
+ * Some of the BSM tokenizer functions take different parameters in the
+ * kernel implementations in order to save the copying of large kernel data
+ * structures.  The prototypes of these functions are declared here.
+ */
+token_t		*kau_to_socket(struct socket_au_info *soi);
+
+/*
+ * audit_klib prototypes
+ */
+int		 au_preselect(au_event_t event, au_class_t class,
+		    au_mask_t *mask_p, int sorf);
+au_event_t	 flags_and_error_to_openevent(int oflags, int error);
+void		 au_evclassmap_init(void);
+void		 au_evclassmap_insert(au_event_t event, au_class_t class);
+au_class_t	 au_event_class(au_event_t event);
+au_event_t	 ctlname_to_sysctlevent(int name[], uint64_t valid_arg);
+int		 auditon_command_event(int cmd);
+int		 msgctl_to_event(int cmd);
+int		 semctl_to_event(int cmr);
+void		 canon_path(struct thread *td, char *path, char *cpath);
+
+/*
+ * Audit trigger events notify user space of kernel audit conditions
+ * asynchronously.
+ */
+void		 audit_trigger_init(void);
+int		 send_trigger(unsigned int trigger);
+
+/*
+ * General audit related functions.
+ */
+struct kaudit_record	*currecord(void);
+void			 audit_free(struct kaudit_record *ar);
+void			 audit_shutdown(void *arg, int howto);
+void			 audit_rotate_vnode(struct ucred *cred,
+			    struct vnode *vp);
+void			 audit_worker_init(void);
+
+/*
+ * Audit pipe functions.
+ */
+int	 audit_pipe_preselect(au_id_t auid, au_event_t event,
+	    au_class_t class, int sorf, int trail_select);
+void	 audit_pipe_submit(au_id_t auid, au_event_t event, au_class_t class,
+	    int sorf, int trail_select, void *record, u_int record_len);
+void	 audit_pipe_submit_user(void *record, u_int record_len);
+
+#endif /* ! _SECURITY_AUDIT_PRIVATE_H_ */


More information about the Midnightbsd-cvs mailing list