X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/55e303ae13a4cf49d70f2294092726f2fffb9ef2..5eebf7385fedb1517b66b53c28e5aa6bb0a2be50:/bsd/kern/kern_audit.c diff --git a/bsd/kern/kern_audit.c b/bsd/kern/kern_audit.c index ce838a4f4..b183d5339 100644 --- a/bsd/kern/kern_audit.c +++ b/bsd/kern/kern_audit.c @@ -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 +#include #include #include #include @@ -35,17 +34,34 @@ #include #include #include -#include -#include #include -#include -#include #include #include #include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include #include #include +#include +#include + +#include #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) {