[PATCH] new audit rule interface

Amy Griffis amy.griffis at hp.com
Thu Dec 22 00:08:21 UTC 2005


Hello,

Follwing is an updated patch which provides an interface for specifing
audit rules with string fields.  The code related to filesystem paths
has been removed, and the interface has been generalized to support
string fields for more than one field type.

To summarize the interface, a new struct audit_rule_xprt and
corresponding netlink message types have been added.  The new struct
allows userspace to supply one or more string fields packed in a
variable length buffer.  The kernel expects the buffer to be neither
null-delimited nor null-terminated.  

The length of string data for a given field is provided as its value
element in the array.  The kernel provides the buflen element for
convenience on rule listing, to allow userspace to allocate memory for
the buffer without walking the array to tabulate lengths.  Buflen is
ignored coming from userspace.  Instead, the kernel ensures that the
total of the lengths specified in the value elements do not exceed the
length of the message payload minus sizeof(struct audit_rule_xprt).

Several routines have been added to auditfilter.c to translate between
the kernel's rule representation and the two userspace rule
representations.

This patch resolves the following issues present in the previous
patch:

- splits string fields interface patch from filesystem audit patch.
- struct audit_krule uses a separate field for operator bits
- struct audit_krule tracks which type of operator bits were used,
  and sends rule back to userspace as it was supplied
- contents of struct audit_rule_xprt reviewed and solidified

  Several of us talked on IRC and decided to split the field flags
  away from the field type in the new structure.  We also decided
  against adding empty fields and versioning the structure.

- documentation related to modified routines has been updated


Signed-off-by: Amy Griffis <amy.griffis at hp.com>

diff --git a/include/linux/audit.h b/include/linux/audit.h
index 79d8271..b5da475 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -50,15 +50,18 @@
  */
 #define AUDIT_GET		1000	/* Get status */
 #define AUDIT_SET		1001	/* Set status (enable/disable/auditd) */
-#define AUDIT_LIST		1002	/* List syscall filtering rules */
-#define AUDIT_ADD		1003	/* Add syscall filtering rule */
-#define AUDIT_DEL		1004	/* Delete syscall filtering rule */
+#define AUDIT_LIST		1002	/* List syscall rules -- deprecated */
+#define AUDIT_ADD		1003	/* Add syscall rule -- deprecated */
+#define AUDIT_DEL		1004	/* Delete syscall rule -- deprecated */
 #define AUDIT_USER		1005	/* Message from userspace -- deprecated */
 #define AUDIT_LOGIN		1006	/* Define the login id and information */
 #define AUDIT_WATCH_INS		1007	/* Insert file/dir watch entry */
 #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 filtering rule */
+#define AUDIT_DEL_RULE		1012	/* Delete syscall filtering rule */
+#define AUDIT_LIST_RULES	1013	/* List syscall filtering rules */
 
 #define AUDIT_FIRST_USER_MSG	1100	/* Userspace messages mostly uninteresting to kernel */
 #define AUDIT_USER_AVC		1107	/* We filter this differently */
@@ -226,6 +229,26 @@ struct audit_status {
 	__u32		backlog;	/* messages waiting in queue */
 };
 
+/* 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		fieldflags[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 {		/* for AUDIT_LIST, AUDIT_ADD, and AUDIT_DEL */
 	__u32		flags;	/* AUDIT_PER_{TASK,CALL}, AUDIT_PREPEND */
 	__u32		action;	/* AUDIT_NEVER, AUDIT_POSSIBLE, AUDIT_ALWAYS */
@@ -337,7 +360,7 @@ extern void		    audit_log_d_path(struct
 extern int audit_filter_user(struct netlink_skb_parms *cb, int type);
 extern int audit_filter_type(int type);
 extern int  audit_receive_filter(int type, int pid, int uid, int seq,
-				 void *data, uid_t loginuid);
+				 void *data, int dlen, uid_t loginuid);
 #else
 #define audit_log(c,g,t,f,...) do { ; } while (0)
 #define audit_log_start(c,g,t) ({ NULL; })
diff --git a/kernel/audit.c b/kernel/audit.c
index d95efd6..d55e8c5 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -52,6 +52,7 @@
 #include <linux/audit.h>
 
 #include <net/sock.h>
+#include <net/netlink.h>
 #include <linux/skbuff.h>
 #include <linux/netlink.h>
 
