[PATCH] audit: Reactive rules

From: Juraj Hlista
Date: Tue Mar 30 2010 - 18:18:09 EST


From: Juraj Hlista <juro.hlista@xxxxxxxxx>

Add support for reactive rules. An audit rule can contain more than one reaction. The reactions are identified by numbers in the kernel and by strings in the user space.

Signed-off-by: Juraj Hlista <juro.hlista@xxxxxxxxx>
---
include/linux/audit.h | 9 ++++++++
kernel/audit.c | 8 +++++++
kernel/audit_tree.c | 1 +
kernel/audit_watch.c | 1 +
kernel/auditfilter.c | 52 ++++++++++++++++++++++++++++++++++++++++++------
kernel/auditsc.c | 23 +++++++++++++++++++++
6 files changed, 87 insertions(+), 7 deletions(-)

diff --git a/include/linux/audit.h b/include/linux/audit.h
index f391d45..0325516 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -102,6 +102,7 @@
#define AUDIT_EOE 1320 /* End of multi-record event */
#define AUDIT_BPRM_FCAPS 1321 /* Information about fcaps increasing perms */
#define AUDIT_CAPSET 1322 /* Record showing argument to sys_capset */
+#define AUDIT_REACT_RULE 1323 /* Reactive rule */

#define AUDIT_AVC 1400 /* SE Linux avc denial or grant */
#define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */
@@ -156,6 +157,7 @@
* AUDIT_LIST commands must be implemented. */
#define AUDIT_MAX_FIELDS 64
#define AUDIT_MAX_KEY_LEN 256
+#define AUDIT_MAX_REACTS 8
#define AUDIT_BITMASK_SIZE 64
#define AUDIT_WORD(nr) ((__u32)((nr)/32))
#define AUDIT_BIT(nr) (1 << ((nr) - AUDIT_WORD(nr)*32))
@@ -227,6 +229,8 @@

#define AUDIT_FILTERKEY 210

+#define AUDIT_REACTION 220
+
#define AUDIT_NEGATE 0x80000000

/* These are the supported operators.
@@ -384,6 +388,8 @@ struct audit_krule {
u32 action;
u32 mask[AUDIT_BITMASK_SIZE];
u32 buflen; /* for data alloc on list rules */
+ u32 react_count;
+ u32 react[AUDIT_MAX_REACTS];
u32 field_count;
char *filterkey; /* ties events to rules */
struct audit_field *fields;
@@ -600,6 +606,8 @@ extern void audit_log_d_path(struct audit_buffer *ab,
struct path *path);
extern void audit_log_key(struct audit_buffer *ab,
char *key);
+extern void audit_log_react(struct audit_buffer *ab,
+ u32 count, u32 *react);
extern void audit_log_lost(const char *message);
extern int audit_update_lsm_rules(void);

@@ -623,6 +631,7 @@ extern int audit_enabled;
#define audit_log_untrustedstring(a,s) do { ; } while (0)
#define audit_log_d_path(b, p, d) do { ; } while (0)
#define audit_log_key(b, k) do { ; } while (0)
+#define audit_log_react(b, c, r) do { ; } while (0)
#define audit_enabled 0
#endif
#endif
diff --git a/kernel/audit.c b/kernel/audit.c
index 05a32f0..6f4fd3b 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -1450,6 +1450,14 @@ void audit_log_key(struct audit_buffer *ab, char *key)
audit_log_format(ab, "(null)");
}

