]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/kern/kern_audit.c
xnu-517.9.5.tar.gz
[apple/xnu.git] / bsd / kern / kern_audit.c
index ce838a4f4273cf7ac4b60373422c36044127bcc2..b183d5339d7fb39296447aea9148c02a106b018e 100644 (file)
@@ -1,28 +1,27 @@
 /*
- * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
- * 
- * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
- * 
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- * 
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ *
+ * The contents of this file constitute Original Code as defined in and
+ * are subject to the Apple Public Source License Version 1.1 (the
+ * "License").  You may not use this file except in compliance with the
+ * License.  Please obtain a copy of the License at
+ * http://www.apple.com/publicsource and read it before using this file.
+ *
+ * This Original Code and all software distributed under the License are
+ * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- * 
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
+ * License for the specific language governing rights and limitations
+ * under the License.
+ *
  * @APPLE_LICENSE_HEADER_END@
  */
+
 #include <sys/param.h>
+#include <sys/file.h>
 #include <sys/fcntl.h>
 #include <sys/kernel.h>
 #include <sys/lock.h>
 #include <sys/uio.h>
 #include <sys/unistd.h>
 #include <sys/vnode.h>
-#include <sys/audit.h>
-#include <sys/kern_audit.h>
 #include <sys/user.h>
-#include <sys/bsm_kevents.h>
-#include <sys/bsm_klib.h>
 #include <sys/syscall.h>
 #include <sys/malloc.h>
 #include <sys/un.h>
+#include <netinet/in.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/domain.h>
+#include <sys/mount.h>
+#include <net/route.h>
+#include <netinet/in_pcb.h>
+
+#include <bsm/audit.h>
+#include <bsm/audit_kevents.h>
+#include <bsm/audit_klib.h>
+#include <bsm/audit_kernel.h>
+
+#include <mach/message.h>
+#include <mach/port.h>
+#include <mach/host_special_ports.h>
+#include <mach/audit_triggers.h>
 
 #include <kern/lock.h>
 #include <kern/wait_queue.h>
+#include <kern/zalloc.h>
+#include <kern/kalloc.h>
+
+#include <audit.h>
 
 #ifdef AUDIT
 
@@ -86,16 +102,26 @@ static mutex_t                             *audit_mtx;
 
 /*
  * Queue of audit records ready for delivery to disk.  We insert new
- * records at the tail, and remove records from the head.
+ * 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 TAILQ_HEAD(, kaudit_record)     audit_q;
+static int                             audit_q_len;
+static int                             audit_pre_q_len;
+
+static wait_queue_t                    audit_wait_queue;
+static zone_t                          audit_zone;
 
 /*
  * 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 wait_queue_t                     audit_wait_queue;
+static int audit_worker_event;
+#define    AUDIT_WORKER_EVENT  ((event_t)&audit_worker_event)
 
 /*
  * When an audit log is rotated, the actual rotation must be performed
@@ -111,18 +137,62 @@ static wait_queue_t                        audit_wait_queue;
  * by the worker thread so a waiting thread can start another replacement.
  * We also store a credential to perform audit log write operations with.
  */
-static wait_queue_t                     audit_replacement_wait_queue;
+static int audit_replacement_event;
+#define    AUDIT_REPLACEMENT_EVENT ((event_t)&audit_replacement_event)
 
-static int                              audit_replacement_flag;
+static int                             audit_replacement_flag;
 static struct vnode                    *audit_replacement_vp;
 static struct ucred                    *audit_replacement_cred;
 
+/*
+ * Wait queue for auditing threads that cannot commit the audit
+ * record at the present time. Also, the queue control parameter
+ * structure.
+ */
+static int audit_commit_event;
+#define    AUDIT_COMMIT_EVENT ((event_t)&audit_commit_event)
+
+static struct au_qctrl                 audit_qctrl;
+
 /*
  * Flags to use on audit files when opening and closing.
  */
 const static int                audit_open_flags = FWRITE | O_APPEND;
 const static int                audit_close_flags = FWRITE | O_APPEND;
 