@@ -361,9 +362,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;
@@ -470,12 +474,23 @@ static int audit_receive_msg(struct sk_b
 		break;
 	case AUDIT_ADD:
 	case AUDIT_DEL:
-		if (nlh->nlmsg_len < sizeof(struct audit_rule))
+		if (nlmsg_len(nlh) < sizeof(struct audit_rule))
 			return -EINVAL;
 		/* fallthrough */
 	case AUDIT_LIST:
 		err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid,
-					   uid, seq, data, loginuid);
+					   uid, seq, data, nlmsg_len(nlh),
+					   loginuid);
+		break;
+	case AUDIT_ADD_RULE:
+	case AUDIT_DEL_RULE:
+		if (nlmsg_len(nlh) < 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, nlmsg_len(nlh),
+					   loginuid);
 		break;
 	case AUDIT_SIGNAL_INFO:
 		sig_data.uid = audit_sig_uid;
diff --git a/kernel/audit.h b/kernel/audit.h
index 7643e46..3e69ab3 100644
--- a/kernel/audit.h
+++ b/kernel/audit.h
@@ -52,12 +52,28 @@ enum audit_state {
 };
 
 /* Rule lists */
-struct audit_entry {
-	struct list_head  list;
-	struct rcu_head   rcu;
-	struct audit_rule rule;
+struct audit_field {
+	u32			type;
+	u32			val;
+	u32			op;
+};
+
+struct audit_krule {
+	int			vers_ops;
+	u32			flags;
+	u32			listnr;
+	u32			action;
+	u32			mask[AUDIT_BITMASK_SIZE];
+	u32			buflen; /* for data alloc on list rules */
+	u32			field_count;
+	struct audit_field	fields[AUDIT_MAX_FIELDS];
 };
 
+struct audit_entry {
+	struct list_head  	list;
+	struct rcu_head   	rcu;
+	struct audit_krule	rule;
+};
 
 extern int audit_pid;
 extern int audit_comparator(const u32 left, const u32 op, const u32 right);
diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c
index a3a3275..1fd4b2a 100644
--- a/kernel/auditfilter.c
+++ b/kernel/auditfilter.c
@@ -40,52 +40,253 @@ struct list_head audit_filter_list[AUDIT
 #endif
 };
 
-/* 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)
+/* Unpack a filter field's string representation from user-space
+ * buffer. */
+static inline char *audit_unpack_string(void **bufp, int *remaining, int len)
 {
+	char *str;
+
+	if (!*bufp || (len > *remaining))
+		return ERR_PTR(-EINVAL);
+
+	str = kmalloc(len + 1, GFP_KERNEL);
+	if (!str)
+		return ERR_PTR(-ENOMEM);
+
+	memcpy(str, *bufp, len);
+	str[len] = 0;
+	*bufp += len;
+	*remaining -= len;
+
+	return str;
+}
+
+/* Common user-space to kernel rule translation. */
+static struct audit_entry *audit_to_entry_common(struct audit_rule *rule)
+{
+	unsigned listnr;
+	struct audit_entry *entry;
+	int i, err;
+
+	err = -EINVAL;
+	listnr = rule->flags & ~AUDIT_FILTER_PREPEND;
+	switch(listnr) {
+	default:
+		goto exit_err;
+	case AUDIT_FILTER_USER:
+	case AUDIT_FILTER_TYPE:
+#ifdef CONFIG_AUDITSYSCALL
+	case AUDIT_FILTER_ENTRY:
+	case AUDIT_FILTER_EXIT:
+	case AUDIT_FILTER_TASK:
+#endif
+		;
+	}
+	if (rule->action != AUDIT_NEVER && rule->action != AUDIT_POSSIBLE &&
+	    rule->action != AUDIT_ALWAYS)
+		goto exit_err;
+	if (rule->field_count < 0 || rule->field_count > AUDIT_MAX_FIELDS)
+		goto exit_err;
+
+	err = -ENOMEM;
+	entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+	if (!entry)
+		goto exit_err;
+	memset(&entry->rule, 0, sizeof(struct audit_krule));
+
+	entry->rule.flags = rule->flags & AUDIT_FILTER_PREPEND;
+	entry->rule.listnr = listnr;
+	entry->rule.action = rule->action;
+	entry->rule.field_count = rule->field_count;
+
+	for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
+		entry->rule.mask[i] = rule->mask[i];
+
+	return entry;
+
+exit_err:
+	return ERR_PTR(err);
+}
+
+/* Translate struct audit_rule to kernel's rule respresentation.
+ * Exists for backward compatibility with userspace. */
+static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
+{
+	struct audit_entry *entry;
+	int err = 0;
 	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];
+	entry = audit_to_entry_common(rule);
+	if (IS_ERR(entry))
+		goto exit_nofree;
+
+	for (i = 0; i < rule->field_count; i++) {
+		struct audit_field *f = &entry->rule.fields[i];
+
+		if (rule->fields[i] & AUDIT_UNUSED_BITS) {
+			err = -EINVAL;
+			goto exit_free;
+		}
+
+		f->op = rule->fields[i] & (AUDIT_NEGATE|AUDIT_OPERATORS);
+		f->type = rule->fields[i] & (~AUDIT_NEGATE|AUDIT_OPERATORS);
+		f->val = rule->values[i];
+
+		entry->rule.vers_ops = (f->op & AUDIT_OPERATORS) ? 2 : 1;
+		if (f->op & AUDIT_NEGATE)
+			f->op |= AUDIT_NOT_EQUAL;
+		else if (!(f->op & AUDIT_OPERATORS))
+			f->op |= AUDIT_EQUAL;
+		f->op &= ~AUDIT_NEGATE;
 	}
-	for (i = 0; i < AUDIT_BITMASK_SIZE; i++) d->mask[i] = s->mask[i];
-	return 0;
+
+exit_nofree:
+	return entry;
+
+exit_free:
+	kfree(entry);
+	return ERR_PTR(err);
 }
 
