Page MenuHomeFreeBSD

D10149.id26790.diff
No OneTemporary

D10149.id26790.diff

Index: head/sys/conf/files
===================================================================
--- head/sys/conf/files
+++ head/sys/conf/files
@@ -4593,6 +4593,7 @@
security/audit/audit_arg.c optional audit
security/audit/audit_bsm.c optional audit
security/audit/audit_bsm_klib.c optional audit
+security/audit/audit_dtrace.c optional dtaudit audit | dtraceall audit compile-with "${CDDL_C}"
security/audit/audit_pipe.c optional audit
security/audit/audit_syscalls.c standard
security/audit/audit_trigger.c optional audit
Index: head/sys/modules/dtrace/Makefile
===================================================================
--- head/sys/modules/dtrace/Makefile
+++ head/sys/modules/dtrace/Makefile
@@ -2,7 +2,8 @@
.include "Makefile.inc"
-SUBDIR= dtmalloc \
+SUBDIR= dtaudit \
+ dtmalloc \
dtnfscl \
dtrace \
dtraceall \
Index: head/sys/modules/dtrace/dtaudit/Makefile
===================================================================
--- head/sys/modules/dtrace/dtaudit/Makefile
+++ head/sys/modules/dtrace/dtaudit/Makefile
@@ -0,0 +1,17 @@
+# $FreeBSD$
+
+SYSDIR?= ${.CURDIR}/../../..
+
+.PATH: ${SYSDIR}/security/audit
+
+KMOD= dtaudit
+SRCS= audit_dtrace.c \
+ vnode_if.h
+
+CFLAGS+= -I${SYSDIR}/cddl/compat/opensolaris \
+ -I${SYSDIR}/cddl/contrib/opensolaris/uts/common \
+ -I${SYSDIR}
+
+.include <bsd.kmod.mk>
+
+CFLAGS+= -include ${SYSDIR}/cddl/compat/opensolaris/sys/debug_compat.h
Index: head/sys/security/audit/audit.c
===================================================================
--- head/sys/security/audit/audit.c
+++ head/sys/security/audit/audit.c
@@ -1,8 +1,13 @@
/*-
* Copyright (c) 1999-2005 Apple Inc.
- * Copyright (c) 2006-2007 Robert N. M. Watson
+ * Copyright (c) 2006-2007, 2016 Robert N. M. Watson
* All rights reserved.
*
+ * Portions of this software were developed by BAE Systems, the University of
+ * Cambridge Computer Laboratory, and Memorial University under DARPA/AFRL
+ * contract FA8650-15-C-7558 ("CADETS"), as part of the DARPA Transparent
+ * Computing (TC) research program.
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -162,6 +167,20 @@
static struct cv audit_fail_cv;
/*
+ * Optional DTrace audit provider support: function pointers for preselection
+ * and commit events.
+ */
+#ifdef KDTRACE_HOOKS
+void *(*dtaudit_hook_preselect)(au_id_t auid, au_event_t event,
+ au_class_t class);
+int (*dtaudit_hook_commit)(struct kaudit_record *kar, au_id_t auid,
+ au_event_t event, au_class_t class, int sorf);
+void (*dtaudit_hook_bsm)(struct kaudit_record *kar, au_id_t auid,
+ au_event_t event, au_class_t class, int sorf,
+ void *bsm_data, size_t bsm_lenlen);
+#endif
+
+/*
* Kernel audit information. This will store the current audit address
* or host information that the kernel will use when it's generating
* audit records. This data is modified by the A_GET{SET}KAUDIT auditon(2)
@@ -410,6 +429,10 @@
if (ar == NULL)
return;
+ ar->k_ar.ar_errno = error;
+ ar->k_ar.ar_retval = retval;
+ nanotime(&ar->k_ar.ar_endtime);
+
/*
* Decide whether to commit the audit record by checking the error
* value from the system call and using the appropriate audit mask.
@@ -461,8 +484,21 @@
if (audit_pipe_preselect(auid, event, class, sorf,
ar->k_ar_commit & AR_PRESELECT_TRAIL) != 0)
ar->k_ar_commit |= AR_PRESELECT_PIPE;
+#ifdef KDTRACE_HOOKS
+ /*
+ * Expose the audit record to DTrace, both to allow the "commit" probe
+ * to fire if it's desirable, and also to allow a decision to be made
+ * about later firing with BSM in the audit worker.
+ */
+ if (dtaudit_hook_commit != NULL) {
+ if (dtaudit_hook_commit(ar, auid, event, class, sorf) != 0)
+ ar->k_ar_commit |= AR_PRESELECT_DTRACE;
+ }
+#endif
+
if ((ar->k_ar_commit & (AR_PRESELECT_TRAIL | AR_PRESELECT_PIPE |
- AR_PRESELECT_USER_TRAIL | AR_PRESELECT_USER_PIPE)) == 0) {
+ AR_PRESELECT_USER_TRAIL | AR_PRESELECT_USER_PIPE |
+ AR_PRESELECT_DTRACE)) == 0) {
mtx_lock(&audit_mtx);
audit_pre_q_len--;
mtx_unlock(&audit_mtx);
@@ -470,10 +506,6 @@
return;
}
- ar->k_ar.ar_errno = error;
- ar->k_ar.ar_retval = retval;
- nanotime(&ar->k_ar.ar_endtime);
-
/*
* Note: it could be that some records initiated while audit was
* enabled should still be committed?
@@ -510,9 +542,13 @@
audit_syscall_enter(unsigned short code, struct thread *td)
{
struct au_mask *aumask;
+#ifdef KDTRACE_HOOKS
+ void *dtaudit_state;
+#endif
au_class_t class;
au_event_t event;
au_id_t auid;
+ int record_needed;
KASSERT(td->td_ar == NULL, ("audit_syscall_enter: td->td_ar != NULL"));
KASSERT((td->td_pflags & TDP_AUDITREC) == 0,
@@ -544,8 +580,8 @@
aumask = &td->td_ucred->cr_audit.ai_mask;
/*
- * Allocate an audit record, if preselection allows it, and store in
- * the thread for later use.
+ * Determine whether trail or pipe preselection would like an audit
+ * record allocated for this system call.
*/
class = au_event_class(event);
if (au_preselect(event, class, aumask, AU_PRS_BOTH)) {
@@ -566,13 +602,51 @@
cv_wait(&audit_fail_cv, &audit_mtx);
panic("audit_failing_stop: thread continued");
}
- td->td_ar = audit_new(event, td);
- if (td->td_ar != NULL)
- td->td_pflags |= TDP_AUDITREC;
+ record_needed = 1;
} else if (audit_pipe_preselect(auid, event, class, AU_PRS_BOTH, 0)) {
+ record_needed = 1;
+ } else {
+ record_needed = 0;
+ }
+
+ /*
+ * After audit trails and pipes have made their policy choices, DTrace
+ * may request that records be generated as well. This is a slightly
+ * complex affair, as the DTrace audit provider needs the audit
+ * framework to maintain some state on the audit record, which has not
+ * been allocated at the point where the decision has to be made.
+ * This hook must run even if we are not changing the decision, as
+ * DTrace may want to stick event state onto a record we were going to
+ * produce due to the trail or pipes. The event state returned by the
+ * DTrace provider must be safe without locks held between here and
+ * below -- i.e., dtaudit_state must must refer to stable memory.
+ */
+#ifdef KDTRACE_HOOKS
+ dtaudit_state = NULL;
+ if (dtaudit_hook_preselect != NULL) {
+ dtaudit_state = dtaudit_hook_preselect(auid, event, class);
+ if (dtaudit_state != NULL)
+ record_needed = 1;
+ }
+#endif
+
+ /*
+ * If a record is required, allocate it and attach it to the thread
+ * for use throughout the system call. Also attach DTrace state if
+ * required.
+ *
+ * XXXRW: If we decide to reference count the evname_elem underlying
+ * dtaudit_state, we will need to free here if no record is allocated
+ * or allocatable.
+ */
+ if (record_needed) {
td->td_ar = audit_new(event, td);
- if (td->td_ar != NULL)
+ if (td->td_ar != NULL) {
td->td_pflags |= TDP_AUDITREC;
+#ifdef KDTRACE_HOOKS
+ td->td_ar->k_dtaudit_state = dtaudit_state;
+#endif
+ }
} else
td->td_ar = NULL;
}
Index: head/sys/security/audit/audit_bsm_klib.c
===================================================================
--- head/sys/security/audit/audit_bsm_klib.c
+++ head/sys/security/audit/audit_bsm_klib.c
@@ -362,6 +362,35 @@
EVNAMEMAP_WUNLOCK();
}
+#ifdef KDTRACE_HOOKS
+/*
+ * Look up an event-to-name mapping table entry by event number. As evname
+ * elements are stable in memory, we can return the pointer without the table
+ * lock held -- but the caller will need to lock the element mutex before
+ * accessing element fields.
+ *
+ * NB: the event identifier in elements is stable and can be read without
+ * holding the evname_elem lock.
+ */
+struct evname_elem *
+au_evnamemap_lookup(au_event_t event)
+{
+ struct evname_list *enl;
+ struct evname_elem *ene;
+
+ EVNAMEMAP_RLOCK();
+ enl = &evnamemap_hash[event % EVNAMEMAP_HASH_TABLE_SIZE];
+ LIST_FOREACH(ene, &enl->enl_head, ene_entry) {
+ if (ene->ene_event == event)
+ goto out;
+ }
+ ene = NULL;
+out:
+ EVNAMEMAP_RUNLOCK();
+ return (ene);
+}
+#endif /* !KDTRACE_HOOKS */
+
/*
* Convert sysctl names and present arguments to events.
*/
Index: head/sys/security/audit/audit_dtrace.c
===================================================================
--- head/sys/security/audit/audit_dtrace.c
+++ head/sys/security/audit/audit_dtrace.c
@@ -0,0 +1,532 @@
+/*-
+ * Copyright (c) 2016 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * This software was developed by BAE Systems, the University of Cambridge
+ * Computer Laboratory, and Memorial University under DARPA/AFRL contract
+ * FA8650-15-C-7558 ("CADETS"), as part of the DARPA Transparent Computing
+ * (TC) research program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/ctype.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/queue.h>
+#include <sys/refcount.h>
+
+#include <sys/dtrace.h>
+#include <sys/dtrace_bsd.h>
+
+#include <bsm/audit.h>
+#include <bsm/audit_internal.h>
+#include <bsm/audit_kevents.h>
+
+#include <security/audit/audit.h>
+#include <security/audit/audit_private.h>
+
+/*-
+ * Audit DTrace provider: allow DTrace to request that audit records be
+ * generated for various audit events, and then expose those records (in
+ * various forms) to probes. The model is that each event type has two
+ * probes, which use the event's name to create the probe:
+ *
+ * - "commit" passes the kernel-internal (unserialised) kaudit_record
+ * synchronously (from the originating thread) of the record as we prepare
+ * to "commit" the record to the audit queue.
+ *
+ * - "bsm" also passes generated BSM, and executes asynchronously in the audit
+ * worker thread, once it has been extracted from the audit queue. This is
+ * the point at which an audit record would be enqueued to the trail on
+ * disk, or to pipes.
+ *
+ * These probes support very different goals. The former executes in the
+ * thread originating the record, making it easier to correlate other DTrace
+ * probe activity with the event described in the record. The latter gives
+ * access to BSM-formatted events (at a cost) allowing DTrace to extract BSM
+ * directly an alternative mechanism to the formal audit trail and audit
+ * pipes.
+ *
+ * To generate names for numeric event IDs, userspace will push the contents
+ * of /etc/security/audit_event into the kernel during audit setup, much as it
+ * does /etc/security/audit_class. We then create the probes for each of
+ * those mappings. If one (or both) of the probes are enabled, then we cause
+ * a record to be generated (as both normal audit preselection and audit pipes
+ * do), and catch it on the way out during commit. There are suitable hook
+ * functions in the audit code that this provider can register to catch
+ * various events in the audit-record life cycle.
+ *
+ * Further ponderings:
+ *
+ * - How do we want to handle events for which there are not names -- perhaps
+ * a catch-all probe for those events without mappings?
+ *
+ * - Should the evname code really be present even if DTrace isn't loaded...?
+ * Right now, we arrange that it is so that userspace can usefully maintain
+ * the list in case DTrace is later loaded (and to prevent userspace
+ * confusion).
+ *
+ * - Should we add an additional set of audit:class::commit probes that use
+ * event class names to match broader categories of events as specified in
+ * /etc/security/event_class?
+ *
+ * - If we pursue that last point, we will want to pass the name of the event
+ * into the probe explicitly (e.g., as arg0), since it would no longer be
+ * available as the probe function name.
+ */
+
+static int dtaudit_unload(void);
+static void dtaudit_getargdesc(void *, dtrace_id_t, void *,
+ dtrace_argdesc_t *);
+static void dtaudit_provide(void *, dtrace_probedesc_t *);
+static void dtaudit_destroy(void *, dtrace_id_t, void *);
+static void dtaudit_enable(void *, dtrace_id_t, void *);
+static void dtaudit_disable(void *, dtrace_id_t, void *);
+static void dtaudit_load(void *);
+
+static dtrace_pattr_t dtaudit_attr = {
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
+};
+
+/*
+ * Strings for the "module" and "name" portions of the probe. The name of the
+ * audit event will be the "function" portion of the probe. All dtaudit
+ * probes therefore take the form audit:event:<event name>:commit.
+ */
+static char *dtaudit_module_str = "event";
+static char *dtaudit_name_commit_str = "commit";
+static char *dtaudit_name_bsm_str = "bsm";
+
+static dtrace_pops_t dtaudit_pops = {
+ .dtps_provide = dtaudit_provide,
+ .dtps_provide_module = NULL,
+ .dtps_enable = dtaudit_enable,
+ .dtps_disable = dtaudit_disable,
+ .dtps_suspend = NULL,
+ .dtps_resume = NULL,
+ .dtps_getargdesc = dtaudit_getargdesc,
+ .dtps_getargval = NULL,
+ .dtps_usermode = NULL,
+ .dtps_destroy = dtaudit_destroy
+};
+
+static dtrace_provider_id_t dtaudit_id;
+
+/*
+ * Because looking up entries in the event-to-name mapping is quite expensive,
+ * maintain a global flag tracking whether any dtaudit probes are enabled. If
+ * not, don't bother doing all that work whenever potential queries about
+ * events turn up during preselection or commit.
+ */
+static uint_t dtaudit_probes_enabled;
+
+/*
+ * Check dtaudit policy for the event to see whether this is an event we would
+ * like to preselect (i.e., cause an audit record to be generated for). To
+ * minimise probe effect when not used at all, we not only check for the probe
+ * on the individual event, but also a global flag indicating that at least
+ * one probe is enabled, before acquiring locks, searching lists, etc.
+ *
+ * If the event is selected, return an evname_elem reference to be stored in
+ * the audit record, which we can use later to avoid further lookups. The
+ * contents of the evname_elem must be sufficiently stable so as to not risk
+ * race conditions here.
+ *
+ * Currently, we take an interest only in the 'event' argument, but in the
+ * future might want to support other types of record selection tied to
+ * additional probe types (e.g., event clases).
+ *
+ * XXXRW: Should we have a catch-all probe here for events without registered
+ * names?
+ */
+static void *
+dtaudit_preselect(au_id_t auid, au_event_t event, au_class_t class)
+{
+ struct evname_elem *ene;
+ int probe_enabled;
+
+ /*
+ * NB: Lockless reads here may return a slightly stale value; this is
+ * considered better than acquiring a lock, however.
+ */
+ if (!dtaudit_probes_enabled)
+ return (NULL);
+ ene = au_evnamemap_lookup(event);
+ if (ene == NULL)
+ return (NULL);
+
+ /*
+ * See if either of the two probes for the audit event are enabled.
+ *
+ * NB: Lock also not acquired here -- but perhaps it wouldn't matter
+ * given that we've already used the list lock above?
+ *
+ * XXXRW: Alternatively, au_evnamemap_lookup() could return these
+ * values while holding the list lock...?
+ */
+ probe_enabled = ene->ene_commit_probe_enabled ||
+ ene->ene_bsm_probe_enabled;
+ if (!probe_enabled)
+ return (NULL);
+ return ((void *)ene);
+}
+
+/*
+ * Commit probe pre-BSM. Fires the probe but also checks to see if we should
+ * ask the audit framework to call us again with BSM arguments in the audit
+ * worker thread.
+ *
+ * XXXRW: Should we have a catch-all probe here for events without registered
+ * names?
+ */
+static int
+dtaudit_commit(struct kaudit_record *kar, au_id_t auid, au_event_t event,
+ au_class_t class, int sorf)
+{
+ char ene_name_lower[EVNAMEMAP_NAME_SIZE];
+ struct evname_elem *ene;
+ int i;
+
+ ene = (struct evname_elem *)kar->k_dtaudit_state;
+ if (ene == NULL)
+ return (0);
+
+ /*
+ * Process a possibly registered commit probe.
+ */
+ if (ene->ene_commit_probe_enabled) {
+ /*
+ * XXXRW: Lock ene to provide stability to the name string. A
+ * bit undesirable! We may want another locking strategy
+ * here. At least we don't run the DTrace probe under the
+ * lock.
+ *
+ * XXXRW: We provide the struct audit_record pointer -- but
+ * perhaps should provide the kaudit_record pointer?
+ */
+ EVNAME_LOCK(ene);
+ for (i = 0; i < sizeof(ene_name_lower); i++)
+ ene_name_lower[i] = tolower(ene->ene_name[i]);
+ EVNAME_UNLOCK(ene);
+ dtrace_probe(ene->ene_commit_probe_id,
+ (uintptr_t)ene_name_lower, (uintptr_t)&kar->k_ar, 0, 0, 0);
+ }
+
+ /*
+ * Return the state of the BSM probe to the caller.
+ */
+ return (ene->ene_bsm_probe_enabled);
+}
+
+/*
+ * Commit probe post-BSM.
+ *
+ * XXXRW: Should we have a catch-all probe here for events without registered
+ * names?
+ */
+static void
+dtaudit_bsm(struct kaudit_record *kar, au_id_t auid, au_event_t event,
+ au_class_t class, int sorf, void *bsm_data, size_t bsm_len)
+{
+ char ene_name_lower[EVNAMEMAP_NAME_SIZE];
+ struct evname_elem *ene;
+ int i;
+
+ ene = (struct evname_elem *)kar->k_dtaudit_state;
+ if (ene == NULL)
+ return;
+ if (!(ene->ene_bsm_probe_enabled))
+ return;
+
+ /*
+ * XXXRW: Lock ene to provide stability to the name string. A bit
+ * undesirable! We may want another locking strategy here. At least
+ * we don't run the DTrace probe under the lock.
+ *
+ * XXXRW: We provide the struct audit_record pointer -- but perhaps
+ * should provide the kaudit_record pointer?
+ */
+ EVNAME_LOCK(ene);
+ for (i = 0; i < sizeof(ene_name_lower); i++)
+ ene_name_lower[i] = tolower(ene->ene_name[i]);
+ EVNAME_UNLOCK(ene);
+ dtrace_probe(ene->ene_bsm_probe_id, (uintptr_t)ene_name_lower,
+ (uintptr_t)&kar->k_ar, (uintptr_t)bsm_data, (uintptr_t)bsm_len,
+ 0);
+}
+
+/*
+ * A very simple provider: argument types are identical across all probes: the
+ * kaudit_record, plus a BSM pointer and length.
+ */
+static void
+dtaudit_getargdesc(void *arg, dtrace_id_t id, void *parg,
+ dtrace_argdesc_t *desc)
+{
+ struct evname_elem *ene;
+ const char *p;
+
+ ene = (struct evname_elem *)parg;
+ p = NULL;
+ switch (desc->dtargd_ndx) {
+ case 0:
+ /* Audit event name. */
+ p = "char *";
+ break;
+
+ case 1:
+ /* In-kernel audit record. */
+ p = "struct audit_record *";
+ break;
+
+ case 2:
+ /* BSM data, if present. */
+ if (id == ene->ene_bsm_probe_id)
+ p = "const void *";
+ else
+ desc->dtargd_ndx = DTRACE_ARGNONE;
+ break;
+
+ case 3:
+ /* BSM length, if present. */
+ if (id == ene->ene_bsm_probe_id)
+ p = "size_t";
+ else
+ desc->dtargd_ndx = DTRACE_ARGNONE;
+ break;
+
+ default:
+ desc->dtargd_ndx = DTRACE_ARGNONE;
+ break;
+ }
+ if (p != NULL)
+ strlcpy(desc->dtargd_native, p, sizeof(desc->dtargd_native));
+}
+
+/*
+ * Callback from the event-to-name mapping code when performing
+ * evname_foreach(). Note that we may update the entry, so the foreach code
+ * must have a write lock. However, as the synchronisation model is private
+ * to the evname code, we cannot easily assert it here.
+ *
+ * XXXRW: How do we want to handle event rename / collision issues here --
+ * e.g., if userspace was using a name to point to one event number, and then
+ * changes it so that the name points at another? For now, paper over this by
+ * skipping event numbers that are already registered, and likewise skipping
+ * names that are already registered. However, this could lead to confusing
+ * behaviour so possibly needs to be resolved in the longer term.
+ */
+static void
+dtaudit_au_evnamemap_callback(struct evname_elem *ene)
+{
+ char ene_name_lower[EVNAMEMAP_NAME_SIZE];
+ int i;
+
+ /*
+ * DTrace, by convention, has lower-case probe names. However, the
+ * in-kernel event-to-name mapping table must maintain event-name case
+ * as submitted by userspace. Create a temporary lower-case version
+ * here, away from the fast path, to use when exposing the event name
+ * to DTrace as part of the name of a probe.
+ *
+ * NB: Convert the entire array, including the terminating nul,
+ * because these strings are short and it's more work not to. If they
+ * become long, we might feel more guilty about this sloppiness!
+ */
+ for (i = 0; i < sizeof(ene_name_lower); i++)
+ ene_name_lower[i] = tolower(ene->ene_name[i]);
+
+ /*
+ * Don't register a new probe if this event number already has an
+ * associated commit probe -- or if another event has already
+ * registered this name.
+ *
+ * XXXRW: There is an argument that if multiple numeric events match
+ * a single name, they should all be exposed to the same named probe.
+ * In particular, we should perhaps use a probe ID returned by this
+ * lookup and just stick that in the saved probe ID?
+ */
+ if ((ene->ene_commit_probe_id == 0) &&
+ (dtrace_probe_lookup(dtaudit_id, dtaudit_module_str,
+ ene_name_lower, dtaudit_name_commit_str) == 0)) {
+
+ /*
+ * Create the commit probe.
+ *
+ * NB: We don't declare any extra stack frames because stack()
+ * will just return the path to the audit commit code, which
+ * is not really interesting anyway.
+ *
+ * We pass in the pointer to the evnam_elem entry so that we
+ * can easily change its enabled flag in the probe
+ * enable/disable interface.
+ */
+ ene->ene_commit_probe_id = dtrace_probe_create(dtaudit_id,
+ dtaudit_module_str, ene_name_lower,
+ dtaudit_name_commit_str, 0, ene);
+ }
+
+ /*
+ * Don't register a new probe if this event number already has an
+ * associated bsm probe -- or if another event has already
+ * registered this name.
+ *
+ * XXXRW: There is an argument that if multiple numeric events match
+ * a single name, they should all be exposed to the same named probe.
+ * In particular, we should perhaps use a probe ID returned by this
+ * lookup and just stick that in the saved probe ID?
+ */
+ if ((ene->ene_bsm_probe_id == 0) &&
+ (dtrace_probe_lookup(dtaudit_id, dtaudit_module_str,
+ ene_name_lower, dtaudit_name_bsm_str) == 0)) {
+
+ /*
+ * Create the bsm probe.
+ *
+ * NB: We don't declare any extra stack frames because stack()
+ * will just return the path to the audit commit code, which
+ * is not really interesting anyway.
+ *
+ * We pass in the pointer to the evnam_elem entry so that we
+ * can easily change its enabled flag in the probe
+ * enable/disable interface.
+ */
+ ene->ene_bsm_probe_id = dtrace_probe_create(dtaudit_id,
+ dtaudit_module_str, ene_name_lower, dtaudit_name_bsm_str,
+ 0, ene);
+ }
+}
+
+static void
+dtaudit_provide(void *arg, dtrace_probedesc_t *desc)
+{
+
+ /*
+ * Walk all registered number-to-name mapping entries, and ensure each
+ * is properly registered.
+ */
+ au_evnamemap_foreach(dtaudit_au_evnamemap_callback);
+}
+
+static void
+dtaudit_destroy(void *arg, dtrace_id_t id, void *parg)
+{
+}
+
+static void
+dtaudit_enable(void *arg, dtrace_id_t id, void *parg)
+{
+ struct evname_elem *ene;
+
+ ene = parg;
+ KASSERT(ene->ene_commit_probe_id == id || ene->ene_bsm_probe_id == id,
+ ("%s: probe ID mismatch (%u, %u != %u)", __func__,
+ ene->ene_commit_probe_id, ene->ene_bsm_probe_id, id));
+
+ if (id == ene->ene_commit_probe_id)
+ ene->ene_commit_probe_enabled = 1;
+ else
+ ene->ene_bsm_probe_enabled = 1;
+ refcount_acquire(&dtaudit_probes_enabled);
+}
+
+static void
+dtaudit_disable(void *arg, dtrace_id_t id, void *parg)
+{
+ struct evname_elem *ene;
+
+ ene = parg;
+ KASSERT(ene->ene_commit_probe_id == id || ene->ene_bsm_probe_id == id,
+ ("%s: probe ID mismatch (%u, %u != %u)", __func__,
+ ene->ene_commit_probe_id, ene->ene_bsm_probe_id, id));
+
+ if (id == ene->ene_commit_probe_id)
+ ene->ene_commit_probe_enabled = 0;
+ else
+ ene->ene_bsm_probe_enabled = 0;
+ (void)refcount_release(&dtaudit_probes_enabled);
+}
+
+static void
+dtaudit_load(void *dummy)
+{
+
+ if (dtrace_register("audit", &dtaudit_attr, DTRACE_PRIV_USER, NULL,
+ &dtaudit_pops, NULL, &dtaudit_id) != 0)
+ return;
+ dtaudit_hook_preselect = dtaudit_preselect;
+ dtaudit_hook_commit = dtaudit_commit;
+ dtaudit_hook_bsm = dtaudit_bsm;
+}
+
+static int
+dtaudit_unload(void)
+{
+ int error;
+
+ dtaudit_hook_preselect = NULL;
+ dtaudit_hook_commit = NULL;
+ dtaudit_hook_bsm = NULL;
+ if ((error = dtrace_unregister(dtaudit_id)) != 0)
+ return (error);
+ return (0);
+}
+
+static int
+dtaudit_modevent(module_t mod __unused, int type, void *data __unused)
+{
+ int error = 0;
+
+ switch (type) {
+ case MOD_LOAD:
+ case MOD_UNLOAD:
+ case MOD_SHUTDOWN:
+ break;
+
+ default:
+ error = EOPNOTSUPP;
+ break;
+ }
+
+ return (error);
+}
+
+SYSINIT(dtaudit_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, dtaudit_load,
+ NULL);
+SYSUNINIT(dtaudit_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY,
+ dtaudit_unload, NULL);
+
+DEV_MODULE(dtaudit, dtaudit_modevent, NULL);
+MODULE_VERSION(dtaudit, 1);
+MODULE_DEPEND(dtaudit, dtrace, 1, 1, 1);
+MODULE_DEPEND(dtaudit, opensolaris, 1, 1, 1);
Index: head/sys/security/audit/audit_private.h
===================================================================
--- head/sys/security/audit/audit_private.h
+++ head/sys/security/audit/audit_private.h
@@ -94,6 +94,8 @@
#define AR_PRESELECT_USER_TRAIL 0x00004000U
#define AR_PRESELECT_USER_PIPE 0x00008000U
+#define AR_PRESELECT_DTRACE 0x00010000U
+
/*
* Audit data is generated as a stream of struct audit_record structures,
* linked by struct kaudit_record, and contain storage for possible audit so
@@ -323,6 +325,9 @@
void *k_udata; /* User data. */
u_int k_ulen; /* User data length. */
struct uthread *k_uthread; /* Audited thread. */
+#ifdef KDTRACE_HOOKS
+ void *k_dtaudit_state;
+#endif
TAILQ_ENTRY(kaudit_record) k_q;
};
TAILQ_HEAD(kaudit_queue, kaudit_record);
@@ -379,7 +384,7 @@
* Audit event-to-name mapping structure, maintained in audit_bsm_klib.c. It
* appears in this header so that the DTrace audit provider can dereference
* instances passed back in the au_evname_foreach() callbacks. Safe access to
- * its fields rquires holding ene_lock (after it is visible in the global
+ * its fields requires holding ene_lock (after it is visible in the global
* table).
*
* Locking:
@@ -393,6 +398,16 @@
char ene_name[EVNAMEMAP_NAME_SIZE]; /* (l) */
LIST_ENTRY(evname_elem) ene_entry; /* (m) */
struct mtx ene_lock;
+
+#ifdef KDTRACE_HOOKS
+ /* DTrace probe IDs; 0 if not yet registered. */
+ uint32_t ene_commit_probe_id; /* (M) */
+ uint32_t ene_bsm_probe_id; /* (M) */
+
+ /* Flags indicating if the probes enabled or not. */
+ int ene_commit_probe_enabled; /* (M) */
+ int ene_bsm_probe_enabled; /* (M) */
+#endif
};
#define EVNAME_LOCK(ene) mtx_lock(&(ene)->ene_lock)
@@ -403,6 +418,21 @@
*/
typedef void (*au_evnamemap_callback_t)(struct evname_elem *ene);
+/*
+ * DTrace audit provider (dtaudit) hooks -- to be set non-NULL when the audit
+ * provider is loaded and ready to be called into.
+ */
+#ifdef KDTRACE_HOOKS
+extern void *(*dtaudit_hook_preselect)(au_id_t auid, au_event_t event,
+ au_class_t class);
+extern int (*dtaudit_hook_commit)(struct kaudit_record *kar,
+ au_id_t auid, au_event_t event, au_class_t class,
+ int sorf);
+extern void (*dtaudit_hook_bsm)(struct kaudit_record *kar, au_id_t auid,
+ au_event_t event, au_class_t class, int sorf,
+ void *bsm_data, size_t bsm_len);
+#endif /* !KDTRACE_HOOKS */
+
#include <sys/fcntl.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
@@ -425,6 +455,9 @@
void au_evnamemap_init(void);
void au_evnamemap_insert(au_event_t event, const char *name);
void au_evnamemap_foreach(au_evnamemap_callback_t callback);
+#ifdef KDTRACE_HOOKS
+struct evname_elem *au_evnamemap_lookup(au_event_t event);
+#endif
int au_event_name(au_event_t event, char *name);
au_event_t audit_ctlname_to_sysctlevent(int name[], uint64_t valid_arg);
au_event_t audit_flags_and_error_to_openevent(int oflags, int error);
Index: head/sys/security/audit/audit_worker.c
===================================================================
--- head/sys/security/audit/audit_worker.c
+++ head/sys/security/audit/audit_worker.c
@@ -1,8 +1,13 @@
/*-
* Copyright (c) 1999-2008 Apple Inc.
- * Copyright (c) 2006-2008 Robert N. M. Watson
+ * Copyright (c) 2006-2008, 2016 Robert N. M. Watson
* All rights reserved.
*
+ * Portions of this software were developed by BAE Systems, the University of
+ * Cambridge Computer Laboratory, and Memorial University under DARPA/AFRL
+ * contract FA8650-15-C-7558 ("CADETS"), as part of the DARPA Transparent
+ * Computing (TC) research program.
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -362,7 +367,8 @@
if (!(ar->k_ar_commit & AR_COMMIT_KERNEL) ||
((ar->k_ar_commit & AR_PRESELECT_PIPE) == 0 &&
- (ar->k_ar_commit & AR_PRESELECT_TRAIL) == 0))
+ (ar->k_ar_commit & AR_PRESELECT_TRAIL) == 0 &&
+ (ar->k_ar_commit & AR_PRESELECT_DTRACE) == 0))
goto out;
auid = ar->k_ar.ar_subj_auid;
@@ -399,6 +405,17 @@
ar->k_ar_commit & AR_PRESELECT_TRAIL, bsm->data,
bsm->len);
+#ifdef KDTRACE_HOOKS
+ /*
+ * Version of the dtaudit commit hook that accepts BSM.
+ */
+ if (ar->k_ar_commit & AR_PRESELECT_DTRACE) {
+ if (dtaudit_hook_bsm != NULL)
+ dtaudit_hook_bsm(ar, auid, event, class, sorf,
+ bsm->data, bsm->len);
+ }
+#endif
+
kau_free(bsm);
out:
if (locked)

File Metadata

Mime Type
text/plain
Expires
Thu, Oct 23, 8:07 PM (15 h, 18 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
24024111
Default Alt Text
D10149.id26790.diff (30 KB)

Event Timeline