+void audit_log_react(struct audit_buffer *ab, u32 count, u32 *react)
+{
+ unsigned int i;
+ for (i = 0; i < count; i++)
+ audit_log_format(ab, " react=%u", react[i]);
+
+}
+
/**
* audit_log_end - end one audit record
* @ab: the audit_buffer
diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c
index 28e5b20..b63faa5 100644
--- a/kernel/audit_tree.c
+++ b/kernel/audit_tree.c
@@ -468,6 +468,7 @@ static void kill_rules(struct audit_tree *tree)
audit_log_format(ab, " dir=");
audit_log_untrustedstring(ab, rule->tree->pathname);
audit_log_key(ab, rule->filterkey);
+ audit_log_react(ab, rule->react_count, rule->react);
audit_log_format(ab, " list=%d res=1", rule->listnr);
audit_log_end(ab);
rule->tree = NULL;
diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c
index 7499397..565fbad 100644
--- a/kernel/audit_watch.c
+++ b/kernel/audit_watch.c
@@ -249,6 +249,7 @@ static void audit_watch_log_rule_change(struct audit_krule *r, struct audit_watc
audit_log_format(ab, " path=");
audit_log_untrustedstring(ab, w->path);
audit_log_key(ab, r->filterkey);
+ audit_log_react(ab, r->react_count, r->react);
audit_log_format(ab, " list=%d res=1", r->listnr);
audit_log_end(ab);
}
diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c
index eb76754..f0eb220 100644
--- a/kernel/auditfilter.c
+++ b/kernel/auditfilter.c
@@ -229,6 +229,7 @@ static inline struct audit_entry *audit_to_entry_common(struct audit_rule *rule)
unsigned listnr;
struct audit_entry *entry;
int i, err;
+ int r_count = 0;

err = -EINVAL;
listnr = rule->flags & ~AUDIT_FILTER_PREPEND;
@@ -253,15 +254,23 @@ static inline struct audit_entry *audit_to_entry_common(struct audit_rule *rule)
if (rule->field_count > AUDIT_MAX_FIELDS)
goto exit_err;

+ for (i = 0; i < rule->field_count; i++) {
+ if (rule->fields[i] == AUDIT_REACTION)
+ ++r_count;
+ if (unlikely(r_count > AUDIT_MAX_REACTS))
+ goto exit_err;
+ }
+
err = -ENOMEM;
- entry = audit_init_entry(rule->field_count);
+ entry = audit_init_entry(rule->field_count - r_count);
if (!entry)
goto exit_err;

entry->rule.flags = rule->flags & AUDIT_FILTER_PREPEND;
entry->rule.listnr = listnr;
entry->rule.action = rule->action;
- entry->rule.field_count = rule->field_count;
+ entry->rule.field_count = rule->field_count - r_count;
+ entry->rule.react_count = r_count;

for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
entry->rule.mask[i] = rule->mask[i];
@@ -415,7 +424,8 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
struct audit_entry *entry;
void *bufp;
size_t remain = datasz - sizeof(struct audit_rule_data);
- int i;
+ int i, j = 0;
+ int k;
char *str;

entry = audit_to_entry_common((struct audit_rule *)data);
@@ -425,7 +435,7 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
bufp = data->buf;
entry->rule.vers_ops = 2;
for (i = 0; i < data->field_count; i++) {
- struct audit_field *f = &entry->rule.fields[i];
+ struct audit_field *f = &entry->rule.fields[i - j];

err = -EINVAL;

@@ -433,6 +443,18 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
if (f->op == Audit_bad)
goto exit_free;

+ if (data->fields[i] == AUDIT_REACTION) {
+ for (k = 0; k < j; k++) {
+ /* reactions must differ */
+ if (entry->rule.react[k] == data->values[i])
+ goto exit_free;
+ }
+ entry->rule.react[j] = data->values[i];
+ ++j;
+ entry->rule.react_count = j;
+ continue;
+ }
+
f->type = data->fields[i];
f->val = data->values[i];
f->lsm_str = NULL;
@@ -601,7 +623,7 @@ static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule)
{
struct audit_rule_data *data;
void *bufp;
- int i;
+ int i, j;

data = kmalloc(sizeof(*data) + krule->buflen, GFP_KERNEL);
if (unlikely(!data))
@@ -610,9 +632,9 @@ static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule)

data->flags = krule->flags | krule->listnr;
data->action = krule->action;
- data->field_count = krule->field_count;
+ data->field_count = krule->field_count + krule->react_count;
bufp = data->buf;
- for (i = 0; i < data->field_count; i++) {
+ for (i = 0; i < krule->field_count; i++) {
struct audit_field *f = &krule->fields[i];

data->fields[i] = f->type;
@@ -649,6 +671,13 @@ static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule)
data->values[i] = f->val;
}
}
+ j = i;
+ for (i = 0; i < krule->react_count; i++, j++) {
+ data->fields[j] = AUDIT_REACTION;
+ data->fieldflags[j] = AUDIT_EQUAL;
+ data->values[j] = krule->react[i];
+ }
+
for (i = 0; i < AUDIT_BITMASK_SIZE; i++) data->mask[i] = krule->mask[i];

