diff options
-rw-r--r-- | sys/security/audit/audit.c | 44 | ||||
-rw-r--r-- | sys/security/audit/audit_bsm_klib.c | 9 | ||||
-rw-r--r-- | sys/security/audit/audit_ioctl.h | 32 | ||||
-rw-r--r-- | sys/security/audit/audit_pipe.c | 406 | ||||
-rw-r--r-- | sys/security/audit/audit_private.h | 16 | ||||
-rw-r--r-- | sys/security/audit/audit_worker.c | 78 |
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); } /* |