--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/c2/audit_start.c Tue Jun 14 00:00:00 2005 -0700
@@ -0,0 +1,580 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This file contains the envelope code for system call auditing.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/kmem.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/file.h>
+#include <sys/user.h>
+#include <sys/stropts.h>
+#include <sys/systm.h>
+#include <sys/pathname.h>
+#include <sys/debug.h>
+#include <sys/cred_impl.h>
+#include <sys/zone.h>
+#include <c2/audit.h>
+#include <c2/audit_kernel.h>
+#include <c2/audit_kevents.h>
+#include <c2/audit_record.h>
+#include "audit_door_infc.h"
+
+extern uint_t num_syscall; /* size of audit_s2e table */
+extern kmutex_t pidlock; /* proc table lock */
+
+int audit_load = 0; /* set from /etc/system */
+
+struct p_audit_data *pad0;
+struct t_audit_data *tad0;
+
+/*
+ * Das Boot. Initialize first process. Also generate an audit record indicating
+ * that the system has been booted.
+ */
+void
+audit_init()
+{
+ kthread_t *au_thread;
+ token_t *rp = NULL;
+ label_t jb;
+ struct audit_path apempty;
+ auditinfo_addr_t *ainfo;
+
+ if (audit_load == 0) {
+ audit_active = 0;
+ au_auditstate = AUC_DISABLED;
+ return;
+#ifdef DEBUG
+ } else if (audit_load == 2) {
+ debug_enter((char *)NULL);
+#endif
+ }
+
+ audit_active = 1;
+ set_all_proc_sys(); /* set pre- and post-syscall flags */
+
+ /* initialize memory allocators */
+ au_mem_init();
+
+ au_zone_setup();
+
+ /* inital thread structure */
+ tad0 = kmem_zalloc(sizeof (struct t_audit_data), KM_SLEEP);
+
+ /* initial process structure */
+ pad0 = kmem_cache_alloc(au_pad_cache, KM_SLEEP);
+ bzero(&pad0->pad_data, sizeof (pad0->pad_data));
+
+ T2A(curthread) = tad0;
+ P2A(curproc) = pad0;
+
+ /*
+ * The kernel allocates a bunch of threads make sure they have
+ * a valid tad
+ */
+
+ mutex_enter(&pidlock);
+
+ au_thread = curthread;
+ do {
+ if (T2A(au_thread) == NULL) {
+ T2A(au_thread) = tad0;
+ }
+ au_thread = au_thread->t_next;
+ } while (au_thread != curthread);
+
+ tad0->tad_ad = NULL;
+ mutex_exit(&pidlock);
+
+ /*
+ * Initialize audit context in our cred (kcred).
+ * No copy-on-write needed here because it's so early in init.
+ */
+ ainfo = crgetauinfo_modifiable(kcred);
+ ASSERT(ainfo != NULL);
+ bzero(ainfo, sizeof (auditinfo_addr_t));
+ ainfo->ai_auid = AU_NOAUDITID;
+
+ /* fabricate an empty audit_path to extend */
+ apempty.audp_cnt = 0;
+ apempty.audp_sect[0] = (char *)(&apempty.audp_sect[1]);
+ pad0->pad_root = au_pathdup(&apempty, 1, 2);
+ bcopy("/", pad0->pad_root->audp_sect[0], 2);
+ au_pathhold(pad0->pad_root);
+ pad0->pad_cwd = pad0->pad_root;
+
+ /*
+ * setup environment for asynchronous auditing. We can't use
+ * audit_async_start() here since it assumes the audit system
+ * has been started via auditd(1m). auditd sets the variable,
+ * auk_auditstate, to indicate audit record generation should
+ * commence. Here we want to always generate an audit record.
+ */
+ if (setjmp(&jb)) {
+ /* process audit policy (AUDIT_AHLT) for asynchronous events */
+ audit_async_drop((caddr_t *)(&rp), 0);
+ return;
+ }
+
+ ASSERT(tad0->tad_errjmp == NULL);
+ tad0->tad_errjmp = (void *)&jb;
+ tad0->tad_ctrl |= PAD_ERRJMP;
+
+ /* generate a system-booted audit record */
+ au_write((caddr_t *)&rp, au_to_text("booting kernel"));
+
+ audit_async_finish((caddr_t *)&rp, AUE_SYSTEMBOOT, NULL);
+}
+
+void
+audit_free()
+{
+}
+
+/*
+ * Check for any pending changes to the audit context for the given proc.
+ * p_crlock and pad_lock for the process are acquired here. Caller is
+ * responsible for assuring the process doesn't go away. If context is
+ * updated, the specified cralloc'ed cred will be used, otherwise it's freed.
+ * If no cred is given, it will be cralloc'ed here and caller assures that
+ * it is safe to allocate memory.
+ */
+void
+audit_update_context(proc_t *p, cred_t *ncr)
+{
+ struct p_audit_data *pad;
+ cred_t *newcred = ncr;
+
+ pad = P2A(p);
+ if (pad == NULL) {
+ if (newcred != NULL)
+ crfree(newcred);
+ return;
+ }
+
+ /* If a mask update is pending, take care of it. */
+ if (pad->pad_flags & PAD_SETMASK) {
+ auditinfo_addr_t *ainfo;
+
+ if (newcred == NULL)
+ newcred = cralloc();
+
+ mutex_enter(&pad->pad_lock);
+ /* the condition may have been handled by the time we lock */
+ if (pad->pad_flags & PAD_SETMASK) {
+ ainfo = crgetauinfo_modifiable(newcred);
+ if (ainfo == NULL) {
+ mutex_enter(&pad->pad_lock);
+ crfree(newcred);
+ return;
+ }
+
+ mutex_enter(&p->p_crlock);
+ crcopy_to(p->p_cred, newcred);
+ p->p_cred = newcred;
+
+ ainfo->ai_mask = pad->pad_newmask;
+
+ /* Unlock and cleanup. */
+ mutex_exit(&p->p_crlock);
+ pad->pad_flags &= ~PAD_SETMASK;
+
+ /*
+ * For curproc, assure that our thread points to right
+ * cred, so CRED() will be correct. Otherwise, no need
+ * to broadcast changes (via set_proc_pre_sys), since
+ * t_pre_sys is ALWAYS on when audit is enabled... due
+ * to syscall auditing.
+ */
+ if (p == curproc)
+ crset(p, newcred);
+ else
+ crfree(newcred);
+ } else {
+ crfree(newcred);
+ }
+ mutex_exit(&pad->pad_lock);
+ } else {
+ if (newcred != NULL)
+ crfree(newcred);
+ }
+}
+
+
+/*
+ * Enter system call. Do any necessary setup here. allocate resouces, etc.
+ */
+
+#include <sys/syscall.h>
+
+
+/*ARGSUSED*/
+int
+audit_start(
+ unsigned type,
+ unsigned scid,
+ int error,
+ klwp_t *lwp)
+{
+ int ctrl;
+ au_event_t event;
+ au_state_t estate;
+ struct t_audit_data *tad;
+ au_kcontext_t *kctx;
+
+ tad = U2A(u);
+ ASSERT(tad != NULL);
+
+ if (error) {
+ tad->tad_ctrl = 0;
+ tad->tad_flag = 0;
+ return (0);
+ }
+
+ audit_update_context(curproc, NULL);
+
+ /*
+ * if this is an indirect system call then don't do anything.
+ * audit_start will be called again from indir() in trap.c
+ */
+ if (scid == 0) {
+ tad->tad_ctrl = 0;
+ tad->tad_flag = 0;
+ return (0);
+ }
+ if (scid >= num_syscall)
+ scid = 0;
+
+ /* we can no longer ber guarantied a valid lwp_ap */
+ /* so we need to force it valid a lot of stuff needs it */
+ (void) save_syscall_args();
+
+ /* get control information */
+ ctrl = audit_s2e[scid].au_ctrl;
+
+ /*
+ * We need to gather paths for certain system calls even if they are
+ * not audited so that we can audit the various f* calls and be
+ * sure to have a CWD and CAR. Thus we thus set tad_ctrl over the
+ * system call regardless if the call is audited or not.
+ * We allow the au_init() routine to adjust the tad_ctrl.
+ */
+ tad->tad_ctrl = ctrl;
+ tad->tad_scid = scid;
+
+ /* get basic event for system call */
+ event = (*audit_s2e[scid].au_init)(audit_s2e[scid].au_event);
+
+ kctx = SET_KCTX_PZ;
+ if (kctx == NULL) {
+ zone_status_t zstate = zone_status_get(curproc->p_zone);
+ ASSERT(zstate != ZONE_IS_READY);
+ return (0);
+ }
+
+ estate = kctx->auk_ets[event];
+
+ /* now do preselection. Audit or not to Audit, that is the question */
+ if ((tad->tad_flag = auditme(kctx, tad, estate)) == 0) {
+ /*
+ * we assume that audit_finish will always be called.
+ */
+ return (0);
+ }
+
+ /*
+ * if auditing not enabled, then don't generate an audit record
+ * and don't count it.
+ */
+ if ((kctx->auk_auditstate != AUC_AUDITING &&
+ kctx->auk_auditstate != AUC_INIT_AUDIT)) {
+ /*
+ * we assume that audit_finish will always be called.
+ */
+ tad->tad_flag = 0;
+ return (0);
+ }
+
+ /*
+ * audit daemon has informed us that there is no longer any
+ * space left to hold audit records. We decide here if records
+ * should be dropped (but counted).
+ */
+ if (kctx->auk_auditstate == AUC_NOSPACE) {
+ if ((kctx->auk_policy & AUDIT_CNT) ||
+ (kctx->auk_policy & AUDIT_SCNT)) {
+ /* assume that audit_finish will always be called. */
+ tad->tad_flag = 0;
+
+ /* just count # of dropped audit records */
+ AS_INC(as_dropped, 1, kctx);
+
+ return (0);
+ }
+ }
+
+ tad->tad_event = event;
+ tad->tad_evmod = 0;
+
+ (*audit_s2e[scid].au_start)(tad);
+
+ return (0);
+}
+
+/*
+ * system call has completed. Now determine if we genearate an audit record
+ * or not.
+ */
+/*ARGSUSED*/
+void
+audit_finish(
+ unsigned type,
+ unsigned scid,
+ int error,
+ rval_t *rval)
+{
+ struct t_audit_data *tad;
+ int flag;
+ au_defer_info_t *attr;
+ au_kcontext_t *kctx = SET_KCTX_PZ;
+
+ if (kctx == NULL) {
+ zone_status_t zstate = zone_status_get(curproc->p_zone);
+ ASSERT(zstate != ZONE_IS_READY);
+ return;
+ }
+
+ tad = U2A(u);
+
+ /*
+ * Process all deferred events first.
+ */
+ attr = tad->tad_defer_head;
+ while (attr != NULL) {
+ au_defer_info_t *tmp_attr = attr;
+
+ au_close_time(kctx, (token_t *)attr->audi_ad, attr->audi_flag,
+ attr->audi_e_type, attr->audi_e_mod, &(attr->audi_atime));
+
+ attr = attr->audi_next;
+ kmem_free(tmp_attr, sizeof (au_defer_info_t));
+ }
+ tad->tad_defer_head = tad->tad_defer_tail = NULL;
+
+ if (tad->tad_flag == 0 && !(tad->tad_ctrl & PAD_SAVPATH)) {
+ /*
+ * clear the ctrl flag so that we don't have spurious
+ * collection of audit information.
+ */
+ tad->tad_scid = 0;
+ tad->tad_event = 0;
+ tad->tad_evmod = 0;
+ tad->tad_ctrl = 0;
+ ASSERT(tad->tad_aupath == NULL);
+ return;
+ }
+
+ scid = tad->tad_scid;
+
+ /*
+ * Perform any extra processing and determine if we are
+ * really going to generate any audit record.
+ */
+ (*audit_s2e[scid].au_finish)(tad, error, rval);
+ if (tad->tad_flag) {
+ tad->tad_flag = 0;
+
+ if (flag = audit_success(kctx, tad, error)) {
+ unsigned int sy_flags;
+ cred_t *cr = CRED();
+ const auditinfo_addr_t *ainfo = crgetauinfo(cr);
+
+ ASSERT(ainfo != NULL);
+
+ /* Add a subject token */
+ AUDIT_SETSUBJ(&(u_ad), cr, ainfo);
+
+ /* Add an optional group token */
+ AUDIT_SETGROUP(&(u_ad), cr, kctx);
+
+ if (tad->tad_evmod & PAD_SPRIVUSE)
+ au_write(&(u_ad),
+ au_to_privset("", &tad->tad_sprivs,
+ AUT_UPRIV, 1));
+
+ if (tad->tad_evmod & PAD_FPRIVUSE)
+ au_write(&(u_ad),
+ au_to_privset("", &tad->tad_fprivs,
+ AUT_UPRIV, 0));
+
+ /* Add a return token */
+#ifdef _SYSCALL32_IMPL
+ if (lwp_getdatamodel(
+ ttolwp(curthread)) == DATAMODEL_NATIVE)
+ sy_flags = sysent[scid].sy_flags & SE_RVAL_MASK;
+ else
+ sy_flags = sysent32[scid].sy_flags & SE_RVAL_MASK;
+#else
+ sy_flags = sysent[scid].sy_flags & SE_RVAL_MASK;
+#endif
+
+ if (sy_flags == SE_32RVAL1) {
+ if (type == 0) {
+ au_write(&(u_ad), au_to_return32(error, 0));
+ } else {
+ au_write(&(u_ad), au_to_return32(error,
+ rval->r_val1));
+ }
+ }
+ if (sy_flags == (SE_32RVAL2|SE_32RVAL1)) {
+ if (type == 0) {
+ au_write(&(u_ad), au_to_return32(error, 0));
+ } else {
+ au_write(&(u_ad), au_to_return32(error,
+ rval->r_val1));
+#ifdef NOTYET /* for possible future support */
+ au_write(&(u_ad), au_to_return32(error,
+ rval->r_val2));
+#endif
+ }
+ }
+ if (sy_flags == SE_64RVAL) {
+ if (type == 0) {
+ au_write(&(u_ad), au_to_return64(error, 0));
+ } else {
+ au_write(&(u_ad), au_to_return64(error,
+ rval->r_vals));
+ }
+ }
+
+ AS_INC(as_generated, 1, kctx);
+ AS_INC(as_kernel, 1, kctx);
+ }
+
+ /* Close up everything */
+ au_close(kctx, &(u_ad), flag, tad->tad_event, tad->tad_evmod);
+ }
+
+ ASSERT(u_ad == NULL);
+
+ /* free up any space remaining with the path's */
+ if (tad->tad_aupath != NULL) {
+ au_pathrele(tad->tad_aupath);
+ tad->tad_aupath = NULL;
+ tad->tad_vn = NULL;
+ }
+
+ /* free up any space remaining with openat path's */
+ if (tad->tad_atpath) {
+ au_pathrele(tad->tad_atpath);
+ tad->tad_atpath = NULL;
+ }
+
+ /*
+ * clear the ctrl flag so that we don't have spurious collection of
+ * audit information.
+ */
+ tad->tad_scid = 0;
+ tad->tad_event = 0;
+ tad->tad_evmod = 0;
+ tad->tad_ctrl = 0;
+}
+
+int
+audit_success(au_kcontext_t *kctx, struct t_audit_data *tad, int error)
+{
+ au_state_t ess;
+ au_state_t esf;
+ au_mask_t amask;
+ const auditinfo_addr_t *ainfo;
+
+ ess = esf = kctx->auk_ets[tad->tad_event];
+
+ if (error)
+ tad->tad_evmod |= PAD_FAILURE;
+
+ /* see if we really want to generate an audit record */
+ if (tad->tad_ctrl & PAD_NOAUDIT)
+ return (0);
+
+ /*
+ * nfs operation and we're auditing privilege or MAC. This
+ * is so we have a client audit record to match a nfs server
+ * audit record.
+ */
+ if (tad->tad_ctrl & PAD_AUDITME)
+ return (AU_OK);
+
+ ainfo = crgetauinfo(CRED());
+ if (ainfo == NULL)
+ return (0);
+ amask = ainfo->ai_mask;
+
+ if (error == 0)
+ return ((ess & amask.as_success) ? AU_OK : 0);
+ else
+ return ((esf & amask.as_failure) ? AU_OK : 0);
+}
+
+/*
+ * determine if we've preselected this event (system call).
+ */
+int
+auditme(au_kcontext_t *kctx, struct t_audit_data *tad, au_state_t estate)
+{
+ int flag = 0;
+ au_mask_t amask;
+ const auditinfo_addr_t *ainfo;
+
+ ainfo = crgetauinfo(CRED());
+ if (ainfo == NULL)
+ return (0);
+ amask = ainfo->ai_mask;
+
+ /* preselected system call */
+
+ if (amask.as_success & estate || amask.as_failure & estate) {
+ flag = 1;
+ } else if ((tad->tad_scid == SYS_putmsg) ||
+ (tad->tad_scid == SYS_getmsg)) {
+ estate = kctx->auk_ets[AUE_SOCKCONNECT] |
+ kctx->auk_ets[AUE_SOCKACCEPT] |
+ kctx->auk_ets[AUE_SOCKSEND] |
+ kctx->auk_ets[AUE_SOCKRECEIVE];
+ if (amask.as_success & estate || amask.as_failure & estate)
+ flag = 1;
+ }
+
+ return (flag);
+}