-/* 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 int audit_compare_rule(struct audit_rule *a, struct audit_rule *b)
+/* Translate struct audit_rule_xprt to kernel's rule respresentation. */
+static struct audit_entry *audit_xprt_to_entry(struct audit_rule_xprt *xprt,
+					       int dlen)
 {
+	int err = 0;
+	struct audit_entry *entry;
+	void *bufp;
+	/* int remaining = dlen - sizeof(struct audit_rule_xprt); */
 	int i;
 
-	if (a->flags != b->flags)
-		return 1;
+	entry = audit_to_entry_common((struct audit_rule *)xprt);
+	if (IS_ERR(entry))
+		goto exit_nofree;
+
+	bufp = xprt->buf;
+	entry->rule.vers_ops = 2;
+	for (i = 0; i < xprt->field_count; i++) {
+		struct audit_field *f = &entry->rule.fields[i];
+
+		if (!(xprt->fieldflags[i] & AUDIT_OPERATORS) ||
+		    xprt->fieldflags[i] & ~AUDIT_OPERATORS) {
+			err = -EINVAL;
+			goto exit_free;
+		}
 
-	if (a->action != b->action)
-		return 1;
+		f->op = xprt->fieldflags[i] & AUDIT_OPERATORS;
+		f->type = xprt->fields[i];
+		switch(f->type) {
+		/* call type-specific conversion routines here */
+		default:
+			f->val = xprt->values[i];
+		}
+	}
+
+exit_nofree:
+	return entry;
+
+exit_free:
+	kfree(entry);
+	return ERR_PTR(err);
+}
+
+/* Pack a filter field's string representation into data block. */
+static inline int audit_pack_string(void **bufp, char *str)
+{
+	int len = strlen(str);
+
+	memcpy(*bufp, str, len);
+	*bufp += len;
+
+	return len;
+}
+
+/* Translate kernel rule respresentation to struct audit_rule.
+ * Exists for backward compatibility with userspace. */
+static struct audit_rule *audit_krule_to_rule(struct audit_krule *krule)
+{
+	struct audit_rule *rule;
+	int i;
+
+	rule = kmalloc(sizeof(*rule), GFP_KERNEL);
+	if (!rule)
+		return ERR_PTR(-ENOMEM);
+	memset(rule, 0, sizeof(*rule));
+
+	rule->flags = krule->flags | krule->listnr;
+	rule->action = krule->action;
+	rule->field_count = krule->field_count;
+	for (i = 0; i < rule->field_count; i++) {
+		rule->values[i] = krule->fields[i].val;
+		rule->fields[i] = krule->fields[i].type;
+
+		if (krule->vers_ops == 1) {
+			if (krule->fields[i].op & AUDIT_NOT_EQUAL)
+				rule->fields[i] |= AUDIT_NEGATE;
+		} else {
+			rule->fields[i] |= krule->fields[i].op;
+		}
+	}
+	for (i = 0; i < AUDIT_BITMASK_SIZE; i++) rule->mask[i] = krule->mask[i];
+
+	return rule;
+}
+
+/* Translate kernel rule respresentation to struct audit_rule_xprt. */
+static struct audit_rule_xprt *audit_krule_to_xprt(struct audit_krule *krule)
+{
+	struct audit_rule_xprt *xprt;
+	void *bufp;
+	int i;
+
+	xprt = kmalloc(sizeof(*xprt) + krule->buflen, GFP_KERNEL);
+	if (!xprt)
+		return ERR_PTR(-ENOMEM);
+	memset(xprt, 0, sizeof(*xprt));
+
+	xprt->flags = krule->flags | krule->listnr;
+	xprt->action = krule->action;
+	xprt->field_count = krule->field_count;
+	bufp = xprt->buf;
+	for (i = 0; i < xprt->field_count; i++) {
+		struct audit_field *f = &krule->fields[i];
+
+		xprt->fields[i] = f->type;
+		xprt->fieldflags[i] = f->op;
+		switch(f->type) {
+		/* call type-specific conversion routines here */
+		default:
+			xprt->values[i] = f->val;
+		}
+	}
+	for (i = 0; i < AUDIT_BITMASK_SIZE; i++) xprt->mask[i] = krule->mask[i];
+
+	return xprt;
+}
 