+/*
+ * Global audit statistiscs. 
+ */
+static struct audit_fstat      audit_fstat;
+
+/*
+ Preselection mask for non-attributable events.
+ */
+static struct au_mask          audit_nae_mask;
+
+/* 
+ * Flags related to Kernel->user-space communication.
+ */
+static int                      audit_file_rotate_wait;
+
+/*
+ * 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?  Are we currently "failing
+ * stop" due to out of disk space?
+ */
+static int                      audit_panic_on_write_fail;
+static int                      audit_fail_stop;
+static int                      audit_in_failure;
+
+/*
+ * When in a fail-stop mode, threads will drop into this wait queue
+ * rather than perform auditable events.  They won't ever get woken
+ * up.
+ */
+static int audit_failure_event;
+#define    AUDIT_FAILURE_EVENT ((event_t)&audit_failure_event)
+
 /*
  * XXX: Couldn't find the include file for this, so copied kern_exec.c's
  * behavior.
@@ -133,32 +203,125 @@ static void
 audit_free(struct kaudit_record *ar)
 {
        if (ar->k_ar.ar_arg_upath1 != NULL) {
-               kmem_free(kernel_map, ar->k_ar.ar_arg_upath1, MAXPATHLEN);
+               kfree((vm_offset_t)ar->k_ar.ar_arg_upath1, MAXPATHLEN);
        }
        if (ar->k_ar.ar_arg_upath2 != NULL) {
-               kmem_free(kernel_map, ar->k_ar.ar_arg_upath2, MAXPATHLEN);
+               kfree((vm_offset_t)ar->k_ar.ar_arg_upath2, MAXPATHLEN);
        }
        if (ar->k_ar.ar_arg_kpath1 != NULL) {
-               kmem_free(kernel_map, ar->k_ar.ar_arg_kpath1, MAXPATHLEN);
+               kfree((vm_offset_t)ar->k_ar.ar_arg_kpath1, MAXPATHLEN);
        }
        if (ar->k_ar.ar_arg_kpath2 != NULL) {
-               kmem_free(kernel_map, ar->k_ar.ar_arg_kpath2, MAXPATHLEN);
+               kfree((vm_offset_t)ar->k_ar.ar_arg_kpath2, MAXPATHLEN);
        }
        if (ar->k_ar.ar_arg_text != NULL) {
-               kmem_free(kernel_map, ar->k_ar.ar_arg_text, MAXPATHLEN);
+               kfree((vm_offset_t)ar->k_ar.ar_arg_text, MAXPATHLEN);
        }
        if (ar->k_udata != NULL) {
-               kmem_free(kernel_map, ar->k_udata, ar->k_ulen);
+               kfree((vm_offset_t)ar->k_udata, (vm_size_t)ar->k_ulen);
        }
-       kmem_free(kernel_map, ar, sizeof(*ar));
+       zfree(audit_zone, (vm_offset_t)ar);
 }
 
 static int
 audit_write(struct vnode *vp, struct kaudit_record *ar, struct ucred *cred,
     struct proc *p)
 {
+       struct statfs *mnt_stat = &vp->v_mount->mnt_stat;
        int ret;
        struct au_record *bsm;
+       struct vattr vattr;
+
+       mach_port_t audit_port;
+
+       /*
+        * 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.
+        */
+       ret = VFS_STATFS(vp->v_mount, mnt_stat, p);
+       if (ret)
+               goto out;
+
+       ret = VOP_GETATTR(vp, &vattr, cred, p);
+       if (ret)
+               goto out;
+
+       /* update the global stats struct */
+       audit_fstat.af_currsz = vattr.va_size; 
+
+       /* 
+        * Send a message to the audit daemon when disk space is getting
+        * low.
+        * XXX Need to decide what to do if the trigger to the audit daemon
+        * fails.
+        */
+       if(host_get_audit_control_port(host_priv_self(), &audit_port) 
+               != KERN_SUCCESS)
+               printf("Cannot get audit control port\n");
+
+       if (audit_port != MACH_PORT_NULL) {
+               long temp;
+
+               /* 
+                * If we fall below percent free blocks, then trigger the
+                * audit daemon to do something about it.
+                */
+               if (audit_qctrl.aq_minfree != 0) {
+                       temp = mnt_stat->f_blocks / (100 / audit_qctrl.aq_minfree);
+                       if (mnt_stat->f_bfree < temp) {
+                               ret = audit_triggers(audit_port, 
+                                       AUDIT_TRIGGER_LOW_SPACE); 
+                               if (ret != KERN_SUCCESS) {
+                                       printf(
+    "Failed audit_triggers(AUDIT_TRIGGER_LOW_SPACE): %d\n", ret);
+                               /*
+                                * XXX: What to do here? Disable auditing?
+                                * panic?
+                                */
+                               }
+                       }
+               }
+               /* 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;
+                       ret = audit_triggers(audit_port, 
+                               AUDIT_TRIGGER_FILE_FULL); 
+                       if (ret != KERN_SUCCESS) {
+                               printf(
+    "Failed audit_triggers(AUDIT_TRIGGER_FILE_FULL): %d\n", ret);
+                       /* XXX what to do here? */
+                       }
+               }
+       }
+
+       /*
+        * 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,
@@ -170,22 +333,36 @@ audit_write(struct vnode *vp, struct kaudit_record *ar, struct ucred *cred,
         * user record? For now, we write the user record first, and
         * we ignore errors.
         */
-       if (ar->k_udata != NULL) {
-               vn_rdwr(UIO_WRITE, vp, (void *)ar->k_udata, ar->k_ulen,
+       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, p);
+               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;
+       }
+
        ret = kaudit_to_bsm(ar, &bsm);
-       if (ret == BSM_NOAUDIT)
-               return (0);
+       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"));
-               return (-1);
+               ret = EINVAL;
+               goto out;
        }
        
        /* XXX This function can be called with the kernel funnel held,
@@ -199,6 +376,21 @@ audit_write(struct vnode *vp, struct kaudit_record *ar, struct ucred *cred,
 
        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, p);
+               (void)VOP_FSYNC(vp, cred, MNT_WAIT, p);
+               VOP_UNLOCK(vp, 0, p);
+               panic("Audit store overflow; record queue drained.");
+       }
+
        return (ret);
 }
 
@@ -209,6 +401,7 @@ audit_worker()
        TAILQ_HEAD(, kaudit_record) ar_worklist;
        struct kaudit_record *ar, *ar_start, *ar_stop;
        struct vnode *audit_vp, *old_vp;
+
        struct ucred *audit_cred, *old_cred;
        struct proc *audit_p;
 
@@ -286,8 +479,8 @@ audit_worker()
                 * successfully.
                 */
                if (do_replacement_signal)
