mirror of
https://github.com/adulau/aha.git
synced 2024-12-28 03:36:19 +00:00
[PATCH] add rule filterkey
Add support for a rule key, which can be used to tie audit records to audit rules. This is useful when a watched file is accessed through a link or symlink, as well as for general audit log analysis. Because this patch uses a string key instead of an integer key, there is a bit of extra overhead to do the kstrdup() when a rule fires. However, we're also allocating memory for the audit record buffer, so it's probably not that significant. I went ahead with a string key because it seems more user-friendly. Note that the user must ensure that filterkeys are unique. The kernel only checks for duplicate rules. Signed-off-by: Amy Griffis <amy.griffis@hpd.com>
This commit is contained in:
parent
9262e9149f
commit
5adc8a6adc
4 changed files with 78 additions and 36 deletions
|
@ -122,6 +122,7 @@
|
||||||
/* Rule structure sizes -- if these change, different AUDIT_ADD and
|
/* Rule structure sizes -- if these change, different AUDIT_ADD and
|
||||||
* AUDIT_LIST commands must be implemented. */
|
* AUDIT_LIST commands must be implemented. */
|
||||||
#define AUDIT_MAX_FIELDS 64
|
#define AUDIT_MAX_FIELDS 64
|
||||||
|
#define AUDIT_MAX_KEY_LEN 32
|
||||||
#define AUDIT_BITMASK_SIZE 64
|
#define AUDIT_BITMASK_SIZE 64
|
||||||
#define AUDIT_WORD(nr) ((__u32)((nr)/32))
|
#define AUDIT_WORD(nr) ((__u32)((nr)/32))
|
||||||
#define AUDIT_BIT(nr) (1 << ((nr) - AUDIT_WORD(nr)*32))
|
#define AUDIT_BIT(nr) (1 << ((nr) - AUDIT_WORD(nr)*32))
|
||||||
|
@ -171,6 +172,8 @@
|
||||||
#define AUDIT_ARG2 (AUDIT_ARG0+2)
|
#define AUDIT_ARG2 (AUDIT_ARG0+2)
|
||||||
#define AUDIT_ARG3 (AUDIT_ARG0+3)
|
#define AUDIT_ARG3 (AUDIT_ARG0+3)
|
||||||
|
|
||||||
|
#define AUDIT_FILTERKEY 210
|
||||||
|
|
||||||
#define AUDIT_NEGATE 0x80000000
|
#define AUDIT_NEGATE 0x80000000
|
||||||
|
|
||||||
/* These are the supported operators.
|
/* These are the supported operators.
|
||||||
|
|
|
@ -81,6 +81,7 @@ struct audit_krule {
|
||||||
u32 mask[AUDIT_BITMASK_SIZE];
|
u32 mask[AUDIT_BITMASK_SIZE];
|
||||||
u32 buflen; /* for data alloc on list rules */
|
u32 buflen; /* for data alloc on list rules */
|
||||||
u32 field_count;
|
u32 field_count;
|
||||||
|
char *filterkey; /* ties events to rules */
|
||||||
struct audit_field *fields;
|
struct audit_field *fields;
|
||||||
struct audit_field *inode_f; /* quick access to an inode field */
|
struct audit_field *inode_f; /* quick access to an inode field */
|
||||||
struct audit_watch *watch; /* associated watch */
|
struct audit_watch *watch; /* associated watch */
|
||||||
|
|
|
@ -141,6 +141,7 @@ static inline void audit_free_rule(struct audit_entry *e)
|
||||||
selinux_audit_rule_free(f->se_rule);
|
selinux_audit_rule_free(f->se_rule);
|
||||||
}
|
}
|
||||||
kfree(e->rule.fields);
|
kfree(e->rule.fields);
|
||||||
|
kfree(e->rule.filterkey);
|
||||||
kfree(e);
|
kfree(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -511,6 +512,16 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
|
||||||
if (err)
|
if (err)
|
||||||
goto exit_free;
|
goto exit_free;
|
||||||
break;
|
break;
|
||||||
|
case AUDIT_FILTERKEY:
|
||||||
|
err = -EINVAL;
|
||||||
|
if (entry->rule.filterkey || f->val > AUDIT_MAX_KEY_LEN)
|
||||||
|
goto exit_free;
|
||||||
|
str = audit_unpack_string(&bufp, &remain, f->val);
|
||||||
|
if (IS_ERR(str))
|
||||||
|
goto exit_free;
|
||||||
|
entry->rule.buflen += f->val;
|
||||||
|
entry->rule.filterkey = str;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
goto exit_free;
|
goto exit_free;
|
||||||
}
|
}
|
||||||
|
@ -612,6 +623,10 @@ static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule)
|
||||||
data->buflen += data->values[i] =
|
data->buflen += data->values[i] =
|
||||||
audit_pack_string(&bufp, krule->watch->path);
|
audit_pack_string(&bufp, krule->watch->path);
|
||||||
break;
|
break;
|
||||||
|
case AUDIT_FILTERKEY:
|
||||||
|
data->buflen += data->values[i] =
|
||||||
|
audit_pack_string(&bufp, krule->filterkey);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
data->values[i] = f->val;
|
data->values[i] = f->val;
|
||||||
}
|
}
|
||||||
|
@ -651,6 +666,11 @@ static int audit_compare_rule(struct audit_krule *a, struct audit_krule *b)
|
||||||
if (strcmp(a->watch->path, b->watch->path))
|
if (strcmp(a->watch->path, b->watch->path))
|
||||||
return 1;
|
return 1;
|
||||||
break;
|
break;
|
||||||
|
case AUDIT_FILTERKEY:
|
||||||
|
/* both filterkeys exist based on above type compare */
|
||||||
|
if (strcmp(a->filterkey, b->filterkey))
|
||||||
|
return 1;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
if (a->fields[i].val != b->fields[i].val)
|
if (a->fields[i].val != b->fields[i].val)
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -730,6 +750,7 @@ static struct audit_entry *audit_dupe_rule(struct audit_krule *old,
|
||||||
u32 fcount = old->field_count;
|
u32 fcount = old->field_count;
|
||||||
struct audit_entry *entry;
|
struct audit_entry *entry;
|
||||||
struct audit_krule *new;
|
struct audit_krule *new;
|
||||||
|
char *fk;
|
||||||
int i, err = 0;
|
int i, err = 0;
|
||||||
|
|
||||||
entry = audit_init_entry(fcount);
|
entry = audit_init_entry(fcount);
|
||||||
|
@ -760,6 +781,13 @@ static struct audit_entry *audit_dupe_rule(struct audit_krule *old,
|
||||||
case AUDIT_SE_CLR:
|
case AUDIT_SE_CLR:
|
||||||
err = audit_dupe_selinux_field(&new->fields[i],
|
err = audit_dupe_selinux_field(&new->fields[i],
|
||||||
&old->fields[i]);
|
&old->fields[i]);
|
||||||
|
break;
|
||||||
|
case AUDIT_FILTERKEY:
|
||||||
|
fk = kstrdup(old->filterkey, GFP_KERNEL);
|
||||||
|
if (unlikely(!fk))
|
||||||
|
err = -ENOMEM;
|
||||||
|
else
|
||||||
|
new->filterkey = fk;
|
||||||
}
|
}
|
||||||
if (err) {
|
if (err) {
|
||||||
audit_free_rule(entry);
|
audit_free_rule(entry);
|
||||||
|
@ -1245,6 +1273,34 @@ static void audit_list_rules(int pid, int seq, struct sk_buff_head *q)
|
||||||
skb_queue_tail(q, skb);
|
skb_queue_tail(q, skb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Log rule additions and removals */
|
||||||
|
static void audit_log_rule_change(uid_t loginuid, u32 sid, char *action,
|
||||||
|
struct audit_krule *rule, int res)
|
||||||
|
{
|
||||||
|
struct audit_buffer *ab;
|
||||||
|
|
||||||
|
ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE);
|
||||||
|
if (!ab)
|
||||||
|
return;
|
||||||
|
audit_log_format(ab, "auid=%u", loginuid);
|
||||||
|
if (sid) {
|
||||||
|
char *ctx = NULL;
|
||||||
|
u32 len;
|
||||||
|
if (selinux_ctxid_to_string(sid, &ctx, &len))
|
||||||
|
audit_log_format(ab, " ssid=%u", sid);
|
||||||
|
else
|
||||||
|
audit_log_format(ab, " subj=%s", ctx);
|
||||||
|
kfree(ctx);
|
||||||
|
}
|
||||||
|
audit_log_format(ab, " %s rule key=", action);
|
||||||
|
if (rule->filterkey)
|
||||||
|
audit_log_untrustedstring(ab, rule->filterkey);
|
||||||
|
else
|
||||||
|
audit_log_format(ab, "(null)");
|
||||||
|
audit_log_format(ab, " list=%d res=%d", rule->listnr, res);
|
||||||
|
audit_log_end(ab);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* audit_receive_filter - apply all rules to the specified message type
|
* audit_receive_filter - apply all rules to the specified message type
|
||||||
* @type: audit message type
|
* @type: audit message type
|
||||||
|
@ -1304,24 +1360,7 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
|
||||||
|
|
||||||
err = audit_add_rule(entry,
|
err = audit_add_rule(entry,
|
||||||
&audit_filter_list[entry->rule.listnr]);
|
&audit_filter_list[entry->rule.listnr]);
|
||||||
|
audit_log_rule_change(loginuid, sid, "add", &entry->rule, !err);
|
||||||
if (sid) {
|
|
||||||
char *ctx = NULL;
|
|
||||||
u32 len;
|
|
||||||
if (selinux_ctxid_to_string(sid, &ctx, &len)) {
|
|
||||||
/* Maybe call audit_panic? */
|
|
||||||
audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
|
|
||||||
"auid=%u ssid=%u add rule to list=%d res=%d",
|
|
||||||
loginuid, sid, entry->rule.listnr, !err);
|
|
||||||
} else
|
|
||||||
audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
|
|
||||||
"auid=%u subj=%s add rule to list=%d res=%d",
|
|
||||||
loginuid, ctx, entry->rule.listnr, !err);
|
|
||||||
kfree(ctx);
|
|
||||||
} else
|
|
||||||
audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
|
|
||||||
"auid=%u add rule to list=%d res=%d",
|
|
||||||
loginuid, entry->rule.listnr, !err);
|
|
||||||
|
|
||||||
if (err)
|
if (err)
|
||||||
audit_free_rule(entry);
|
audit_free_rule(entry);
|
||||||
|
@ -1337,24 +1376,8 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
|
||||||
|
|
||||||
err = audit_del_rule(entry,
|
err = audit_del_rule(entry,
|
||||||
&audit_filter_list[entry->rule.listnr]);
|
&audit_filter_list[entry->rule.listnr]);
|
||||||
|
audit_log_rule_change(loginuid, sid, "remove", &entry->rule,
|
||||||
if (sid) {
|
!err);
|
||||||
char *ctx = NULL;
|
|
||||||
u32 len;
|
|
||||||
if (selinux_ctxid_to_string(sid, &ctx, &len)) {
|
|
||||||
/* Maybe call audit_panic? */
|
|
||||||
audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
|
|
||||||
"auid=%u ssid=%u remove rule from list=%d res=%d",
|
|
||||||
loginuid, sid, entry->rule.listnr, !err);
|
|
||||||
} else
|
|
||||||
audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
|
|
||||||
"auid=%u subj=%s remove rule from list=%d res=%d",
|
|
||||||
loginuid, ctx, entry->rule.listnr, !err);
|
|
||||||
kfree(ctx);
|
|
||||||
} else
|
|
||||||
audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
|
|
||||||
"auid=%u remove rule from list=%d res=%d",
|
|
||||||
loginuid, entry->rule.listnr, !err);
|
|
||||||
|
|
||||||
audit_free_rule(entry);
|
audit_free_rule(entry);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -186,6 +186,7 @@ struct audit_context {
|
||||||
int auditable; /* 1 if record should be written */
|
int auditable; /* 1 if record should be written */
|
||||||
int name_count;
|
int name_count;
|
||||||
struct audit_names names[AUDIT_NAMES];
|
struct audit_names names[AUDIT_NAMES];
|
||||||
|
char * filterkey; /* key for rule that triggered record */
|
||||||
struct dentry * pwd;
|
struct dentry * pwd;
|
||||||
struct vfsmount * pwdmnt;
|
struct vfsmount * pwdmnt;
|
||||||
struct audit_context *previous; /* For nested syscalls */
|
struct audit_context *previous; /* For nested syscalls */
|
||||||
|
@ -348,11 +349,17 @@ static int audit_filter_rules(struct task_struct *tsk,
|
||||||
if (ctx)
|
if (ctx)
|
||||||
result = audit_comparator(ctx->argv[f->type-AUDIT_ARG0], f->op, f->val);
|
result = audit_comparator(ctx->argv[f->type-AUDIT_ARG0], f->op, f->val);
|
||||||
break;
|
break;
|
||||||
|
case AUDIT_FILTERKEY:
|
||||||
|
/* ignore this field for filtering */
|
||||||
|
result = 1;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!result)
|
if (!result)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
if (rule->filterkey)
|
||||||
|
ctx->filterkey = kstrdup(rule->filterkey, GFP_ATOMIC);
|
||||||
switch (rule->action) {
|
switch (rule->action) {
|
||||||
case AUDIT_NEVER: *state = AUDIT_DISABLED; break;
|
case AUDIT_NEVER: *state = AUDIT_DISABLED; break;
|
||||||
case AUDIT_ALWAYS: *state = AUDIT_RECORD_CONTEXT; break;
|
case AUDIT_ALWAYS: *state = AUDIT_RECORD_CONTEXT; break;
|
||||||
|
@ -627,6 +634,7 @@ static inline void audit_free_context(struct audit_context *context)
|
||||||
}
|
}
|
||||||
audit_free_names(context);
|
audit_free_names(context);
|
||||||
audit_free_aux(context);
|
audit_free_aux(context);
|
||||||
|
kfree(context->filterkey);
|
||||||
kfree(context);
|
kfree(context);
|
||||||
context = previous;
|
context = previous;
|
||||||
} while (context);
|
} while (context);
|
||||||
|
@ -735,6 +743,11 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
|
||||||
context->euid, context->suid, context->fsuid,
|
context->euid, context->suid, context->fsuid,
|
||||||
context->egid, context->sgid, context->fsgid, tty);
|
context->egid, context->sgid, context->fsgid, tty);
|
||||||
audit_log_task_info(ab, tsk);
|
audit_log_task_info(ab, tsk);
|
||||||
|
if (context->filterkey) {
|
||||||
|
audit_log_format(ab, " key=");
|
||||||
|
audit_log_untrustedstring(ab, context->filterkey);
|
||||||
|
} else
|
||||||
|
audit_log_format(ab, " key=(null)");
|
||||||
audit_log_end(ab);
|
audit_log_end(ab);
|
||||||
|
|
||||||
for (aux = context->aux; aux; aux = aux->next) {
|
for (aux = context->aux; aux; aux = aux->next) {
|
||||||
|
@ -1060,6 +1073,8 @@ void audit_syscall_exit(int valid, long return_code)
|
||||||
} else {
|
} else {
|
||||||
audit_free_names(context);
|
audit_free_names(context);
|
||||||
audit_free_aux(context);
|
audit_free_aux(context);
|
||||||
|
kfree(context->filterkey);
|
||||||
|
context->filterkey = NULL;
|
||||||
tsk->audit_context = context;
|
tsk->audit_context = context;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue