[RFC][PATCH] new audit rule interface

Amy Griffis amy.griffis at hp.com
Thu Dec 15 15:40:20 UTC 2005


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;




More information about the Linux-audit mailing list