-                       wait_queue_wakeup_all(audit_replacement_wait_queue,
-                           0, THREAD_AWAKENED);
+                       wait_queue_wakeup_all(audit_wait_queue,
+                           AUDIT_REPLACEMENT_EVENT, THREAD_AWAKENED);
 
                /*
                 * Next, check to see if we have any records to drain into
@@ -297,8 +490,9 @@ audit_worker()
                        int ret;
 
                        AUDIT_PRINTF(("audit_worker waiting\n"));
-                       ret = wait_queue_assert_wait(audit_wait_queue, 0, 
-                                                    THREAD_UNINT);
+                       ret = wait_queue_assert_wait(audit_wait_queue,
+                                                    AUDIT_WORKER_EVENT,
+                                                    THREAD_UNINT);
                        mutex_unlock(audit_mtx);
 
                        assert(ret == THREAD_WAITING);
@@ -307,7 +501,6 @@ audit_worker()
                        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));
-
                        mutex_lock(audit_mtx);
                        continue;
                }
@@ -328,6 +521,13 @@ audit_worker()
                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)
+                                       wait_queue_wakeup_one(
+                                               audit_wait_queue,
+                                               AUDIT_COMMIT_EVENT, 
+                                               THREAD_AWAKENED);
+
                                TAILQ_INSERT_TAIL(&ar_worklist, ar, k_q);
                        }
                        mutex_unlock(audit_mtx);
@@ -353,8 +553,15 @@ audit_worker()
                 */
                while ((ar = TAILQ_FIRST(&audit_q))) {
                        TAILQ_REMOVE(&audit_q, ar, k_q);
+                       audit_q_len--;
+                       if (audit_q_len <= audit_qctrl.aq_lowater) {
+                               wait_queue_wakeup_one(audit_wait_queue,
+                                        AUDIT_COMMIT_EVENT, THREAD_AWAKENED);
+                       }
+
                        TAILQ_INSERT_TAIL(&ar_worklist, ar, k_q);
                }
+
                mutex_unlock(audit_mtx);
                release_funnel = 0;
                while ((ar = TAILQ_FIRST(&ar_worklist))) {
@@ -372,7 +579,10 @@ audit_worker()
                                    LEASE_WRITE);
                                error = audit_write(audit_vp, ar, audit_cred,
                                    audit_p);
-                               if (error)
+                               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);
                        }
@@ -399,14 +609,26 @@ audit_init(void)
 
        printf("Security auditing service present\n");
        TAILQ_INIT(&audit_q);
+       audit_q_len = 0;
        audit_enabled = 0;
        audit_suspended = 0;
        audit_replacement_cred = NULL;
        audit_replacement_flag = 0;
+       audit_file_rotate_wait = 0;
        audit_replacement_vp = NULL;
+       audit_fstat.af_filesz = 0;      /* '0' means unset, unbounded */
+       audit_fstat.af_currsz = 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;
+
        audit_mtx = mutex_alloc(ETAP_NO_TRACE);
        audit_wait_queue = wait_queue_alloc(SYNC_POLICY_FIFO);
-       audit_replacement_wait_queue = wait_queue_alloc(SYNC_POLICY_FIFO);
+       audit_zone = zinit(sizeof(struct kaudit_record), 
+                          AQ_HIWATER*sizeof(struct kaudit_record),
+                          8192,
+                          "audit_zone");
 
        /* Initialize the BSM audit subsystem. */
        kau_init();
@@ -428,8 +650,9 @@ audit_rotate_vnode(struct ucred *cred, struct vnode *vp)
 
                AUDIT_PRINTF(("audit_rotate_vnode: sleeping to wait for "
                    "flag\n"));
-               ret = wait_queue_assert_wait(audit_replacement_wait_queue, 0,
-                                            THREAD_UNINT);
+               ret = wait_queue_assert_wait(audit_wait_queue,
+                                            AUDIT_REPLACEMENT_EVENT,
+                                            THREAD_UNINT);
                mutex_unlock(audit_mtx);
 
                assert(ret == THREAD_WAITING);
@@ -448,7 +671,7 @@ audit_rotate_vnode(struct ucred *cred, struct vnode *vp)
         * Wake up the audit worker to perform the exchange once we
         * release the mutex.
         */
-       wait_queue_wakeup_one(audit_wait_queue, 0, THREAD_AWAKENED);
+       wait_queue_wakeup_one(audit_wait_queue, AUDIT_WORKER_EVENT, THREAD_AWAKENED);
 
        /*
         * Wait for the audit_worker to broadcast that a replacement has
@@ -457,8 +680,9 @@ audit_rotate_vnode(struct ucred *cred, struct vnode *vp)
         */
        AUDIT_PRINTF(("audit_rotate_vnode: waiting for news of "
            "replacement\n"));
-       ret = wait_queue_assert_wait(audit_replacement_wait_queue, 0,
-                                    THREAD_UNINT);
+       ret = wait_queue_assert_wait(audit_wait_queue, 
+                                    AUDIT_REPLACEMENT_EVENT,
+                                    THREAD_UNINT);
        mutex_unlock(audit_mtx);
 
        assert(ret == THREAD_WAITING);