-	if (a->field_count != b->field_count)
+/* Compare two rules in kernel format.  Considered success if rules
+ * don't match. */
+static int audit_compare_rule(struct audit_krule *a, struct audit_krule *b)
+{
+	int i;
+
+	if (a->flags != b->flags ||
+	    a->listnr != b->listnr ||
+	    a->action != b->action ||
+	    a->field_count != b->field_count)
 		return 1;
 
 	for (i = 0; i < a->field_count; i++) {
-		if (a->fields[i] != b->fields[i]
-		    || a->values[i] != b->values[i])
+		if (a->fields[i].type != b->fields[i].type ||
+		    a->fields[i].op != b->fields[i].op)
 			return 1;
+
+		switch(a->fields[i].type) {
+		/* call type-specific comparison routines here */
+		default:
+			if (a->fields[i].val != b->fields[i].val)
+				return 1;
+		}
 	}
 
 	for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
@@ -95,41 +296,21 @@ static int audit_compare_rule(struct aud
 	return 0;
 }
 
-/* Note that audit_add_rule and audit_del_rule are called via
- * audit_receive() in audit.c, and are protected by
+/* Add rule to given filterlist if not a duplicate.  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(struct audit_entry *entry,
+				 struct list_head *list)
 {
-	struct audit_entry  *entry;
-	int i;
+	struct audit_entry *e;
 
 	/* 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))
+	list_for_each_entry(e, list, list) {
+		if (!audit_compare_rule(&entry->rule, &e->rule))
 			return -EEXIST;
 	}
 
-	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 (!(entry = kmalloc(sizeof(*entry), GFP_KERNEL)))
-		return -ENOMEM;
-	if (audit_copy_rule(&entry->rule, rule)) {
-		kfree(entry);
-		return -EINVAL;
-	}
-
 	if (entry->rule.flags & AUDIT_FILTER_PREPEND) {
-		entry->rule.flags &= ~AUDIT_FILTER_PREPEND;
 		list_add_rcu(&entry->list, list);
 	} else {
 		list_add_tail_rcu(&entry->list, list);
@@ -144,10 +325,9 @@ static inline void audit_free_rule(struc
 	kfree(e);
 }
 
-/* Note that audit_add_rule and audit_del_rule are called via
- * audit_receive() in audit.c, and are protected by
+/* Remove an existing rule from filterlist.  Protected by
  * audit_netlink_sem. */
-static inline int audit_del_rule(struct audit_rule *rule,
+static inline int audit_del_rule(struct audit_entry *entry,
 				 struct list_head *list)
 {
 	struct audit_entry  *e;
@@ -155,7 +335,7 @@ static inline int audit_del_rule(struct 
 	/* 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)) {
+		if (!audit_compare_rule(&entry->rule, &e->rule)) {
 			list_del_rcu(&e->list);
 			call_rcu(&e->rcu, audit_free_rule);
 			return 0;
@@ -164,7 +344,9 @@ static inline int audit_del_rule(struct 
 	return -ENOENT;		/* No matching rule */
 }
 
-static int audit_list_rules(void *_dest)
+/* List rules using struct audit_rule.  Exists for backward
+ * compatibility with userspace. */
+static int audit_list(void *_dest)
 {
 	int pid, seq;
 	int *dest = _dest;
@@ -180,9 +362,16 @@ static int audit_list_rules(void *_dest)
 	/* 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)
+		list_for_each_entry(entry, &audit_filter_list[i], list) {
+			struct audit_rule *rule;
+
+			rule = audit_krule_to_rule(&entry->rule);
+			if (!rule)
+				break;
 			audit_send_reply(pid, seq, AUDIT_LIST, 0, 1,
-					 &entry->rule, sizeof(entry->rule));
+					 rule, sizeof(*rule));
+			kfree(rule);
+		}
 	}
 	audit_send_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0);
 	
@@ -190,6 +379,40 @@ static int audit_list_rules(void *_dest)
 	return 0;
 }
 
+/* List rules using struct audit_rule_xprt. */
+static int audit_list_rules(void *_dest)
+{
+	int pid, seq;
+	int *dest = _dest;
+	struct audit_entry *e;
+	int i;
+
+	pid = dest[0];
+	seq = dest[1];
+	kfree(dest);
+
+	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(e, &audit_filter_list[i], list) {
+			struct audit_rule_xprt *xprt;
+
+			xprt = audit_krule_to_xprt(&e->rule);
+			if (!xprt)
+				break;
+			audit_send_reply(pid, seq, AUDIT_LIST_RULES, 0, 1,
+					 xprt, sizeof(*xprt));
+			kfree(xprt);
+		}
+	}
+	audit_send_reply(pid, seq, AUDIT_LIST_RULES, 1, 1, NULL, 0);
+
+	up(&audit_netlink_sem);
+	return 0;
+}
+
 /**
  * audit_receive_filter - apply all rules to the specified message type
  * @type: audit message type
@@ -197,18 +420,20 @@ static int audit_list_rules(void *_dest)
  * @uid: target uid for netlink audit messages
  * @seq: netlink audit message sequence (serial) number
  * @data: payload data
+ * @dlen: length of payload data
  * @loginuid: loginuid of sender
  */
 int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
-							uid_t loginuid)
+			 int dlen, uid_t loginuid)
 {
 	struct task_struct *tsk;
 	int *dest;
-	int		   err = 0;
-	unsigned listnr;
+	int err = 0;
+	struct audit_entry *entry;
 
 	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
@@ -221,41 +446,48 @@ int audit_receive_filter(int type, int p
 		dest[0] = pid;
 		dest[1] = seq;
 
-		tsk = kthread_run(audit_list_rules, dest, "audit_list_rules");
+		if (type == AUDIT_LIST)
+			tsk = kthread_run(audit_list, dest, "audit_list");
+		else
+			tsk = kthread_run(audit_list_rules, dest,
+					  "audit_list_rules");
 		if (IS_ERR(tsk)) {
 			kfree(dest);
 			err = PTR_ERR(tsk);
 		}
 		break;
 	case AUDIT_ADD:
-		listnr = ((struct audit_rule *)data)->flags & ~AUDIT_FILTER_PREPEND;
-		switch(listnr) {
-		default:
-			return -EINVAL;
+	case AUDIT_ADD_RULE:
+		if (type == AUDIT_ADD)
+			entry = audit_rule_to_entry(data);
+		else
+			entry = audit_xprt_to_entry(data, dlen);
+		if (IS_ERR(entry))
+			return PTR_ERR(entry);
 
-		case AUDIT_FILTER_USER:
-		case AUDIT_FILTER_TYPE:
-#ifdef CONFIG_AUDITSYSCALL
-		case AUDIT_FILTER_ENTRY:
-		case AUDIT_FILTER_EXIT:
-		case AUDIT_FILTER_TASK:
-#endif
-			;
-		}
-		err = audit_add_rule(data, &audit_filter_list[listnr]);
+		err = audit_add_rule(entry,
+				     &audit_filter_list[entry->rule.listnr]);
 		if (!err)
 			audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
 				  "auid=%u added an audit rule\n", loginuid);
+		else
+			kfree(entry);
 		break;
 	case AUDIT_DEL:
-		listnr =((struct audit_rule *)data)->flags & ~AUDIT_FILTER_PREPEND;
-		if (listnr >= AUDIT_NR_FILTERS)
-			return -EINVAL;
+	case AUDIT_DEL_RULE:
+		if (type == AUDIT_DEL)
+			entry = audit_rule_to_entry(data);
+		else
+			entry = audit_xprt_to_entry(data, dlen);
+		if (IS_ERR(entry))
+			return PTR_ERR(entry);
 
-		err = audit_del_rule(data, &audit_filter_list[listnr]);
+		err = audit_del_rule(entry,
+				     &audit_filter_list[entry->rule.listnr]);
 		if (!err)
 			audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
 				  "auid=%u removed an audit rule\n", loginuid);
+		kfree(entry);
 		break;
 	default:
 		return -EINVAL;
@@ -287,29 +519,27 @@ int audit_comparator(const u32 left, con
 
 
 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;
 
 	for (i = 0; i < rule->field_count; i++) {
-		u32 field  = rule->fields[i] & ~AUDIT_OPERATORS;
-		u32 op  = rule->fields[i] & AUDIT_OPERATORS;
-		u32 value  = rule->values[i];
+		struct audit_field *f = &rule->fields[i];
 		int result = 0;
 
-		switch (field) {
+		switch (f->type) {
 		case AUDIT_PID:
-			result = audit_comparator(cb->creds.pid, op, value);
+			result = audit_comparator(cb->creds.pid, f->op, f->val);
 			break;
 		case AUDIT_UID:
-			result = audit_comparator(cb->creds.uid, op, value);
+			result = audit_comparator(cb->creds.uid, f->op, f->val);
 			break;
 		case AUDIT_GID:
-			result = audit_comparator(cb->creds.gid, op, value);
+			result = audit_comparator(cb->creds.gid, f->op, f->val);
 			break;
 		case AUDIT_LOGINUID:
-			result = audit_comparator(cb->loginuid, op, value);
+			result = audit_comparator(cb->loginuid, f->op, f->val);
 			break;
 		}
 
@@ -354,14 +584,11 @@ int audit_filter_type(int type)
 
 	list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TYPE],
 				list) {
-		struct audit_rule *rule = &e->rule;
 		int i;
-		for (i = 0; i < rule->field_count; i++) {
-			u32 field  = rule->fields[i] & ~AUDIT_OPERATORS;
-			u32 op  = rule->fields[i] & AUDIT_OPERATORS;
-			u32 value  = rule->values[i];
-			if ( field == AUDIT_MSGTYPE ) {
-				result = audit_comparator(type, op, value); 
+		for (i = 0; i < e->rule.field_count; i++) {
+			struct audit_field *f = &e->rule.fields[i];
+			if (f->type == AUDIT_MSGTYPE) {
+				result = audit_comparator(type, f->op, f->val);
 				if (!result)
 					break;
 			}
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 8f0a61c..e4f7096 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -161,70 +161,68 @@ struct audit_context {
 /* 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)
 {
 	int i, j;
 
 	for (i = 0; i < rule->field_count; i++) {
-		u32 field  = rule->fields[i] & ~AUDIT_OPERATORS;
-		u32 op  = rule->fields[i] & AUDIT_OPERATORS;
-		u32 value  = rule->values[i];
+		struct audit_field *f = &rule->fields[i];
 		int result = 0;
 
-		switch (field) {
+		switch (f->type) {
 		case AUDIT_PID:
-			result = audit_comparator(tsk->pid, op, value);
+			result = audit_comparator(tsk->pid, f->op, f->val);
 			break;
 		case AUDIT_UID:
-			result = audit_comparator(tsk->uid, op, value);
+			result = audit_comparator(tsk->uid, f->op, f->val);
 			break;
 		case AUDIT_EUID:
-			result = audit_comparator(tsk->euid, op, value);
+			result = audit_comparator(tsk->euid, f->op, f->val);
 			break;
 		case AUDIT_SUID:
-			result = audit_comparator(tsk->suid, op, value);
+			result = audit_comparator(tsk->suid, f->op, f->val);
 			break;
 		case AUDIT_FSUID:
-			result = audit_comparator(tsk->fsuid, op, value);
+			result = audit_comparator(tsk->fsuid, f->op, f->val);
 			break;
 		case AUDIT_GID:
-			result = audit_comparator(tsk->gid, op, value);
+			result = audit_comparator(tsk->gid, f->op, f->val);
 			break;
 		case AUDIT_EGID:
-			result = audit_comparator(tsk->egid, op, value);
+			result = audit_comparator(tsk->egid, f->op, f->val);
 			break;
 		case AUDIT_SGID:
-			result = audit_comparator(tsk->sgid, op, value);
+			result = audit_comparator(tsk->sgid, f->op, f->val);
 			break;
 		case AUDIT_FSGID:
-			result = audit_comparator(tsk->fsgid, op, value);
+			result = audit_comparator(tsk->fsgid, f->op, f->val);
 			break;
 		case AUDIT_PERS:
-			result = audit_comparator(tsk->personality, op, value);
+			result = audit_comparator(tsk->personality, f->op, f->val);
 			break;
 		case AUDIT_ARCH:
  			if (ctx)
-				result = audit_comparator(ctx->arch, op, value);
+				result = audit_comparator(ctx->arch, f->op, f->val);
 			break;
 
 		case AUDIT_EXIT:
 			if (ctx && ctx->return_valid)
-				result = audit_comparator(ctx->return_code, op, value);
+				result = audit_comparator(ctx->return_code, f->op, f->val);
 			break;
 		case AUDIT_SUCCESS:
 			if (ctx && ctx->return_valid) {
-				if (value)
-					result = audit_comparator(ctx->return_valid, op, AUDITSC_SUCCESS);
+				if (f->val)
+					result = audit_comparator(ctx->return_valid, f->op, AUDITSC_SUCCESS);
 				else
-					result = audit_comparator(ctx->return_valid, op, AUDITSC_FAILURE);
+					result = audit_comparator(ctx->return_valid, f->op, AUDITSC_FAILURE);
 			}
 			break;
 		case AUDIT_DEVMAJOR:
 			if (ctx) {
 				for (j = 0; j < ctx->name_count; j++) {
-					if (audit_comparator(MAJOR(ctx->names[j].dev),	op, value)) {
+					if (audit_comparator(MAJOR(ctx->names[j].dev),	f->op, f->val)) {
 						++result;
 						break;
 					}
@@ -234,7 +232,7 @@ static int audit_filter_rules(struct tas
 		case AUDIT_DEVMINOR:
 			if (ctx) {
 				for (j = 0; j < ctx->name_count; j++) {
-					if (audit_comparator(MINOR(ctx->names[j].dev), op, value)) {
+					if (audit_comparator(MINOR(ctx->names[j].dev), f->op, f->val)) {
 						++result;
 						break;
 					}
@@ -244,8 +242,8 @@ static int audit_filter_rules(struct tas
 		case AUDIT_INODE:
 			if (ctx) {
 				for (j = 0; j < ctx->name_count; j++) {
-					if (audit_comparator(ctx->names[j].ino, op, value) ||
-					    audit_comparator(ctx->names[j].pino, op, value)) {
+					if (audit_comparator(ctx->names[j].ino, f->op, f->val) ||
+					    audit_comparator(ctx->names[j].pino, f->op, f->val)) {
 						++result;
 						break;
 					}
@@ -255,14 +253,14 @@ static int audit_filter_rules(struct tas
 		case AUDIT_LOGINUID:
 			result = 0;
 			if (ctx)
-				result = audit_comparator(ctx->loginuid, op, value);
+				result = audit_comparator(ctx->loginuid, f->op, f->val);
 			break;
 		case AUDIT_ARG0:
 		case AUDIT_ARG1:
 		case AUDIT_ARG2:
 		case AUDIT_ARG3:
 			if (ctx)
-				result = audit_comparator(ctx->argv[field-AUDIT_ARG0], op, value);
+				result = audit_comparator(ctx->argv[f->type-AUDIT_ARG0], f->op, f->val);
 			break;
 		}
 




More information about the Linux-audit mailing list