return data;
@@ -663,6 +692,7 @@ static int audit_compare_rule(struct audit_krule *a, struct audit_krule *b)
if (a->flags != b->flags ||
a->listnr != b->listnr ||
a->action != b->action ||
+ a->react_count != b->react_count ||
a->field_count != b->field_count)
return 1;

@@ -705,6 +735,10 @@ static int audit_compare_rule(struct audit_krule *a, struct audit_krule *b)
return 1;
}
}
+ for (i = 0; i < a->react_count; i++) {
+ if (a->react[i] != b->react[i])
+ return 1;
+ }

for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
if (a->mask[i] != b->mask[i])
@@ -769,6 +803,9 @@ struct audit_entry *audit_dupe_rule(struct audit_krule *old)
new->prio = old->prio;
new->buflen = old->buflen;
new->inode_f = old->inode_f;
+ new->react_count = old->react_count;
+ for (i = 0; i < new->react_count; i++)
+ new->react[i] = old->react[i];
new->field_count = old->field_count;

/*
@@ -1075,6 +1112,7 @@ static void audit_log_rule_change(uid_t loginuid, u32 sessionid, u32 sid,
audit_log_format(ab, " op=");
audit_log_string(ab, action);
audit_log_key(ab, rule->filterkey);
+ audit_log_react(ab, rule->react_count, rule->react);
audit_log_format(ab, " list=%d res=%d", rule->listnr, res);
audit_log_end(ab);
}
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index bc2b57a..57ad669 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -176,6 +176,8 @@ struct audit_context {
int return_valid; /* return code is valid */
int name_count;
struct audit_names names[AUDIT_NAMES];
+ int react_count;
+ u32 react[AUDIT_MAX_REACTS];
char * filterkey; /* key for rule that triggered record */
struct path pwd;
struct audit_context *previous; /* For nested syscalls */
@@ -622,6 +624,7 @@ static int audit_filter_rules(struct task_struct *tsk,
result = audit_comparator(ctx->argv[f->type-AUDIT_ARG0], f->op, f->val);
break;
case AUDIT_FILTERKEY:
+ case AUDIT_REACTION:
/* ignore this field for filtering */
result = 1;
break;
@@ -642,6 +645,9 @@ static int audit_filter_rules(struct task_struct *tsk,
if (ctx) {
if (rule->prio <= ctx->prio)
return 0;
+ ctx->react_count = rule->react_count;
+ for (i = 0; i < ctx->react_count; i++)
+ ctx->react[i] = rule->react[i];
if (rule->filterkey) {
kfree(ctx->filterkey);
ctx->filterkey = kstrdup(rule->filterkey, GFP_ATOMIC);
@@ -1332,6 +1338,19 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
context->fsgid = cred->fsgid;
context->personality = tsk->personality;

+ if (context->react_count) {
+ ab = audit_log_start(context, GFP_KERNEL, AUDIT_REACT_RULE);
+ if (!ab)
+ return;
+ for (i = 0; i < context->react_count; i++) {
+ if (!i)
+ audit_log_format(ab, "react=%u", context->react[i]);
+ else
+ audit_log_format(ab, " react=%u", context->react[i]);
+ }
+ audit_log_end(ab);
+ }
+
ab = audit_log_start(context, GFP_KERNEL, AUDIT_SYSCALL);
if (!ab)
return; /* audit_panic has been called */
@@ -1645,6 +1664,7 @@ void audit_syscall_entry(int arch, int major,

void audit_finish_fork(struct task_struct *child)
{
+ int i;
struct audit_context *ctx = current->audit_context;
struct audit_context *p = child->audit_context;
if (!p || !ctx)
@@ -1658,6 +1678,9 @@ void audit_finish_fork(struct task_struct *child)
p->dummy = ctx->dummy;
p->in_syscall = ctx->in_syscall;
p->filterkey = kstrdup(ctx->filterkey, GFP_KERNEL);
+ p->react_count = ctx->react_count;
+ for (i = 0; i < p->react_count; i++)
+ p->react[i] = ctx->react[i];
p->ppid = current->pid;
p->prio = ctx->prio;
p->current_state = ctx->current_state;
--
1.6.4.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/