@@ -466,6 +690,8 @@ audit_rotate_vnode(struct ucred *cred, struct vnode *vp)
        assert(ret == THREAD_AWAKENED);
        AUDIT_PRINTF(("audit_rotate_vnode: change acknowledged by "
            "audit_worker (flag " "now %d)\n", audit_replacement_flag));
+
+       audit_file_rotate_wait = 0; /* We can now request another rotation */
 }
 
 /*
@@ -474,21 +700,18 @@ audit_rotate_vnode(struct ucred *cred, struct vnode *vp)
 void
 audit_shutdown(void)
 {
-
        audit_rotate_vnode(NULL, NULL);
 }
 
 static __inline__ struct uthread *
 curuthread(void)
 {
-
        return (get_bsdthread_info(current_act()));
 }
 
 static __inline__ struct kaudit_record *
 currecord(void)
 {
-
        return (curuthread()->uu_ar);
 }
 
@@ -516,25 +739,39 @@ audit(struct proc *p, struct audit_args *uap, register_t *retval)
        int error;
        void * rec;
        struct kaudit_record *ar;
-
-       ar = currecord();
-
-       /* XXX: What's the proper error code if a user audit record can't
-        * be written due to auditing off, or otherwise unavailable?
-        */
-       if (ar == NULL)
-               return (ENOTSUP);
+       struct uthread *uthr;
 
        error = suser(pc->pc_ucred, &p->p_acflag);
        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) {
+               uthr = curuthread();
+               if (uthr == NULL)       /* can this happen? */
+                       return (ENOTSUP);
+
+               /* This is not very efficient; we're required to allocate
+                * a complete kernel audit record just so the user record
+                * can tag along.
+                */
+               uthr->uu_ar = audit_new(AUE_NULL, p, uthr);
+               if (uthr->uu_ar == NULL) /* auditing not on, or memory error */
+                       return (ENOTSUP);
+               ar = uthr->uu_ar;
+       }
+
        if (uap->length > MAX_AUDIT_RECORD_SIZE) 
                return (EINVAL);
 
-       error = kmem_alloc(kernel_map, (vm_offset_t *)&rec, uap->length);
-       if (error != KERN_SUCCESS)
-               return(ENOMEM);
+       rec = (void *)kalloc((vm_size_t)uap->length);
 
        error = copyin(uap->record, rec, uap->length);
        if (error)
@@ -551,11 +788,15 @@ audit(struct proc *p, struct audit_args *uap, register_t *retval)
         * record along with the record for this audit event.
         */
        ar->k_udata = rec;
+       ar->k_ar_commit |= AR_COMMIT_USER;
        ar->k_ulen  = uap->length;
        return (0);
 
 free_out:
-       kmem_free(kernel_map, (vm_offset_t)rec, uap->length);
+       /* audit_syscall_exit() will free the audit record on the thread
+        * even if we allocated it above.
+        */
+       kfree((vm_offset_t)rec, (vm_size_t)uap->length);
        return (error);
 }
 
@@ -572,32 +813,196 @@ int
 auditon(struct proc *p, struct auditon_args *uap, register_t *retval)
 {
        register struct pcred *pc = p->p_cred;
-       int error;
+       int ret;
+       int len;
+       union auditon_udata udata;
+       struct proc *tp;
 
-       error = suser(pc->pc_ucred, &p->p_acflag);
-       if (error)
-               return (error);
-       return (ENOSYS);
-}
+       AUDIT_ARG(cmd, uap->cmd);
+       ret = suser(pc->pc_ucred, &p->p_acflag);
+       if (ret)
+               return (ret);
 
-/*
- *  System call to pass in file descriptor for audit log.
- */
-struct auditsvc_args {
-       int     fd;
-       int     limit;
-};
-/* ARGSUSED */
-int
-auditsvc(struct proc *p, struct auditsvc_args *uap, register_t *retval)
-{
-       register struct pcred *pc = p->p_cred;
-       int error;
+       len = uap->length;
+       if ((len <= 0) || (len > sizeof(union auditon_udata)))
+               return (EINVAL);
 
-       error = suser(pc->pc_ucred, &p->p_acflag);
-       if (error)
-               return (error);
-       return (ENOSYS);
+       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:
+               ret = copyin(uap->data, (void *)&udata, uap->length);
+               if (ret)
+                       return (ret);
+               AUDIT_ARG(auditon, &udata);
+               break;
+       }
+
+       /* XXX Need to implement these commands by accessing the global
+        * values associated with the commands.
+        */
+       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();
+               }
+               break;
+       case A_GETCLASS:
+               udata.au_evclass.ec_class = 
+                       au_event_class(udata.au_evclass.ec_number);
+               break;
+       case A_SETCLASS:
+               au_evclassmap_insert(udata.au_evclass.ec_number,
+                                       udata.au_evclass.ec_class);
+               break;
+       case A_GETPINFO:
+               if (udata.au_aupinfo.ap_pid < 1) 
+                       return (EINVAL);
+               if ((tp = pfind(udata.au_aupinfo.ap_pid)) == NULL)
+                       return (EINVAL);
+
+               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;
+               break;
+       case A_SETPMASK:
+               if (udata.au_aupinfo.ap_pid < 1) 
+                       return (EINVAL);
+               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;
+               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;
+       }
+       /* 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:
+               ret = copyout((void *)&udata, uap->data, uap->length);
+               if (ret)
+                       return (ret);
+               break;
+       }
+
+       return (0);
 }
 
 /* 
@@ -645,6 +1050,9 @@ setauid(struct proc *p, struct setauid_args *uap, register_t *retval)
        if (error)
                return (error);
 
+       /* propagate the change from the process to Mach task */
+       set_security_token(p);
+
        audit_arg_auid(p->p_au->ai_auid);
        return (0);
 }
