X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/e2fac8b15b12a7979f72090454d850e612fc5b13..b0d623f7f2ae71ed96e60569f61f9a9a27016e80:/bsd/kern/kern_audit.c?ds=inline diff --git a/bsd/kern/kern_audit.c b/bsd/kern/kern_audit.c deleted file mode 100644 index df9f61d2d..000000000 --- a/bsd/kern/kern_audit.c +++ /dev/null @@ -1,2798 +0,0 @@ -/* - * Copyright (c) 2003-2007 Apple Inc. All rights reserved. - * - * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ - * - * 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. The rights granted to you under the License - * may not be used to create, or enable the creation or redistribution of, - * unlawful or unlicensed copies of an Apple operating system, or to - * circumvent, violate, or enable the circumvention or violation of, any - * terms of an Apple operating system software license agreement. - * - * 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 - * 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. - * - * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ - */ -/* - * NOTICE: This file was modified by McAfee Research in 2004 to introduce - * support for mandatory and extensible security protections. This notice - * is included in support of clause 2.2 (b) of the Apple Public License, - * Version 2.0. - */ -#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 -#include - -#include -#include -#include -#include -#include -#include - -#if CONFIG_MACF -#include -#include -#include -#include -#define MAC_ARG_PREFIX "arg: " -#define MAC_ARG_PREFIX_LEN 5 -#endif - -#include - -#include -#include - -#if AUDIT - -/* - * The AUDIT_EXCESSIVELY_VERBOSE define enables a number of - * gratuitously noisy printf's to the console. Due to the - * volume, it should be left off unless you want your system - * to churn a lot whenever the audit record flow gets high. - */ -/* #define AUDIT_EXCESSIVELY_VERBOSE */ -#ifdef AUDIT_EXCESSIVELY_VERBOSE -#define AUDIT_PRINTF_ONLY -#define AUDIT_PRINTF(x) printf x -#else -#define AUDIT_PRINTF_ONLY __unused -#define AUDIT_PRINTF(X) -#endif - -#if DIAGNOSTIC -#if defined(assert) -#undef assert() -#endif -#define assert(cond) \ - ((void) ((cond) ? 0 : panic("Assert failed: %s", # cond))) -#else -#include -#endif /* DIAGNOSTIC */ - -/* - * Define the audit control flags. - */ -int audit_enabled; -int audit_suspended; - -/* - * Mutex to protect global variables shared between various threads and - * processes. - */ -static lck_grp_t *audit_grp; -static lck_attr_t *audit_attr; -static lck_grp_attr_t *audit_grp_attr; -static lck_mtx_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. 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 size_t audit_q_len; -static size_t audit_pre_q_len; - -static wait_queue_t audit_wait_queue; -static zone_t audit_zone; -#if CONFIG_MACF -static zone_t audit_mac_label_zone; -#endif - -/* - * 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 int audit_worker_event; -#define AUDIT_WORKER_EVENT ((event_t)&audit_worker_event) - -/* - * The audit worker thread (which is lazy started when we first - * rotate the audit log. - */ -static thread_t audit_worker_thread = THREAD_NULL; - -/* - * When an audit log is rotated, the actual rotation must be performed - * by the audit worker thread, as it may have outstanding writes on the - * current audit log. audit_replacement_vp holds the vnode replacing - * the current vnode. We can't let more than one replacement occur - * at a time, so if more than one thread requests a replacement, only - * one can have the replacement "in progress" at any given moment. If - * a thread tries to replace the audit vnode and discovers a replacement - * is already in progress (i.e., audit_replacement_flag != 0), then it - * will sleep on audit_replacement_cv waiting its turn to perform a - * replacement. When a replacement is completed, this cv is signalled - * by the worker thread so a waiting thread can start another replacement. - * We also store a credential to perform audit log write operations with. - */ -static int audit_replacement_event; -#define AUDIT_REPLACEMENT_EVENT ((event_t)&audit_replacement_event) - -static int audit_replacement_flag; -static struct vnode *audit_replacement_vp; -static kauth_cred_t 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. - */ -static const int audit_open_flags = FWRITE | O_APPEND; -static const 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. - */ -extern task_t kernel_task; - -extern zone_t mac_audit_data_zone; -static void -audit_free(struct kaudit_record *ar) -{ - if (ar->k_ar.ar_arg_upath1 != NULL) { - kfree(ar->k_ar.ar_arg_upath1, MAXPATHLEN); - } - if (ar->k_ar.ar_arg_upath2 != NULL) { - kfree(ar->k_ar.ar_arg_upath2, MAXPATHLEN); - - } - if (ar->k_ar.ar_arg_kpath1 != NULL) { - kfree(ar->k_ar.ar_arg_kpath1, MAXPATHLEN); - - } - if (ar->k_ar.ar_arg_kpath2 != NULL) { - kfree(ar->k_ar.ar_arg_kpath2, MAXPATHLEN); - - } - if (ar->k_ar.ar_arg_text != NULL) { - kfree(ar->k_ar.ar_arg_text, MAXPATHLEN); - - } - if (ar->k_udata != NULL) { - kfree(ar->k_udata, ar->k_ulen); - } - -#if CONFIG_MACF - if (ar->k_ar.ar_vnode1_mac_labels != NULL) { - zfree(audit_mac_label_zone, ar->k_ar.ar_vnode1_mac_labels); - } - if (ar->k_ar.ar_vnode2_mac_labels != NULL) { - zfree(audit_mac_label_zone, ar->k_ar.ar_vnode2_mac_labels); - } - if (ar->k_ar.ar_cred_mac_labels != NULL) { - zfree(audit_mac_label_zone, ar->k_ar.ar_cred_mac_labels); - } - if (ar->k_ar.ar_arg_mac_string != NULL) { - kfree(ar->k_ar.ar_arg_mac_string, - MAC_MAX_LABEL_BUF_LEN + MAC_ARG_PREFIX_LEN); - } - - /* Free the audit data from the MAC policies. */ - do { - struct mac_audit_record *head, *next; - - head = LIST_FIRST(ar->k_ar.ar_mac_records); - while (head != NULL) { - next = LIST_NEXT(head, records); - zfree(mac_audit_data_zone, head->data); - kfree(head, sizeof(*head)); - head = next; - } - - kfree(ar->k_ar.ar_mac_records, - sizeof(*ar->k_ar.ar_mac_records)); - } while (0); -#endif - - zfree(audit_zone, ar); -} - -/* - * Converts an audit record into the BSM format before writing out to the - * audit logfile. Will perform it's own vnode iocounting. - * - * Returns: - * -1 if it could not get an ioreference on the vnode. - * EINVAL if the kaudit_record ar is not a valid audit record. - */ -static int -audit_write(struct vnode *vp, struct kaudit_record *ar, vfs_context_t ctx) -{ - struct vfsstatfs *mnt_stat = &vp->v_mount->mnt_vfsstat; - int ret = 0; - struct au_record *bsm; - off_t file_size; - - mach_port_t audit_port; - - if (vnode_getwithref(vp)) - return ENOENT; - - /* - * 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_update_vfsstat(vp->v_mount, ctx, VFS_KERNEL_EVENT); - if (ret) - goto out; - - /* update the global stats struct */ - if ((ret = vnode_size(vp, &file_size, ctx)) != 0) - goto out; - audit_fstat.af_currsz = file_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) { - uint64_t 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) && - (file_size >= (off_t)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, - * then write the user record. - */ - /* XXX Need to decide a few things here: IF the user audit - * record is written, but the write of the kernel record fails, - * what to do? Should the kernel record come before or after the - * user record? For now, we write the user record first, and - * we ignore errors. - */ - if (ar->k_ar_commit & AR_COMMIT_USER) { - ret = vn_rdwr(UIO_WRITE, vp, (void *)ar->k_udata, ar->k_ulen, - (off_t)0, UIO_SYSSPACE32, IO_APPEND|IO_UNIT, vfs_context_ucred(ctx), NULL, vfs_context_proc(ctx)); - 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) { - ret = 0; - goto out; - } - - /* - * XXX: We drop the record on BSM conversion failure, but really - * this is an assertion failure. - */ - if (ret == BSM_FAILURE) { - AUDIT_PRINTF(("BSM conversion failure\n")); - ret = EINVAL; - goto out; - } - - /* XXX: We should break the write functionality - * away from the BSM record generation and have the BSM generation - * done before this function is called. This function will then - * take the BSM record as a parameter. - */ - ret = (vn_rdwr(UIO_WRITE, vp, (void *)bsm->data, bsm->len, - (off_t)0, UIO_SYSSPACE32, IO_APPEND|IO_UNIT, vfs_context_ucred(ctx), NULL, vfs_context_proc(ctx))); - 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) { - (void)VNOP_FSYNC(vp, MNT_WAIT, ctx); - panic("Audit store overflow; record queue drained."); - } - - vnode_put(vp); - return (ret); -} - -static void -audit_worker(void) -{ - int do_replacement_signal, error; - TAILQ_HEAD(, kaudit_record) ar_worklist; - struct kaudit_record *ar; - struct vnode *audit_vp, *old_vp; - kauth_cred_t audit_cred; - proc_t audit_p; - - AUDIT_PRINTF(("audit_worker starting\n")); - - TAILQ_INIT(&ar_worklist); - audit_cred = NOCRED; - audit_p = current_proc(); - audit_vp = NULL; - - - lck_mtx_lock(audit_mtx); - while (1) { - struct vfs_context context; - - /* - * First priority: replace the audit log target if requested. - * - * XXX It could well be we should drain existing records - * first to ensure that the timestamps and ordering - * are right. - */ - do_replacement_signal = 0; - while (audit_replacement_flag != 0) { - kauth_cred_t old_cred = audit_cred; - - old_vp = audit_vp; - audit_cred = audit_replacement_cred; - audit_vp = audit_replacement_vp; - audit_replacement_cred = NOCRED; - audit_replacement_vp = NULL; - audit_replacement_flag = 0; - - audit_enabled = (audit_vp != NULL); - - /* - * XXX: What to do about write failures here? - */ - if (old_vp != NULL) { - AUDIT_PRINTF(("Closing old audit file vnode %p\n", old_vp)); - if (vnode_get(old_vp) == 0) { - vn_close(old_vp, audit_close_flags, vfs_context_kernel()); - vnode_put(old_vp); - AUDIT_PRINTF(("Audit file closed\n")); - } - else - printf("audit_worker(): Couldn't close audit file.\n"); - kauth_cred_unref(&old_cred); - old_vp = NULL; - } - if (audit_vp != NULL) { - AUDIT_PRINTF(("Opening new audit file\n")); - } - do_replacement_signal = 1; - } - /* - * Signal that replacement have occurred to wake up and - * start any other replacements started in parallel. We can - * continue about our business in the mean time. We - * broadcast so that both new replacements can be inserted, - * but also so that the source(s) of replacement can return - * successfully. - */ - if (do_replacement_signal) - wait_queue_wakeup_all(audit_wait_queue, - AUDIT_REPLACEMENT_EVENT, THREAD_AWAKENED); - - /* - * Next, check to see if we have any records to drain into - * the vnode. If not, go back to waiting for an event. - */ - if (TAILQ_EMPTY(&audit_q)) { - int ret; - - AUDIT_PRINTF(("audit_worker waiting\n")); - ret = wait_queue_assert_wait(audit_wait_queue, - AUDIT_WORKER_EVENT, - THREAD_UNINT, - 0); - lck_mtx_unlock(audit_mtx); - - assert(ret == THREAD_WAITING); - ret = thread_block(THREAD_CONTINUE_NULL); - assert(ret == THREAD_AWAKENED); - 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)); - - lck_mtx_lock(audit_mtx); - continue; - } - - /* - * If we have records, but there's no active vnode to - * write to, drain the record queue. Generally, we - * prevent the unnecessary allocation of records - * elsewhere, but we need to allow for races between - * conditional allocation and queueing. Go back to - * waiting when we're done. - * - * XXX: We go out of our way to avoid calling audit_free() - * with the audit_mtx held, to avoid a lock order reversal - * as free() may grab the funnel. This will be fixed at - * some point. - */ - if (audit_vp == NULL) { - while ((ar = TAILQ_FIRST(&audit_q))) { - TAILQ_REMOVE(&audit_q, ar, k_q); - audit_q_len--; - if (audit_q_len <= audit_qctrl.aq_lowater) - wait_queue_wakeup_one( - audit_wait_queue, - AUDIT_COMMIT_EVENT, - THREAD_AWAKENED); - - TAILQ_INSERT_TAIL(&ar_worklist, ar, k_q); - } - lck_mtx_unlock(audit_mtx); - while ((ar = TAILQ_FIRST(&ar_worklist))) { - TAILQ_REMOVE(&ar_worklist, ar, k_q); - audit_free(ar); - } - lck_mtx_lock(audit_mtx); - continue; - } - - /* - * We have both records to write, and an active vnode - * to write to. Dequeue a record, and start the write. - * Eventually, it might make sense to dequeue several - * records and perform our own clustering, if the lower - * layers aren't doing it automatically enough. - * - * XXX: We go out of our way to avoid calling audit_free() - * with the audit_mtx held, to avoid a lock order reversal - * as free() may grab the funnel. This will be fixed at - * some point. - */ - 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); - } - lck_mtx_unlock(audit_mtx); - context.vc_thread = current_thread(); - context.vc_ucred = audit_cred; - while ((ar = TAILQ_FIRST(&ar_worklist))) { - TAILQ_REMOVE(&ar_worklist, ar, k_q); - if (audit_vp != NULL) { - /* - * XXX: What should happen if there's a write - * error here? - */ - error = audit_write(audit_vp, ar, &context); - if (error && audit_panic_on_write_fail) { - panic("audit_worker: write error %d\n", - error); - } else if (error) { - printf("audit_worker: write error %d\n", - error); - } - } - audit_free(ar); - } - lck_mtx_lock(audit_mtx); - } -} - -void -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_grp_attr = lck_grp_attr_alloc_init(); - audit_grp = lck_grp_alloc_init("audit", audit_grp_attr); - audit_attr = lck_attr_alloc_init(); - audit_mtx = lck_mtx_alloc_init(audit_grp, audit_attr); - - audit_wait_queue = wait_queue_alloc(SYNC_POLICY_FIFO); - audit_zone = zinit(sizeof(struct kaudit_record), - AQ_HIWATER*sizeof(struct kaudit_record), - 8192, - "audit_zone"); -#if CONFIG_MACF - /* Assume 3 MAC labels for each audit record: two for vnodes, - * one for creds. - */ - audit_mac_label_zone = zinit(MAC_AUDIT_LABEL_LEN, - AQ_HIWATER * 3*MAC_AUDIT_LABEL_LEN, - 8192, - "audit_mac_label_zone"); -#endif - - /* Initialize the BSM audit subsystem. */ - kau_init(); -} - -static void -audit_rotate_vnode(kauth_cred_t cred, struct vnode *vp) -{ - int ret; - - /* - * If other parallel log replacements have been requested, we wait - * until they've finished before continuing. - */ - lck_mtx_lock(audit_mtx); - while (audit_replacement_flag != 0) { - - AUDIT_PRINTF(("audit_rotate_vnode: sleeping to wait for " - "flag\n")); - ret = wait_queue_assert_wait(audit_wait_queue, - AUDIT_REPLACEMENT_EVENT, - THREAD_UNINT, - 0); - lck_mtx_unlock(audit_mtx); - - assert(ret == THREAD_WAITING); - ret = thread_block(THREAD_CONTINUE_NULL); - assert(ret == THREAD_AWAKENED); - AUDIT_PRINTF(("audit_rotate_vnode: woken up (flag %d)\n", - audit_replacement_flag)); - - lck_mtx_lock(audit_mtx); - } - audit_replacement_cred = cred; - audit_replacement_flag = 1; - audit_replacement_vp = vp; - - /* - * Start or wake up the audit worker to perform the exchange. - * It will have to wait until we release the mutex. - */ - if (audit_worker_thread == THREAD_NULL) - audit_worker_thread = kernel_thread(kernel_task, - audit_worker); - else - wait_queue_wakeup_one(audit_wait_queue, - AUDIT_WORKER_EVENT, - THREAD_AWAKENED); - - /* - * Wait for the audit_worker to broadcast that a replacement has - * taken place; we know that once this has happened, our vnode - * has been replaced in, so we can return successfully. - */ - AUDIT_PRINTF(("audit_rotate_vnode: waiting for news of " - "replacement\n")); - ret = wait_queue_assert_wait(audit_wait_queue, - AUDIT_REPLACEMENT_EVENT, - THREAD_UNINT, - 0); - lck_mtx_unlock(audit_mtx); - - assert(ret == THREAD_WAITING); - ret = thread_block(THREAD_CONTINUE_NULL); - 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 */ -} - -/* - * Drain the audit queue and close the log at shutdown. - */ -void -audit_shutdown(void) -{ - if (audit_mtx) - audit_rotate_vnode(NULL, NULL); -} - -static __inline__ struct uthread * -curuthread(void) -{ - return (get_bsdthread_info(current_thread())); -} - -static __inline__ struct kaudit_record * -currecord(void) -{ - return (curuthread()->uu_ar); -} - -/********************************** - * Begin system calls. * - **********************************/ -/* - * System call to allow a user space application to submit a BSM audit - * record to the kernel for inclusion in the audit log. This function - * does little verification on the audit record that is submitted. - * - * XXXAUDIT: Audit preselection for user records does not currently - * work, since we pre-select only based on the AUE_audit event type, - * not the event type submitted as part of the user audit data. - */ -/* ARGSUSED */ -int -audit(proc_t p, struct audit_args *uap, __unused register_t *retval) -{ - int error; - void * rec; - struct kaudit_record *ar; - struct uthread *uthr; - - error = suser(kauth_cred_get(), &p->p_acflag); - if (error) - return (error); - - lck_mtx_lock(audit_mtx); - if ((uap->length <= 0) || (uap->length > (int)audit_qctrl.aq_bufsz)) { - lck_mtx_unlock(audit_mtx); - return (EINVAL); - } - lck_mtx_unlock(audit_mtx); - - 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); - - rec = (void *)kalloc((vm_size_t)uap->length); - - error = copyin(uap->record, rec, uap->length); - if (error) - goto free_out; - -#if CONFIG_MACF - error = mac_system_check_audit(kauth_cred_get(), rec, uap->length); - if (error) - goto free_out; -#endif - - /* Verify the record */ - if (bsm_rec_verify(rec) == 0) { - error = EINVAL; - goto free_out; - } - - /* Attach the user audit record to the kernel audit record. Because - * this system call is an auditable event, we will write the user - * record along with the record for this audit event. - */ - ar->k_udata = rec; - ar->k_ar_commit |= AR_COMMIT_USER; - ar->k_ulen = uap->length; - return (0); - -free_out: - /* audit_syscall_exit() will free the audit record on the thread - * even if we allocated it above. - */ - kfree(rec, uap->length); - return (error); -} - -/* - * System call to manipulate auditing. - */ -/* ARGSUSED */ -int -auditon(proc_t p, struct auditon_args *uap, __unused register_t *retval) -{ - int ret; - int len; - union auditon_udata udata; - proc_t tp = PROC_NULL; - kauth_cred_t my_cred; - - AUDIT_ARG(cmd, uap->cmd); - ret = suser(kauth_cred_get(), &p->p_acflag); - if (ret) - return (ret); - -#if CONFIG_MACF - ret = mac_system_check_auditon(kauth_cred_get(), uap->cmd); - if (ret) - return (ret); -#endif - - len = uap->length; - if ((len <= 0) || (len > (int)sizeof(union auditon_udata))) - return (EINVAL); - - memset((void *)&udata, 0, sizeof(udata)); - - switch (uap->cmd) { - /* Some of the GET commands use the arguments too */ - case A_SETPOLICY: - case A_SETKMASK: - case A_SETQCTRL: - case A_SETSTAT: - case A_SETUMASK: - case A_SETSMASK: - case A_SETCOND: - case A_SETCLASS: - case A_SETPMASK: - case A_SETFSIZE: - case A_SETKAUDIT: - case A_GETCLASS: - case A_GETPINFO: - case A_GETPINFO_ADDR: - 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. - */ - lck_mtx_lock(audit_mtx); - 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)) { - ret = EINVAL; - break; - } - /* - * 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)) { - ret = EINVAL; - break; - } - - 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: - ret = ENOSYS; - break; - case A_GETCAR: - ret = ENOSYS; - break; - case A_GETSTAT: - ret = ENOSYS; - break; - case A_SETSTAT: - ret = ENOSYS; - break; - case A_SETUMASK: - ret = ENOSYS; - break; - case A_SETSMASK: - ret = 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) { - ret = EINVAL; - break; - } - if ((tp = proc_find(udata.au_aupinfo.ap_pid)) == NULL) { - ret = EINVAL; - break; - } - - lck_mtx_unlock(audit_mtx); - my_cred = kauth_cred_proc_ref(tp); - - udata.au_aupinfo.ap_auid = my_cred->cr_au.ai_auid; - udata.au_aupinfo.ap_mask.am_success = - my_cred->cr_au.ai_mask.am_success; - udata.au_aupinfo.ap_mask.am_failure = - my_cred->cr_au.ai_mask.am_failure; - udata.au_aupinfo.ap_termid.machine = - my_cred->cr_au.ai_termid.machine; - udata.au_aupinfo.ap_termid.port = - my_cred->cr_au.ai_termid.port; - udata.au_aupinfo.ap_asid = my_cred->cr_au.ai_asid; - - kauth_cred_unref(&my_cred); - - proc_rele(tp); - tp = PROC_NULL; - lck_mtx_lock(audit_mtx); - break; - case A_SETPMASK: - if (udata.au_aupinfo.ap_pid < 1) { - ret = EINVAL; - break; - } - if ((tp = proc_find(udata.au_aupinfo.ap_pid)) == NULL) { - ret = EINVAL; - break; - } - - /* - * we are modifying the audit info in a credential so we need a new - * credential (or take another reference on an existing credential that - * matches our new one). We must do this because the audit info in the - * credential is used as part of our hash key. Get current credential - * in the target process and take a reference while we muck with it. - */ - lck_mtx_unlock(audit_mtx); - for (;;) { - kauth_cred_t my_new_cred; - struct auditinfo temp_auditinfo; - - my_cred = kauth_cred_proc_ref(tp); - /* - * Set the credential with new info. If there is no - * change, we get back the same credential we passed - * in; if there is a change, we drop the reference on - * the credential we passed in. The subsequent - * compare is safe, because it is a pointer compare - * rather than a contents compare. - */ - temp_auditinfo = my_cred->cr_au; - temp_auditinfo.ai_mask.am_success = - udata.au_aupinfo.ap_mask.am_success; - temp_auditinfo.ai_mask.am_failure = - udata.au_aupinfo.ap_mask.am_failure; - my_new_cred = kauth_cred_setauditinfo(my_cred, &temp_auditinfo); - - if (my_cred != my_new_cred) { - proc_lock(tp); - /* need to protect for a race where another thread also changed - * the credential after we took our reference. If p_ucred has - * changed then we should restart this again with the new cred. - */ - if (tp->p_ucred != my_cred) { - proc_unlock(tp); - kauth_cred_unref(&my_new_cred); - /* try again */ - continue; - } - tp->p_ucred = my_new_cred; - proc_unlock(tp); - } - /* drop old proc reference or our extra reference */ - kauth_cred_unref(&my_cred); - break; - } - proc_rele(tp); - lck_mtx_lock(audit_mtx); - break; - case A_SETFSIZE: - if ((udata.au_fstat.af_filesz != 0) && - (udata.au_fstat.af_filesz < MIN_AUDIT_FILE_SIZE)) { - ret = EINVAL; - break; - } - 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: - ret = ENOSYS; - break; - case A_GETKAUDIT: - ret = ENOSYS; - break; - case A_SETKAUDIT: - ret = ENOSYS; - break; - } - /* Copy data back to userspace for the GET comands */ - if (ret == 0) { - 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); - break; - } - } - - lck_mtx_unlock(audit_mtx); - return (ret); -} - -/* - * System calls to manage the user audit information. - */ -/* ARGSUSED */ -int -getauid(__unused proc_t p, struct getauid_args *uap, __unused register_t *retval) -{ - int error; - -#if CONFIG_MACF - error = mac_proc_check_getauid(p); - if (error) - return (error); -#endif - - error = copyout((void *)&kauth_cred_get()->cr_au.ai_auid, - uap->auid, sizeof(au_id_t)); - if (error) - return (error); - - return (0); -} - -/* ARGSUSED */ -int -setauid(proc_t p, struct setauid_args *uap, __unused register_t *retval) -{ - int error; - au_id_t temp_au_id; - - error = suser(kauth_cred_get(), &p->p_acflag); - if (error) - return (error); - - error = copyin(uap->auid, - (void *)&temp_au_id, - sizeof(au_id_t)); - if (error) - return (error); -#if CONFIG_MACF - error = mac_proc_check_setauid(p, temp_au_id); - if (error) - return (error); -#endif - - /* - * we are modifying the audit info in a credential so we need a new - * credential (or take another reference on an existing credential that - * matches our new one). We must do this because the audit info in the - * credential is used as part of our hash key. Get current credential - * in the target process and take a reference while we muck with it. - */ - for (;;) { - kauth_cred_t my_cred, my_new_cred; - struct auditinfo temp_auditinfo; - - my_cred = kauth_cred_proc_ref(p); - /* - * Set the credential with new info. If there is no change, - * we get back the same credential we passed in; if there is - * a change, we drop the reference on the credential we - * passed in. The subsequent compare is safe, because it is - * a pointer compare rather than a contents compare. - */ - temp_auditinfo = my_cred->cr_au; - temp_auditinfo.ai_auid = temp_au_id; - my_new_cred = kauth_cred_setauditinfo(my_cred, &temp_auditinfo); - - if (my_cred != my_new_cred) { - proc_lock(p); - /* need to protect for a race where another thread also changed - * the credential after we took our reference. If p_ucred has - * changed then we should restart this again with the new cred. - */ - if (p->p_ucred != my_cred) { - proc_unlock(p); - kauth_cred_unref(&my_new_cred); - /* try again */ - continue; - } - p->p_ucred = my_new_cred; - proc_unlock(p); - } - /* drop old proc reference or our extra reference */ - kauth_cred_unref(&my_cred); - break; - } - - /* propagate the change from the process to Mach task */ - set_security_token(p); - - audit_arg_auid(kauth_cred_get()->cr_au.ai_auid); - return (0); -} - -/* - * System calls to get and set process audit information. - * If the caller is privileged, they get the whole set of - * audit information. Otherwise, the real audit mask is - * filtered out - but the rest of the information is - * returned. - */ -/* ARGSUSED */ -int -getaudit(proc_t p, struct getaudit_args *uap, __unused register_t *retval) -{ - struct auditinfo ai; - int error; - -#if CONFIG_MACF - error = mac_proc_check_getaudit(p); - if (error) - return (error); -#endif - - ai = kauth_cred_get()->cr_au; - - /* only superuser gets to see the real mask */ - error = suser(kauth_cred_get(), &p->p_acflag); - if (error) { - ai.ai_mask.am_success = ~0; - ai.ai_mask.am_failure = ~0; - } - - error = copyout(&ai, uap->auditinfo, sizeof(ai)); - if (error) - return (error); - - return (0); -} - -/* ARGSUSED */ -int -setaudit(proc_t p, struct setaudit_args *uap, __unused register_t *retval) -{ - int error; - struct auditinfo temp_auditinfo; - kauth_cred_t safecred; - - error = suser(kauth_cred_get(), &p->p_acflag); - if (error) - return (error); - error = copyin(uap->auditinfo, - (void *)&temp_auditinfo, - sizeof(temp_auditinfo)); - if (error) - return (error); -#if CONFIG_MACF - error = mac_proc_check_setaudit(p, &temp_auditinfo); - if (error) - return (error); - -#endif - - - /* - * we are modifying the audit info in a credential so we need a new - * credential (or take another reference on an existing credential that - * matches our new one). We must do this because the audit info in the - * credential is used as part of our hash key. Get current credential - * in the target process and take a reference while we muck with it. - */ - for (;;) { - kauth_cred_t my_cred, my_new_cred; - - my_cred = kauth_cred_proc_ref(p); - /* - * Set the credential with new info. If there is no change, - * we get back the same credential we passed in; if there is - * a change, we drop the reference on the credential we - * passed in. The subsequent compare is safe, because it is - * a pointer compare rather than a contents compare. - */ - my_new_cred = kauth_cred_setauditinfo(my_cred, &temp_auditinfo); - - if (my_cred != my_new_cred) { - proc_lock(p); - /* need to protect for a race where another thread also changed - * the credential after we took our reference. If p_ucred has - * changed then we should restart this again with the new cred. - */ - if (p->p_ucred != my_cred) { - proc_unlock(p); - kauth_cred_unref(&my_new_cred); - /* try again */ - continue; - } - p->p_ucred = my_new_cred; - proc_unlock(p); - } - /* drop old proc reference or our extra reference */ - kauth_cred_unref(&my_cred); - break; - } - - /* propagate the change from the process to Mach task */ - set_security_token(p); - - safecred = kauth_cred_proc_ref(p); - audit_arg_auditinfo(&safecred->cr_au); - kauth_cred_unref(&safecred); - - return (0); -} - -/* ARGSUSED */ -int -getaudit_addr(__unused proc_t p, __unused struct getaudit_addr_args *uap, __unused register_t *retval) -{ - return (ENOSYS); -} - -/* ARGSUSED */ -int -setaudit_addr(proc_t p, __unused struct setaudit_addr_args *uap, __unused register_t *retval) -{ - int error; - - error = suser(kauth_cred_get(), &p->p_acflag); - if (error) - return (error); - return (ENOSYS); -} - -/* - * Syscall to manage audit files. - * - */ -/* ARGSUSED */ -int -auditctl(proc_t p, struct auditctl_args *uap, __unused register_t *retval) -{ - struct nameidata nd; - kauth_cred_t cred; - struct vnode *vp; - int error; - - error = suser(kauth_cred_get(), &p->p_acflag); - if (error) - return (error); - - vp = NULL; - cred = NULL; - - /* - * If a path is specified, open the replacement vnode, perform - * validity checks, and grab another reference to the current - * credential. - */ - if (uap->path != USER_ADDR_NULL) { - NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNPATH1, - (IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32), - uap->path, vfs_context_current()); - error = vn_open(&nd, audit_open_flags, 0); - if (error) - goto out; - vp = nd.ni_vp; - - if (vp->v_type != VREG) { - vn_close(vp, audit_close_flags, vfs_context_current()); - vnode_put(vp); - error = EINVAL; - goto out; - } -#if CONFIG_MACF - /* - * Accessibility of the vnode was determined in - * vn_open; the mac_system_check_auditctl should only - * determine whether that vnode is appropriate for - * storing audit data, or that the caller was - * permitted to control the auditing system at all. - * For example, a confidentiality policy may want to - * ensure that audit files are always high - * sensitivity. - */ - - error = mac_system_check_auditctl(kauth_cred_get(), vp); - if (error) { - vn_close(vp, audit_close_flags, vfs_context_current()); - vnode_put(vp); - goto out; - } -#endif - cred = kauth_cred_get_with_ref(); - lck_mtx_lock(audit_mtx); - audit_suspended = 0; - lck_mtx_unlock(audit_mtx); - } -#if CONFIG_MACF - else { - error = mac_system_check_auditctl(kauth_cred_get(), NULL); - if (error) - return (error); - } -#endif - - /* - * a vp and cred of NULL is valid at this point - * and indicates we're to turn off auditing... - */ - audit_rotate_vnode(cred, vp); - if (vp) - vnode_put(vp); -out: - return (error); -} - -/********************************** - * End of system calls. * - **********************************/ - -/* - * MPSAFE - */ -struct kaudit_record * -audit_new(int event, proc_t p, __unused struct uthread *uthread) -{ - struct kaudit_record *ar; - int no_record; - kauth_cred_t safecred; - - /* - * Eventually, there may be certain classes of events that - * we will audit regardless of the audit state at the time - * the record is created. These events will generally - * correspond to changes in the audit state. The dummy - * code below is from our first prototype, but may also - * be used in the final version (with modified event numbers). - */ -#if 0 - if (event != AUDIT_EVENT_FILESTOP && event != AUDIT_EVENT_FILESTART) { -#endif - lck_mtx_lock(audit_mtx); - no_record = (audit_suspended || !audit_enabled); - lck_mtx_unlock(audit_mtx); - if (no_record) - return (NULL); -#if 0 - } -#endif - - /* - * Initialize the audit record header. - * XXX: We may want to fail-stop if allocation fails. - * XXX: The number of outstanding uncommitted audit records is - * limited by the number of concurrent threads servicing system - * calls in the kernel. - */ - - ar = (struct kaudit_record *)zalloc(audit_zone); - if (ar == NULL) - return NULL; - - bzero(ar, sizeof(*ar)); - ar->k_ar.ar_magic = AUDIT_RECORD_MAGIC; - ar->k_ar.ar_event = event; - nanotime(&ar->k_ar.ar_starttime); - - safecred = kauth_cred_proc_ref(p); - /* Export the subject credential. */ - cru2x(safecred, &ar->k_ar.ar_subj_cred); - - ar->k_ar.ar_subj_ruid = safecred->cr_ruid; - ar->k_ar.ar_subj_rgid = safecred->cr_rgid; - ar->k_ar.ar_subj_egid = safecred->cr_groups[0]; - ar->k_ar.ar_subj_auid = safecred->cr_au.ai_auid; - ar->k_ar.ar_subj_asid = safecred->cr_au.ai_asid; - ar->k_ar.ar_subj_amask = safecred->cr_au.ai_mask; - ar->k_ar.ar_subj_term = safecred->cr_au.ai_termid; - kauth_cred_unref(&safecred); - - ar->k_ar.ar_subj_pid = p->p_pid; - bcopy(p->p_comm, ar->k_ar.ar_subj_comm, MAXCOMLEN); - -#if CONFIG_MACF - do { - struct mac mac; - - /* Retrieve the MAC labels for the process. */ - ar->k_ar.ar_cred_mac_labels = - (char *)zalloc(audit_mac_label_zone); - if (ar->k_ar.ar_cred_mac_labels == NULL) { - zfree(audit_zone, ar); - return (NULL); - } - mac.m_buflen = MAC_AUDIT_LABEL_LEN; - mac.m_string = ar->k_ar.ar_cred_mac_labels; - mac_cred_label_externalize_audit(p, &mac); - - /* - * grab space for the reconds. - */ - ar->k_ar.ar_mac_records = (struct mac_audit_record_list_t *) - kalloc(sizeof(*ar->k_ar.ar_mac_records)); - if (ar->k_ar.ar_mac_records == NULL) { - zfree(audit_mac_label_zone, - ar->k_ar.ar_cred_mac_labels); - zfree(audit_zone, ar); - return (NULL); - } - - LIST_INIT(ar->k_ar.ar_mac_records); - - ar->k_ar.ar_forced_by_mac = 0; - - } while (0); -#endif - - lck_mtx_lock(audit_mtx); - audit_pre_q_len++; - lck_mtx_unlock(audit_mtx); - - return (ar); -} - -/* - * MPSAFE - * XXXAUDIT: So far, this is unused, and should probably be GC'd. - */ -void -audit_abort(struct kaudit_record *ar) -{ - lck_mtx_lock(audit_mtx); - audit_pre_q_len--; - lck_mtx_unlock(audit_mtx); - audit_free(ar); -} - -/* - * MPSAFE - */ -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) { - lck_mtx_lock(audit_mtx); - audit_pre_q_len--; - lck_mtx_unlock(audit_mtx); - audit_free(ar); - return; - } - - ar->k_ar.ar_errno = error; - ar->k_ar.ar_retval = retval; - - /* - * We might want to do some system-wide post-filtering - * here at some point. - */ - - /* - * Timestamp system call end. - */ - nanotime(&ar->k_ar.ar_endtime); - - lck_mtx_lock(audit_mtx); - /* - * Note: it could be that some records initiated while audit was - * enabled should still be committed? - */ - if (audit_suspended || !audit_enabled) { - audit_pre_q_len--; - lck_mtx_unlock(audit_mtx); - audit_free(ar); - return; - } - - /* - * Constrain the number of committed audit records based on - * the configurable parameter. - */ - while (audit_q_len >= audit_qctrl.aq_hiwater) { - - ret = wait_queue_assert_wait(audit_wait_queue, - AUDIT_COMMIT_EVENT, - THREAD_UNINT, - 0); - lck_mtx_unlock(audit_mtx); - - assert(ret == THREAD_WAITING); - - ret = thread_block(THREAD_CONTINUE_NULL); - assert(ret == THREAD_AWAKENED); - lck_mtx_lock(audit_mtx); - } - - TAILQ_INSERT_TAIL(&audit_q, ar, k_q); - audit_q_len++; - audit_pre_q_len--; - wait_queue_wakeup_one(audit_wait_queue, AUDIT_WORKER_EVENT, THREAD_AWAKENED); - lck_mtx_unlock(audit_mtx); -} - -/* - * If we're out of space and need to suspend unprivileged - * processes, do that here rather than trying to allocate - * another audit record. - */ -static void -audit_new_wait(int audit_event, proc_t proc, struct uthread *uthread) -{ - int ret; - - if (audit_in_failure && - suser(kauth_cred_get(), &proc->p_acflag) != 0) { - ret = wait_queue_assert_wait(audit_wait_queue, - AUDIT_FAILURE_EVENT, THREAD_UNINT, 0); - 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); -} - -/* - * Calls to set up and tear down audit structures associated with - * each system call. - */ -void -audit_syscall_enter(unsigned short code, proc_t proc, - struct uthread *uthread) -{ - int audit_event; - struct au_mask *aumask; - kauth_cred_t my_cred; - - 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. - */ - my_cred = kauth_cred_proc_ref(proc); - - if (my_cred->cr_au.ai_auid == AU_DEFAUDITID) - aumask = &audit_nae_mask; - else - aumask = &my_cred->cr_au.ai_mask; - - /* - * Allocate an audit record, if preselection allows it, and store - * in the BSD thread for later use. - */ - -#if CONFIG_MACF - do { - int error; - - error = mac_audit_check_preselect(my_cred, code, - (void *) uthread->uu_arg); - - if (error == MAC_AUDIT_YES) { - uthread->uu_ar = audit_new(audit_event, proc, uthread); - uthread->uu_ar->k_ar.ar_forced_by_mac = 1; - au_to_text("Forced by a MAC policy"); - } - else if (error == MAC_AUDIT_NO) { - uthread->uu_ar = NULL; - } - else if (error == MAC_AUDIT_DEFAULT && - au_preselect(audit_event, &my_cred->cr_au.ai_mask, - AU_PRS_FAILURE | AU_PRS_SUCCESS)) - audit_new_wait(audit_event, proc, uthread); - } while (0); -#else - if (au_preselect(audit_event, &my_cred->cr_au.ai_mask, - AU_PRS_FAILURE | AU_PRS_SUCCESS)) { - audit_new_wait(audit_event, proc, uthread); - } else { - uthread->uu_ar = NULL; - } -#endif - kauth_cred_unref(&my_cred); -} - -/* - * Note: The audit_syscall_exit() parameter list was modified to support - * mac_audit_check_postselect(), which requires the Darwin syscall number. - */ -#if CONFIG_MACF -void -audit_syscall_exit(unsigned short code, int error, AUDIT_PRINTF_ONLY proc_t proc, struct uthread *uthread) -#else -void -audit_syscall_exit(int error, AUDIT_PRINTF_ONLY proc_t proc, struct uthread *uthread) -#endif -{ - int retval; - - /* - * Commit the audit record as desired; once we pass the record - * into audit_commit(), the memory is owned by the audit - * subsystem. - * The return value from the system call is stored on the user - * thread. If there was an error, the return value is set to -1, - * imitating the behavior of the cerror routine. - */ - if (error) - retval = -1; - else - retval = uthread->uu_rval[0]; - -#if CONFIG_MACF - do { - int mac_error; - - if (uthread->uu_ar == NULL) /* syscall wasn't audited */ - goto out; - - /* - * Note, no other postselect mechanism exists. If - * mac_audit_check_postselect returns MAC_AUDIT_NO, the - * record will be suppressed. Other values at this - * point result in the audit record being committed. - * This suppression behavior will probably go away in - * the port to 10.3.4. - */ - mac_error = mac_audit_check_postselect(kauth_cred_get(), code, - (void *) uthread->uu_arg, error, retval, - uthread->uu_ar->k_ar.ar_forced_by_mac); - - if (mac_error == MAC_AUDIT_YES) - uthread->uu_ar->k_ar_commit |= AR_COMMIT_KERNEL; - else if (mac_error == MAC_AUDIT_NO) { - audit_free(uthread->uu_ar); - goto out; - } - - } while (0); - -#endif - audit_commit(uthread->uu_ar, error, retval); - if (uthread->uu_ar != NULL) { - AUDIT_PRINTF(("audit record committed by pid %d\n", proc->p_pid)); - } - -#if CONFIG_MACF -out: -#endif - uthread->uu_ar = NULL; - -} - -/* - * 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; - proc_t proc; - struct au_mask *aumask; - kauth_cred_t my_cred; - - if (audit_event == AUE_NULL) - return; - - uthread = curuthread(); - if (uthread == NULL) - return; - - proc = current_proc(); - if (proc == NULL) - return; - - assert(uthread->uu_ar == NULL); - - my_cred = kauth_cred_proc_ref(proc); - - /* Check which audit mask to use; either the kernel non-attributable - * event mask or the process audit mask. - */ - if (my_cred->cr_au.ai_auid == AU_DEFAUDITID) - aumask = &audit_nae_mask; - else - aumask = &my_cred->cr_au.ai_mask; - - kauth_cred_unref(&my_cred); - - /* - * 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 - * entered if auditing is disabled, avoiding the function call cost. We - * check the thread audit record pointer anyway, as the audit condition - * could change, and pre-selection may not have allocated an audit - * record for this event. - */ -void -audit_arg_addr(user_addr_t addr) -{ - struct kaudit_record *ar; - - ar = currecord(); - if (ar == NULL) - return; - - ar->k_ar.ar_arg_addr = CAST_DOWN(void *, addr); /* XXX */ - ar->k_ar.ar_valid_arg |= ARG_ADDR; -} - -void -audit_arg_len(user_size_t len) -{ - struct kaudit_record *ar; - - ar = currecord(); - if (ar == NULL) - return; - - ar->k_ar.ar_arg_len = CAST_DOWN(int, len); /* XXX */ - ar->k_ar.ar_valid_arg |= ARG_LEN; -} - -void -audit_arg_fd(int fd) -{ - struct kaudit_record *ar; - - ar = currecord(); - if (ar == NULL) - return; - - ar->k_ar.ar_arg_fd = fd; - ar->k_ar.ar_valid_arg |= ARG_FD; -} - -void -audit_arg_fflags(int fflags) -{ - struct kaudit_record *ar; - - ar = currecord(); - if (ar == NULL) - return; - - ar->k_ar.ar_arg_fflags = fflags; - ar->k_ar.ar_valid_arg |= ARG_FFLAGS; -} - -void -audit_arg_gid(gid_t gid, gid_t egid, gid_t rgid, gid_t sgid) -{ - struct kaudit_record *ar; - - ar = currecord(); - if (ar == NULL) - return; - - ar->k_ar.ar_arg_gid = gid; - ar->k_ar.ar_arg_egid = egid; - ar->k_ar.ar_arg_rgid = rgid; - ar->k_ar.ar_arg_sgid = sgid; - ar->k_ar.ar_valid_arg |= (ARG_GID | ARG_EGID | ARG_RGID | ARG_SGID); -} - -void -audit_arg_uid(uid_t uid, uid_t euid, uid_t ruid, uid_t suid) -{ - struct kaudit_record *ar; - - ar = currecord(); - if (ar == NULL) - return; - - ar->k_ar.ar_arg_uid = uid; - ar->k_ar.ar_arg_euid = euid; - ar->k_ar.ar_arg_ruid = ruid; - ar->k_ar.ar_arg_suid = suid; - ar->k_ar.ar_valid_arg |= (ARG_UID | ARG_EUID | ARG_RUID | ARG_SUID); -} - -void -audit_arg_groupset(const gid_t *gidset, u_int gidset_size) -{ - uint i; - struct kaudit_record *ar; - - ar = currecord(); - if (ar == NULL) - return; - - for (i = 0; i < gidset_size; i++) - ar->k_ar.ar_arg_groups.gidset[i] = gidset[i]; - ar->k_ar.ar_arg_groups.gidset_size = gidset_size; - ar->k_ar.ar_valid_arg |= ARG_GROUPSET; -} - -void -audit_arg_login(const char *login) -{ - struct kaudit_record *ar; - - ar = currecord(); - if (ar == NULL) - return; - - strlcpy(ar->k_ar.ar_arg_login, login, MAXLOGNAME); - - ar->k_ar.ar_valid_arg |= ARG_LOGIN; -} - -void -audit_arg_ctlname(const 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) -{ - struct kaudit_record *ar; - - ar = currecord(); - if (ar == NULL) - return; - - ar->k_ar.ar_arg_mask = mask; - ar->k_ar.ar_valid_arg |= ARG_MASK; -} - -void -audit_arg_mode(mode_t mode) -{ - struct kaudit_record *ar; - - ar = currecord(); - if (ar == NULL) - return; - - ar->k_ar.ar_arg_mode = mode; - ar->k_ar.ar_valid_arg |= ARG_MODE; -} - -void -audit_arg_dev(int dev) -{ - struct kaudit_record *ar; - - ar = currecord(); - if (ar == NULL) - return; - - ar->k_ar.ar_arg_dev = dev; - 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) -{ - struct kaudit_record *ar; - - ar = currecord(); - if (ar == NULL) - return; - - ar->k_ar.ar_arg_uid = uid; - ar->k_ar.ar_arg_gid = gid; - ar->k_ar.ar_valid_arg |= (ARG_UID | ARG_GID); -} - -void -audit_arg_pid(pid_t pid) -{ - struct kaudit_record *ar; - - ar = currecord(); - if (ar == NULL) - return; - - ar->k_ar.ar_arg_pid = pid; - ar->k_ar.ar_valid_arg |= ARG_PID; -} - -void -audit_arg_process(proc_t p) -{ - struct kaudit_record *ar; - kauth_cred_t my_cred; - - ar = currecord(); - if ((ar == NULL) || (p == NULL)) - return; - - my_cred = kauth_cred_proc_ref(p); - ar->k_ar.ar_arg_auid = my_cred->cr_au.ai_auid; - ar->k_ar.ar_arg_euid = my_cred->cr_uid; - ar->k_ar.ar_arg_egid = my_cred->cr_groups[0]; - ar->k_ar.ar_arg_ruid = my_cred->cr_ruid; - ar->k_ar.ar_arg_rgid = my_cred->cr_rgid; - ar->k_ar.ar_arg_asid = my_cred->cr_au.ai_asid; - ar->k_ar.ar_arg_termid = my_cred->cr_au.ai_termid; - kauth_cred_unref(&my_cred); - - ar->k_ar.ar_valid_arg |= ARG_AUID | ARG_EUID | ARG_EGID | ARG_RUID | - ARG_RGID | ARG_ASID | ARG_TERMID | ARG_PROCESS; -} - -void -audit_arg_signum(u_int signum) -{ - struct kaudit_record *ar; - - ar = currecord(); - if (ar == NULL) - return; - - ar->k_ar.ar_arg_signum = signum; - ar->k_ar.ar_valid_arg |= ARG_SIGNUM; -} - -void -audit_arg_socket(int sodomain, int sotype, int soprotocol) -{ - - struct kaudit_record *ar; - - ar = currecord(); - if (ar == NULL) - return; - - ar->k_ar.ar_arg_sockinfo.so_domain = sodomain; - ar->k_ar.ar_arg_sockinfo.so_type = sotype; - ar->k_ar.ar_arg_sockinfo.so_protocol = soprotocol; - ar->k_ar.ar_valid_arg |= ARG_SOCKINFO; -} - -/* - * Note that the current working directory vp must be supplied at the audit - * call site to permit per thread current working directories, and that it - * must take a upath starting with '/' into account for chroot if the path - * is absolute. This results in the real (non-chroot) path being recorded - * in the audit record. - */ -void -audit_arg_sockaddr(struct vnode *cwd_vp, struct sockaddr *so) -{ - struct kaudit_record *ar; - - ar = currecord(); - if (ar == NULL || cwd_vp == NULL || so == NULL) - return; - - bcopy(so, &ar->k_ar.ar_arg_sockaddr, sizeof(ar->k_ar.ar_arg_sockaddr)); - switch (so->sa_family) { - case AF_INET: - ar->k_ar.ar_valid_arg |= ARG_SADDRINET; - break; - case AF_INET6: - ar->k_ar.ar_valid_arg |= ARG_SADDRINET6; - break; - case AF_UNIX: - audit_arg_upath(cwd_vp, ((struct sockaddr_un *)so)->sun_path, - ARG_UPATH1); - ar->k_ar.ar_valid_arg |= ARG_SADDRUNIX; - break; - } -} - -void -audit_arg_auid(uid_t auid) -{ - struct kaudit_record *ar; - - ar = currecord(); - if (ar == NULL) - return; - - ar->k_ar.ar_arg_auid = auid; - ar->k_ar.ar_valid_arg |= ARG_AUID; -} - -void -audit_arg_auditinfo(const 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(const char *text) -{ - struct kaudit_record *ar; - - ar = currecord(); - if (ar == NULL) - return; - - /* Invalidate the text string */ - ar->k_ar.ar_valid_arg &= (ARG_ALL ^ ARG_TEXT); - if (text == NULL) - return; - - if (ar->k_ar.ar_arg_text == NULL) { - ar->k_ar.ar_arg_text = (char *)kalloc(MAXPATHLEN); - if (ar->k_ar.ar_arg_text == NULL) - return; - } - - strlcpy(ar->k_ar.ar_arg_text, text, MAXPATHLEN); - ar->k_ar.ar_valid_arg |= ARG_TEXT; -} - -void -audit_arg_cmd(int cmd) -{ - struct kaudit_record *ar; - - ar = currecord(); - if (ar == NULL) - return; - - ar->k_ar.ar_arg_cmd = cmd; - ar->k_ar.ar_valid_arg |= ARG_CMD; -} - -void -audit_arg_svipc_cmd(int cmd) -{ - struct kaudit_record *ar; - - ar = currecord(); - if (ar == NULL) - return; - - ar->k_ar.ar_arg_svipc_cmd = cmd; - ar->k_ar.ar_valid_arg |= ARG_SVIPC_CMD; -} - -void -audit_arg_svipc_perm(const struct ipc_perm *perm) -{ - struct kaudit_record *ar; - - ar = currecord(); - if (ar == NULL) - return; - - bcopy(perm, &ar->k_ar.ar_arg_svipc_perm, - sizeof(ar->k_ar.ar_arg_svipc_perm)); - ar->k_ar.ar_valid_arg |= ARG_SVIPC_PERM; -} - -void -audit_arg_svipc_id(int id) -{ - struct kaudit_record *ar; - - ar = currecord(); - if (ar == NULL) - return; - - ar->k_ar.ar_arg_svipc_id = id; - ar->k_ar.ar_valid_arg |= ARG_SVIPC_ID; -} - -void -audit_arg_svipc_addr(user_addr_t addr) -{ - struct kaudit_record *ar; - - ar = currecord(); - if (ar == NULL) - return; - - ar->k_ar.ar_arg_svipc_addr = 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(const union auditon_udata *udata) -{ - struct kaudit_record *ar; - - ar = currecord(); - if (ar == NULL) - return; - - bcopy((const 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(__unused proc_t p, const struct fileproc *fp) -{ - struct kaudit_record *ar; - struct socket *so; - struct inpcb *pcb; - - if (fp->f_fglob->fg_type == DTYPE_VNODE) { - audit_arg_vnpath_withref((struct vnode *)fp->f_fglob->fg_data, ARG_VNODE1); - return; - } - - if (fp->f_fglob->fg_type == DTYPE_SOCKET) { - ar = currecord(); - if (ar == NULL) - return; - so = (struct socket *)fp->f_fglob->fg_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; - } - } - -} - - -/* - * Store a path as given by the user process for auditing into the audit - * record stored on the user thread. This function will allocate the memory to - * store the path info if not already available. This memory will be - * freed when the audit record is freed. Note that the current working - * directory vp must be supplied at the audit call site to permit per thread - * current working directories, and that it must take a upath starting with - * '/' into account for chroot if the path is absolute. This results in the - * real (non-chroot) path being recorded in the audit record. - */ -void -audit_arg_upath(struct vnode *cwd_vp, char *upath, u_int64_t flags) -{ - struct kaudit_record *ar; - char **pathp; - - if (cwd_vp == NULL || upath == NULL) - return; /* nothing to do! */ - - if ((flags & (ARG_UPATH1 | ARG_UPATH2)) == 0) - return; - - ar = currecord(); - if (ar == NULL) /* This will be the case for unaudited system calls */ - return; - - if (flags & ARG_UPATH1) { - ar->k_ar.ar_valid_arg &= (ARG_ALL ^ ARG_UPATH1); - pathp = &ar->k_ar.ar_arg_upath1; - } - else { - ar->k_ar.ar_valid_arg &= (ARG_ALL ^ ARG_UPATH2); - pathp = &ar->k_ar.ar_arg_upath2; - } - - if (*pathp == NULL) { - *pathp = (char *)kalloc(MAXPATHLEN); - if (*pathp == NULL) - return; - } - - if (canon_path(cwd_vp, 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(*pathp, MAXPATHLEN); - *pathp = NULL; - } -} - -/* - * Function to save the path and vnode attr information into the audit - * record. - * - * It is assumed that the caller will hold any vnode locks necessary to - * perform a VNOP_GETATTR() on the passed vnode. - * - * XXX: The attr code is very similar to vfs_vnops.c:vn_stat(), but - * always provides access to the generation number as we need that - * to construct the BSM file ID. - * XXX: We should accept the process argument from the caller, since - * it's very likely they already have a reference. - * XXX: Error handling in this function is poor. - */ -void -audit_arg_vnpath(struct vnode *vp, u_int64_t flags) -{ - struct kaudit_record *ar; - struct vnode_attr va; - int error; - int len; - char **pathp; - struct vnode_au_info *vnp; - proc_t p; -#if CONFIG_MACF - char **vnode_mac_labelp; - struct mac mac; -#endif - - if (vp == NULL) - return; - - ar = currecord(); - if (ar == NULL) /* This will be the case for unaudited system calls */ - return; - - if ((flags & (ARG_VNODE1 | ARG_VNODE2)) == 0) - return; - - p = current_proc(); - - if (flags & ARG_VNODE1) { - ar->k_ar.ar_valid_arg &= (ARG_ALL ^ ARG_KPATH1); - ar->k_ar.ar_valid_arg &= (ARG_ALL ^ ARG_VNODE1); - pathp = &ar->k_ar.ar_arg_kpath1; - vnp = &ar->k_ar.ar_arg_vnode1; -#if CONFIG_MACF - vnode_mac_labelp = &ar->k_ar.ar_vnode1_mac_labels; -#endif - } - else { - ar->k_ar.ar_valid_arg &= (ARG_ALL ^ ARG_KPATH2); - ar->k_ar.ar_valid_arg &= (ARG_ALL ^ ARG_VNODE2); - pathp = &ar->k_ar.ar_arg_kpath2; - vnp = &ar->k_ar.ar_arg_vnode2; -#if CONFIG_MACF - vnode_mac_labelp = &ar->k_ar.ar_vnode2_mac_labels; -#endif - } - - if (*pathp == NULL) { - *pathp = (char *)kalloc(MAXPATHLEN); - if (*pathp == NULL) - return; - } - - /* - * 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; - 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(*pathp, MAXPATHLEN); - *pathp = NULL; - } - - VATTR_INIT(&va); - VATTR_WANTED(&va, va_mode); - VATTR_WANTED(&va, va_uid); - VATTR_WANTED(&va, va_gid); - VATTR_WANTED(&va, va_rdev); - VATTR_WANTED(&va, va_fsid); - VATTR_WANTED(&va, va_fileid); - VATTR_WANTED(&va, va_gen); - error = vnode_getattr(vp, &va, vfs_context_current()); - if (error) { - /* XXX: How to handle this case? */ - return; - } - -#if CONFIG_MACF - if (*vnode_mac_labelp == NULL) { - *vnode_mac_labelp = (char *)zalloc(audit_mac_label_zone); - if (*vnode_mac_labelp != NULL) { - mac.m_buflen = MAC_AUDIT_LABEL_LEN; - mac.m_string = *vnode_mac_labelp; - mac_vnode_label_externalize_audit(vp, &mac); - } - - - - } -#endif - - /* XXX do we want to fall back here when these aren't supported? */ - vnp->vn_mode = va.va_mode; - vnp->vn_uid = va.va_uid; - vnp->vn_gid = va.va_gid; - vnp->vn_dev = va.va_rdev; - vnp->vn_fsid = va.va_fsid; - vnp->vn_fileid = (u_long)va.va_fileid; - vnp->vn_gen = va.va_gen; - if (flags & ARG_VNODE1) - ar->k_ar.ar_valid_arg |= ARG_VNODE1; - else - ar->k_ar.ar_valid_arg |= ARG_VNODE2; - -} - -void -audit_arg_vnpath_withref(struct vnode *vp, u_int64_t flags) -{ - if (vp == NULL || vnode_getwithref(vp)) - return; - audit_arg_vnpath(vp, flags); - (void)vnode_put(vp); -} - -void -audit_arg_mach_port1(mach_port_name_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_name_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(proc_t p, int fd) -{ - struct fileproc *fp; - struct vnode *vp; - - audit_arg_fd(fd); - - if (fp_getfvp(p, fd, &fp, &vp) != 0) - return; - - audit_arg_vnpath_withref((struct vnode *)fp->f_fglob->fg_data, ARG_VNODE1); - file_drop(fd); -} - -#if CONFIG_MACF -/* - * This function is called by the MAC Framework to add audit data - * from a policy to the current audit record. - */ -int -audit_mac_data(int type, int len, u_char *data) { - struct kaudit_record *cur; - struct mac_audit_record *record; - int ret = 0; - - if (audit_enabled == 0) { - ret = ENOTSUP; - goto out_fail; - } - - cur = currecord(); - if (cur == NULL) { - ret = ENOTSUP; - goto out_fail; - } - - /* - * XXX: Note that we silently drop the audit data if this - * allocation fails - this is consistent with the rest of the - * audit implementation. - */ - record = (struct mac_audit_record *)kalloc(sizeof(*record)); - if (record == NULL) - goto out_fail; - - record->type = type; - record->length = len; - record->data = data; - LIST_INSERT_HEAD(cur->k_ar.ar_mac_records, record, records); - - return (0); - -out_fail: - kfree(data, len); - return (ret); -} - -void -audit_arg_mac_string(const char *string) -{ - struct kaudit_record *ar; - - ar = currecord(); - if (ar == NULL) - return; - - if (ar->k_ar.ar_arg_mac_string == NULL) { - ar->k_ar.ar_arg_mac_string = - (char *)kalloc(MAC_MAX_LABEL_BUF_LEN + MAC_ARG_PREFIX_LEN); - /* This should be a rare event. If kalloc() returns NULL, the - * system is low on kernel virtual memory. To be consistent with the - * rest of audit, just return (may need to panic if required to for audit6). - */ - if (ar->k_ar.ar_arg_mac_string == NULL) - return; - } - strncpy(ar->k_ar.ar_arg_mac_string, MAC_ARG_PREFIX, MAC_ARG_PREFIX_LEN); - strncpy(ar->k_ar.ar_arg_mac_string + MAC_ARG_PREFIX_LEN, string, MAC_MAX_LABEL_BUF_LEN); - ar->k_ar.ar_valid_arg |= ARG_MAC_STRING; - -} -#endif /* MAC */ - -/* - * kau_will_audit can be used by a security policy to determine - * if an audit record will be stored, reducing wasted memory allocation - * and string handling. - */ - -int -kau_will_audit(void) -{ - - return (audit_enabled && currecord() != NULL); -} - -#else /* !AUDIT */ - -void -audit_init(void) -{ - -} - -void -audit_shutdown(void) -{ - -} - -int -audit(proc_t p, struct audit_args *uap, register_t *retval) -{ - return (ENOSYS); -} - -int -auditon(proc_t p, struct auditon_args *uap, register_t *retval) -{ - return (ENOSYS); -} - -int -getauid(proc_t p, struct getauid_args *uap, register_t *retval) -{ - return (ENOSYS); -} - -int -setauid(proc_t p, struct setauid_args *uap, register_t *retval) -{ - return (ENOSYS); -} - -int -getaudit(proc_t p, struct getaudit_args *uap, register_t *retval) -{ - return (ENOSYS); -} - -int -setaudit(proc_t p, struct setaudit_args *uap, register_t *retval) -{ - return (ENOSYS); -} - -int -getaudit_addr(proc_t p, struct getaudit_addr_args *uap, register_t *retval) -{ - return (ENOSYS); -} - -int -setaudit_addr(proc_t p, struct setaudit_addr_args *uap, register_t *retval) -{ - return (ENOSYS); -} - -int -auditctl(proc_t p, struct auditctl_args *uap, register_t *retval) -{ - return (ENOSYS); -} - -#if CONFIG_MACF -void -audit_mac_data(int type, int len, u_char *data) -{ -} - -int -kau_will_audit() -{ - return (0); -} -#endif - -#endif /* AUDIT */