Hello,
Following is a first cut at a patch which implements an interface for
specifying filesystem paths in audit rules. I've identified some
issues, and would like to offer the patch up for discussion before
attempting to resolve them. If you see issues that I haven't
identified below, please mention them as well.
Overview
--------
The kernel-userspace interface changes required to specify audit rules
with string fields were implemented based on the proposal:
https://www.redhat.com/archives/linux-audit/2005-November/msg00015.html
Support for specifying filesystem paths in audit rules was included as
a proof-of-concept.
With this patch, userspace may specify a path filter, akin to
specifying an inode filter. If a filename exists at the specified
path, audit records will be generated as they are for inode filters.
Watched paths are tracked via a master_watchlist. The
master_watchlist is composed of one or more audit_watch structs, each
of which is associated with one or more struct audit_krule's in the
syscall exit filter list (AUDIT_FILTER_EXIT). Audit's filtering data
structures are still updated exclusively through explicit rule
addition and removal, so this patch did not require any locking
changes.
To reduce the complexity of the patch, I omitted the functionality
necessary to provide persistence for path-based filters. At present,
audit is not aware when a file is created or removed from watched path
locations. I plan to submit this functionality in a separate patch.
Issues
------
I have identified some issues with this patch that I'll list here.
1) struct audit_rule_xprt
Introducing a new data structure for specifying audit rules via
netlink provides a good opportunity to revisit the data structure
design and determine if we want to make any other changes, e.g.
adding a structure version field, reserving fields, etc. At
present, I've only added the empty buf[] array.
How much we deviate from the current structure should be a factor
of how long we expect to continue using netlink for all
userspace-kernel communication.
2) struct audit_krule
The kernel structure defining an audit rule isn't keeping enough
information about what it received from userspace.
- the kernel currently converts the upper bits of a field[]
element to its own representation; when the rule is listed back
to userspace, the bits are not converted back to the way in
which they were originally specified
- the difference between an inode-based and a path-based filter is
not always discernible, as it needs to be (e.g. kernel currently
won't allow a rule to be added for an inode # that can be
resolved from an existing path-based filter)
- might not be an issue now, but should probably track which type
or version of structure was used to add a given rule
- the operator bits consistently need to be masked out; they
should just be a separate field in kernel
3) audit_rule_in()
This function is long and somewhat specific to specifying paths in
audit rules. This should be more generalized, or the
path-specific code should be split out to another function.
4) in general
Perhaps most importantly, this patch mixes the interface changes
required to specify audit rules with string fields with some of
the initial filesystem audit functionality. I think these pieces
should be split into separate patches to allow a more generalized
approach.
I plan to update this patch to resolve these issues, along with any
other identified issues.
Thanks for your review.
Amy
diff --git a/include/linux/audit.h b/include/linux/audit.h
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -59,6 +59,9 @@
#define AUDIT_WATCH_REM 1008 /* Remove file/dir watch entry */
#define AUDIT_WATCH_LIST 1009 /* List all file/dir watches */
#define AUDIT_SIGNAL_INFO 1010 /* Get info about sender of signal to auditd */
+#define AUDIT_ADD_RULE 1011 /* Add syscall filter (string supp) */
+#define AUDIT_DEL_RULE 1012 /* Delete syscall filter (string supp)*/
+#define AUDIT_LIST_RULES 1013 /* List syscall filters (string supp) */
#define AUDIT_FIRST_USER_MSG 1100 /* Userspace messages mostly uninteresting to kernel
*/
#define AUDIT_USER_AVC 1107 /* We filter this differently */
@@ -142,6 +145,7 @@
#define AUDIT_INODE 102
#define AUDIT_EXIT 103
#define AUDIT_SUCCESS 104 /* exit >= 0; value ignored */
+#define AUDIT_PATHNAME 105 /* location in filesystem (attached path len */
#define AUDIT_ARG0 200
#define AUDIT_ARG1 (AUDIT_ARG0+1)
@@ -226,7 +230,26 @@ struct audit_status {
__u32 backlog; /* messages waiting in queue */
};
-struct audit_rule { /* for AUDIT_LIST, AUDIT_ADD, and AUDIT_DEL */
+/* audit_rule_xprt supports filter rules with both integer and string
+ * fields. It corresponds with AUDIT_ADD_RULE, AUDIT_DEL_RULE and
+ * AUDIT_LIST_RULES requests.
+ */
+struct audit_rule_xprt {
+ __u32 flags; /* AUDIT_PER_{TASK,CALL}, AUDIT_PREPEND */
+ __u32 action; /* AUDIT_NEVER, AUDIT_POSSIBLE, AUDIT_ALWAYS */
+ __u32 field_count;
+ __u32 mask[AUDIT_BITMASK_SIZE];
+ __u32 fields[AUDIT_MAX_FIELDS];
+ __u32 values[AUDIT_MAX_FIELDS];
+ __u32 buflen; /* total length of string fields */
+ char buf[0]; /* string fields buffer */
+};
+
+/* audit_rule is supported to maintain backward compatibility with
+ * userspace. It supports integer fields only and corresponds to
+ * AUDIT_ADD, AUDIT_DEL and AUDIT_LIST requests.
+ */
+struct audit_rule {
__u32 flags; /* AUDIT_PER_{TASK,CALL}, AUDIT_PREPEND */
__u32 action; /* AUDIT_NEVER, AUDIT_POSSIBLE, AUDIT_ALWAYS */
__u32 field_count;
diff --git a/kernel/audit.c b/kernel/audit.c
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -358,9 +358,12 @@ static int audit_netlink_ok(kernel_cap_t
switch (msg_type) {
case AUDIT_GET:
case AUDIT_LIST:
+ case AUDIT_LIST_RULES:
case AUDIT_SET:
case AUDIT_ADD:
+ case AUDIT_ADD_RULE:
case AUDIT_DEL:
+ case AUDIT_DEL_RULE:
case AUDIT_SIGNAL_INFO:
if (!cap_raised(eff_cap, CAP_AUDIT_CONTROL))
err = -EPERM;
@@ -474,6 +477,15 @@ static int audit_receive_msg(struct sk_b
err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid,
uid, seq, data, loginuid);
break;
+ case AUDIT_ADD_RULE:
+ case AUDIT_DEL_RULE:
+ if (nlh->nlmsg_len < sizeof(struct audit_rule_xprt))
+ return -EINVAL;
+ /* fallthrough */
+ case AUDIT_LIST_RULES:
+ err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid,
+ uid, seq, data, loginuid);
+ break;
case AUDIT_SIGNAL_INFO:
sig_data.uid = audit_sig_uid;
sig_data.pid = audit_sig_pid;
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -182,7 +182,33 @@ struct audit_context {
#endif
};
- /* Public API */
+/* Audit Filters */
+
+/* kernel's representation of a watched filesystem location */
+struct audit_watch {
+ char *path; /* watch insertion path */
+ struct list_head mlist; /* entry in master_watchlist */
+ struct list_head rules; /* associated rules*/
+};
+
+/* kernel's internal filter rule representation */
+struct audit_krule {
+ u32 flags;
+ u32 action;
+ u32 field_count;
+ u32 mask[AUDIT_BITMASK_SIZE];
+ u32 fields[AUDIT_MAX_FIELDS];
+ u32 values[AUDIT_MAX_FIELDS];
+ struct audit_watch *watch; /* associated watch */
+ struct list_head rlist; /* entry in audit_watch.rules list */
+};
+
+struct audit_entry {
+ struct list_head list; /* entry in audit_filter_list */
+ struct rcu_head rcu;
+ struct audit_krule rule;
+};
+
/* There are three lists of rules -- one to search at task creation
* time, one to search at syscall entry time, and another to search at
* syscall exit time. */
@@ -198,101 +224,290 @@ static struct list_head audit_filter_lis
#endif
};
-struct audit_entry {
- struct list_head list;
- struct rcu_head rcu;
- struct audit_rule rule;
-};
+static LIST_HEAD(master_watchlist);
extern int audit_pid;
-/* Copy rule from user-space to kernel-space. Called from
- * audit_add_rule during AUDIT_ADD. */
-static inline int audit_copy_rule(struct audit_rule *d, struct audit_rule *s)
+/* Check to see if two rules are identical. */
+static inline int audit_compare_rule(struct audit_krule *a,
+ struct audit_krule *b,
+ unsigned skip_ino)
{
int i;
- if (s->action != AUDIT_NEVER
- && s->action != AUDIT_POSSIBLE
- && s->action != AUDIT_ALWAYS)
- return -1;
- if (s->field_count < 0 || s->field_count > AUDIT_MAX_FIELDS)
- return -1;
- if ((s->flags & ~AUDIT_FILTER_PREPEND) >= AUDIT_NR_FILTERS)
- return -1;
-
- d->flags = s->flags;
- d->action = s->action;
- d->field_count = s->field_count;
- for (i = 0; i < d->field_count; i++) {
- d->fields[i] = s->fields[i];
- d->values[i] = s->values[i];
+ if (a->flags != b->flags ||
+ a->action != b->action ||
+ a->field_count != b->field_count)
+ return 1;
+
+ /* rules must have same field ordering to match */
+ for (i = 0; i < a->field_count; i++) {
+ /* skip inode comparison with matching paths */
+ if (skip_ino &&
+ (a->fields[i] & ~AUDIT_OPERATORS) == AUDIT_INODE &&
+ (b->fields[i] & ~AUDIT_OPERATORS) == AUDIT_INODE)
+ continue;
+ if (a->fields[i] != b->fields[i] ||
+ a->values[i] != b->values[i])
+ return 1;
}
- for (i = 0; i < AUDIT_BITMASK_SIZE; i++) d->mask[i] = s->mask[i];
+
+ for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
+ if (a->mask[i] != b->mask[i])
+ return 1;
+
return 0;
}
-/* Check to see if two rules are identical. It is called from
- * audit_add_rule during AUDIT_ADD and
- * audit_del_rule during AUDIT_DEL. */
-static inline int audit_compare_rule(struct audit_rule *a, struct audit_rule *b)
+static inline void audit_destroy_rule(struct rcu_head *head)
+{
+ struct audit_entry *e = container_of(head, struct audit_entry, rcu);
+ kfree(e);
+}
+
+static inline void audit_remove_watch(struct audit_krule *rule)
+{
+ struct audit_watch *watch;
+
+ watch = rule->watch;
+ list_del(&rule->rlist);
+ rule->watch = NULL;
+
+ if (list_empty(&watch->rules)) {
+ list_del(&watch->mlist);
+ kfree(watch->path);
+ kfree(watch);
+ }
+}
+
+static int audit_add_watch(struct audit_krule *rule, char *path)
{
+ int rc = 0;
+ struct audit_watch *w, *watch;
+ struct audit_entry *e;
+ struct nameidata nd;
int i;
- if (a->flags != b->flags)
- return 1;
+ list_for_each_entry(w, &master_watchlist, mlist) {
+ if (strcmp(path, w->path) != 0)
+ continue;
+
+ list_for_each_entry(e, &audit_filter_list[AUDIT_FILTER_EXIT],
+ list)
+ if (!audit_compare_rule(rule, &e->rule, 1)) {
+ rc = -EEXIST;
+ goto exit;
+ }
+ watch = w;
+ goto attach_rule;
+ }
- if (a->action != b->action)
- return 1;
+ watch = kmalloc(sizeof(*watch), GFP_KERNEL);
+ if (!watch) {
+ rc = -ENOMEM;
+ goto exit;
+ }
+ watch->path = path;
+ list_add(&watch->mlist, &master_watchlist);
+ INIT_LIST_HEAD(&watch->rules);
- if (a->field_count != b->field_count)
- return 1;
+attach_rule:
+ list_add(&rule->rlist, &watch->rules);
+ rule->watch = watch;
- for (i = 0; i < a->field_count; i++) {
- if (a->fields[i] != b->fields[i]
- || a->values[i] != b->values[i])
- return 1;
+ /* update inode filter field if possible */
+ if (path_lookup(path, 0, &nd) == 0)
+ for (i = 0; i < rule->field_count; i++)
+ if ((rule->fields[i] & ~AUDIT_OPERATORS) == AUDIT_INODE)
+ rule->values[i] = nd.dentry->d_inode->i_ino;
+ path_release(&nd);
+
+exit:
+ return rc;
+}
+
+static inline char *audit_path_in(char *buf, int buflen)
+{
+ char *path;
+
+ if (buflen > PATH_MAX)
+ return ERR_PTR(-ENAMETOOLONG);
+
+ if (buflen <= 1 || !buf || buf[0] != '/')
+ return ERR_PTR(-EINVAL);
+
+ path = kmalloc(buflen + 1, GFP_KERNEL);
+ if (!path)
+ return ERR_PTR(-ENOMEM);
+ memcpy(path, buf, buflen);
+ path[buflen] = 0;
+
+ return path;
+}
+
+/* Copy rule from user-space to kernel-space. */
+static struct audit_entry *audit_rule_in(struct audit_rule_xprt *rulex,
+ char **path, int type)
+{
+ int i, ret;
+ struct audit_entry *entry;
+ void *buf = NULL;
+ char *tmp = NULL;
+ unsigned listnr;
+ unsigned int offset = 0;
+
+ if (!(entry = kmalloc(sizeof(*entry), GFP_KERNEL)))
+ return ERR_PTR(-ENOMEM);
+
+ ret = -EINVAL;
+ listnr = rulex->flags & ~AUDIT_FILTER_PREPEND;
+ if (listnr >= AUDIT_NR_FILTERS)
+ goto exit_err;
+ if (rulex->action != AUDIT_NEVER && rulex->action != AUDIT_POSSIBLE
&&
+ rulex->action != AUDIT_ALWAYS)
+ goto exit_err;
+ if (rulex->field_count < 0 || rulex->field_count > AUDIT_MAX_FIELDS)
+ goto exit_err;
+
+ entry->rule.flags = rulex->flags;
+ entry->rule.action = rulex->action;
+ entry->rule.field_count = rulex->field_count;
+ entry->rule.watch = NULL;
+
+ for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
+ entry->rule.mask[i] = rulex->mask[i];
+
+ if (type == AUDIT_ADD_RULE || type == AUDIT_DEL_RULE)
+ buf = rulex->buf;
+
+ for (i = 0; i < rulex->field_count; i++) {
+ u32 rfield = rulex->fields[i] & (~AUDIT_NEGATE|AUDIT_OPERATORS);
+ u32 rvalue = rulex->values[i];
+
+ ret = -EINVAL;
+ if (rulex->fields[i] & AUDIT_UNUSED_BITS)
+ goto exit_err;
+
+ if (rfield == AUDIT_PATHNAME) {
+ if (listnr != AUDIT_FILTER_EXIT)
+ goto exit_err;
+ if (offset + rvalue > rulex->buflen)
+ goto exit_err;
+
+ tmp = audit_path_in(buf, rvalue);
+ if (IS_ERR(tmp)) {
+ ret = PTR_ERR(tmp);
+ tmp = NULL;
+ goto exit_err;
+ }
+ *path = tmp;
+
+ offset += rvalue;
+ buf = rulex->buf + offset;
+
+ entry->rule.fields[i] = AUDIT_INODE|AUDIT_EQUAL;
+ entry->rule.values[i] = -1;
+ } else {
+ entry->rule.fields[i] = rulex->fields[i];
+ entry->rule.values[i] = rvalue;
+
+ if (entry->rule.fields[i] & AUDIT_NEGATE) {
+ entry->rule.fields[i] &= ~AUDIT_NEGATE;
+ entry->rule.fields[i] |= AUDIT_NOT_EQUAL;
+ } else if ((entry->rule.fields[i] &
+ AUDIT_OPERATORS) == 0)
+ entry->rule.fields[i] |= AUDIT_EQUAL;
+ }
}
+ return entry;
+
+exit_err:
+ kfree(tmp);
+ kfree(entry);
+ return ERR_PTR(ret);
+}
+
+/* Copy rule into user-space compatible struct */
+static struct audit_rule_xprt *audit_rule_out(struct audit_krule *rule)
+{
+ int i, len;
+ int pathlen = 0;
+ int watched = 0; /* flag rules with attached watches */
+ struct audit_rule_xprt *rulex;
+ void *buf;
+
+ if (rule->watch) {
+ pathlen = strlen(rule->watch->path) + 1;
+ watched = 1;
+ }
+
+ len = sizeof(struct audit_rule_xprt) + pathlen;
+ rulex = kmalloc(len, GFP_KERNEL);
+ if (!rulex)
+ return ERR_PTR(-ENOMEM);
+ memset(rulex, 0, len);
+
+ rulex->flags = rule->flags;
+ rulex->action = rule->action;
+ rulex->field_count = rule->field_count;
for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
- if (a->mask[i] != b->mask[i])
- return 1;
+ rulex->mask[i] = rule->mask[i];
- return 0;
+ buf = rulex->buf;
+ for (i = 0; i < rule->field_count; i++) {
+ u32 field = rule->fields[i] & ~AUDIT_OPERATORS;
+ if (watched && field == AUDIT_INODE) {
+ memcpy(buf, rule->watch->path, pathlen);
+ rulex->buflen += pathlen;
+ buf = rulex->buf + rulex->buflen;
+
+ rulex->fields[i] = AUDIT_PATHNAME|AUDIT_EQUAL;
+ rulex->values[i] = pathlen;
+ } else {
+ rulex->fields[i] = rule->fields[i];
+ rulex->values[i] = rule->values[i];
+ }
+ }
+ return rulex;
}
/* Note that audit_add_rule and audit_del_rule are called via
* audit_receive() in audit.c, and are protected by
* audit_netlink_sem. */
-static inline int audit_add_rule(struct audit_rule *rule,
- struct list_head *list)
+static inline int audit_add_rule(void *data, int type)
{
- struct audit_entry *entry;
- int i;
+ int ret = 0;
+ struct audit_entry *e, *entry;
+ char *path = NULL;
+ unsigned listnr;
+ struct list_head *list;
- /* Do not use the _rcu iterator here, since this is the only
- * addition routine. */
- list_for_each_entry(entry, list, list) {
- if (!audit_compare_rule(rule, &entry->rule)) {
- return -EEXIST;
- }
+ entry = audit_rule_in(data, &path, type);
+ if (IS_ERR(entry)) {
+ ret = PTR_ERR(entry);
+ goto out;
}
- for (i = 0; i < rule->field_count; i++) {
- if (rule->fields[i] & AUDIT_UNUSED_BITS)
- return -EINVAL;
- if ( rule->fields[i] & AUDIT_NEGATE )
- rule->fields[i] |= AUDIT_NOT_EQUAL;
- else if ( (rule->fields[i] & AUDIT_OPERATORS) == 0 )
- rule->fields[i] |= AUDIT_EQUAL;
- rule->fields[i] &= (~AUDIT_NEGATE);
+ if (path && (ret = audit_add_watch(&entry->rule, path))) {
+ kfree(path);
+ kfree(entry);
+ goto out;
}
- if (!(entry = kmalloc(sizeof(*entry), GFP_KERNEL)))
- return -ENOMEM;
- if (audit_copy_rule(&entry->rule, rule)) {
- kfree(entry);
- return -EINVAL;
+ listnr = entry->rule.flags & ~AUDIT_FILTER_PREPEND;
+ list = &audit_filter_list[listnr];
+
+ /* Do not use the _rcu iterator here, since this is the only
+ * addition routine. */
+ if (!path) {
+ list_for_each_entry(e, list, list) {
+ if (!audit_compare_rule(&entry->rule, &e->rule, 0)) {
+ ret = -EEXIST;
+ kfree(entry);
+ goto out;
+ }
+ }
}
if (entry->rule.flags & AUDIT_FILTER_PREPEND) {
@@ -302,56 +517,91 @@ static inline int audit_add_rule(struct
list_add_tail_rcu(&entry->list, list);
}
- return 0;
-}
-
-static inline void audit_free_rule(struct rcu_head *head)
-{
- struct audit_entry *e = container_of(head, struct audit_entry, rcu);
- kfree(e);
+out:
+ return ret;
}
/* Note that audit_add_rule and audit_del_rule are called via
* audit_receive() in audit.c, and are protected by
* audit_netlink_sem. */
-static inline int audit_del_rule(struct audit_rule *rule,
- struct list_head *list)
+static inline int audit_del_rule(void *data, int type)
{
- struct audit_entry *e;
+ int ret = 0;
+ struct audit_entry *e, *entry;
+ char *path = NULL;
+ struct list_head *list;
+ unsigned listnr;
+
+ entry = audit_rule_in(data, &path, type);
+ if (IS_ERR(entry)) {
+ ret = PTR_ERR(entry);
+ goto out;
+ }
+
+ listnr = entry->rule.flags & ~AUDIT_FILTER_PREPEND;
+ list = &audit_filter_list[listnr];
/* Do not use the _rcu iterator here, since this is the only
* deletion routine. */
list_for_each_entry(e, list, list) {
- if (!audit_compare_rule(rule, &e->rule)) {
+ unsigned skip_ino = 0;
+
+ if (path) {
+ if (!e->rule.watch ||
+ strcmp(path, e->rule.watch->path))
+ continue;
+ skip_ino = 1; /* watch paths match */
+ } else if (e->rule.watch)
+ continue;
+
+ if (!audit_compare_rule(&entry->rule, &e->rule, skip_ino)) {
+ if (path)
+ audit_remove_watch(&e->rule);
list_del_rcu(&e->list);
- call_rcu(&e->rcu, audit_free_rule);
- return 0;
+ call_rcu(&e->rcu, audit_destroy_rule);
+ kfree(path);
+ kfree(entry);
+ goto out;
}
}
- return -ENOENT; /* No matching rule */
+ ret = -ENOENT; /* No matching rule */
+out:
+ return ret;
}
-static int audit_list_rules(void *_dest)
+static int audit_list_rules(void *_args)
{
- int pid, seq;
- int *dest = _dest;
+ int pid, seq, type;
+ int *args = _args;
struct audit_entry *entry;
int i;
- pid = dest[0];
- seq = dest[1];
- kfree(dest);
+ pid = args[0];
+ seq = args[1];
+ type = args[2];
+ kfree(args);
down(&audit_netlink_sem);
/* The *_rcu iterators not needed here because we are
always called with audit_netlink_sem held. */
for (i=0; i<AUDIT_NR_FILTERS; i++) {
- list_for_each_entry(entry, &audit_filter_list[i], list)
- audit_send_reply(pid, seq, AUDIT_LIST, 0, 1,
- &entry->rule, sizeof(entry->rule));
+ list_for_each_entry(entry, &audit_filter_list[i], list) {
+ struct audit_rule_xprt *rule;
+ int len;
+
+ rule = audit_rule_out(&entry->rule);
+ if (type == AUDIT_LIST)
+ len = sizeof(struct audit_rule);
+ else
+ len = sizeof(struct audit_rule_xprt) +
+ rule->buflen;
+ audit_send_reply(pid, seq, type, 0, 1, rule, len);
+
+ kfree(rule);
+ }
}
- audit_send_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0);
+ audit_send_reply(pid, seq, type, 1, 1, NULL, 0);
up(&audit_netlink_sem);
return 0;
@@ -370,46 +620,42 @@ int audit_receive_filter(int type, int p
uid_t loginuid)
{
struct task_struct *tsk;
- int *dest;
- int err = 0;
- unsigned listnr;
+ int *args;
+ int err = 0;
switch (type) {
case AUDIT_LIST:
+ case AUDIT_LIST_RULES:
/* We can't just spew out the rules here because we might fill
* the available socket buffer space and deadlock waiting for
* auditctl to read from it... which isn't ever going to
* happen if we're actually running in the context of auditctl
* trying to _send_ the stuff */
-
- dest = kmalloc(2 * sizeof(int), GFP_KERNEL);
- if (!dest)
+
+ args = kmalloc(3 * sizeof(int), GFP_KERNEL);
+ if (!args)
return -ENOMEM;
- dest[0] = pid;
- dest[1] = seq;
+ args[0] = pid;
+ args[1] = seq;
+ args[2] = type;
+
+ tsk = kthread_run(audit_list_rules, args, "audit_list_rules");
- tsk = kthread_run(audit_list_rules, dest, "audit_list_rules");
if (IS_ERR(tsk)) {
- kfree(dest);
+ kfree(args);
err = PTR_ERR(tsk);
}
break;
case AUDIT_ADD:
- listnr =((struct audit_rule *)data)->flags & ~AUDIT_FILTER_PREPEND;
- if (listnr >= AUDIT_NR_FILTERS)
- return -EINVAL;
-
- err = audit_add_rule(data, &audit_filter_list[listnr]);
+ case AUDIT_ADD_RULE:
+ err = audit_add_rule(data, type);
if (!err)
audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
"auid=%u added an audit rule\n", loginuid);
break;
case AUDIT_DEL:
- listnr =((struct audit_rule *)data)->flags & ~AUDIT_FILTER_PREPEND;
- if (listnr >= AUDIT_NR_FILTERS)
- return -EINVAL;
-
- err = audit_del_rule(data, &audit_filter_list[listnr]);
+ case AUDIT_DEL_RULE:
+ err = audit_del_rule(data, type);
if (!err)
audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
"auid=%u removed an audit rule\n", loginuid);
@@ -444,7 +690,7 @@ static int audit_comparator(const u32 le
/* Compare a task_struct with an audit_rule. Return 1 on match, 0
* otherwise. */
static int audit_filter_rules(struct task_struct *tsk,
- struct audit_rule *rule,
+ struct audit_krule *rule,
struct audit_context *ctx,
enum audit_state *state)
{
@@ -525,9 +771,9 @@ static int audit_filter_rules(struct tas
}
break;
case AUDIT_INODE:
- if (ctx) {
+ if (ctx && value != (unsigned int)-1) {
for (j = 0; j < ctx->name_count; j++) {
- if (audit_comparator(ctx->names[j].pino, op, value) ||
+ if (audit_comparator(ctx->names[j].ino, op, value) ||
audit_comparator(ctx->names[j].pino, op, value)) {
++result;
break;
@@ -602,7 +848,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)) {
rcu_read_unlock();
return state;
}
@@ -613,7 +860,7 @@ static enum audit_state audit_filter_sys
}
static int audit_filter_user_rules(struct netlink_skb_parms *cb,
- struct audit_rule *rule,
+ struct audit_krule *rule,
enum audit_state *state)
{
int i;
@@ -680,7 +927,7 @@ int audit_filter_exclude(int type)
list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_EXCLUDE],
list) {
- struct audit_rule *rule = &e->rule;
+ struct audit_krule *rule = &e->rule;
int i;
for (i = 0; i < rule->field_count; i++) {
u32 field = rule->fields[i] & ~AUDIT_OPERATORS;