@@ -691,6 +1099,11 @@ setaudit(struct proc *p, struct setaudit_args *uap, register_t *retval)
        if (error)
                return (error);
 
+       /* propagate the change from the process to Mach task */
+       set_security_token(p);
+
+       audit_arg_auditinfo(p->p_au);
+
        return (0);
 }
 
@@ -731,7 +1144,6 @@ setaudit_addr(struct proc *p, struct setaudit_addr_args *uap, register_t *retval
 /*
  * Syscall to manage audit files.
  *
- * XXX: Should generate an audit event.
  */
 struct auditctl_args {
        char    *path;
@@ -759,8 +1171,8 @@ auditctl(struct proc *p, struct auditctl_args *uap)
         * credential.
         */
        if (uap->path != NULL) {
-               NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE,
-                   uap->path, p);
+               NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNPATH1, 
+                       UIO_USERSPACE, uap->path, p);
                flags = audit_open_flags;
                error = vn_open(&nd, flags, 0);
                if (error)
@@ -774,6 +1186,7 @@ auditctl(struct proc *p, struct auditctl_args *uap)
                }
                cred = p->p_ucred;
                crhold(cred);
+               audit_suspended = 0;
        }
 
        audit_rotate_vnode(cred, vp);
@@ -814,26 +1227,22 @@ audit_new(int event, struct proc *p, struct uthread *uthread)
        }
 #endif
 
-       /*
-        * Eventually, we might want to have global event filtering
-        * by event type here.
-        */
-
-       /*
-        * XXX: Process-based event preselection should occur here.
-        * Currently, we only post-select.
-        */
-
        /*
         * Initialize the audit record header.
-        * XXX: Should probably use a zone; whatever we use must be
-        * safe to call from the non-BSD side of the house.
         * 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.  
         */
-       (void)kmem_alloc(kernel_map, &ar, sizeof(*ar));
+
+       ar = (struct kaudit_record *)zalloc(audit_zone);
        if (ar == NULL)
                return NULL;
 
+       mutex_lock(audit_mtx);
+       audit_pre_q_len++;
+       mutex_unlock(audit_mtx);
+
        bzero(ar, sizeof(*ar));
        ar->k_ar.ar_magic = AUDIT_RECORD_MAGIC;
        ar->k_ar.ar_event = event;
@@ -845,10 +1254,11 @@ audit_new(int event, struct proc *p, struct uthread *uthread)
        ar->k_ar.ar_subj_rgid = p->p_cred->p_rgid;
        ar->k_ar.ar_subj_egid = p->p_ucred->cr_groups[0];
        ar->k_ar.ar_subj_auid = p->p_au->ai_auid;
+       ar->k_ar.ar_subj_asid = p->p_au->ai_asid;
        ar->k_ar.ar_subj_pid = p->p_pid;
+       ar->k_ar.ar_subj_amask = p->p_au->ai_mask;
+       ar->k_ar.ar_subj_term = p->p_au->ai_termid;
        bcopy(p->p_comm, ar->k_ar.ar_subj_comm, MAXCOMLEN);
-       bcopy(&p->p_au->ai_mask, &ar->k_ar.ar_subj_amask, 
-                       sizeof(p->p_au->ai_mask));
 
        return (ar);
 }
@@ -861,6 +1271,9 @@ void
 audit_abort(struct kaudit_record *ar)
 {
 
+       mutex_lock(audit_mtx);
+       audit_pre_q_len--;
+       mutex_unlock(audit_mtx);
        audit_free(ar);
 }
 
