summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrwatson <rwatson@FreeBSD.org>2006-02-01 20:01:18 +0000
committerrwatson <rwatson@FreeBSD.org>2006-02-01 20:01:18 +0000
commite100506eafc12e959fd8a34a3d8e4cdc9da2ff9f (patch)
tree4cdf557824d88e0f2c4a5193059f38fedaeeb2bd
parent57bf2d086c80402caafcfbaf6bc910bbbb8b35bc (diff)
downloadFreeBSD-src-e100506eafc12e959fd8a34a3d8e4cdc9da2ff9f.zip
FreeBSD-src-e100506eafc12e959fd8a34a3d8e4cdc9da2ff9f.tar.gz
Import kernel audit framework:
- Management of audit state on processes. - Audit system calls to configure process and system audit state. - Reliable audit record queue implementation, audit_worker kernel thread to asynchronously store records on disk. - Audit event argument. - Internal audit data structure -> BSM audit trail conversion library. - Audit event pre-selection. - Audit pseudo-device permitting kernel->user upcalls to notify auditd of kernel audit events. Much work by: wsalamon Obtained from: TrustedBSD Project, Apple Computer, Inc.
-rw-r--r--sys/security/audit/audit.c1083
-rw-r--r--sys/security/audit/audit.h238
-rw-r--r--sys/security/audit/audit_arg.c803
-rw-r--r--sys/security/audit/audit_bsm.c1261
-rw-r--r--sys/security/audit/audit_bsm_klib.c538
-rw-r--r--sys/security/audit/audit_bsm_token.c1181
-rw-r--r--sys/security/audit/audit_private.h300
-rw-r--r--sys/security/audit/audit_syscalls.c652
-rw-r--r--sys/security/audit/audit_trigger.c172
9 files changed, 6228 insertions, 0 deletions
diff --git a/sys/security/audit/audit.c b/sys/security/audit/audit.c
new file mode 100644
index 0000000..b2f1143
--- /dev/null
+++ b/sys/security/audit/audit.c
@@ -0,0 +1,1083 @@
+/*
+ * 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$
+ */
+
+#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 <netinet/in.h>
+#include <netinet/in_pcb.h>
+
+#include <bsm/audit.h>
+#include <bsm/audit_kevents.h>
+#include <security/audit/audit.h>
+#include <security/audit/audit_private.h>
+
+/*
+ * The AUDIT_EXCESSIVELY_VERBOSE define enables a number of
+ * gratuitously noisy printf's to the console. Due to the
+ * volume, it should be left off unless you want your system
+ * to churn a lot whenever the audit record flow gets high.
+ */
+//#define AUDIT_EXCESSIVELY_VERBOSE
+#ifdef AUDIT_EXCESSIVELY_VERBOSE
+#define AUDIT_PRINTF(x) printf x
+#else
+#define AUDIT_PRINTF(X)
+#endif
+
+static MALLOC_DEFINE(M_AUDITPROC, "audit_proc", "Audit process storage");
+static MALLOC_DEFINE(M_AUDITREC, "audit_rec", "Audit event records");
+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");
+
+/*
+ * 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;
+
+/*
+ * Are we currently "failing stop" due to out of disk space?
+ */
+static int audit_in_failure;
+
+/*
+ * Global audit statistiscs.
+ */
+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.
+ */
+static 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.
+ */
+static TAILQ_HEAD(, kaudit_record) audit_q;
+static int audit_q_len;
+static 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.
+ */
+static struct cv audit_cv;
+
+/*
+ * 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;
+
+/*
+ * 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.
+ */
+static struct cv audit_commit_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;
+
+/*
+ * Flags related to Kernel->user-space communication.
+ */
+static int audit_file_rotate_wait;
+
+/*
+ * Perform a deep free of an audit record (core record and referenced objects)
+ */
+static void
+audit_record_free(struct kaudit_record *ar)
+{
+
+ 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);
+ }
+ free(ar, M_AUDITREC);
+}
+
+/*
+ * XXXAUDIT: Should adjust comments below to make it clear that we get to
+ * this point only if we believe we have storage, so not having space here
+ * is a violation of invariants derived from administrative procedures.
+ * I.e., someone else has written to the audit partition, leaving less space
+ * than we accounted for.
+ */
+static int
+audit_record_write(struct vnode *vp, struct kaudit_record *ar,
+ struct ucred *cred, struct thread *td)
+{
+ int ret;
+ long temp;
+ struct au_record *bsm;
+ struct vattr vattr;
+ struct statfs *mnt_stat = &vp->v_mount->mnt_stat;
+ int vfslocked;
+
+ 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. In both cases,
+ * if we're unable to perform the operation, we drop the record
+ * and return. However, this is arguably an assertion failure.
+ * XXX Need a FreeBSD equivalent.
+ */
+ ret = VFS_STATFS(vp->v_mount, mnt_stat, td);
+ if (ret)
+ goto out;
+
+ ret = VOP_GETATTR(vp, &vattr, cred, td);
+ if (ret)
+ goto out;
+
+ /* update the global stats struct */
+ audit_fstat.af_currsz = vattr.va_size;
+
+ /*
+ * XXX Need to decide what to do if the trigger to the audit daemon
+ * fails.
+ */
+
+ /*
+ * If we fall below minimum free blocks (hard limit), tell the audit
+ * daemon to force a rotation off of the file system. We also stop
+ * writing, which means this audit record is probably lost.
+ * If we fall below the minimum percent free blocks (soft limit),
+ * then kindly suggest to the audit daemon to do something.
+ */
+ if (mnt_stat->f_bfree < AUDIT_HARD_LIMIT_FREE_BLOCKS) {
+ send_trigger(AUDIT_TRIGGER_NO_SPACE);
+ /* Hopefully userspace did something about all the previous
+ * triggers that were sent prior to this critical condition.
+ * If fail-stop is set, then we're done; goodnight Gracie.
+ */
+ if (audit_fail_stop)
+ panic("Audit log space exhausted and fail-stop set.");
+ else {
+ audit_suspended = 1;
+ ret = ENOSPC;
+ goto out;
+ }
+ } else
+ /*
+ * Send a message to the audit daemon that disk space
+ * is getting low.
+ *
+ * XXXAUDIT: Check math and block size calculation here.
+ */
+ if (audit_qctrl.aq_minfree != 0) {
+ temp = mnt_stat->f_blocks / (100 /
+ audit_qctrl.aq_minfree);
+ if (mnt_stat->f_bfree < temp)
+ send_trigger(AUDIT_TRIGGER_LOW_SPACE);
+ }
+
+ /* Check if the current log file is full; if so, call for
+ * a log rotate. This is not an exact comparison; we may
+ * write some records over the limit. If that's not
+ * acceptable, then add a fudge factor here.
+ */
+ if ((audit_fstat.af_filesz != 0) &&
+ (audit_file_rotate_wait == 0) &&
+ (vattr.va_size >= audit_fstat.af_filesz)) {
+ audit_file_rotate_wait = 1;
+ send_trigger(AUDIT_TRIGGER_OPEN_NEW);
+ }
+
+ /*
+ * 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 packets 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 &&
+ (unsigned long)
+ ((audit_q_len + audit_pre_q_len + 1) * MAX_AUDIT_RECORD_SIZE) /
+ mnt_stat->f_bsize >= (unsigned long)(mnt_stat->f_bfree)) {
+ printf(
+ "audit_worker: free space below size of audit queue, failing stop\n");
+ audit_in_failure = 1;
+ }
+
+ /*
+ * If there is a user audit record attached to the kernel record,
+ * then write the user record.
+ */
+ /* XXX Need to decide a few things here: IF the user audit
+ * record is written, but the write of the kernel record fails,
+ * what to do? Should the kernel record come before or after the
+ * user record? For now, we write the user record first, and
+ * we ignore errors.
+ */
+ if (ar->k_ar_commit & AR_COMMIT_USER) {
+ ret = vn_rdwr(UIO_WRITE, vp, (void *)ar->k_udata, ar->k_ulen,
+ (off_t)0, UIO_SYSSPACE, IO_APPEND|IO_UNIT, cred, NULL,
+ NULL, td);
+ if (ret)
+ goto out;
+ }
+
+ /*
+ * Convert the internal kernel record to BSM format and write it
+ * out if everything's OK.
+ */
+ if (!(ar->k_ar_commit & AR_COMMIT_KERNEL)) {
+ ret = 0;
+ goto out;
+ }
+
+ /*
+ * XXXAUDIT: Should we actually allow this conversion to fail? With
+ * sleeping memory allocation and invariants checks, perhaps not.
+ */
+ ret = kaudit_to_bsm(ar, &bsm);
+ if (ret == BSM_NOAUDIT) {
+ ret = 0;
+ goto out;
+ }
+
+ /*
+ * XXX: We drop the record on BSM conversion failure, but really
+ * this is an assertion failure.
+ */
+ if (ret == BSM_FAILURE) {
+ AUDIT_PRINTF(("BSM conversion failure\n"));
+ ret = EINVAL;
+ goto out;
+ }
+
+ /*
+ * XXX
+ * We should break the write functionality away from the BSM record
+ * generation and have the BSM generation done before this function
+ * is called. This function will then take the BSM record as a
+ * parameter.
+ */
+ ret = (vn_rdwr(UIO_WRITE, vp, (void *)bsm->data, bsm->len,
+ (off_t)0, UIO_SYSSPACE, IO_APPEND|IO_UNIT, cred, NULL, NULL, td));
+
+ kau_free(bsm);
+
+out:
+ /*
+ * When we're done processing the current record, we have to
+ * check to see if we're in a failure mode, and if so, whether
+ * this was the last record left to be drained. If we're done
+ * draining, then we fsync the vnode and panic.
+ */
+ if (audit_in_failure &&
+ 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 (ret);
+}
+
+/*
+ * 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)
+{
+ int do_replacement_signal, error;
+ TAILQ_HEAD(, kaudit_record) ar_worklist;
+ struct kaudit_record *ar;
+ struct vnode *audit_vp, *old_vp;
+ int vfslocked;
+
+ struct ucred *audit_cred, *old_cred;
+ struct thread *audit_td;
+
+ AUDIT_PRINTF(("audit_worker starting\n"));
+
+ /*
+ * 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) {
+ /*
+ * First priority: replace the audit log target if requested.
+ * Accessing the vnode here requires dropping the audit_mtx;
+ * in case another replacement was scheduled while the mutex
+ * was released, we loop.
+ *
+ * XXX It could well be we should drain existing records
+ * first to ensure that the timestamps and ordering
+ * are right.
+ */
+ do_replacement_signal = 0;
+ while (audit_replacement_flag != 0) {
+ old_cred = audit_cred;
+ old_vp = audit_vp;
+ audit_cred = audit_replacement_cred;
+ audit_vp = audit_replacement_vp;
+ audit_replacement_cred = NULL;
+ audit_replacement_vp = NULL;
+ audit_replacement_flag = 0;
+
+ audit_enabled = (audit_vp != NULL);
+
+ /*
+ * XXX: What to do about write failures here?
+ */
+ if (old_vp != NULL) {
+ AUDIT_PRINTF(("Closing old audit file\n"));
+ 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;
+ AUDIT_PRINTF(("Audit file closed\n"));
+ }
+ if (audit_vp != NULL) {
+ AUDIT_PRINTF(("Opening new audit file\n"));
+ }
+ 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);
+
+ /*
+ * Next, check to see if we have any records to drain into
+ * the vnode. If not, go back to waiting for an event.
+ */
+ if (TAILQ_EMPTY(&audit_q)) {
+ AUDIT_PRINTF(("audit_worker waiting\n"));
+ cv_wait(&audit_cv, &audit_mtx);
+ AUDIT_PRINTF(("audit_worker woken up\n"));
+ AUDIT_PRINTF(("audit_worker: new vp = %p; value of flag %d\n",
+ audit_replacement_vp, audit_replacement_flag));
+ continue;
+ }
+
+ /*
+ * If we have records, but there's no active vnode to
+ * write to, drain the record queue. Generally, we
+ * prevent the unnecessary allocation of records
+ * elsewhere, but we need to allow for races between
+ * conditional allocation and queueing. Go back to
+ * waiting when we're done.
+ *
+ * XXX: We go out of our way to avoid calling
+ * audit_record_free().
+ * with the audit_mtx held, to avoid a lock order reversal
+ * as free() may grab Giant. This should be fixed at
+ * some point.
+ */
+ if (audit_vp == NULL) {
+ while ((ar = TAILQ_FIRST(&audit_q))) {
+ TAILQ_REMOVE(&audit_q, ar, k_q);
+ audit_q_len--;
+ if (audit_q_len <= audit_qctrl.aq_lowater)
+ cv_broadcast(&audit_commit_cv);
+
+ TAILQ_INSERT_TAIL(&ar_worklist, ar, k_q);
+ }
+ mtx_unlock(&audit_mtx);
+ while ((ar = TAILQ_FIRST(&ar_worklist))) {
+ TAILQ_REMOVE(&ar_worklist, ar, k_q);
+ audit_record_free(ar);
+ }
+ mtx_lock(&audit_mtx);
+ continue;
+ }
+
+ /*
+ * We have both records to write and an active vnode
+ * to write to. Dequeue a record, and start the write.
+ * Eventually, it might make sense to dequeue several
+ * records and perform our own clustering, if the lower
+ * layers aren't doing it automatically enough.
+ *
+ * XXX: We go out of our way to avoid calling
+ * audit_record_free()
+ * with the audit_mtx held, to avoid a lock order reversal
+ * as free() may grab Giant. This should be fixed at
+ * some point.
+ *
+ * XXXAUDIT: free() no longer grabs Giant.
+ */
+ while ((ar = TAILQ_FIRST(&audit_q))) {
+ TAILQ_REMOVE(&audit_q, ar, k_q);
+ audit_q_len--;
+ if (audit_q_len <= audit_qctrl.aq_lowater)
+ cv_broadcast(&audit_commit_cv);
+
+ TAILQ_INSERT_TAIL(&ar_worklist, ar, k_q);
+ }
+
+ mtx_unlock(&audit_mtx);
+ while ((ar = TAILQ_FIRST(&ar_worklist))) {
+ TAILQ_REMOVE(&ar_worklist, ar, k_q);
+ if (audit_vp != NULL) {
+ error = audit_record_write(audit_vp, ar,
+ audit_cred, audit_td);
+ if (error && audit_panic_on_write_fail)
+ panic("audit_worker: write error %d\n",
+ error);
+ else if (error)
+ printf("audit_worker: write error %d\n",
+ error);
+ }
+ audit_record_free(ar);
+ }
+ mtx_lock(&audit_mtx);
+ }
+}
+
+/*
+ * 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)
+{
+ int error;
+
+ printf("Security auditing service present\n");
+ audit_enabled = 0;
+ audit_suspended = 0;
+ audit_panic_on_write_fail = 0;
+ audit_fail_stop = 0;
+ audit_in_failure = 0;
+
+ audit_replacement_vp = NULL;
+ audit_replacement_cred = NULL;
+ audit_replacement_flag = 0;
+
+ audit_fstat.af_filesz = 0; /* '0' means unset, unbounded */
+ audit_fstat.af_currsz = 0;
+ audit_nae_mask.am_success = AU_NULL;
+ audit_nae_mask.am_failure = AU_NULL;
+
+ 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_cv, "audit_cv");
+ cv_init(&audit_replacement_cv, "audit_replacement_cv");
+ cv_init(&audit_commit_cv, "audit_commit_cv");
+ cv_init(&audit_fail_cv, "audit_fail_cv");
+
+ /* Initialize the BSM audit subsystem. */
+ kau_init();
+
+ audit_file_rotate_wait = 0;
+ audit_trigger_init();
+
+ /* Register shutdown handler. */
+ EVENTHANDLER_REGISTER(shutdown_pre_sync, audit_shutdown, NULL,
+ SHUTDOWN_PRI_FIRST);
+
+ error = kthread_create(audit_worker, NULL, &audit_thread, RFHIGHPID,
+ 0, "audit_worker");
+ if (error != 0)
+ panic("audit_init: kthread_create returned %d", error);
+}
+
+SYSINIT(audit_init, SI_SUB_AUDIT, SI_ORDER_FIRST, audit_init, NULL)
+
+/*
+ * 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.
+ *
+ * XXXAUDIT: Enhance the comments below to indicate that we are basically
+ * 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) {
+ AUDIT_PRINTF(("audit_rotate_vnode: sleeping to wait for "
+ "flag\n"));
+ cv_wait(&audit_replacement_cv, &audit_mtx);
+ AUDIT_PRINTF(("audit_rotate_vnode: woken up (flag %d)\n",
+ audit_replacement_flag));
+ }
+ 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_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.
+ */
+ AUDIT_PRINTF(("audit_rotate_vnode: waiting for news of "
+ "replacement\n"));
+ cv_wait(&audit_replacement_cv, &audit_mtx);
+ AUDIT_PRINTF(("audit_rotate_vnode: change acknowledged by "
+ "audit_worker (flag " "now %d)\n", audit_replacement_flag));
+ mtx_unlock(&audit_mtx);
+
+ audit_file_rotate_wait = 0; /* We can now request another rotation */
+}
+
+/*
+ * 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.
+ */
+__inline__ struct kaudit_record *
+currecord(void)
+{
+
+ return (curthread->td_ar);
+}
+
+/*
+ * MPSAFE
+ *
+ * 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;
+
+ /*
+ * Eventually, there may be certain classes of events that
+ * we will audit regardless of the audit state at the time
+ * the record is created. These events will generally
+ * correspond to changes in the audit state. The dummy
+ * code below is from our first prototype, but may also
+ * be used in the final version (with modified event numbers).
+ */
+#if 0
+ if (event != AUDIT_EVENT_FILESTOP && event != AUDIT_EVENT_FILESTART) {
+#endif
+ mtx_lock(&audit_mtx);
+ no_record = (audit_suspended || !audit_enabled);
+ mtx_unlock(&audit_mtx);
+ if (no_record)
+ return (NULL);
+#if 0
+ }
+#endif
+
+ /*
+ * Initialize the audit record header.
+ * XXX: We may want to fail-stop if allocation fails.
+ * XXX: The number of outstanding uncommitted audit records is
+ * limited by the number of concurrent threads servicing system
+ * calls in the kernel.
+ */
+
+ ar = malloc(sizeof(*ar), M_AUDITREC, M_WAITOK);
+ if (ar == NULL)
+ return NULL;
+
+ mtx_lock(&audit_mtx);
+ audit_pre_q_len++;
+ mtx_unlock(&audit_mtx);
+
+ bzero(ar, sizeof(*ar));
+ ar->k_ar.ar_magic = AUDIT_RECORD_MAGIC;
+ ar->k_ar.ar_event = event;
+ nanotime(&ar->k_ar.ar_starttime);
+
+ /*
+ * Export the subject credential.
+ *
+ * XXXAUDIT: td_ucred access is OK without proc lock, but some other
+ * fields here may require the proc lock.
+ */
+ 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_proc->p_au->ai_auid;
+ ar->k_ar.ar_subj_asid = td->td_proc->p_au->ai_asid;
+ ar->k_ar.ar_subj_pid = td->td_proc->p_pid;
+ ar->k_ar.ar_subj_amask = td->td_proc->p_au->ai_mask;
+ ar->k_ar.ar_subj_term = td->td_proc->p_au->ai_termid;
+
+ bcopy(td->td_proc->p_comm, ar->k_ar.ar_subj_comm, MAXCOMLEN);
+
+ return (ar);
+}
+
+/*
+ * MPSAFE
+ */
+void
+audit_commit(struct kaudit_record *ar, int error, int retval)
+{
+ 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.
+ *
+ * XXXAUDIT: Synchronize access to audit_nae_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;
+ }
+
+ if (au_preselect(ar->k_ar.ar_event, aumask, sorf) != 0)
+ ar->k_ar_commit |= AR_COMMIT_KERNEL;
+
+ if ((ar->k_ar_commit & (AR_COMMIT_USER | AR_COMMIT_KERNEL)) == 0) {
+ mtx_lock(&audit_mtx);
+ audit_pre_q_len--;
+ mtx_unlock(&audit_mtx);
+ audit_record_free(ar);
+ return;
+ }
+
+ ar->k_ar.ar_errno = error;
+ ar->k_ar.ar_retval = retval;
+
+ /*
+ * We might want to do some system-wide post-filtering
+ * here at some point.
+ */
+
+ /*
+ * Timestamp system call end.
+ */
+ nanotime(&ar->k_ar.ar_endtime);
+
+ mtx_lock(&audit_mtx);
+
+ /*
+ * Note: it could be that some records initiated while audit was
+ * enabled should still be committed?
+ */
+ if (audit_suspended || !audit_enabled) {
+ audit_pre_q_len--;
+ mtx_unlock(&audit_mtx);
+ audit_record_free(ar);
+ return;
+ }
+
+ /*
+ * Constrain the number of committed audit records based on
+ * the configurable parameter.
+ */
+ while (audit_q_len >= audit_qctrl.aq_hiwater) {
+ AUDIT_PRINTF(("audit_commit: sleeping to wait for "
+ "audit queue to drain below high water mark\n"));
+ cv_wait(&audit_commit_cv, &audit_mtx);
+ AUDIT_PRINTF(("audit_commit: woke up waiting for "
+ "audit queue draining\n"));
+ }
+
+ TAILQ_INSERT_TAIL(&audit_q, ar, k_q);
+ audit_q_len++;
+ audit_pre_q_len--;
+ cv_signal(&audit_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)
+{
+ int audit_event;
+ struct au_mask *aumask;
+
+ 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.
+ *
+ * XXXAUDIT: Should we audit that a bad system call was made, and if
+ * so, how?
+ */
+ if (code >= td->td_proc->p_sysent->sv_size)
+ return;
+
+ audit_event = td->td_proc->p_sysent->sv_table[code].sy_auevent;
+ if (audit_event == AUE_NULL)
+ return;
+
+ /*
+ * Check which audit mask to use; either the kernel non-attributable
+ * event mask or the process audit mask.
+ */
+ if (td->td_proc->p_au->ai_auid == AU_DEFAUDITID)
+ aumask = &audit_nae_mask;
+ else
+ aumask = &td->td_proc->p_au->ai_mask;
+
+ /*
+ * Allocate an audit record, if preselection allows it, and store
+ * in the thread for later use.
+ */
+ if (au_preselect(audit_event, aumask,
+ AU_PRS_FAILURE | AU_PRS_SUCCESS)) {
+ /*
+ * If we're out of space and need to suspend unprivileged
+ * processes, do that here rather than trying to allocate
+ * another audit record.
+ *
+ * XXXRW: 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 && suser(td) != 0) {
+ cv_wait(&audit_fail_cv, &audit_mtx);
+ panic("audit_failing_stop: thread continued");
+ }
+ td->td_ar = audit_new(audit_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);
+ if (td->td_ar != NULL)
+ AUDIT_PRINTF(("audit record committed by pid %d\n",
+ td->td_proc->p_pid));
+ td->td_ar = NULL;
+
+}
+
+/*
+ * Allocate storage for a new process (init, or otherwise).
+ */
+void
+audit_proc_alloc(struct proc *p)
+{
+
+ KASSERT(p->p_au == NULL, ("audit_proc_alloc: p->p_au != NULL (%d)",
+ p->p_pid));
+ p->p_au = malloc(sizeof(*(p->p_au)), M_AUDITPROC, M_WAITOK);
+ /* XXXAUDIT: Zero? Slab allocate? */
+ //printf("audit_proc_alloc: pid %d p_au %p\n", p->p_pid, p->p_au);
+}
+
+/*
+ * Initialize the audit information for the a process, presumably the first
+ * process in the system.
+ * XXX It is not clear what the initial values should be for audit ID,
+ * session ID, etc.
+ */
+void
+audit_proc_kproc0(struct proc *p)
+{
+
+ KASSERT(p->p_au != NULL, ("audit_proc_kproc0: p->p_au == NULL (%d)",
+ p->p_pid));
+ //printf("audit_proc_kproc0: pid %d p_au %p\n", p->p_pid, p->p_au);
+ bzero(p->p_au, sizeof(*(p)->p_au));
+}
+
+void
+audit_proc_init(struct proc *p)
+{
+
+ KASSERT(p->p_au != NULL, ("audit_proc_init: p->p_au == NULL (%d)",
+ p->p_pid));
+ //printf("audit_proc_init: pid %d p_au %p\n", p->p_pid, p->p_au);
+ bzero(p->p_au, sizeof(*(p)->p_au));
+}
+
+/*
+ * Copy the audit info from the parent process to the child process when
+ * a fork takes place.
+ */
+void
+audit_proc_fork(struct proc *parent, struct proc *child)
+{
+
+ PROC_LOCK_ASSERT(parent, MA_OWNED);
+ PROC_LOCK_ASSERT(child, MA_OWNED);
+ KASSERT(parent->p_au != NULL,
+ ("audit_proc_fork: parent->p_au == NULL (%d)", parent->p_pid));
+ KASSERT(child->p_au != NULL,
+ ("audit_proc_fork: child->p_au == NULL (%d)", child->p_pid));
+ //printf("audit_proc_fork: parent pid %d p_au %p\n", parent->p_pid,
+ // parent->p_au);
+ //printf("audit_proc_fork: child pid %d p_au %p\n", child->p_pid,
+ // child->p_au);
+ bcopy(parent->p_au, child->p_au, sizeof(*child->p_au));
+ /*
+ * XXXAUDIT: Zero pointers to external memory, or assert they are
+ * zero?
+ */
+}
+
+/*
+ * Free the auditing structure for the process.
+ */
+void
+audit_proc_free(struct proc *p)
+{
+
+ KASSERT(p->p_au != NULL, ("p->p_au == NULL (%d)", p->p_pid));
+ //printf("audit_proc_free: pid %d p_au %p\n", p->p_pid, p->p_au);
+ /*
+ * XXXAUDIT: Assert that external memory pointers are NULL?
+ */
+ free(p->p_au, M_AUDITPROC);
+ p->p_au = NULL;
+}
diff --git a/sys/security/audit/audit.h b/sys/security/audit/audit.h
new file mode 100644
index 0000000..3dbabcd
--- /dev/null
+++ b/sys/security/audit/audit.h
@@ -0,0 +1,238 @@
+/*
+ * 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$
+ */
+
+/*
+ * This header includes function prototypes and type definitions that are
+ * necessary for the kernel as a whole to interact with the audit subsystem.
+ */
+
+#ifndef _BSM_AUDIT_KERNEL_H
+#define _BSM_AUDIT_KERNEL_H
+
+#ifndef _KERNEL
+#error "no user-serviceable parts inside"
+#endif
+
+#include <bsm/audit.h>
+
+#include <sys/file.h>
+#include <sys/sysctl.h>
+#include <sys/user.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_UNUSED1 0x0000000000800000ULL
+#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_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 *so);
+void audit_arg_auid(uid_t auid);
+void audit_arg_auditinfo(struct auditinfo *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_sysclose(struct thread *td, int fd);
+
+void audit_proc_alloc(struct proc *p);
+void audit_proc_kproc0(struct proc *p);
+void audit_proc_init(struct proc *p);
+void audit_proc_fork(struct proc *parent,
+ struct proc *child);
+void audit_proc_free(struct proc *p);
+
+/*
+ * 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 */
+
+void audit_proc_init(struct proc *p);
+void audit_proc_fork(struct proc *parent,
+ struct proc *child);
+void audit_proc_free(struct proc *p);
+
+#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 /* !_BSM_AUDIT_KERNEL_H */
diff --git a/sys/security/audit/audit_arg.c b/sys/security/audit/audit_arg.c
new file mode 100644
index 0000000..8aaab69
--- /dev/null
+++ b/sys/security/audit/audit_arg.c
@@ -0,0 +1,803 @@
+/*
+ * 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$
+ */
+
+#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/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;
+
+ ar = currecord();
+ if ((ar == NULL) || (p == NULL))
+ return;
+
+ /*
+ * XXXAUDIT: PROC_LOCK_ASSERT(p);
+ */
+ ar->k_ar.ar_arg_auid = p->p_au->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_au->ai_asid;
+ ar->k_ar.ar_arg_termid = p->p_au->ai_termid;
+ ARG_SET_VALID(ar, ARG_AUID | ARG_EUID | ARG_EGID | ARG_RUID |
+ ARG_RGID | ARG_ASID | ARG_TERMID | 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);
+}
+
+/*
+ * XXXAUDIT: Argument here should be 'sa' not 'so'. Caller is responsible
+ * for synchronizing access to the source of the address.
+ */
+void
+audit_arg_sockaddr(struct thread *td, struct sockaddr *so)
+{
+ struct kaudit_record *ar;
+
+ ar = currecord();
+ if (ar == NULL || td == NULL || so == NULL)
+ return;
+
+ bcopy(so, &ar->k_ar.ar_arg_sockaddr, sizeof(ar->k_ar.ar_arg_sockaddr));
+ switch (so->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 *)so)->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_text(char *text)
+{
+ struct kaudit_record *ar;
+
+ ar = currecord();
+ if (ar == NULL)
+ return;
+
+ /*
+ * XXXAUDIT: Why do we accept a possibly NULL string here?
+ */
+ /* Invalidate the text string */
+ ar->k_ar.ar_valid_arg &= (ARG_ALL ^ ARG_TEXT);
+ if (text == NULL)
+ return;
+
+ 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;
+
+ /*
+ * XXXAUDIT: Why is the (ar == NULL) test only in the socket case?
+ */
+ 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:
+ ar = currecord();
+ if (ar == NULL)
+ return;
+
+ /*
+ * XXXAUDIT: Socket locking? Inpcb locking?
+ */
+ so = (struct socket *)fp->f_data;
+ if (INP_CHECK_SOCKAF(so, PF_INET)) {
+ if (so->so_pcb == NULL)
+ return;
+ 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;
+ pcb = (struct inpcb *)so->so_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;
+ 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;
+
+ if (td == NULL || upath == NULL)
+ return; /* nothing to do! */
+
+ /*
+ * XXXAUDIT: Witness warning for possible sleep here?
+ */
+ KASSERT((flag == ARG_UPATH1) || (flag == ARG_UPATH2),
+ ("audit_arg_upath: flag %llu", flag));
+ KASSERT((flag != ARG_UPATH1) || (flag != ARG_UPATH2),
+ ("audit_arg_upath: flag %llu", flag));
+
+ ar = currecord();
+ if (ar == NULL)
+ return;
+
+ 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;
+ struct thread *td;
+
+ /*
+ * XXXAUDIT: Why is vp possibly NULL here?
+ */
+ if (vp == NULL)
+ return;
+
+ /*
+ * 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) /* This will be the case for unaudited system calls */
+ return;
+
+ /*
+ * XXXAUDIT: KASSERT argument validity instead?
+ *
+ * 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 | ARG_VNODE2)) == 0)
+ return;
+
+ td = curthread;
+
+ 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, td->td_ucred, td);
+ 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);
+}
+
+/*
+ * 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 vnode *vp;
+ struct file *fp;
+ int vfslocked;
+
+ 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);
+}
diff --git a/sys/security/audit/audit_bsm.c b/sys/security/audit/audit_bsm.c
new file mode 100644
index 0000000..1a2c5e5
--- /dev/null
+++ b/sys/security/audit/audit_bsm.c
@@ -0,0 +1,1261 @@
+/*
+ * 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$
+ */
+
+#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/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");
+
+/*
+ * Forward declares.
+ */
+static void audit_sys_auditon(struct audit_record *ar,
+ struct au_record *rec);
+
+/*
+ * Initialize the BSM auditing subsystem.
+ */
+void
+kau_init(void)
+{
+
+ printf("BSM auditing present\n");
+ 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 = malloc(MAX_AUDIT_RECORD_SIZE * sizeof(u_char),
+ M_AUDITBSM, M_WAITOK | M_ZERO);
+ 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 + BSM_HEADER_SIZE + BSM_TRAILER_SIZE;
+ if (tot_rec_size <= MAX_AUDIT_RECORD_SIZE) {
+ /* Create the header token */
+ tm.tv_usec = ctime->tv_nsec / 1000;
+ tm.tv_sec = ctime->tv_sec;
+ hdr = au_to_header32(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);
+
+ /* Serialize token data to the record. */
+
+ 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 (ARG_IS_VALID(kar, ARG_PID)) { \
+ if ((ar->ar_arg_pid > 0) /* Kill a single process */ \
+ && (ARG_IS_VALID(kar, ARG_PROCESS))) { \
+ tok = au_to_process(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); \
+ kau_write(rec, tok); \
+ } else { \
+ tok = au_to_arg32(argn, "process", \
+ ar->ar_arg_pid); \
+ 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 */
+ tid.port = ar->ar_subj_term.port;
+ tid.machine = ar->ar_subj_term.machine;
+ 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);
+
+ /* 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) {
+
+ /*
+ * Socket-related events.
+ */
+ case AUE_ACCEPT:
+ case AUE_BIND:
+ case AUE_CONNECT:
+ case AUE_RECVFROM:
+ case AUE_RECVMSG:
+ case AUE_SENDMSG:
+ case AUE_SENDTO:
+ 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)) {
+ 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:
+ break; /* XXX need to add arguments */
+
+ 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_AUDIT:
+ case AUE_GETAUDIT:
+ case AUE_GETAUDIT_ADDR:
+ case AUE_GETAUID:
+ case AUE_GETFSSTAT:
+ case AUE_PIPE:
+ case AUE_SETPGRP:
+ case AUE_SETRLIMIT:
+ case AUE_SETSID:
+ case AUE_SETTIMEOFDAY:
+ case AUE_NEWSYSTEMSHREG:
+ /* Header, subject, and return tokens added at end */
+ break;
+
+ case AUE_ACCESS:
+ case AUE_CHDIR:
+ case AUE_CHROOT:
+ case AUE_EXECVE:
+ case AUE_GETATTRLIST:
+ case AUE_NFS_GETFH:
+ case AUE_LSTAT:
+ case AUE_MKFIFO:
+ 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_TRUNCATE:
+ case AUE_UNDELETE:
+ case AUE_UNLINK:
+ case AUE_UTIMES:
+ UPATH1_VNODE1_TOKENS;
+ 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_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;
+
+ case AUE_FCHDIR:
+ case AUE_FPATHCONF:
+ case AUE_FSTAT: /* XXX Need to handle sockets and shm */
+ case AUE_FSTATFS:
+ case AUE_FSYNC:
+ case AUE_FTRUNCATE:
+ case AUE_FUTIMES:
+ case AUE_GETDIRENTRIES:
+ case AUE_GETDIRENTRIESATTR:
+ 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)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:
+ 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)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)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:
+ /* 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)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_O_CREAT: */ /* AUE_O_CREAT == AUE_OPEN_RWTC */
+ 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)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)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)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;
+
+ default: /* We shouldn't fall through to here. */
+ 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);
+}
diff --git a/sys/security/audit/audit_bsm_klib.c b/sys/security/audit/audit_bsm_klib.c
new file mode 100644
index 0000000..abd78d3
--- /dev/null
+++ b/sys/security/audit/audit_bsm_klib.c
@@ -0,0 +1,538 @@
+/*
+ * 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$
+ */
+
+#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 = AU_NULL;
+ 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, AU_NULL);
+ }
+}
+
+/*
+ * 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_mask_t *mask_p, int sorf)
+{
+ au_class_t effmask = 0;
+ au_class_t ae_class;
+
+ if (mask_p == NULL)
+ return (-1);
+
+ ae_class = au_event_class(event);
+
+ /*
+ * Perform the actual check of the masks against the event.
+ */
+ if (sorf & AU_PRS_SUCCESS)
+ effmask |= (mask_p->am_success & ae_class);
+
+ if (sorf & AU_PRS_FAILURE)
+ effmask |= (mask_p->am_failure & ae_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 directoty, 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 error, vfslocked;
+
+ fdp = td->td_proc->p_fd;
+ bufp = path;
+ FILEDESC_LOCK(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 */
+ vref(vnp);
+ bufp = path;
+ }
+ FILEDESC_UNLOCK(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() */
+ snprintf(cpath, MAXPATHLEN, "%s/%s", retbuf, bufp);
+ free(freebuf, M_TEMP);
+ } else {
+ cpath[0] = '\0';
+ }
+ vput(vnp);
+ VFS_UNLOCK_GIANT(vfslocked);
+ } else {
+ strlcpy(cpath, bufp, MAXPATHLEN);
+ }
+}
diff --git a/sys/security/audit/audit_bsm_token.c b/sys/security/audit/audit_bsm_token.c
new file mode 100644
index 0000000..ed7f108
--- /dev/null
+++ b/sys/security/audit/audit_bsm_token.c
@@ -0,0 +1,1181 @@
+/*
+ * 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.
+ *
+ * $P4: //depot/projects/trustedbsd/audit3/sys/security/audit/audit_bsm_token.c#7 $
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#ifdef __APPLE__
+#include <compat/endian.h>
+#else /* !__APPLE__ */
+#include <sys/endian.h>
+#endif /* __APPLE__*/
+#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 <sys/socketvar.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)
+{
+
+ return (NULL);
+}
+
+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:
+ datasize = AUR_BYTE_SIZE;
+ break;
+
+ case AUR_SHORT:
+ datasize = AUR_SHORT_SIZE;
+ break;
+
+ case AUR_LONG:
+ datasize = AUR_LONG_SIZE;
+ break;
+
+ default:
+ return (NULL);
+ }
+
+ totdata = datasize * unit_count;
+
+ GET_TOKEN_AREA(t, dptr, totdata + 4 * sizeof(u_char));
+
+ 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(BSM_MAX_GROUPS, 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(u_int32_t));
+
+ ADD_U_CHAR(dptr, AUT_IN_ADDR);
+ ADD_U_INT32(dptr, internet_addr->s_addr);
+
+ 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 = AF_INET6;
+
+ GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 5 * sizeof(u_int32_t));
+
+ ADD_U_CHAR(dptr, AUT_IN_ADDR_EX);
+ ADD_U_INT32(dptr, type);
+ ADD_U_INT32(dptr, internet_addr->__u6_addr.__u6_addr32[0]);
+ ADD_U_INT32(dptr, internet_addr->__u6_addr.__u6_addr32[1]);
+ ADD_U_INT32(dptr, internet_addr->__u6_addr.__u6_addr32[2]);
+ ADD_U_INT32(dptr, internet_addr->__u6_addr.__u6_addr32[3]);
+
+ return (t);
+}
+
+/*
+ * token ID 1 byte
+ * ip header 20 bytes
+ */
+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);
+ /*
+ * XXXRW: Any byte order work needed on the IP header before writing?
+ */
+ 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 *
+#if defined(KERNEL) || defined(_KERNEL)
+au_to_file(char *file, struct timeval tm)
+#else
+au_to_file(char *file)
+#endif
+{
+ token_t *t;
+ u_char *dptr = NULL;
+ u_int16_t filelen;
+ u_int32_t timems;
+#if !defined(KERNEL) && !defined(_KERNEL)
+ struct timeval tm;
+ struct timezone tzp;
+
+ if (gettimeofday(&tm, &tzp) == -1)
+ return (NULL);
+#endif
+ /* XXXRW: else ...? */
+
+ 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_U_INT32(dptr, tid->machine);
+
+ return (t);
+}
+
+token_t *
+au_to_process64(__unused au_id_t auid, __unused uid_t euid,
+ __unused gid_t egid, __unused uid_t ruid, __unused gid_t rgid,
+ __unused pid_t pid, __unused au_asid_t sid, __unused au_tid_t *tid)
+{
+
+ return (NULL);
+}
+
+token_t *
+au_to_process(__unused au_id_t auid, __unused uid_t euid,
+ __unused gid_t egid, __unused uid_t ruid, __unused gid_t rgid,
+ __unused pid_t pid, __unused au_asid_t sid, __unused 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 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;
+
+ GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 13 * 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);
+ ADD_U_INT32(dptr, tid->at_addr[0]);
+ ADD_U_INT32(dptr, tid->at_addr[1]);
+ ADD_U_INT32(dptr, tid->at_addr[2]);
+ ADD_U_INT32(dptr, tid->at_addr[3]);
+
+ 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)
+{
+
+ return (NULL);
+}
+
+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;
+
+ GET_TOKEN_AREA(t, dptr, 3 * sizeof(u_char) + sizeof(u_int16_t) +
+ sizeof(u_int32_t));
+
+ ADD_U_CHAR(dptr, AUT_SOCKINET32);
+ /*
+ * In Darwin, sin_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->sin_family);
+ ADD_U_INT16(dptr, so->sin_port);
+ ADD_U_INT32(dptr, so->sin_addr.s_addr);
+
+ 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_U_INT32(dptr, so->sin6_addr.__u6_addr.__u6_addr32[0]);
+ ADD_U_INT32(dptr, so->sin6_addr.__u6_addr.__u6_addr32[1]);
+ ADD_U_INT32(dptr, so->sin6_addr.__u6_addr.__u6_addr32[2]);
+ ADD_U_INT32(dptr, so->sin6_addr.__u6_addr.__u6_addr32[3]);
+
+ 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_U_INT32(dptr, tid->machine);
+
+ 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)
+{
+
+ return (NULL);
+}
+
+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 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;
+
+ GET_TOKEN_AREA(t, dptr, sizeof(u_char) + 13 * 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);
+ ADD_U_INT32(dptr, tid->at_addr[0]);
+ ADD_U_INT32(dptr, tid->at_addr[1]);
+ ADD_U_INT32(dptr, tid->at_addr[2]);
+ ADD_U_INT32(dptr, tid->at_addr[3]);
+
+ 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)
+{
+
+ return (NULL);
+}
+
+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)
+/*
+ * 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
+
+/*
+ * token ID 1 byte
+ * count 4 bytes
+ * text count null-terminated strings
+ */
+token_t *
+au_to_exec_args(const char **args)
+{
+ token_t *t;
+ u_char *dptr = NULL;
+ const char *nextarg;
+ int i, count = 0;
+ size_t totlen = 0;
+
+ nextarg = *args;
+
+ while (nextarg != NULL) {
+ int nextlen;
+
+ nextlen = strlen(nextarg);
+ totlen += nextlen + 1;
+ count++;
+ nextarg = *(args + 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 = *(args + i);
+ ADD_MEM(dptr, nextarg, strlen(nextarg) + 1);
+ }
+
+ return (t);
+}
+
+/*
+ * token ID 1 byte
+ * count 4 bytes
+ * text count null-terminated strings
+ */
+token_t *
+au_to_exec_env(const char **env)
+{
+ token_t *t;
+ u_char *dptr = NULL;
+ int i, count = 0;
+ size_t totlen = 0;
+ const char *nextenv;
+
+ nextenv = *env;
+
+ while (nextenv != NULL) {
+ int nextlen;
+
+ nextlen = strlen(nextenv);
+ totlen += nextlen + 1;
+ count++;
+ nextenv = *(env + 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 = *(env + i);
+ ADD_MEM(dptr, nextenv, strlen(nextenv) + 1);
+ }
+
+ return (t);
+}
+
+/*
+ * 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 *
+#if defined(KERNEL) || defined(_KERNEL)
+au_to_header32(int rec_size, au_event_t e_type, au_emod_t e_mod,
+ struct timeval tm)
+#else
+au_to_header32(int rec_size, au_event_t e_type, au_emod_t e_mod)
+#endif
+{
+ token_t *t;
+ u_char *dptr = NULL;
+ u_int32_t timems;
+#if !defined(KERNEL) && !defined(_KERNEL)
+ struct timeval tm;
+ struct timezone tzp;
+
+ if (gettimeofday(&tm, &tzp) == -1)
+ return (NULL);
+#endif
+ /* XXXRW: else ...? */
+
+ 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, HEADER_VERSION);
+ 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(__unused int rec_size, __unused au_event_t e_type,
+ __unused au_emod_t e_mod)
+{
+
+ return (NULL);
+}
+
+token_t *
+#if defined(KERNEL) || defined(_KERNEL)
+au_to_header(int rec_size, au_event_t e_type, au_emod_t e_mod,
+ struct timeval tm)
+{
+
+ return (au_to_header32(rec_size, e_type, e_mod, tm));
+}
+#else
+au_to_header(int rec_size, au_event_t e_type, au_emod_t e_mod)
+{
+
+ return (au_to_header32(rec_size, e_type, e_mod));
+}
+#endif
+
+/*
+ * 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);
+}
diff --git a/sys/security/audit/audit_private.h b/sys/security/audit/audit_private.h
new file mode 100644
index 0000000..4d6d4b4
--- /dev/null
+++ b/sys/security/audit/audit_private.h
@@ -0,0 +1,300 @@
+/*
+ * 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$
+ */
+
+/*
+ * This include file contains function prototypes and type definitions used
+ * within the audit implementation.
+ */
+
+#ifndef _BSM_AUDIT_PRIVATE_H
+#define _BSM_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;
+
+/*
+ * 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.
+ */
+#define AR_COMMIT_KERNEL 0x00000001U
+#define AR_COMMIT_USER 0x00000010U
+
+/*
+ * 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;
+ char ar_subj_comm[MAXCOMLEN + 1];
+ 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;
+ 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 sockaddr ar_arg_sockaddr;
+ 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;
+ int ar_arg_exitstatus;
+ int ar_arg_exitretval;
+};
+
+/*
+ * 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; /* thread we are auditing */
+ TAILQ_ENTRY(kaudit_record) k_q;
+};
+
+/*
+ * 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.
+ */
+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)
+
+/*
+ * 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>
+#include <security/audit/audit.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_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);
+void send_trigger(unsigned int trigger);
+
+/*
+ * General audit related functions.
+ */
+struct kaudit_record *currecord(void);
+void audit_shutdown(void *arg, int howto);
+void audit_rotate_vnode(struct ucred *cred,
+ struct vnode *vp);
+
+#endif /* ! _BSM_AUDIT_PRIVATE_H */
diff --git a/sys/security/audit/audit_syscalls.c b/sys/security/audit/audit_syscalls.c
new file mode 100644
index 0000000..19f1d30
--- /dev/null
+++ b/sys/security/audit/audit_syscalls.c
@@ -0,0 +1,652 @@
+/*
+ * 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$
+ */
+
+#include <sys/param.h>
+#include <sys/namei.h>
+#include <sys/proc.h>
+#include <sys/sysproto.h>
+#include <sys/systm.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>
+
+#ifdef AUDIT
+
+/*
+ * MPSAFE
+ *
+ * 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;
+
+ error = suser(td);
+ 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;
+ }
+
+ /* 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;
+ 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);
+}
+
+/*
+ * MPSAFE
+ *
+ * System call to manipulate auditing.
+ */
+/* ARGSUSED */
+int
+auditon(struct thread *td, struct auditon_args *uap)
+{
+ int error;
+ union auditon_udata udata;
+ struct proc *tp;
+
+ AUDIT_ARG(cmd, uap->cmd);
+ error = suser(td);
+ if (error)
+ return (error);
+
+ if ((uap->length <= 0) || (uap->length > sizeof(union auditon_udata)))
+ return (EINVAL);
+
+ memset((void *)&udata, 0, sizeof(udata));
+
+ switch (uap->cmd) {
+ /* Some of the GET commands use the arguments too */
+ 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;
+ }
+
+ /* XXX Need to implement these commands by accessing the global
+ * values associated with the commands.
+ *
+ * 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;
+ break;
+
+ case A_SETPOLICY:
+ if (udata.au_policy & ~(AUDIT_CNT|AUDIT_AHLT))
+ 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);
+ 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);
+
+ /* XXXAUDIT: p_cansee()? */
+ if ((tp = pfind(udata.au_aupinfo.ap_pid)) == NULL)
+ return (EINVAL);
+
+ udata.au_aupinfo.ap_auid = tp->p_au->ai_auid;
+ udata.au_aupinfo.ap_mask.am_success =
+ tp->p_au->ai_mask.am_success;
+ udata.au_aupinfo.ap_mask.am_failure =
+ tp->p_au->ai_mask.am_failure;
+ udata.au_aupinfo.ap_termid.machine =
+ tp->p_au->ai_termid.machine;
+ udata.au_aupinfo.ap_termid.port =
+ tp->p_au->ai_termid.port;
+ udata.au_aupinfo.ap_asid = tp->p_au->ai_asid;
+ PROC_UNLOCK(tp);
+ break;
+
+ case A_SETPMASK:
+ if (udata.au_aupinfo.ap_pid < 1)
+ return (EINVAL);
+
+ /* XXXAUDIT: p_cansee()? */
+ if ((tp = pfind(udata.au_aupinfo.ap_pid)) == NULL)
+ return (EINVAL);
+
+ tp->p_au->ai_mask.am_success =
+ udata.au_aupinfo.ap_mask.am_success;
+ tp->p_au->ai_mask.am_failure =
+ udata.au_aupinfo.ap_mask.am_failure;
+ PROC_UNLOCK(tp);
+ 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:
+ return (ENOSYS);
+ 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);
+ send_trigger(udata.au_trigger);
+ break;
+ }
+ /* 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);
+}
+
+/*
+ * MPSAFE
+ *
+ * System calls to manage the user audit information.
+ */
+/* ARGSUSED */
+int
+getauid(struct thread *td, struct getauid_args *uap)
+{
+ int error;
+ au_id_t id;
+
+ error = suser(td);
+ if (error)
+ return (error);
+
+ /*
+ * XXX:
+ * Integer read on static pointer dereference: doesn't need locking?
+ */
+ PROC_LOCK(td->td_proc);
+ id = td->td_proc->p_au->ai_auid;
+ PROC_UNLOCK(td->td_proc);
+ return copyout(&id, uap->auid, sizeof(id));
+}
+
+/* MPSAFE */
+/* ARGSUSED */
+int
+setauid(struct thread *td, struct setauid_args *uap)
+{
+ int error;
+ au_id_t id;
+
+ error = suser(td);
+ if (error)
+ return (error);
+
+ error = copyin(uap->auid, &id, sizeof(id));
+ if (error)
+ return (error);
+
+ audit_arg_auid(id);
+
+ /*
+ * XXX:
+ * Integer write on static pointer dereference: doesn't need locking?
+ *
+ * XXXAUDIT: Might need locking to serialize audit events in the same
+ * order as change events? Or maybe that's an under-solveable
+ * problem.
+ *
+ * XXXRW: Test privilege while holding the proc lock?
+ */
+ PROC_LOCK(td->td_proc);
+ td->td_proc->p_au->ai_auid = id;
+ PROC_UNLOCK(td->td_proc);
+
+ return (0);
+}
+
+/*
+ * MPSAFE
+ * System calls to get and set process audit information.
+ */
+/* ARGSUSED */
+int
+getaudit(struct thread *td, struct getaudit_args *uap)
+{
+ struct auditinfo ai;
+ int error;
+
+ error = suser(td);
+ if (error)
+ return (error);
+
+ PROC_LOCK(td->td_proc);
+ ai = *td->td_proc->p_au;
+ PROC_UNLOCK(td->td_proc);
+
+ return (copyout(&ai, uap->auditinfo, sizeof(ai)));
+}
+
+/* MPSAFE */
+/* ARGSUSED */
+int
+setaudit(struct thread *td, struct setaudit_args *uap)
+{
+ struct auditinfo ai;
+ int error;
+
+ error = suser(td);
+ if (error)
+ return (error);
+
+ error = copyin(uap->auditinfo, &ai, sizeof(ai));
+ if (error)
+ return (error);
+
+ audit_arg_auditinfo(&ai);
+
+ /*
+ * XXXRW: Test privilege while holding the proc lock?
+ */
+ PROC_LOCK(td->td_proc);
+ *td->td_proc->p_au = ai;
+ PROC_UNLOCK(td->td_proc);
+
+ return (0);
+}
+
+/* MPSAFE */
+/* ARGSUSED */
+int
+getaudit_addr(struct thread *td, struct getaudit_addr_args *uap)
+{
+ int error;
+
+ error = suser(td);
+ if (error)
+ return (error);
+ return (ENOSYS);
+}
+
+/* MPSAFE */
+/* ARGSUSED */
+int
+setaudit_addr(struct thread *td, struct setaudit_addr_args *uap)
+{
+ int error;
+
+ error = suser(td);
+ if (error)
+ return (error);
+ return (ENOSYS);
+}
+
+/*
+ * MPSAFE
+ * Syscall to manage audit files.
+ *
+ * XXX: Should generate an audit event.
+ */
+/* ARGSUSED */
+int
+auditctl(struct thread *td, struct auditctl_args *uap)
+{
+ struct nameidata nd;
+ struct ucred *cred;
+ struct vnode *vp;
+ int error = 0;
+ int flags;
+
+ error = suser(td);
+ 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.
+ *
+ * XXXAUDIT: On Darwin, a NULL path is used to disable audit.
+ */
+ if (uap->path == NULL)
+ return (EINVAL);
+
+ /*
+ * XXXAUDIT: Giant may no longer be required here.
+ */
+ mtx_lock(&Giant);
+ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, td);
+ flags = AUDIT_OPEN_FLAGS;
+ error = vn_open(&nd, &flags, 0, -1);
+ if (error) {
+ mtx_unlock(&Giant);
+ goto err_out;
+ }
+ VOP_UNLOCK(nd.ni_vp, 0, td);
+ vp = nd.ni_vp;
+ if (vp->v_type != VREG) {
+ vn_close(vp, AUDIT_CLOSE_FLAGS, td->td_ucred, td);
+ mtx_unlock(&Giant);
+ error = EINVAL;
+ goto err_out;
+ }
+ cred = td->td_ucred;
+ crhold(cred);
+
+ /*
+ * XXXAUDIT: Should audit_suspended actually be cleared by
+ * audit_worker?
+ */
+ audit_suspended = 0;
+
+ mtx_unlock(&Giant);
+ audit_rotate_vnode(cred, vp);
+
+err_out:
+ return (error);
+}
+
+#else /* !AUDIT */
+
+int
+audit(struct thread *td, struct audit_args *uap)
+{
+
+ return (ENOSYS);
+}
+
+int
+auditon(struct thread *td, struct auditon_args *uap)
+{
+
+ return (ENOSYS);
+}
+
+int
+getauid(struct thread *td, struct getauid_args *uap)
+{
+
+ return (ENOSYS);
+}
+
+int
+setauid(struct thread *td, struct setauid_args *uap)
+{
+
+ return (ENOSYS);
+}
+
+int
+getaudit(struct thread *td, struct getaudit_args *uap)
+{
+
+ return (ENOSYS);
+}
+
+int
+setaudit(struct thread *td, struct setaudit_args *uap)
+{
+
+ return (ENOSYS);
+}
+
+int
+getaudit_addr(struct thread *td, struct getaudit_addr_args *uap)
+{
+
+ return (ENOSYS);
+}
+
+int
+setaudit_addr(struct thread *td, struct setaudit_addr_args *uap)
+{
+
+ return (ENOSYS);
+}
+
+int
+auditctl(struct thread *td, struct auditctl_args *uap)
+{
+
+ return (ENOSYS);
+}
+
+void
+audit_proc_init(struct proc *p)
+{
+
+}
+
+void
+audit_proc_fork(struct proc *parent, struct proc *child)
+{
+
+}
+
+void
+audit_proc_free(struct proc *p)
+{
+
+}
+
+#endif /* AUDIT */
diff --git a/sys/security/audit/audit_trigger.c b/sys/security/audit/audit_trigger.c
new file mode 100644
index 0000000..081858b
--- /dev/null
+++ b/sys/security/audit/audit_trigger.c
@@ -0,0 +1,172 @@
+/*-
+ * 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$
+ */
+
+#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.
+ */
+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);
+}
+
+void
+send_trigger(unsigned int trigger)
+{
+ struct trigger_info *ti;
+
+ /* If nobody's listening, we ain't talking. */
+ if (!audit_isopen)
+ return;
+
+ /*
+ * XXXAUDIT: Use a condition variable instead of msleep/wakeup?
+ */
+ ti = malloc(sizeof *ti, M_AUDITTRIGGER, M_WAITOK);
+ mtx_lock(&audit_trigger_mtx);
+ ti->trigger = trigger;
+ TAILQ_INSERT_TAIL(&trigger_list, ti, list);
+ wakeup(&trigger_list);
+ mtx_unlock(&audit_trigger_mtx);
+}
+
+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);
OpenPOWER on IntegriCloud