[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