@@ -870,10 +1283,59 @@ audit_abort(struct kaudit_record *ar)
 void
 audit_commit(struct kaudit_record *ar, int error, int retval)
 {
+       int ret;
+       int sorf;
+       struct au_mask *aumask;
 
        if (ar == NULL)
                return;
 
+       /*
+        * Decide whether to commit the audit record by checking the
+        * error value from the system call and using the appropriate
+        * audit mask. 
+        */
+       if (ar->k_ar.ar_subj_auid == AU_DEFAUDITID)
+               aumask = &audit_nae_mask;
+       else
+               aumask = &ar->k_ar.ar_subj_amask;
+       
+       if (error)
+               sorf = AU_PRS_FAILURE;
+       else
+               sorf = AU_PRS_SUCCESS;
+
+       switch(ar->k_ar.ar_event) {
+
+       case AUE_OPEN_RWTC:
+               /* The open syscall always writes a OPEN_RWTC event; limit the
+                * 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) {
+               mutex_lock(audit_mtx);
+               audit_pre_q_len--;
+               mutex_unlock(audit_mtx);
+               audit_free(ar);
+               return;
+       }
+
        ar->k_ar.ar_errno = error;
        ar->k_ar.ar_retval = retval;
 
@@ -887,27 +1349,41 @@ audit_commit(struct kaudit_record *ar, int error, int retval)
         */
        nanotime(&ar->k_ar.ar_endtime);
 
-       /*
-        * XXXAUDIT: The number of outstanding uncommitted audit records is
-        * limited by the number of concurrent threads servicing system
-        * calls in the kernel.  However, there is currently no bound on
-        * the size of the committed records in the audit event queue
-        * before they are sent to disk.  Probably, there should be a fixed
-        * size bound (perhaps configurable), and if that bound is reached,
-        * threads should sleep in audit_commit() until there's room.
-        */
        mutex_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--;
                mutex_unlock(audit_mtx);
                audit_free(ar);
                return;
        }
+       
+       /*
+        * Constrain the number of committed audit records based on
+        * the configurable parameter.
+        */
+       while (audit_q_len >= audit_qctrl.aq_hiwater) {
+
+               ret = wait_queue_assert_wait(audit_wait_queue,
+                                            AUDIT_COMMIT_EVENT,
+                                            THREAD_UNINT);
+               mutex_unlock(audit_mtx);
+
+               assert(ret == THREAD_WAITING);
+
+               ret = thread_block(THREAD_CONTINUE_NULL);
+               assert(ret == THREAD_AWAKENED);
+               mutex_lock(audit_mtx);
+       }
+
        TAILQ_INSERT_TAIL(&audit_q, ar, k_q);
-       wait_queue_wakeup_one(audit_wait_queue, 0, THREAD_AWAKENED);
+       audit_q_len++;
+       audit_pre_q_len--;
+       wait_queue_wakeup_one(audit_wait_queue, AUDIT_WORKER_EVENT, THREAD_AWAKENED);
        mutex_unlock(audit_mtx);
 }
 
@@ -920,27 +1396,46 @@ audit_syscall_enter(unsigned short code, struct proc *proc,
                        struct uthread *uthread)
 {
        int audit_event;
-
-       assert(uthread->uu_ar == NULL);
+       struct au_mask *aumask;
 
        audit_event = sys_au_event[code];
+       if (audit_event == AUE_NULL)
+               return;
+
+       assert(uthread->uu_ar == NULL);
 
+       /* Check which audit mask to use; either the kernel non-attributable
+        * event mask or the process audit mask.
+        */
+       if (proc->p_au->ai_auid == AU_DEFAUDITID)
+               aumask = &audit_nae_mask;
+       else
+               aumask = &proc->p_au->ai_mask;
+       
        /*
-        * Allocate an audit record, if desired, and store in the BSD
-        * thread for later use.
+        * Allocate an audit record, if preselection allows it, and store 
+        * in the BSD thread for later use.
         */
-       if (audit_event != AUE_NULL) {
-#if 0
-               AUDIT_PRINTF(("Allocated record type %d for syscall %d\n",
-                   audit_event, code));
-#endif
-               if (au_preselect(audit_event, &proc->p_au->ai_mask,
-                               AU_PRS_FAILURE | AU_PRS_SUCCESS)) {
-                       uthread->uu_ar = audit_new(audit_event, proc, uthread);
-               } else {
-                       uthread->uu_ar = NULL;
+       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.
+                */
+               if (audit_in_failure &&
+                   suser(proc->p_ucred, &proc->p_acflag) != 0) {
+                       int ret;
+
+                       ret = wait_queue_assert_wait(audit_wait_queue,
+                           AUDIT_FAILURE_EVENT, THREAD_UNINT);
+                       assert(ret == THREAD_WAITING);
+                       (void)thread_block(THREAD_CONTINUE_NULL);
+                       panic("audit_failing_stop: thread continued");
                }
-       }
+               uthread->uu_ar = audit_new(audit_event, proc, uthread);
+       } else
+               uthread->uu_ar = NULL;
 }
 
 void
@@ -968,6 +1463,62 @@ audit_syscall_exit(int error, struct proc *proc, struct uthread *uthread)
 
 }
 
+/*
+ * Calls to set up and tear down audit structures used during Mach 
+ * system calls.
+ */
+void
+audit_mach_syscall_enter(unsigned short audit_event)
+{
+       struct uthread *uthread;
+       struct proc *proc;
+       struct au_mask *aumask;
+
+       if (audit_event == AUE_NULL)
+               return;
+
+       uthread = curuthread();
+       if (uthread == NULL)
+               return;
+
+       proc = current_proc();
+       if (proc == NULL) 
+               return;
+
+       assert(uthread->uu_ar == NULL);
+
+       /* Check which audit mask to use; either the kernel non-attributable
+        * event mask or the process audit mask.
+        */
+       if (proc->p_au->ai_auid == AU_DEFAUDITID)
+               aumask = &audit_nae_mask;
+       else
+               aumask = &proc->p_au->ai_mask;
+       
+       /*
+        * Allocate an audit record, if desired, and store in the BSD
+        * thread for later use.
+        */
+       if (au_preselect(audit_event, aumask,
+                       AU_PRS_FAILURE | AU_PRS_SUCCESS)) {
+               uthread->uu_ar = audit_new(audit_event, proc, uthread);
+       } else {
+               uthread->uu_ar = NULL;
+       }
+}
+
+void
+audit_mach_syscall_exit(int retval, struct uthread *uthread)
+{
+       /* The error code from Mach system calls is the same as the
+        * return value  
+        */
+       /* XXX Is the above statement always true? */
+       audit_commit(uthread->uu_ar, retval, retval);
+       uthread->uu_ar = NULL;
+
+}
+
 /*
  * Calls to manipulate elements of the audit record structure from system
  * call code.  Macro wrappers will prevent this functions from being
@@ -977,7 +1528,7 @@ audit_syscall_exit(int error, struct proc *proc, struct uthread *uthread)
  * record for this event.
  */
 void
