I've reworked this patch based on the work that Darrel Goedel has recently
submitted in which he has implemented an SELinux interface for the Audit
subsystem to call to match security context label components. This follows
the advice of Stephen Smalley, and Darrel based his interface at least in
part on work that James Morris had started.
These patches are against Al Viro's latest git tree plus the following two
patches:
1. Minor cleanup patch of audit_comparator(); no real dependency here, but might
cause some patch fuzz:
https://www.redhat.com/archives/linux-audit/2006-February/msg00102.html
2. Darrel's SELinux audit api patch:
https://www.redhat.com/archives/linux-audit/2006-February/msg00101.html
The audit system needs to store (and eventually free) pointers to
Darrel's new selinux_audit_rule structure. I implemented this as a
struct selinux_audit_rule *se_rule in kernel/audit.h:audit_field. I
considered making this a void *ext (something more generic), which would
have the advantage of being extensible if something like this ever came
up again, where an audit rule field interfaces with some other kernel
system. Of course, it would need to be cast properly when referenced.
Please speak up if you have strong feelings here.
Amy: In audit_data_to_entry(), you're using an effectively temporary
char *path. I, too, needed a temporary string pointer, so I declared
char *str and replaced your couple of instances of path with str. Let
me know if this is ok by you. I couldn't very well call my temp string
"path". And it didn't make much sense to me to declare another throwaway
pointer. There's a little code duplication with the audit_unpack_string too.
Darrel: I realize now that your selinux_audit_rule structure duplicates a
little data that it doesn't need to, such as au_op and au_field. I could
very easily pass those to you when I call selinux_audit_rule_match. I think
that would be a little cleaner. Just a suggestion.
Steve Grubb mentioned that there's a chance we might get this into the test
kernel by EOB Friday... That would be great. Of course, I've built and
tested the basic functionality and it seems to filter messages in/out
appropriately. I'll submit an updated userspace patch tomorrow.
One last point... This patch only covers matching on process context.
As Darrel and I have discussed, we'll move onto the other contexts once
this methodology is acceptable.
Thanks for your review.
:-Dustin
diff --git a/kernel/audit.h b/kernel/audit.h
--- a/kernel/audit.h
+++ b/kernel/audit.h
@@ -59,9 +59,10 @@ struct audit_watch {
};
struct audit_field {
- u32 type;
- u32 val;
- u32 op;
+ u32 type;
+ u32 val;
+ u32 op;
+ struct selinux_audit_rule *se_rule;
};
struct audit_krule {
diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c
--- a/kernel/auditfilter.c
+++ b/kernel/auditfilter.c
@@ -25,6 +25,7 @@
#include <linux/fs.h>
#include <linux/namei.h>
#include <linux/netlink.h>
+#include <linux/selinux.h>
#include "audit.h"
/* There are three lists of rules -- one to search at task creation
@@ -50,6 +51,12 @@ static inline void audit_free_watch(stru
static inline void audit_free_rule(struct audit_entry *e)
{
+ int i;
+ for (i=0; i<e->rule.field_count; i++) {
+ struct audit_field *f = &e->rule.fields[i];
+ if (f->se_rule)
+ selinux_audit_rule_free(f->se_rule);
+ }
kfree(e->rule.fields);
kfree(e);
}
@@ -228,7 +235,7 @@ static struct audit_entry *audit_data_to
void *bufp;
size_t remain = datasz - sizeof(struct audit_rule_data);
int i;
- char *path;
+ char *str;
entry = audit_to_entry_common((struct audit_rule *)data);
if (IS_ERR(entry))
@@ -247,16 +254,33 @@ static struct audit_entry *audit_data_to
f->op = data->fieldflags[i] & AUDIT_OPERATORS;
f->type = data->fields[i];
f->val = data->values[i];
+ f->se_rule = NULL;
switch(f->type) {
+ case AUDIT_SE_USER:
+ case AUDIT_SE_ROLE:
+ case AUDIT_SE_TYPE:
+ case AUDIT_SE_SEN:
+ case AUDIT_SE_CLR:
+ str = audit_unpack_string(&bufp, &remain, f->val);
+ if (IS_ERR(str))
+ goto exit_free;
+ entry->rule.buflen += f->val;
+
+ err = selinux_audit_rule_init(f->type, f->op, str, &f->se_rule);
+ if (err) {
+ kfree(str);
+ goto exit_free;
+ }
+ break;
case AUDIT_WATCH:
- path = audit_unpack_string(&bufp, &remain, f->val);
- if (IS_ERR(path))
+ str = audit_unpack_string(&bufp, &remain, f->val);
+ if (IS_ERR(str))
goto exit_free;
entry->rule.buflen += f->val;
- err = audit_to_watch(path, &entry->rule, i);
+ err = audit_to_watch(str, &entry->rule, i);
if (err) {
- kfree(path);
+ kfree(str);
goto exit_free;
}
break;
@@ -644,6 +668,9 @@ int audit_comparator(const u32 left, con
case AUDIT_GREATER_THAN_OR_EQUAL:
return (left >= right);
}
+ /* should NEVER get here */
+ BUG();
+ return 0;
}
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -58,6 +58,7 @@
#include <linux/security.h>
#include <linux/list.h>
#include <linux/tty.h>
+#include <linux/selinux.h>
#include "audit.h"
@@ -165,7 +166,8 @@ struct audit_context {
static int audit_filter_rules(struct task_struct *tsk,
struct audit_krule *rule,
struct audit_context *ctx,
- enum audit_state *state)
+ enum audit_state *state,
+ u32 sid)
{
int i, j;
@@ -258,6 +260,13 @@ static int audit_filter_rules(struct tas
if (ctx)
result = audit_comparator(ctx->loginuid, f->op, f->val);
break;
+ case AUDIT_SE_USER:
+ case AUDIT_SE_ROLE:
+ case AUDIT_SE_TYPE:
+ case AUDIT_SE_SEN:
+ case AUDIT_SE_CLR:
+ result = selinux_audit_rule_match(sid, f->se_rule);
+ break;
case AUDIT_ARG0:
case AUDIT_ARG1:
case AUDIT_ARG2:
@@ -286,10 +295,13 @@ static enum audit_state audit_filter_tas
{
struct audit_entry *e;
enum audit_state state;
+ u32 sid;
+
+ selinux_task_ctxid(tsk, &sid);
rcu_read_lock();
list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TASK], list) {
- if (audit_filter_rules(tsk, &e->rule, NULL, &state)) {
+ if (audit_filter_rules(tsk, &e->rule, NULL, &state, sid)) {
rcu_read_unlock();
return state;
}
@@ -309,6 +321,9 @@ static enum audit_state audit_filter_sys
{
struct audit_entry *e;
enum audit_state state;
+ u32 sid;
+
+ selinux_task_ctxid(tsk, &sid);
if (audit_pid && tsk->tgid == audit_pid)
return AUDIT_DISABLED;
@@ -320,7 +335,8 @@ static enum audit_state audit_filter_sys
list_for_each_entry_rcu(e, list, list) {
if ((e->rule.mask[word] & bit) == bit
- && audit_filter_rules(tsk, &e->rule, ctx, &state)) {
+ && audit_filter_rules(tsk, &e->rule,
+ ctx, &state, sid)) {
rcu_read_unlock();
return state;
}