summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/security/audit/audit.c44
-rw-r--r--sys/security/audit/audit_bsm_klib.c9
-rw-r--r--sys/security/audit/audit_ioctl.h32
-rw-r--r--sys/security/audit/audit_pipe.c406
-rw-r--r--sys/security/audit/audit_private.h16
-rw-r--r--sys/security/audit/audit_worker.c78
6 files changed, 519 insertions, 66 deletions
diff --git a/sys/security/audit/audit.c b/sys/security/audit/audit.c
index 0161c43..d61bb99 100644
--- a/sys/security/audit/audit.c
+++ b/sys/security/audit/audit.c
@@ -332,6 +332,9 @@ audit_free(struct kaudit_record *ar)
void
audit_commit(struct kaudit_record *ar, int error, int retval)
{
+ au_event_t event;
+ au_class_t class;
+ au_id_t auid;
int sorf;
struct au_mask *aumask;
@@ -377,14 +380,18 @@ audit_commit(struct kaudit_record *ar, int error, int retval)
break;
}
- if (au_preselect(ar->k_ar.ar_event, aumask, sorf) != 0)
- ar->k_ar_commit |= AR_COMMIT_KERNEL;
-
- /*
- * XXXRW: Why is this necessary? Should we ever accept a record that
- * we're not willing to commit?
- */
- if ((ar->k_ar_commit & (AR_COMMIT_USER | AR_COMMIT_KERNEL)) == 0) {
+ auid = ar->k_ar.ar_subj_auid;
+ event = ar->k_ar.ar_event;
+ class = au_event_class(event);
+
+ ar->k_ar_commit |= AR_COMMIT_KERNEL;
+ if (au_preselect(event, class, aumask, sorf) != 0)
+ ar->k_ar_commit |= AR_PRESELECT_TRAIL;
+ if (audit_pipe_preselect(auid, event, class, sorf,
+ ar->k_ar_commit & AR_PRESELECT_TRAIL) != 0)
+ ar->k_ar_commit |= AR_PRESELECT_PIPE;
+ if ((ar->k_ar_commit & (AR_PRESELECT_TRAIL | AR_PRESELECT_PIPE)) ==
+ 0) {
mtx_lock(&audit_mtx);
audit_pre_q_len--;
mtx_unlock(&audit_mtx);
@@ -446,8 +453,10 @@ audit_commit(struct kaudit_record *ar, int error, int retval)
void
audit_syscall_enter(unsigned short code, struct thread *td)
{
- int audit_event;
struct au_mask *aumask;
+ au_class_t class;
+ au_event_t event;
+ au_id_t auid;
KASSERT(td->td_ar == NULL, ("audit_syscall_enter: td->td_ar != NULL"));
@@ -464,15 +473,16 @@ audit_syscall_enter(unsigned short code, struct thread *td)
if (code >= td->td_proc->p_sysent->sv_size)
return;
- audit_event = td->td_proc->p_sysent->sv_table[code].sy_auevent;
- if (audit_event == AUE_NULL)
+ event = td->td_proc->p_sysent->sv_table[code].sy_auevent;
+ if (event == AUE_NULL)
return;
/*
* Check which audit mask to use; either the kernel non-attributable
* event mask or the process audit mask.
*/
- if (td->td_proc->p_au->ai_auid == AU_DEFAUDITID)
+ auid = td->td_proc->p_au->ai_auid;
+ if (auid == AU_DEFAUDITID)
aumask = &audit_nae_mask;
else
aumask = &td->td_proc->p_au->ai_mask;
@@ -481,8 +491,8 @@ audit_syscall_enter(unsigned short code, struct thread *td)
* Allocate an audit record, if preselection allows it, and store
* in the thread for later use.
*/
- if (au_preselect(audit_event, aumask,
- AU_PRS_FAILURE | AU_PRS_SUCCESS)) {
+ class = au_event_class(event);
+ if (au_preselect(event, class, aumask, AU_PRS_BOTH)) {
/*
* If we're out of space and need to suspend unprivileged
* processes, do that here rather than trying to allocate
@@ -499,8 +509,10 @@ 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(audit_event, td);
- } else
+ td->td_ar = audit_new(event, td);
+ } else if (audit_pipe_preselect(auid, event, class, AU_PRS_BOTH, 0))
+ td->td_ar = audit_new(event, td);
+ else
td->td_ar = NULL;
}
diff --git a/sys/security/audit/audit_bsm_klib.c b/sys/security/audit/audit_bsm_klib.c
index 87d715a..7844ef9 100644
--- a/sys/security/audit/audit_bsm_klib.c
+++ b/sys/security/audit/audit_bsm_klib.c
@@ -154,24 +154,21 @@ au_evclassmap_init(void)
* event is part of against the given mask.
*/
int
-au_preselect(au_event_t event, au_mask_t *mask_p, int sorf)
+au_preselect(au_event_t event, au_class_t class, au_mask_t *mask_p, int sorf)
{
au_class_t effmask = 0;
- au_class_t ae_class;
if (mask_p == NULL)
return (-1);
- ae_class = au_event_class(event);
-
/*
* Perform the actual check of the masks against the event.
*/
if (sorf & AU_PRS_SUCCESS)
- effmask |= (mask_p->am_success & ae_class);
+ effmask |= (mask_p->am_success & class);
if (sorf & AU_PRS_FAILURE)
- effmask |= (mask_p->am_failure & ae_class);
+ effmask |= (mask_p->am_failure & class);
if (effmask)
return (1);
diff --git a/sys/security/audit/audit_ioctl.h b/sys/security/audit/audit_ioctl.h
index af0a389..e80346f 100644
--- a/sys/security/audit/audit_ioctl.h
+++ b/sys/security/audit/audit_ioctl.h
@@ -34,6 +34,25 @@
#define AUDITPIPE_IOBASE 'A'
/*
+ * Data structures used for complex ioctl arguments. Do not change existing
+ * structures, add new revised ones to be used by new ioctls, and keep the
+ * old structures and ioctls for backwards compatibility.
+ */
+struct auditpipe_ioctl_preselect {
+ au_id_t aip_auid;
+ au_mask_t aip_mask;
+};
+
+/*
+ * Possible modes of operation for audit pipe preselection.
+ */
+#define AUDITPIPE_PRESELECT_MODE_TRAIL 1 /* Global audit trail. */
+#define AUDITPIPE_PRESELECT_MODE_LOCAL 2 /* Local audit trail. */
+#ifdef NOTYET
+#define AUDITPIPE_PRESELECT_MODE_PRIORITY 3 /* Prioritized trail. */
+#endif
+
+/*
* Ioctls to read and control the behavior of individual audit pipe devices.
*/
#define AUDITPIPE_GET_QLEN _IOR(AUDITPIPE_IOBASE, 1, u_int)
@@ -41,6 +60,19 @@
#define AUDITPIPE_SET_QLIMIT _IOW(AUDITPIPE_IOBASE, 3, u_int)
#define AUDITPIPE_GET_QLIMIT_MIN _IOR(AUDITPIPE_IOBASE, 4, u_int)
#define AUDITPIPE_GET_QLIMIT_MAX _IOR(AUDITPIPE_IOBASE, 5, u_int)
+#define AUDITPIPE_GET_PRESELECT_FLAGS _IOR(AUDITPIPE_IOBASE, 6, au_mask_t)
+#define AUDITPIPE_SET_PRESELECT_FLAGS _IOW(AUDITPIPE_IOBASE, 7, au_mask_t)
+#define AUDITPIPE_GET_PRESELECT_NAFLAGS _IOR(AUDITPIPE_IOBASE, 8, au_mask_t)
+#define AUDITPIPE_SET_PRESELECT_NAFLAGS _IOW(AUDITPIPE_IOBASE, 9, au_mask_t)
+#define AUDITPIPE_GET_PRESELECT_AUID _IOR(AUDITPIPE_IOBASE, 10, \
+ struct auditpipe_ioctl_preselect)
+#define AUDITPIPE_SET_PRESELECT_AUID _IOW(AUDITPIPE_IOBASE, 11, \
+ struct auditpipe_ioctl_preselect)
+#define AUDITPIPE_DELETE_PRESELECT_AUID _IOW(AUDITPIPE_IOBASE, 12, au_id_t)
+#define AUDITPIPE_FLUSH_PRESELECT_AUID _IO(AUDITPIPE_IOBASE, 13)
+#define AUDITPIPE_GET_PRESELECT_MODE _IOR(AUDITPIPE_IOBASE, 14, int)
+#define AUDITPIPE_SET_PRESELECT_MODE _IOW(AUDITPIPE_IOBASE, 15, int)
+#define AUDITPIPE_FLUSH _IO(AUDITPIPE_IOBASE, 16)
/*
* Ioctls to retrieve audit pipe statistics.
diff --git a/sys/security/audit/audit_pipe.c b/sys/security/audit/audit_pipe.c
index 501254b..f188f0b 100644
--- a/sys/security/audit/audit_pipe.c
+++ b/sys/security/audit/audit_pipe.c
@@ -55,7 +55,8 @@
* Implementation of a clonable special device providing a live stream of BSM
* audit data. This is a "tee" of the data going to the file. It provides
* unreliable but timely access to audit events. Consumers of this interface
- * should be very careful to avoid introducing event cycles.
+ * should be very careful to avoid introducing event cycles. Consumers may
+ * express interest via a set of preselection ioctls.
*/
/*
@@ -64,6 +65,8 @@
static MALLOC_DEFINE(M_AUDIT_PIPE, "audit_pipe", "Audit pipes");
static MALLOC_DEFINE(M_AUDIT_PIPE_ENTRY, "audit_pipeent",
"Audit pipe entries and buffers");
+static MALLOC_DEFINE(M_AUDIT_PIPE_PRESELECT, "audit_pipe_preselect",
+ "Audit pipe preselection structure");
/*
* Audit pipe buffer parameters.
@@ -82,6 +85,23 @@ struct audit_pipe_entry {
};
/*
+ * Audit pipes allow processes to express "interest" in the set of records
+ * that are delivered via the pipe. They do this in a similar manner to the
+ * mechanism for audit trail configuration, by expressing two global masks,
+ * and optionally expressing per-auid masks. The following data structure is
+ * the per-auid mask description. The global state is stored in the audit
+ * pipe data structure.
+ *
+ * We may want to consider a more space/time-efficient data structure once
+ * usage patterns for per-auid specifications are clear.
+ */
+struct audit_pipe_preselect {
+ au_id_t app_auid;
+ au_mask_t app_mask;
+ TAILQ_ENTRY(audit_pipe_preselect) app_list;
+};
+
+/*
* Description of an individual audit_pipe. Consists largely of a bounded
* length queue.
*/
@@ -102,21 +122,38 @@ struct audit_pipe {
u_int64_t ap_drops; /* Records dropped. */
u_int64_t ap_truncates; /* Records too long. */
+ /*
+ * Fields relating to pipe interest: global masks for unmatched
+ * processes (attributable, non-attributable), and a list of specific
+ * interest specifications by auid.
+ */
+ int ap_preselect_mode;
+ au_mask_t ap_preselect_flags;
+ au_mask_t ap_preselect_naflags;
+ TAILQ_HEAD(, audit_pipe_preselect) ap_preselect_list;
+
+ /*
+ * Current pending record list.
+ */
TAILQ_HEAD(, audit_pipe_entry) ap_queue;
+ /*
+ * Global pipe list.
+ */
TAILQ_ENTRY(audit_pipe) ap_list;
};
/*
- * Global list of audit pipes, mutex to protect it and the pipes. Finder
+ * Global list of audit pipes, mutex to protect it and the pipes. Finer
* grained locking may be desirable at some point.
*/
static TAILQ_HEAD(, audit_pipe) audit_pipe_list;
static struct mtx audit_pipe_mtx;
/*
- * This CV is used to wakeup on an audit record write. Eventually, it should
- * probably be per-pipe.
+ * This CV is used to wakeup on an audit record write. Eventually, it might
+ * be per-pipe to avoid unnecessary wakeups when several pipes with different
+ * preselection masks are present.
*/
static struct cv audit_pipe_cv;
@@ -138,7 +175,7 @@ static d_poll_t audit_pipe_poll;
static struct cdevsw audit_pipe_cdevsw = {
.d_version = D_VERSION,
- .d_flags = D_PSEUDO,
+ .d_flags = D_PSEUDO | D_NEEDGIANT,
.d_open = audit_pipe_open,
.d_close = audit_pipe_close,
.d_read = audit_pipe_read,
@@ -167,7 +204,185 @@ audit_pipe_entry_free(struct audit_pipe_entry *ape)
}
/*
- * Apparent individual record to a queue -- allocate queue-local buffer, and
+ * Find an audit pipe preselection specification for an auid, if any.
+ */
+static struct audit_pipe_preselect *
+audit_pipe_preselect_find(struct audit_pipe *ap, au_id_t auid)
+{
+ struct audit_pipe_preselect *app;
+
+ mtx_assert(&audit_pipe_mtx, MA_OWNED);
+
+ TAILQ_FOREACH(app, &ap->ap_preselect_list, app_list) {
+ if (app->app_auid == auid)
+ return (app);
+ }
+ return (NULL);
+}
+
+/*
+ * Query the per-pipe mask for a specific auid.
+ */
+static int
+audit_pipe_preselect_get(struct audit_pipe *ap, au_id_t auid,
+ au_mask_t *maskp)
+{
+ struct audit_pipe_preselect *app;
+ int error;
+
+ mtx_lock(&audit_pipe_mtx);
+ app = audit_pipe_preselect_find(ap, auid);
+ if (app != NULL) {
+ *maskp = app->app_mask;
+ error = 0;
+ } else
+ error = ENOENT;
+ mtx_unlock(&audit_pipe_mtx);
+ return (error);
+}
+
+/*
+ * Set the per-pipe mask for a specific auid. Add a new entry if needed;
+ * otherwise, update the current entry.
+ */
+static void
+audit_pipe_preselect_set(struct audit_pipe *ap, au_id_t auid, au_mask_t mask)
+{
+ struct audit_pipe_preselect *app, *app_new;
+
+ /*
+ * Pessimistically assume that the auid doesn't already have a mask
+ * set, and allocate. We will free it if it is unneeded.
+ */
+ app_new = malloc(sizeof(*app_new), M_AUDIT_PIPE_PRESELECT, M_WAITOK);
+ mtx_lock(&audit_pipe_mtx);
+ app = audit_pipe_preselect_find(ap, auid);
+ if (app == NULL) {
+ app = app_new;
+ app_new = NULL;
+ app->app_auid = auid;
+ TAILQ_INSERT_TAIL(&ap->ap_preselect_list, app, app_list);
+ }
+ app->app_mask = mask;
+ mtx_unlock(&audit_pipe_mtx);
+ if (app_new != NULL)
+ free(app_new, M_AUDIT_PIPE_PRESELECT);
+}
+
+/*
+ * Delete a per-auid mask on an audit pipe.
+ */
+static int
+audit_pipe_preselect_delete(struct audit_pipe *ap, au_id_t auid)
+{
+ struct audit_pipe_preselect *app;
+ int error;
+
+ mtx_lock(&audit_pipe_mtx);
+ app = audit_pipe_preselect_find(ap, auid);
+ if (app != NULL) {
+ TAILQ_REMOVE(&ap->ap_preselect_list, app, app_list);
+ error = 0;
+ } else
+ error = ENOENT;
+ mtx_unlock(&audit_pipe_mtx);
+ if (app != NULL)
+ free(app, M_AUDIT_PIPE_PRESELECT);
+ return (error);
+}
+
+/*
+ * Delete all per-auid masks on an audit pipe.
+ */
+static void
+audit_pipe_preselect_flush_locked(struct audit_pipe *ap)
+{
+ struct audit_pipe_preselect *app;
+
+ mtx_assert(&audit_pipe_mtx, MA_OWNED);
+
+ while ((app = TAILQ_FIRST(&ap->ap_preselect_list)) != NULL) {
+ TAILQ_REMOVE(&ap->ap_preselect_list, app, app_list);
+ free(app, M_AUDIT_PIPE_PRESELECT);
+ }
+}
+
+static void
+audit_pipe_preselect_flush(struct audit_pipe *ap)
+{
+
+ mtx_lock(&audit_pipe_mtx);
+ audit_pipe_preselect_flush_locked(ap);
+ mtx_unlock(&audit_pipe_mtx);
+}
+
+/*
+ * Determine whether a specific audit pipe matches a record with these
+ * properties. Algorithm is as follows:
+ *
+ * - If the pipe is configured to track the default trail configuration, then
+ * use the results of global preselection matching.
+ * - If not, search for a specifically configured auid entry matching the
+ * event. If an entry is found, use that.
+ * - Otherwise, use the default flags or naflags configured for the pipe.
+ */
+static int
+audit_pipe_preselect_check(struct audit_pipe *ap, au_id_t auid,
+ au_event_t event, au_class_t class, int sorf, int trail_preselect)
+{
+ struct audit_pipe_preselect *app;
+
+ mtx_assert(&audit_pipe_mtx, MA_OWNED);
+
+ switch (ap->ap_preselect_mode) {
+ case AUDITPIPE_PRESELECT_MODE_TRAIL:
+ return (trail_preselect);
+
+ case AUDITPIPE_PRESELECT_MODE_LOCAL:
+ app = audit_pipe_preselect_find(ap, auid);
+ if (app == NULL) {
+ if (auid == AU_DEFAUDITID)
+ return (au_preselect(event, class,
+ &ap->ap_preselect_naflags, sorf));
+ else
+ return (au_preselect(event, class,
+ &ap->ap_preselect_flags, sorf));
+ } else
+ return (au_preselect(event, class, &app->app_mask,
+ sorf));
+
+ default:
+ panic("audit_pipe_preselect_check: mode %d",
+ ap->ap_preselect_mode);
+ }
+
+ return (0);
+}
+
+/*
+ * Determine whether there exists a pipe interested in a record with specific
+ * properties.
+ */
+int
+audit_pipe_preselect(au_id_t auid, au_event_t event, au_class_t class,
+ int sorf, int trail_preselect)
+{
+ struct audit_pipe *ap;
+
+ mtx_lock(&audit_pipe_mtx);
+ TAILQ_FOREACH(ap, &audit_pipe_list, ap_list) {
+ if (audit_pipe_preselect_check(ap, auid, event, class, sorf,
+ trail_preselect)) {
+ mtx_unlock(&audit_pipe_mtx);
+ return (1);
+ }
+ }
+ mtx_unlock(&audit_pipe_mtx);
+ return (0);
+}
+
+/*
+ * Append individual record to a queue -- allocate queue-local buffer, and
* add to the queue. We try to drop from the head of the queue so that more
* recent events take precedence over older ones, but if allocation fails we
* do drop the new event.
@@ -219,7 +434,38 @@ audit_pipe_append(struct audit_pipe *ap, void *record, u_int record_len)
* interface, which arranges for them to be delivered to pipe queues.
*/
void
-audit_pipe_submit(void *record, u_int record_len)
+audit_pipe_submit(au_id_t auid, au_event_t event, au_class_t class, int sorf,
+ int trail_select, void *record, u_int record_len)
+{
+ struct audit_pipe *ap;
+
+ /*
+ * Lockless read to avoid mutex overhead if pipes are not in use.
+ */
+ if (TAILQ_FIRST(&audit_pipe_list) == NULL)
+ return;
+
+ mtx_lock(&audit_pipe_mtx);
+ TAILQ_FOREACH(ap, &audit_pipe_list, ap_list) {
+ if (audit_pipe_preselect_check(ap, auid, event, class, sorf,
+ trail_select))
+ audit_pipe_append(ap, record, record_len);
+ }
+ audit_pipe_records++;
+ mtx_unlock(&audit_pipe_mtx);
+ cv_signal(&audit_pipe_cv);
+}
+
+/*
+ * audit_pipe_submit_user(): the same as audit_pipe_submit(), except that
+ * since we don't currently have selection information available, it is
+ * delivered to the pipe unconditionally.
+ *
+ * XXXRW: This is a bug. The BSM check routine for submitting a user record
+ * should parse that information and return it.
+ */
+void
+audit_pipe_submit_user(void *record, u_int record_len)
{
struct audit_pipe *ap;
@@ -237,8 +483,9 @@ audit_pipe_submit(void *record, u_int record_len)
cv_signal(&audit_pipe_cv);
}
+
/*
- * Read the next record off of an audit pipe.
+ * Pop the next record off of an audit pipe.
*/
static struct audit_pipe_entry *
audit_pipe_pop(struct audit_pipe *ap)
@@ -273,30 +520,59 @@ audit_pipe_alloc(void)
return (NULL);
ap->ap_qlimit = AUDIT_PIPE_QLIMIT_DEFAULT;
TAILQ_INIT(&ap->ap_queue);
+
+ /*
+ * Default flags, naflags, and auid-specific preselection settings to
+ * 0. Initialize the mode to the global trail so that if praudit(1)
+ * is run on /dev/auditpipe, it sees events associated with the
+ * default trail. Pipe-aware application can clear the flag, set
+ * custom masks, and flush the pipe as needed.
+ */
+ bzero(&ap->ap_preselect_flags, sizeof(ap->ap_preselect_flags));
+ bzero(&ap->ap_preselect_naflags, sizeof(ap->ap_preselect_naflags));
+ TAILQ_INIT(&ap->ap_preselect_list);
+ ap->ap_preselect_mode = AUDITPIPE_PRESELECT_MODE_TRAIL;
+
TAILQ_INSERT_HEAD(&audit_pipe_list, ap, ap_list);
audit_pipe_count++;
audit_pipe_ever++;
+
return (ap);
}
/*
- * Free an audit pipe. Assumes mutex is held, audit_pipe is still on the
- * global list. Frees any audit pipe entries in the queue.
+ * Flush all records currently present in an audit pipe; assume mutex is held.
*/
static void
-audit_pipe_free(struct audit_pipe *ap)
+audit_pipe_flush(struct audit_pipe *ap)
{
struct audit_pipe_entry *ape;
mtx_assert(&audit_pipe_mtx, MA_OWNED);
- TAILQ_REMOVE(&audit_pipe_list, ap, ap_list);
while ((ape = TAILQ_FIRST(&ap->ap_queue)) != NULL) {
TAILQ_REMOVE(&ap->ap_queue, ape, ape_queue);
audit_pipe_entry_free(ape);
ap->ap_qlen--;
}
KASSERT(ap->ap_qlen == 0, ("audit_pipe_free: ap_qlen"));
+}
+
+/*
+ * Free an audit pipe; this means freeing all preselection state and all
+ * records in the pipe. Assumes mutex is held to prevent any new records
+ * from being inserted during the free, and that the audit pipe is still on
+ * the global list.
+ */
+static void
+audit_pipe_free(struct audit_pipe *ap)
+{
+
+ mtx_assert(&audit_pipe_mtx, MA_OWNED);
+
+ audit_pipe_preselect_flush_locked(ap);
+ audit_pipe_flush(ap);
+ TAILQ_REMOVE(&audit_pipe_list, ap, ap_list);
free(ap, M_AUDIT_PIPE);
audit_pipe_count--;
}
@@ -391,11 +667,20 @@ static int
audit_pipe_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag,
struct thread *td)
{
+ struct auditpipe_ioctl_preselect *aip;
struct audit_pipe *ap;
- int error;
+ au_mask_t *maskp;
+ int error, mode;
+ au_id_t auid;
ap = dev->si_drv1;
KASSERT(ap != NULL, ("audit_pipe_ioctl: ap == NULL"));
+
+ /*
+ * Audit pipe ioctls: first come standard device node ioctls, then
+ * manipulation of pipe settings, and finally, statistics query
+ * ioctls.
+ */
switch (cmd) {
case FIONBIO:
mtx_lock(&audit_pipe_mtx);
@@ -467,6 +752,90 @@ audit_pipe_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag,
error = 0;
break;
+ case AUDITPIPE_GET_PRESELECT_FLAGS:
+ mtx_lock(&audit_pipe_mtx);
+ maskp = (au_mask_t *)data;
+ *maskp = ap->ap_preselect_flags;
+ mtx_unlock(&audit_pipe_mtx);
+ error = 0;
+ break;
+
+ case AUDITPIPE_SET_PRESELECT_FLAGS:
+ mtx_lock(&audit_pipe_mtx);
+ maskp = (au_mask_t *)data;
+ ap->ap_preselect_flags = *maskp;
+ mtx_unlock(&audit_pipe_mtx);
+ error = 0;
+ break;
+
+ case AUDITPIPE_GET_PRESELECT_NAFLAGS:
+ mtx_lock(&audit_pipe_mtx);
+ maskp = (au_mask_t *)data;
+ *maskp = ap->ap_preselect_naflags;
+ mtx_unlock(&audit_pipe_mtx);
+ error = 0;
+ break;
+
+ case AUDITPIPE_SET_PRESELECT_NAFLAGS:
+ mtx_lock(&audit_pipe_mtx);
+ maskp = (au_mask_t *)data;
+ ap->ap_preselect_naflags = *maskp;
+ mtx_unlock(&audit_pipe_mtx);
+ error = 0;
+ break;
+
+ case AUDITPIPE_GET_PRESELECT_AUID:
+ aip = (struct auditpipe_ioctl_preselect *)data;
+ error = audit_pipe_preselect_get(ap, aip->aip_auid,
+ &aip->aip_mask);
+ break;
+
+ case AUDITPIPE_SET_PRESELECT_AUID:
+ aip = (struct auditpipe_ioctl_preselect *)data;
+ audit_pipe_preselect_set(ap, aip->aip_auid, aip->aip_mask);
+ error = 0;
+ break;
+
+ case AUDITPIPE_DELETE_PRESELECT_AUID:
+ auid = *(au_id_t *)data;
+ error = audit_pipe_preselect_delete(ap, auid);
+ break;
+
+ case AUDITPIPE_FLUSH_PRESELECT_AUID:
+ audit_pipe_preselect_flush(ap);
+ error = 0;
+ break;
+
+ case AUDITPIPE_GET_PRESELECT_MODE:
+ mtx_lock(&audit_pipe_mtx);
+ *(int *)data = ap->ap_preselect_mode;
+ mtx_unlock(&audit_pipe_mtx);
+ error = 0;
+ break;
+
+ case AUDITPIPE_SET_PRESELECT_MODE:
+ mode = *(int *)data;
+ switch (mode) {
+ case AUDITPIPE_PRESELECT_MODE_TRAIL:
+ case AUDITPIPE_PRESELECT_MODE_LOCAL:
+ mtx_lock(&audit_pipe_mtx);
+ ap->ap_preselect_mode = mode;
+ mtx_unlock(&audit_pipe_mtx);
+ error = 0;
+ break;
+
+ default:
+ error = EINVAL;
+ }
+ break;
+
+ case AUDITPIPE_FLUSH:
+ mtx_lock(&audit_pipe_mtx);
+ audit_pipe_flush(ap);
+ mtx_unlock(&audit_pipe_mtx);
+ error = 0;
+ break;
+
case AUDITPIPE_GET_INSERTS:
*(u_int *)data = ap->ap_inserts;
error = 0;
@@ -496,6 +865,17 @@ audit_pipe_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag,
/*
* Audit pipe read. Pull one record off the queue and copy to user space.
* On error, the record is dropped.
+ *
+ * Providing more sophisticated behavior, such as partial reads, is tricky
+ * due to the potential for parallel I/O. If partial read support is
+ * required, it will require a per-pipe "current record being read" along
+ * with an offset into that trecord which has already been read. Threads
+ * performing partial reads will need to allocate per-thread copies of the
+ * data so that if another thread completes the read of the record, it can be
+ * freed without adding reference count logic. If this is added, a flag to
+ * indicate that only atomic record reads are desired would be useful, as if
+ * different threads are all waiting for records on the pipe, they will want
+ * independent record reads, which is currently the behavior.
*/
static int
audit_pipe_read(struct cdev *dev, struct uio *uio, int flag)
diff --git a/sys/security/audit/audit_private.h b/sys/security/audit/audit_private.h
index 543bb60..2dc61ec 100644
--- a/sys/security/audit/audit_private.h
+++ b/sys/security/audit/audit_private.h
@@ -84,11 +84,16 @@ extern int audit_fail_stop;
#define BSM_NOAUDIT 2
/*
- * Defines for the kernel audit record k_ar_commit field.
+ * Defines for the kernel audit record k_ar_commit field. Flags are set to
+ * indicate what sort of record it is, and which preselection mechanism
+ * selected it.
*/
#define AR_COMMIT_KERNEL 0x00000001U
#define AR_COMMIT_USER 0x00000010U
+#define AR_PRESELECT_TRAIL 0x00001000U
+#define AR_PRESELECT_PIPE 0x00002000U
+
/*
* Audit data is generated as a stream of struct audit_record structures,
* linked by struct kaudit_record, and contain storage for possible audit so
@@ -296,7 +301,8 @@ token_t *kau_to_socket(struct socket_au_info *soi);
/*
* audit_klib prototypes
*/
-int au_preselect(au_event_t event, au_mask_t *mask_p, int sorf);
+int au_preselect(au_event_t event, au_class_t class,
+ au_mask_t *mask_p, int sorf);
au_event_t flags_and_error_to_openevent(int oflags, int error);
void au_evclassmap_init(void);
void au_evclassmap_insert(au_event_t event, au_class_t class);
@@ -327,6 +333,10 @@ void audit_worker_init(void);
/*
* Audit pipe functions.
*/
-void audit_pipe_submit(void *record, u_int record_len);
+int audit_pipe_preselect(au_id_t auid, au_event_t event,
+ au_class_t class, int sorf, int trail_select);
+void audit_pipe_submit(au_id_t auid, au_event_t event, au_class_t class,
+ int sorf, int trail_select, void *record, u_int record_len);
+void audit_pipe_submit_user(void *record, u_int record_len);
#endif /* ! _SECURITY_AUDIT_PRIVATE_H_ */
diff --git a/sys/security/audit/audit_worker.c b/sys/security/audit/audit_worker.c
index a89966e..fa6485f 100644
--- a/sys/security/audit/audit_worker.c
+++ b/sys/security/audit/audit_worker.c
@@ -315,45 +315,67 @@ audit_worker_process_record(struct vnode *audit_vp, struct ucred *audit_cred,
struct thread *audit_td, struct kaudit_record *ar)
{
struct au_record *bsm;
+ au_class_t class;
+ au_event_t event;
int error, ret;
+ au_id_t auid;
+ int sorf;
- if (ar->k_ar_commit & AR_COMMIT_USER) {
+ if ((ar->k_ar_commit & AR_COMMIT_USER) &&
+ (ar->k_ar_commit & AR_PRESELECT_TRAIL)) {
error = audit_record_write(audit_vp, audit_cred, audit_td,
ar->k_udata, ar->k_ulen);
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_pipe_submit(ar->k_udata, ar->k_ulen);
+ }
+ if ((ar->k_ar_commit & AR_COMMIT_USER) &&
+ (ar->k_ar_commit & AR_PRESELECT_PIPE))
+ audit_pipe_submit_user(ar->k_udata, ar->k_ulen);
+
+ if (!(ar->k_ar_commit & AR_COMMIT_KERNEL))
+ return;
+
+ auid = ar->k_ar.ar_subj_auid;
+ event = ar->k_ar.ar_event;
+ class = au_event_class(event);
+ if (ar->k_ar.ar_errno == 0)
+ sorf = AU_PRS_SUCCESS;
+ else
+ sorf = AU_PRS_FAILURE;
+
+ ret = kaudit_to_bsm(ar, &bsm);
+ switch (ret) {
+ case BSM_NOAUDIT:
+ return;
+
+ case BSM_FAILURE:
+ printf("audit_worker_process_record: BSM_FAILURE\n");
+ return;
+
+ case BSM_SUCCESS:
+ break;
+
+ default:
+ panic("kaudit_to_bsm returned %d", ret);
}
- if (ar->k_ar_commit & AR_COMMIT_KERNEL) {
- ret = kaudit_to_bsm(ar, &bsm);
- switch (ret) {
- case BSM_NOAUDIT:
- break;
-
- case BSM_FAILURE:
- printf("audit_worker_process_record: BSM_FAILURE\n");
- break;
-
- case BSM_SUCCESS:
- error = audit_record_write(audit_vp, audit_cred,
- audit_td, bsm->data, bsm->len);
- 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_pipe_submit(bsm->data, bsm->len);
- kau_free(bsm);
- break;
-
- default:
- panic("kaudit_to_bsm returned %d", ret);
- }
+ if (ar->k_ar_commit & AR_PRESELECT_TRAIL) {
+ error = audit_record_write(audit_vp, audit_cred,
+ audit_td, bsm->data, bsm->len);
+ 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);
}
+ if (ar->k_ar_commit & AR_PRESELECT_PIPE)
+ audit_pipe_submit(auid, event, class, sorf,
+ ar->k_ar_commit & AR_PRESELECT_TRAIL, bsm->data,
+ bsm->len);
+ kau_free(bsm);
}
/*
OpenPOWER on IntegriCloud