/*
- * 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
/*
* 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
* 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.
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,
* 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,
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);
}
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;
* 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
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);
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;
}
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);
*/
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))) {
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);
}
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();
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);
* 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
*/
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);
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 */
}
/*
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);
}
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)
* 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);
}
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);
}
/*
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);
}
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);
}
/*
* Syscall to manage audit files.
*
- * XXX: Should generate an audit event.
*/
struct auditctl_args {
char *path;
* 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)
}
cred = p->p_ucred;
crhold(cred);
+ audit_suspended = 0;
}
audit_rotate_vnode(cred, vp);
}
#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;
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);
}
audit_abort(struct kaudit_record *ar)
{
+ mutex_lock(audit_mtx);
+ audit_pre_q_len--;
+ mutex_unlock(audit_mtx);
audit_free(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;
*/
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);
}
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
}
+/*
+ * 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
* record for this event.
*/
void
-audit_arg_accmode(int accmode)
+audit_arg_addr(void * addr)
{
struct kaudit_record *ar;
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;
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
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)
{
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)
{
audit_arg_pid(pid_t pid)
{
struct kaudit_record *ar;
+ struct proc *p;
ar = currecord();
if (ar == NULL)
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
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;
}
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)
{
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;
}
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)
M_SUBPROC, M_WAITOK);
bzero((void *)p->p_au, sizeof(*p->p_au));
+
+ p->p_au->ai_auid = AU_DEFAUDITID;
}
/*
}
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;
+ }
}
/*
}
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
}
+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
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)
{