-audit_arg_accmode(int accmode)
+audit_arg_addr(void * addr)
 {
        struct kaudit_record *ar;
 
@@ -985,12 +1536,12 @@ audit_arg_accmode(int accmode)
        if (ar == NULL)
                return;
 
-       ar->k_ar.ar_arg_accmode = accmode;
-       ar->k_ar.ar_valid_arg |= ARG_ACCMODE;
+       ar->k_ar.ar_arg_addr = addr;
+       ar->k_ar.ar_valid_arg |= ARG_ADDR;
 }
 
 void
-audit_arg_cmode(int cmode)
+audit_arg_len(int len)
 {
        struct kaudit_record *ar;
 
@@ -998,8 +1549,8 @@ audit_arg_cmode(int cmode)
        if (ar == NULL)
                return;
 
-       ar->k_ar.ar_arg_cmode = cmode;
-       ar->k_ar.ar_valid_arg |= ARG_CMODE;
+       ar->k_ar.ar_arg_len = len;
+       ar->k_ar.ar_valid_arg |= ARG_LEN;
 }
 
 void
@@ -1097,6 +1648,20 @@ audit_arg_login(char *login)
        ar->k_ar.ar_valid_arg |= 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;
+       ar->k_ar.ar_valid_arg |= (ARG_CTLNAME | ARG_LEN);
+}
+
 void
 audit_arg_mask(int mask)
 {
@@ -1136,6 +1701,19 @@ audit_arg_dev(int dev)
        ar->k_ar.ar_valid_arg |= 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;
+       ar->k_ar.ar_valid_arg |= ARG_VALUE;
+}
+
 void
 audit_arg_owner(uid_t uid, gid_t gid)
 {
@@ -1154,6 +1732,7 @@ void
 audit_arg_pid(pid_t pid)
 {
        struct kaudit_record *ar;
+       struct proc *p;
 
        ar = currecord();
        if (ar == NULL)
@@ -1161,6 +1740,30 @@ audit_arg_pid(pid_t pid)
 
        ar->k_ar.ar_arg_pid = pid;
        ar->k_ar.ar_valid_arg |= ARG_PID;
+
+}
+
+void
+audit_arg_process(struct proc *p)
+{
+       struct kaudit_record *ar;
+
+       ar = currecord();
+       if ((ar == NULL) || (p == NULL))
+               return;
+
+       /* XXX May need to lock the credentials structures */
+       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_cred->p_ruid;
+       ar->k_ar.ar_arg_rgid = p->p_cred->p_rgid;
+       ar->k_ar.ar_arg_asid = p->p_au->ai_asid;
+
+       ar->k_ar.ar_arg_termid = p->p_au->ai_termid;
+
+       ar->k_ar.ar_valid_arg |= ARG_AUID | ARG_EUID | ARG_EGID | ARG_RUID | 
+               ARG_RGID | ARG_ASID | ARG_TERMID | ARG_PROCESS;
 }
 
 void
@@ -1186,9 +1789,9 @@ audit_arg_socket(int sodomain, int sotype, int soprotocol)
        if (ar == NULL)
                return;
 
-       ar->k_ar.ar_arg_sockinfo.sodomain = sodomain;
-       ar->k_ar.ar_arg_sockinfo.sotype = sotype;
-       ar->k_ar.ar_arg_sockinfo.soprotocol = soprotocol;
+       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;
        ar->k_ar.ar_valid_arg |= ARG_SOCKINFO;
 }
 
@@ -1230,6 +1833,24 @@ audit_arg_auid(uid_t auid)
        ar->k_ar.ar_valid_arg |= 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;
+       ar->k_ar.ar_valid_arg |= ARG_AUID | ARG_ASID | ARG_AMASK | ARG_TERMID;
+}
+
 void
 audit_arg_text(char *text)
 {
@@ -1245,12 +1866,12 @@ audit_arg_text(char *text)
                return; 
 
        if (ar->k_ar.ar_arg_text == NULL) {
-               kmem_alloc(kernel_map, &ar->k_ar.ar_arg_text, MAXPATHLEN);
+               ar->k_ar.ar_arg_text = (char *)kalloc(MAXPATHLEN);
                if (ar->k_ar.ar_arg_text == NULL)
                        return; 
        }
 
-       strcpy(ar->k_ar.ar_arg_text, text);
+       strncpy(ar->k_ar.ar_arg_text, text, MAXPATHLEN);
        ar->k_ar.ar_valid_arg |= ARG_TEXT;
 }
 
