xref: /freebsd/sys/security/audit/audit_dtrace.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
11811d6bfSRobert Watson /*-
2*2ddefb6dSRobert Watson  * Copyright (c) 2016, 2018 Robert N. M. Watson
31811d6bfSRobert Watson  * All rights reserved.
41811d6bfSRobert Watson  *
51811d6bfSRobert Watson  * This software was developed by BAE Systems, the University of Cambridge
61811d6bfSRobert Watson  * Computer Laboratory, and Memorial University under DARPA/AFRL contract
71811d6bfSRobert Watson  * FA8650-15-C-7558 ("CADETS"), as part of the DARPA Transparent Computing
81811d6bfSRobert Watson  * (TC) research program.
91811d6bfSRobert Watson  *
101811d6bfSRobert Watson  * Redistribution and use in source and binary forms, with or without
111811d6bfSRobert Watson  * modification, are permitted provided that the following conditions
121811d6bfSRobert Watson  * are met:
131811d6bfSRobert Watson  * 1. Redistributions of source code must retain the above copyright
141811d6bfSRobert Watson  *    notice, this list of conditions and the following disclaimer.
151811d6bfSRobert Watson  * 2. Redistributions in binary form must reproduce the above copyright
161811d6bfSRobert Watson  *    notice, this list of conditions and the following disclaimer in the
171811d6bfSRobert Watson  *    documentation and/or other materials provided with the distribution.
181811d6bfSRobert Watson  *
191811d6bfSRobert Watson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
201811d6bfSRobert Watson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
211811d6bfSRobert Watson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
221811d6bfSRobert Watson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
231811d6bfSRobert Watson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
241811d6bfSRobert Watson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
251811d6bfSRobert Watson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
261811d6bfSRobert Watson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
271811d6bfSRobert Watson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
281811d6bfSRobert Watson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
291811d6bfSRobert Watson  * SUCH DAMAGE.
301811d6bfSRobert Watson  */
311811d6bfSRobert Watson 
321811d6bfSRobert Watson #include <sys/param.h>
331811d6bfSRobert Watson #include <sys/conf.h>
341811d6bfSRobert Watson #include <sys/ctype.h>
351811d6bfSRobert Watson #include <sys/kernel.h>
361811d6bfSRobert Watson #include <sys/malloc.h>
371811d6bfSRobert Watson #include <sys/module.h>
381811d6bfSRobert Watson #include <sys/queue.h>
391811d6bfSRobert Watson #include <sys/refcount.h>
401811d6bfSRobert Watson 
411811d6bfSRobert Watson #include <sys/dtrace.h>
421811d6bfSRobert Watson #include <sys/dtrace_bsd.h>
431811d6bfSRobert Watson 
441811d6bfSRobert Watson #include <bsm/audit.h>
451811d6bfSRobert Watson #include <bsm/audit_internal.h>
461811d6bfSRobert Watson #include <bsm/audit_kevents.h>
471811d6bfSRobert Watson 
481811d6bfSRobert Watson #include <security/audit/audit.h>
491811d6bfSRobert Watson #include <security/audit/audit_private.h>
501811d6bfSRobert Watson 
511811d6bfSRobert Watson /*-
521811d6bfSRobert Watson  * Audit DTrace provider: allow DTrace to request that audit records be
531811d6bfSRobert Watson  * generated for various audit events, and then expose those records (in
541811d6bfSRobert Watson  * various forms) to probes.  The model is that each event type has two
551811d6bfSRobert Watson  * probes, which use the event's name to create the probe:
561811d6bfSRobert Watson  *
571811d6bfSRobert Watson  * - "commit" passes the kernel-internal (unserialised) kaudit_record
581811d6bfSRobert Watson  *   synchronously (from the originating thread) of the record as we prepare
591811d6bfSRobert Watson  *   to "commit" the record to the audit queue.
601811d6bfSRobert Watson  *
611811d6bfSRobert Watson  * - "bsm" also passes generated BSM, and executes asynchronously in the audit
621811d6bfSRobert Watson  *   worker thread, once it has been extracted from the audit queue.  This is
631811d6bfSRobert Watson  *   the point at which an audit record would be enqueued to the trail on
641811d6bfSRobert Watson  *   disk, or to pipes.
651811d6bfSRobert Watson  *
661811d6bfSRobert Watson  * These probes support very different goals.  The former executes in the
671811d6bfSRobert Watson  * thread originating the record, making it easier to correlate other DTrace
681811d6bfSRobert Watson  * probe activity with the event described in the record.  The latter gives
691811d6bfSRobert Watson  * access to BSM-formatted events (at a cost) allowing DTrace to extract BSM
701811d6bfSRobert Watson  * directly an alternative mechanism to the formal audit trail and audit
711811d6bfSRobert Watson  * pipes.
721811d6bfSRobert Watson  *
731811d6bfSRobert Watson  * To generate names for numeric event IDs, userspace will push the contents
741811d6bfSRobert Watson  * of /etc/security/audit_event into the kernel during audit setup, much as it
751811d6bfSRobert Watson  * does /etc/security/audit_class.  We then create the probes for each of
761811d6bfSRobert Watson  * those mappings.  If one (or both) of the probes are enabled, then we cause
771811d6bfSRobert Watson  * a record to be generated (as both normal audit preselection and audit pipes
781811d6bfSRobert Watson  * do), and catch it on the way out during commit.  There are suitable hook
791811d6bfSRobert Watson  * functions in the audit code that this provider can register to catch
801811d6bfSRobert Watson  * various events in the audit-record life cycle.
811811d6bfSRobert Watson  *
821811d6bfSRobert Watson  * Further ponderings:
831811d6bfSRobert Watson  *
841811d6bfSRobert Watson  * - How do we want to handle events for which there are not names -- perhaps
851811d6bfSRobert Watson  *   a catch-all probe for those events without mappings?
861811d6bfSRobert Watson  *
871811d6bfSRobert Watson  * - Should the evname code really be present even if DTrace isn't loaded...?
881811d6bfSRobert Watson  *   Right now, we arrange that it is so that userspace can usefully maintain
891811d6bfSRobert Watson  *   the list in case DTrace is later loaded (and to prevent userspace
901811d6bfSRobert Watson  *   confusion).
911811d6bfSRobert Watson  *
921811d6bfSRobert Watson  * - Should we add an additional set of audit:class::commit probes that use
931811d6bfSRobert Watson  *   event class names to match broader categories of events as specified in
941811d6bfSRobert Watson  *   /etc/security/event_class?
951811d6bfSRobert Watson  *
961811d6bfSRobert Watson  * - If we pursue that last point, we will want to pass the name of the event
971811d6bfSRobert Watson  *   into the probe explicitly (e.g., as arg0), since it would no longer be
981811d6bfSRobert Watson  *   available as the probe function name.
991811d6bfSRobert Watson  */
1001811d6bfSRobert Watson 
1011811d6bfSRobert Watson static int	dtaudit_unload(void);
1021811d6bfSRobert Watson static void	dtaudit_getargdesc(void *, dtrace_id_t, void *,
1031811d6bfSRobert Watson 		    dtrace_argdesc_t *);
1041811d6bfSRobert Watson static void	dtaudit_provide(void *, dtrace_probedesc_t *);
1051811d6bfSRobert Watson static void	dtaudit_destroy(void *, dtrace_id_t, void *);
1061811d6bfSRobert Watson static void	dtaudit_enable(void *, dtrace_id_t, void *);
1071811d6bfSRobert Watson static void	dtaudit_disable(void *, dtrace_id_t, void *);
1081811d6bfSRobert Watson static void	dtaudit_load(void *);
1091811d6bfSRobert Watson 
1101811d6bfSRobert Watson static dtrace_pattr_t dtaudit_attr = {
1111811d6bfSRobert Watson { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
1121811d6bfSRobert Watson { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
1131811d6bfSRobert Watson { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
1141811d6bfSRobert Watson { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
1151811d6bfSRobert Watson { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
1161811d6bfSRobert Watson };
1171811d6bfSRobert Watson 
1181811d6bfSRobert Watson /*
1191811d6bfSRobert Watson  * Strings for the "module" and "name" portions of the probe.  The name of the
1201811d6bfSRobert Watson  * audit event will be the "function" portion of the probe.  All dtaudit
1211811d6bfSRobert Watson  * probes therefore take the form audit:event:<event name>:commit.
1221811d6bfSRobert Watson  */
1231811d6bfSRobert Watson static char	*dtaudit_module_str = "event";
1241811d6bfSRobert Watson static char	*dtaudit_name_commit_str = "commit";
1251811d6bfSRobert Watson static char	*dtaudit_name_bsm_str = "bsm";
1261811d6bfSRobert Watson 
1271811d6bfSRobert Watson static dtrace_pops_t dtaudit_pops = {
1281811d6bfSRobert Watson 	.dtps_provide =		dtaudit_provide,
1291811d6bfSRobert Watson 	.dtps_provide_module =	NULL,
1301811d6bfSRobert Watson 	.dtps_enable =		dtaudit_enable,
1311811d6bfSRobert Watson 	.dtps_disable =		dtaudit_disable,
1321811d6bfSRobert Watson 	.dtps_suspend =		NULL,
1331811d6bfSRobert Watson 	.dtps_resume =		NULL,
1341811d6bfSRobert Watson 	.dtps_getargdesc =	dtaudit_getargdesc,
1351811d6bfSRobert Watson 	.dtps_getargval =	NULL,
1361811d6bfSRobert Watson 	.dtps_usermode =	NULL,
1371811d6bfSRobert Watson 	.dtps_destroy =		dtaudit_destroy
1381811d6bfSRobert Watson };
1391811d6bfSRobert Watson 
1401811d6bfSRobert Watson static dtrace_provider_id_t	dtaudit_id;
1411811d6bfSRobert Watson 
1421811d6bfSRobert Watson /*
1431811d6bfSRobert Watson  * Because looking up entries in the event-to-name mapping is quite expensive,
1441811d6bfSRobert Watson  * maintain a global flag tracking whether any dtaudit probes are enabled.  If
1451811d6bfSRobert Watson  * not, don't bother doing all that work whenever potential queries about
1461811d6bfSRobert Watson  * events turn up during preselection or commit.
147*2ddefb6dSRobert Watson  *
148*2ddefb6dSRobert Watson  * NB: We used to maintain our own variable in dtaudit, but now use the
149*2ddefb6dSRobert Watson  * centralized audit_dtrace_enabled variable imported from the audit code.
150*2ddefb6dSRobert Watson  *
151*2ddefb6dSRobert Watson  * static uint_t		dtaudit_probes_enabled;
1521811d6bfSRobert Watson  */
1531811d6bfSRobert Watson 
1541811d6bfSRobert Watson /*
1551811d6bfSRobert Watson  * Check dtaudit policy for the event to see whether this is an event we would
1561811d6bfSRobert Watson  * like to preselect (i.e., cause an audit record to be generated for).  To
1571811d6bfSRobert Watson  * minimise probe effect when not used at all, we not only check for the probe
1581811d6bfSRobert Watson  * on the individual event, but also a global flag indicating that at least
1591811d6bfSRobert Watson  * one probe is enabled, before acquiring locks, searching lists, etc.
1601811d6bfSRobert Watson  *
1611811d6bfSRobert Watson  * If the event is selected, return an evname_elem reference to be stored in
1621811d6bfSRobert Watson  * the audit record, which we can use later to avoid further lookups.  The
1631811d6bfSRobert Watson  * contents of the evname_elem must be sufficiently stable so as to not risk
1641811d6bfSRobert Watson  * race conditions here.
1651811d6bfSRobert Watson  *
1661811d6bfSRobert Watson  * Currently, we take an interest only in the 'event' argument, but in the
1671811d6bfSRobert Watson  * future might want to support other types of record selection tied to
1681811d6bfSRobert Watson  * additional probe types (e.g., event clases).
1691811d6bfSRobert Watson  *
1701811d6bfSRobert Watson  * XXXRW: Should we have a catch-all probe here for events without registered
1711811d6bfSRobert Watson  * names?
1721811d6bfSRobert Watson  */
1731811d6bfSRobert Watson static void *
dtaudit_preselect(au_id_t auid,au_event_t event,au_class_t class)1741811d6bfSRobert Watson dtaudit_preselect(au_id_t auid, au_event_t event, au_class_t class)
1751811d6bfSRobert Watson {
1761811d6bfSRobert Watson 	struct evname_elem *ene;
1771811d6bfSRobert Watson 	int probe_enabled;
1781811d6bfSRobert Watson 
1791811d6bfSRobert Watson 	/*
1801811d6bfSRobert Watson 	 * NB: Lockless reads here may return a slightly stale value; this is
1811811d6bfSRobert Watson 	 * considered better than acquiring a lock, however.
1821811d6bfSRobert Watson 	 */
183*2ddefb6dSRobert Watson 	if (!audit_dtrace_enabled)
1841811d6bfSRobert Watson 		return (NULL);
1851811d6bfSRobert Watson 	ene = au_evnamemap_lookup(event);
1861811d6bfSRobert Watson 	if (ene == NULL)
1871811d6bfSRobert Watson 		return (NULL);
1881811d6bfSRobert Watson 
1891811d6bfSRobert Watson 	/*
1901811d6bfSRobert Watson 	 * See if either of the two probes for the audit event are enabled.
1911811d6bfSRobert Watson 	 *
1921811d6bfSRobert Watson 	 * NB: Lock also not acquired here -- but perhaps it wouldn't matter
1931811d6bfSRobert Watson 	 * given that we've already used the list lock above?
1941811d6bfSRobert Watson 	 *
1951811d6bfSRobert Watson 	 * XXXRW: Alternatively, au_evnamemap_lookup() could return these
1961811d6bfSRobert Watson 	 * values while holding the list lock...?
1971811d6bfSRobert Watson 	 */
1981811d6bfSRobert Watson 	probe_enabled = ene->ene_commit_probe_enabled ||
1991811d6bfSRobert Watson 	    ene->ene_bsm_probe_enabled;
2001811d6bfSRobert Watson 	if (!probe_enabled)
2011811d6bfSRobert Watson 		return (NULL);
2021811d6bfSRobert Watson 	return ((void *)ene);
2031811d6bfSRobert Watson }
2041811d6bfSRobert Watson 
2051811d6bfSRobert Watson /*
2061811d6bfSRobert Watson  * Commit probe pre-BSM.  Fires the probe but also checks to see if we should
2071811d6bfSRobert Watson  * ask the audit framework to call us again with BSM arguments in the audit
2081811d6bfSRobert Watson  * worker thread.
2091811d6bfSRobert Watson  *
2101811d6bfSRobert Watson  * XXXRW: Should we have a catch-all probe here for events without registered
2111811d6bfSRobert Watson  * names?
2121811d6bfSRobert Watson  */
2131811d6bfSRobert Watson static int
dtaudit_commit(struct kaudit_record * kar,au_id_t auid,au_event_t event,au_class_t class,int sorf)2141811d6bfSRobert Watson dtaudit_commit(struct kaudit_record *kar, au_id_t auid, au_event_t event,
2151811d6bfSRobert Watson     au_class_t class, int sorf)
2161811d6bfSRobert Watson {
2171811d6bfSRobert Watson 	char ene_name_lower[EVNAMEMAP_NAME_SIZE];
2181811d6bfSRobert Watson 	struct evname_elem *ene;
2191811d6bfSRobert Watson 	int i;
2201811d6bfSRobert Watson 
2211811d6bfSRobert Watson 	ene = (struct evname_elem *)kar->k_dtaudit_state;
2221811d6bfSRobert Watson 	if (ene == NULL)
2231811d6bfSRobert Watson 		return (0);
2241811d6bfSRobert Watson 
2251811d6bfSRobert Watson 	/*
2261811d6bfSRobert Watson 	 * Process a possibly registered commit probe.
2271811d6bfSRobert Watson 	 */
2281811d6bfSRobert Watson 	if (ene->ene_commit_probe_enabled) {
2291811d6bfSRobert Watson 		/*
2301811d6bfSRobert Watson 		 * XXXRW: Lock ene to provide stability to the name string.  A
2311811d6bfSRobert Watson 		 * bit undesirable!  We may want another locking strategy
2321811d6bfSRobert Watson 		 * here.  At least we don't run the DTrace probe under the
2331811d6bfSRobert Watson 		 * lock.
2341811d6bfSRobert Watson 		 *
2351811d6bfSRobert Watson 		 * XXXRW: We provide the struct audit_record pointer -- but
2361811d6bfSRobert Watson 		 * perhaps should provide the kaudit_record pointer?
2371811d6bfSRobert Watson 		 */
2381811d6bfSRobert Watson 		EVNAME_LOCK(ene);
2391811d6bfSRobert Watson 		for (i = 0; i < sizeof(ene_name_lower); i++)
2401811d6bfSRobert Watson 			ene_name_lower[i] = tolower(ene->ene_name[i]);
2411811d6bfSRobert Watson 		EVNAME_UNLOCK(ene);
2421811d6bfSRobert Watson 		dtrace_probe(ene->ene_commit_probe_id,
2431811d6bfSRobert Watson 		    (uintptr_t)ene_name_lower, (uintptr_t)&kar->k_ar, 0, 0, 0);
2441811d6bfSRobert Watson 	}
2451811d6bfSRobert Watson 
2461811d6bfSRobert Watson 	/*
2471811d6bfSRobert Watson 	 * Return the state of the BSM probe to the caller.
2481811d6bfSRobert Watson 	 */
2491811d6bfSRobert Watson 	return (ene->ene_bsm_probe_enabled);
2501811d6bfSRobert Watson }
2511811d6bfSRobert Watson 
2521811d6bfSRobert Watson /*
2531811d6bfSRobert Watson  * Commit probe post-BSM.
2541811d6bfSRobert Watson  *
2551811d6bfSRobert Watson  * XXXRW: Should we have a catch-all probe here for events without registered
2561811d6bfSRobert Watson  * names?
2571811d6bfSRobert Watson  */
2581811d6bfSRobert Watson 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)2591811d6bfSRobert Watson dtaudit_bsm(struct kaudit_record *kar, au_id_t auid, au_event_t event,
2601811d6bfSRobert Watson     au_class_t class, int sorf, void *bsm_data, size_t bsm_len)
2611811d6bfSRobert Watson {
2621811d6bfSRobert Watson 	char ene_name_lower[EVNAMEMAP_NAME_SIZE];
2631811d6bfSRobert Watson 	struct evname_elem *ene;
2641811d6bfSRobert Watson 	int i;
2651811d6bfSRobert Watson 
2661811d6bfSRobert Watson 	ene = (struct evname_elem *)kar->k_dtaudit_state;
2671811d6bfSRobert Watson 	if (ene == NULL)
2681811d6bfSRobert Watson 		return;
2691811d6bfSRobert Watson 	if (!(ene->ene_bsm_probe_enabled))
2701811d6bfSRobert Watson 		return;
2711811d6bfSRobert Watson 
2721811d6bfSRobert Watson 	/*
2731811d6bfSRobert Watson 	 * XXXRW: Lock ene to provide stability to the name string.  A bit
2741811d6bfSRobert Watson 	 * undesirable!  We may want another locking strategy here.  At least
2751811d6bfSRobert Watson 	 * we don't run the DTrace probe under the lock.
2761811d6bfSRobert Watson 	 *
2771811d6bfSRobert Watson 	 * XXXRW: We provide the struct audit_record pointer -- but perhaps
2781811d6bfSRobert Watson 	 * should provide the kaudit_record pointer?
2791811d6bfSRobert Watson 	 */
2801811d6bfSRobert Watson 	EVNAME_LOCK(ene);
2811811d6bfSRobert Watson 	for (i = 0; i < sizeof(ene_name_lower); i++)
2821811d6bfSRobert Watson 		ene_name_lower[i] = tolower(ene->ene_name[i]);
2831811d6bfSRobert Watson 	EVNAME_UNLOCK(ene);
2841811d6bfSRobert Watson 	dtrace_probe(ene->ene_bsm_probe_id, (uintptr_t)ene_name_lower,
2851811d6bfSRobert Watson 	    (uintptr_t)&kar->k_ar, (uintptr_t)bsm_data, (uintptr_t)bsm_len,
2861811d6bfSRobert Watson 	    0);
2871811d6bfSRobert Watson }
2881811d6bfSRobert Watson 
2891811d6bfSRobert Watson /*
2901811d6bfSRobert Watson  * A very simple provider: argument types are identical across all probes: the
2911811d6bfSRobert Watson  * kaudit_record, plus a BSM pointer and length.
2921811d6bfSRobert Watson  */
2931811d6bfSRobert Watson static void
dtaudit_getargdesc(void * arg,dtrace_id_t id,void * parg,dtrace_argdesc_t * desc)2941811d6bfSRobert Watson dtaudit_getargdesc(void *arg, dtrace_id_t id, void *parg,
2951811d6bfSRobert Watson     dtrace_argdesc_t *desc)
2961811d6bfSRobert Watson {
2971811d6bfSRobert Watson 	struct evname_elem *ene;
2981811d6bfSRobert Watson 	const char *p;
2991811d6bfSRobert Watson 
3001811d6bfSRobert Watson 	ene = (struct evname_elem *)parg;
3011811d6bfSRobert Watson 	p = NULL;
3021811d6bfSRobert Watson 	switch (desc->dtargd_ndx) {
3031811d6bfSRobert Watson 	case 0:
3041811d6bfSRobert Watson 		/* Audit event name. */
3051811d6bfSRobert Watson 		p = "char *";
3061811d6bfSRobert Watson 		break;
3071811d6bfSRobert Watson 
3081811d6bfSRobert Watson 	case 1:
3091811d6bfSRobert Watson 		/* In-kernel audit record. */
3101811d6bfSRobert Watson 		p = "struct audit_record *";
3111811d6bfSRobert Watson 		break;
3121811d6bfSRobert Watson 
3131811d6bfSRobert Watson 	case 2:
3141811d6bfSRobert Watson 		/* BSM data, if present. */
3151811d6bfSRobert Watson 		if (id == ene->ene_bsm_probe_id)
3161811d6bfSRobert Watson 			p = "const void *";
3171811d6bfSRobert Watson 		else
3181811d6bfSRobert Watson 			desc->dtargd_ndx = DTRACE_ARGNONE;
3191811d6bfSRobert Watson 		break;
3201811d6bfSRobert Watson 
3211811d6bfSRobert Watson 	case 3:
3221811d6bfSRobert Watson 		/* BSM length, if present. */
3231811d6bfSRobert Watson 		if (id == ene->ene_bsm_probe_id)
3241811d6bfSRobert Watson 			p = "size_t";
3251811d6bfSRobert Watson 		else
3261811d6bfSRobert Watson 			desc->dtargd_ndx = DTRACE_ARGNONE;
3271811d6bfSRobert Watson 		break;
3281811d6bfSRobert Watson 
3291811d6bfSRobert Watson 	default:
3301811d6bfSRobert Watson 		desc->dtargd_ndx = DTRACE_ARGNONE;
3311811d6bfSRobert Watson 		break;
3321811d6bfSRobert Watson 	}
3331811d6bfSRobert Watson 	if (p != NULL)
3341811d6bfSRobert Watson 		strlcpy(desc->dtargd_native, p, sizeof(desc->dtargd_native));
3351811d6bfSRobert Watson }
3361811d6bfSRobert Watson 
3371811d6bfSRobert Watson /*
3381811d6bfSRobert Watson  * Callback from the event-to-name mapping code when performing
3391811d6bfSRobert Watson  * evname_foreach().  Note that we may update the entry, so the foreach code
3401811d6bfSRobert Watson  * must have a write lock.  However, as the synchronisation model is private
3411811d6bfSRobert Watson  * to the evname code, we cannot easily assert it here.
3421811d6bfSRobert Watson  *
3431811d6bfSRobert Watson  * XXXRW: How do we want to handle event rename / collision issues here --
3441811d6bfSRobert Watson  * e.g., if userspace was using a name to point to one event number, and then
3451811d6bfSRobert Watson  * changes it so that the name points at another?  For now, paper over this by
3461811d6bfSRobert Watson  * skipping event numbers that are already registered, and likewise skipping
3471811d6bfSRobert Watson  * names that are already registered.  However, this could lead to confusing
3481811d6bfSRobert Watson  * behaviour so possibly needs to be resolved in the longer term.
3491811d6bfSRobert Watson  */
3501811d6bfSRobert Watson static void
dtaudit_au_evnamemap_callback(struct evname_elem * ene)3511811d6bfSRobert Watson dtaudit_au_evnamemap_callback(struct evname_elem *ene)
3521811d6bfSRobert Watson {
3531811d6bfSRobert Watson 	char ene_name_lower[EVNAMEMAP_NAME_SIZE];
3541811d6bfSRobert Watson 	int i;
3551811d6bfSRobert Watson 
3561811d6bfSRobert Watson 	/*
3571811d6bfSRobert Watson 	 * DTrace, by convention, has lower-case probe names.  However, the
3581811d6bfSRobert Watson 	 * in-kernel event-to-name mapping table must maintain event-name case
3591811d6bfSRobert Watson 	 * as submitted by userspace.  Create a temporary lower-case version
3601811d6bfSRobert Watson 	 * here, away from the fast path, to use when exposing the event name
3611811d6bfSRobert Watson 	 * to DTrace as part of the name of a probe.
3621811d6bfSRobert Watson 	 *
3631811d6bfSRobert Watson 	 * NB: Convert the entire array, including the terminating nul,
3641811d6bfSRobert Watson 	 * because these strings are short and it's more work not to.  If they
3651811d6bfSRobert Watson 	 * become long, we might feel more guilty about this sloppiness!
3661811d6bfSRobert Watson 	 */
3671811d6bfSRobert Watson 	for (i = 0; i < sizeof(ene_name_lower); i++)
3681811d6bfSRobert Watson 		ene_name_lower[i] = tolower(ene->ene_name[i]);
3691811d6bfSRobert Watson 
3701811d6bfSRobert Watson 	/*
3711811d6bfSRobert Watson 	 * Don't register a new probe if this event number already has an
3721811d6bfSRobert Watson 	 * associated commit probe -- or if another event has already
3731811d6bfSRobert Watson 	 * registered this name.
3741811d6bfSRobert Watson 	 *
3751811d6bfSRobert Watson 	 * XXXRW: There is an argument that if multiple numeric events match
3761811d6bfSRobert Watson 	 * a single name, they should all be exposed to the same named probe.
3771811d6bfSRobert Watson 	 * In particular, we should perhaps use a probe ID returned by this
3781811d6bfSRobert Watson 	 * lookup and just stick that in the saved probe ID?
3791811d6bfSRobert Watson 	 */
3801811d6bfSRobert Watson 	if ((ene->ene_commit_probe_id == 0) &&
3811811d6bfSRobert Watson 	    (dtrace_probe_lookup(dtaudit_id, dtaudit_module_str,
3821811d6bfSRobert Watson 	    ene_name_lower, dtaudit_name_commit_str) == 0)) {
3831811d6bfSRobert Watson 		/*
3841811d6bfSRobert Watson 		 * Create the commit probe.
3851811d6bfSRobert Watson 		 *
3861811d6bfSRobert Watson 		 * NB: We don't declare any extra stack frames because stack()
3871811d6bfSRobert Watson 		 * will just return the path to the audit commit code, which
3881811d6bfSRobert Watson 		 * is not really interesting anyway.
3891811d6bfSRobert Watson 		 *
3901811d6bfSRobert Watson 		 * We pass in the pointer to the evnam_elem entry so that we
3911811d6bfSRobert Watson 		 * can easily change its enabled flag in the probe
3921811d6bfSRobert Watson 		 * enable/disable interface.
3931811d6bfSRobert Watson 		 */
3941811d6bfSRobert Watson 		ene->ene_commit_probe_id = dtrace_probe_create(dtaudit_id,
3951811d6bfSRobert Watson 		    dtaudit_module_str, ene_name_lower,
3961811d6bfSRobert Watson 		    dtaudit_name_commit_str, 0, ene);
3971811d6bfSRobert Watson 	}
3981811d6bfSRobert Watson 
3991811d6bfSRobert Watson 	/*
4001811d6bfSRobert Watson 	 * Don't register a new probe if this event number already has an
4011811d6bfSRobert Watson 	 * associated bsm probe -- or if another event has already
4021811d6bfSRobert Watson 	 * registered this name.
4031811d6bfSRobert Watson 	 *
4041811d6bfSRobert Watson 	 * XXXRW: There is an argument that if multiple numeric events match
4051811d6bfSRobert Watson 	 * a single name, they should all be exposed to the same named probe.
4061811d6bfSRobert Watson 	 * In particular, we should perhaps use a probe ID returned by this
4071811d6bfSRobert Watson 	 * lookup and just stick that in the saved probe ID?
4081811d6bfSRobert Watson 	 */
4091811d6bfSRobert Watson 	if ((ene->ene_bsm_probe_id == 0) &&
4101811d6bfSRobert Watson 	    (dtrace_probe_lookup(dtaudit_id, dtaudit_module_str,
4111811d6bfSRobert Watson 	    ene_name_lower, dtaudit_name_bsm_str) == 0)) {
4121811d6bfSRobert Watson 		/*
4131811d6bfSRobert Watson 		 * Create the bsm probe.
4141811d6bfSRobert Watson 		 *
4151811d6bfSRobert Watson 		 * NB: We don't declare any extra stack frames because stack()
4161811d6bfSRobert Watson 		 * will just return the path to the audit commit code, which
4171811d6bfSRobert Watson 		 * is not really interesting anyway.
4181811d6bfSRobert Watson 		 *
4191811d6bfSRobert Watson 		 * We pass in the pointer to the evnam_elem entry so that we
4201811d6bfSRobert Watson 		 * can easily change its enabled flag in the probe
4211811d6bfSRobert Watson 		 * enable/disable interface.
4221811d6bfSRobert Watson 		 */
4231811d6bfSRobert Watson 		ene->ene_bsm_probe_id = dtrace_probe_create(dtaudit_id,
4241811d6bfSRobert Watson 		    dtaudit_module_str, ene_name_lower, dtaudit_name_bsm_str,
4251811d6bfSRobert Watson 		    0, ene);
4261811d6bfSRobert Watson 	}
4271811d6bfSRobert Watson }
4281811d6bfSRobert Watson 
4291811d6bfSRobert Watson static void
dtaudit_provide(void * arg,dtrace_probedesc_t * desc)4301811d6bfSRobert Watson dtaudit_provide(void *arg, dtrace_probedesc_t *desc)
4311811d6bfSRobert Watson {
4321811d6bfSRobert Watson 
4331811d6bfSRobert Watson 	/*
4341811d6bfSRobert Watson 	 * Walk all registered number-to-name mapping entries, and ensure each
4351811d6bfSRobert Watson 	 * is properly registered.
4361811d6bfSRobert Watson 	 */
4371811d6bfSRobert Watson 	au_evnamemap_foreach(dtaudit_au_evnamemap_callback);
4381811d6bfSRobert Watson }
4391811d6bfSRobert Watson 
4401811d6bfSRobert Watson static void
dtaudit_destroy(void * arg,dtrace_id_t id,void * parg)4411811d6bfSRobert Watson dtaudit_destroy(void *arg, dtrace_id_t id, void *parg)
4421811d6bfSRobert Watson {
4431811d6bfSRobert Watson }
4441811d6bfSRobert Watson 
4451811d6bfSRobert Watson static void
dtaudit_enable(void * arg,dtrace_id_t id,void * parg)4461811d6bfSRobert Watson dtaudit_enable(void *arg, dtrace_id_t id, void *parg)
4471811d6bfSRobert Watson {
4481811d6bfSRobert Watson 	struct evname_elem *ene;
4491811d6bfSRobert Watson 
4501811d6bfSRobert Watson 	ene = parg;
4511811d6bfSRobert Watson 	KASSERT(ene->ene_commit_probe_id == id || ene->ene_bsm_probe_id == id,
4521811d6bfSRobert Watson 	    ("%s: probe ID mismatch (%u, %u != %u)", __func__,
4531811d6bfSRobert Watson 	    ene->ene_commit_probe_id, ene->ene_bsm_probe_id, id));
4541811d6bfSRobert Watson 
4551811d6bfSRobert Watson 	if (id == ene->ene_commit_probe_id)
4561811d6bfSRobert Watson 		ene->ene_commit_probe_enabled = 1;
4571811d6bfSRobert Watson 	else
4581811d6bfSRobert Watson 		ene->ene_bsm_probe_enabled = 1;
459*2ddefb6dSRobert Watson 	refcount_acquire(&audit_dtrace_enabled);
460*2ddefb6dSRobert Watson 	audit_syscalls_enabled_update();
4611811d6bfSRobert Watson }
4621811d6bfSRobert Watson 
4631811d6bfSRobert Watson static void
dtaudit_disable(void * arg,dtrace_id_t id,void * parg)4641811d6bfSRobert Watson dtaudit_disable(void *arg, dtrace_id_t id, void *parg)
4651811d6bfSRobert Watson {
4661811d6bfSRobert Watson 	struct evname_elem *ene;
4671811d6bfSRobert Watson 
4681811d6bfSRobert Watson 	ene = parg;
4691811d6bfSRobert Watson 	KASSERT(ene->ene_commit_probe_id == id || ene->ene_bsm_probe_id == id,
4701811d6bfSRobert Watson 	    ("%s: probe ID mismatch (%u, %u != %u)", __func__,
4711811d6bfSRobert Watson 	    ene->ene_commit_probe_id, ene->ene_bsm_probe_id, id));
4721811d6bfSRobert Watson 
4731811d6bfSRobert Watson 	if (id == ene->ene_commit_probe_id)
4741811d6bfSRobert Watson 		ene->ene_commit_probe_enabled = 0;
4751811d6bfSRobert Watson 	else
4761811d6bfSRobert Watson 		ene->ene_bsm_probe_enabled = 0;
477*2ddefb6dSRobert Watson 	(void)refcount_release(&audit_dtrace_enabled);
478*2ddefb6dSRobert Watson 	audit_syscalls_enabled_update();
4791811d6bfSRobert Watson }
4801811d6bfSRobert Watson 
4811811d6bfSRobert Watson static void
dtaudit_load(void * dummy)4821811d6bfSRobert Watson dtaudit_load(void *dummy)
4831811d6bfSRobert Watson {
4841811d6bfSRobert Watson 
4851811d6bfSRobert Watson 	if (dtrace_register("audit", &dtaudit_attr, DTRACE_PRIV_USER, NULL,
4861811d6bfSRobert Watson 	    &dtaudit_pops, NULL, &dtaudit_id) != 0)
4871811d6bfSRobert Watson 		return;
4881811d6bfSRobert Watson 	dtaudit_hook_preselect = dtaudit_preselect;
4891811d6bfSRobert Watson 	dtaudit_hook_commit = dtaudit_commit;
4901811d6bfSRobert Watson 	dtaudit_hook_bsm = dtaudit_bsm;
4911811d6bfSRobert Watson }
4921811d6bfSRobert Watson 
4931811d6bfSRobert Watson static int
dtaudit_unload(void)4941811d6bfSRobert Watson dtaudit_unload(void)
4951811d6bfSRobert Watson {
4961811d6bfSRobert Watson 	int error;
4971811d6bfSRobert Watson 
4981811d6bfSRobert Watson 	dtaudit_hook_preselect = NULL;
4991811d6bfSRobert Watson 	dtaudit_hook_commit = NULL;
5001811d6bfSRobert Watson 	dtaudit_hook_bsm = NULL;
5011811d6bfSRobert Watson 	if ((error = dtrace_unregister(dtaudit_id)) != 0)
5021811d6bfSRobert Watson 		return (error);
5031811d6bfSRobert Watson 	return (0);
5041811d6bfSRobert Watson }
5051811d6bfSRobert Watson 
5061811d6bfSRobert Watson static int
dtaudit_modevent(module_t mod __unused,int type,void * data __unused)5071811d6bfSRobert Watson dtaudit_modevent(module_t mod __unused, int type, void *data __unused)
5081811d6bfSRobert Watson {
5091811d6bfSRobert Watson 	int error = 0;
5101811d6bfSRobert Watson 
5111811d6bfSRobert Watson 	switch (type) {
5121811d6bfSRobert Watson 	case MOD_LOAD:
5131811d6bfSRobert Watson 	case MOD_UNLOAD:
5141811d6bfSRobert Watson 	case MOD_SHUTDOWN:
5151811d6bfSRobert Watson 		break;
5161811d6bfSRobert Watson 
5171811d6bfSRobert Watson 	default:
5181811d6bfSRobert Watson 		error = EOPNOTSUPP;
5191811d6bfSRobert Watson 		break;
5201811d6bfSRobert Watson 	}
5211811d6bfSRobert Watson 
5221811d6bfSRobert Watson 	return (error);
5231811d6bfSRobert Watson }
5241811d6bfSRobert Watson 
5251811d6bfSRobert Watson SYSINIT(dtaudit_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, dtaudit_load,
5261811d6bfSRobert Watson     NULL);
5271811d6bfSRobert Watson SYSUNINIT(dtaudit_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY,
5281811d6bfSRobert Watson     dtaudit_unload, NULL);
5291811d6bfSRobert Watson 
5301811d6bfSRobert Watson DEV_MODULE(dtaudit, dtaudit_modevent, NULL);
5311811d6bfSRobert Watson MODULE_VERSION(dtaudit, 1);
5321811d6bfSRobert Watson MODULE_DEPEND(dtaudit, dtrace, 1, 1, 1);
5331811d6bfSRobert Watson MODULE_DEPEND(dtaudit, opensolaris, 1, 1, 1);
534