Skip to content

Commit

Permalink
Add an experimental DTrace audit provider, which allows users of DTra…
Browse files Browse the repository at this point in the history
…ce to

instrument security event auditing rather than relying on conventional BSM
trail files or audit pipes:

- Add a set of per-event 'commit' probes, which provide access to
  particular auditable events at the time of commit in system-call return.
  These probes gain access to audit data via the in-kernel audit_record
  data structure, providing convenient access to system-call arguments and
  return values in a single probe.

- Add a set of per-event 'bsm' probes, which provide access to particular
  auditable events at the time of BSM record generation in the audit
  worker thread. These probes have access to the in-kernel audit_record
  data structure and BSM representation as would be written to a trail
  file or audit pipe -- i.e., asynchronously in the audit worker thread.

DTrace probe arguments consist of the name of the audit event (to support
future mechanisms of instrumenting multiple events via a single probe --
e.g., using classes), a pointer to the in-kernel audit record, and an
optional pointer to the BSM data and its length. For human convenience,
upper-case audit event names (AUE_...) are converted to lower case in
DTrace.

DTrace scripts can now cause additional audit-based data to be collected
on system calls, and inspect internal and BSM representations of the data.
They do not affect data captured in the audit trail or audit pipes
configured in the system. auditd(8) must be configured and running in
order to provide a database of event information, as well as other audit
configuration parameters (e.g., to capture command-line arguments or
environmental variables) for the provider to operate.

Reviewed by:	gnn, jonathan, markj
Sponsored by:	DARPA, AFRL
MFC after:	3 weeks
Differential Revision:	https://reviews.freebsd.org/D10149
  • Loading branch information
rwatson committed Mar 29, 2017
1 parent 0d75d0d commit 1811d6b
Show file tree
Hide file tree
Showing 8 changed files with 720 additions and 16 deletions.
1 change: 1 addition & 0 deletions sys/conf/files
Original file line number Diff line number Diff line change
Expand Up @@ -4593,6 +4593,7 @@ security/audit/audit.c optional audit
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
Expand Down
3 changes: 2 additions & 1 deletion sys/modules/dtrace/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

.include "Makefile.inc"

SUBDIR= dtmalloc \
SUBDIR= dtaudit \
dtmalloc \
dtnfscl \
dtrace \
dtraceall \
Expand Down
17 changes: 17 additions & 0 deletions sys/modules/dtrace/dtaudit/Makefile
Original file line number Diff line number Diff line change
@@ -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
98 changes: 86 additions & 12 deletions sys/security/audit/audit.c
Original file line number Diff line number Diff line change
@@ -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:
Expand Down Expand Up @@ -161,6 +166,20 @@ struct cv audit_watermark_cv;
*/
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
Expand Down Expand Up @@ -410,6 +429,10 @@ audit_commit(struct kaudit_record *ar, int error, int retval)
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.
Expand Down Expand Up @@ -461,19 +484,28 @@ audit_commit(struct kaudit_record *ar, int error, int retval)
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);
audit_free(ar);
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?
Expand Down Expand Up @@ -510,9 +542,13 @@ void
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,
Expand Down Expand Up @@ -544,8 +580,8 @@ audit_syscall_enter(unsigned short code, struct thread *td)
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)) {
Expand All @@ -566,13 +602,51 @@ audit_syscall_enter(unsigned short code, struct thread *td)
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;
}
Expand Down
29 changes: 29 additions & 0 deletions sys/security/audit/audit_bsm_klib.c
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,35 @@ au_evnamemap_foreach(au_evnamemap_callback_t callback)
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.
*/
Expand Down
Loading

0 comments on commit 1811d6b

Please sign in to comment.