@@ -1320,11 +1941,85 @@ audit_arg_svipc_addr(void * addr)
        ar->k_ar.ar_valid_arg |= 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;
+       ar->k_ar.ar_valid_arg |= 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));
+       ar->k_ar.ar_valid_arg |= 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;
+
+       if (fp->f_type == DTYPE_VNODE) {
+               audit_arg_vnpath((struct vnode *)fp->f_data, ARG_VNODE1);
+               return;
+       }
+
+       if (fp->f_type == DTYPE_SOCKET) {
+               ar = currecord();
+               if (ar == NULL)
+                       return;
+               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;
+                       ar->k_ar.ar_valid_arg |= ARG_SOCKINFO;
+               }
+       }
+
+}
+
 /* 
  * 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. 
+ * process in the system. 
+ * XXX It is not clear what the initial values should be for session ID, 
+ * terminal ID etc. 
  */
 void
 audit_proc_init(struct proc *p)
@@ -1333,6 +2028,8 @@ audit_proc_init(struct proc *p)
                        M_SUBPROC, M_WAITOK);
 
        bzero((void *)p->p_au, sizeof(*p->p_au));
+
+       p->p_au->ai_auid = AU_DEFAUDITID;
 }
 
 /* 
@@ -1396,17 +2093,20 @@ audit_arg_upath(struct proc *p, char *upath, u_int64_t flags)
        }
 
        if (*pathp == NULL) {
-               kmem_alloc(kernel_map, pathp, MAXPATHLEN);
+               *pathp = (char *)kalloc(MAXPATHLEN);
                if (*pathp == NULL)
                        return;
        }
 
-       canon_path(p, upath, *pathp);
-
-       if (flags & ARG_UPATH1)
-               ar->k_ar.ar_valid_arg |= ARG_UPATH1;
-       else
-               ar->k_ar.ar_valid_arg |= ARG_UPATH2;
+       if (canon_path(p, upath, *pathp) == 0) {
+               if (flags & ARG_UPATH1)
+                       ar->k_ar.ar_valid_arg |= ARG_UPATH1;
+               else
+                       ar->k_ar.ar_valid_arg |= ARG_UPATH2;
+       } else {
+               kfree((vm_offset_t)*pathp, MAXPATHLEN);
+               *pathp = NULL;
+       }
 }
 
 /*
@@ -1460,18 +2160,26 @@ audit_arg_vnpath(struct vnode *vp, u_int64_t flags)
        }
 
        if (*pathp == NULL) {
-               kmem_alloc(kernel_map, pathp, MAXPATHLEN);
+               *pathp = (char *)kalloc(MAXPATHLEN);
                if (*pathp == NULL)
                        return;
        }
 
-       /* Copy the path looked up by the vn_getpath() function */
+       /*
+        * If vn_getpath() succeeds, place it in a string buffer
+        * attached to the audit record, and set a flag indicating
+        * it is present.
+        */
        len = MAXPATHLEN;
-       vn_getpath(vp, *pathp, &len);
-       if (flags & ARG_VNODE1)
-               ar->k_ar.ar_valid_arg |= ARG_KPATH1;
-       else
-               ar->k_ar.ar_valid_arg |= ARG_KPATH2;
+       if (vn_getpath(vp, *pathp, &len) == 0) {
+               if (flags & ARG_VNODE1)
+                       ar->k_ar.ar_valid_arg |= ARG_KPATH1;
+               else
+                       ar->k_ar.ar_valid_arg |= ARG_KPATH2;
+       } else {
+               kfree((vm_offset_t)*pathp, MAXPATHLEN);
+               *pathp = NULL;
+       }
 
        /*
         * XXX: We'd assert the vnode lock here, only Darwin doesn't
@@ -1497,6 +2205,51 @@ audit_arg_vnpath(struct vnode *vp, u_int64_t flags)
 
 }
 
+void
+audit_arg_mach_port1(mach_port_t port)
+{
+       struct kaudit_record *ar;
+
+       ar = currecord();
+       if (ar == NULL)
+               return;
+
+       ar->k_ar.ar_arg_mach_port1 = port;
+       ar->k_ar.ar_valid_arg |= ARG_MACHPORT1;
+}
+
+void
+audit_arg_mach_port2(mach_port_t port)
+{
+       struct kaudit_record *ar;
+
+       ar = currecord();
+       if (ar == NULL)
+               return;
+
+       ar->k_ar.ar_arg_mach_port2 = port;
+       ar->k_ar.ar_valid_arg |= ARG_MACHPORT2;
+}
+
+/*
+ * 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 proc *p, int fd)
+{
+       struct file *fp;
+
+       audit_arg_fd(fd);
+
+       if (getvnode(p, fd, &fp) != 0)
+               return;
+
+       audit_arg_vnpath((struct vnode *)fp->f_data, ARG_VNODE1);
+
+} 
+
 #else /* !AUDIT */
 
 void
@@ -1523,12 +2276,6 @@ auditon(struct proc *p, struct auditon_args *uap, register_t *retval)
        return (ENOSYS);
 }
 
-int
-auditsvc(struct proc *p, struct auditsvc_args *uap, register_t *retval)
-{
-       return (ENOSYS);
-}
-
 int
 getauid(struct proc *p, struct getauid_args *uap, register_t *retval)
 {