[PATCH] Split audit filtering into auditfilter.c

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


David,

See comment below.

On Thu, Dec 15, 2005 at 06:47:52PM +0000, David Woodhouse wrote:
> I've committed this to my tree locally and checked that it builds with
> all three possible combinations of CONFIG_AUDIT, CONFIG_AUDITSYSCALL.
> 
> My DSL line is down at the moment so I'll push it to the git tree later
> or tomorrow. This mail will be delayed too, but it'll probably get out
> as soon as the line comes back, and long before I notice and respond by
> pushing to the git tree.
> 
>  [AUDIT] Fix audit record filtering with !CONFIG_AUDITSYSCALL
> 
>     This fixes the per-user and per-message-type filtering when syscall
>     auditing isn't enabled.
> 
>     Signed-off-by: David Woodhouse <dwmw2 at infradead.org>
> 
> diff --git a/include/linux/audit.h b/include/linux/audit.h
> index 36e5090..8817141 100644
> --- a/include/linux/audit.h
> +++ b/include/linux/audit.h
> @@ -278,8 +278,6 @@ static inline void audit_inode_child(con
>  }
>  
>  				/* Private API (for audit.c only) */
> -extern int  audit_receive_filter(int type, int pid, int uid, int seq,
> -				 void *data, uid_t loginuid);
>  extern unsigned int audit_serial(void);
>  extern void auditsc_get_stamp(struct audit_context *ctx,
>  			      struct timespec *t, unsigned int *serial);
> @@ -290,8 +288,6 @@ extern int audit_socketcall(int nargs, u
>  extern int audit_sockaddr(int len, void *addr);
>  extern int audit_avc_path(struct dentry *dentry, struct vfsmount *mnt);
>  extern void audit_signal_info(int sig, struct task_struct *t);
> -extern int audit_filter_user(struct netlink_skb_parms *cb, int type);
> -extern int audit_filter_exclude(int type);
>  extern char *audit_ipc_context(struct kern_ipc_perm *ipcp);
>  extern int audit_set_macxattr(const char *name);
>  #else
> @@ -305,7 +301,6 @@ extern int audit_set_macxattr(const char
>  #define __audit_inode_child(d,i,p) do { ; } while (0)
>  #define audit_inode(n,i,f) do { ; } while (0)
>  #define audit_inode_child(d,i,p) do { ; } while (0)
> -#define audit_receive_filter(t,p,u,s,d,l) ({ -EOPNOTSUPP; })
>  #define auditsc_get_stamp(c,t,s) do { BUG(); } while (0)
>  #define audit_get_loginuid(c) ({ -1; })
>  #define audit_ipc_perms(q,u,g,m,i) ({ 0; })
> @@ -313,7 +308,6 @@ extern int audit_set_macxattr(const char
>  #define audit_sockaddr(len, addr) ({ 0; })
>  #define audit_avc_path(dentry, mnt) ({ 0; })
>  #define audit_signal_info(s,t) do { ; } while (0)
> -#define audit_filter_user(cb,t) ({ 1; })
>  #define audit_ipc_context(i) do { ; } while (0)
>  #define audit_set_macxattr(n) do { ; } while (0)
>  #endif
> @@ -339,13 +333,11 @@ extern void		    audit_log_d_path(struct
>  					     const char *prefix,
>  					     struct dentry *dentry,
>  					     struct vfsmount *vfsmnt);
> -				/* Private API (for auditsc.c only) */
> -extern void		    audit_send_reply(int pid, int seq, int type,
> -					     int done, int multi,
> -					     void *payload, int size);
> -extern void		    audit_log_lost(const char *message);
> -extern void		    audit_panic(const char *message);
> -extern struct semaphore audit_netlink_sem;
> +				/* Private API (for audit.c only) */
> +extern int audit_filter_user(struct netlink_skb_parms *cb, int type);
> +extern int audit_filter_exclude(int type);
> +extern int  audit_receive_filter(int type, int pid, int uid, int seq,
> +				 void *data, 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/Makefile b/kernel/Makefile
> index 4f5a145..56f6189 100644
> --- a/kernel/Makefile
> +++ b/kernel/Makefile
> @@ -23,7 +23,7 @@ obj-$(CONFIG_COMPAT) += compat.o
>  obj-$(CONFIG_CPUSETS) += cpuset.o
>  obj-$(CONFIG_IKCONFIG) += configs.o
>  obj-$(CONFIG_STOP_MACHINE) += stop_machine.o
> -obj-$(CONFIG_AUDIT) += audit.o
> +obj-$(CONFIG_AUDIT) += audit.o auditfilter.o
>  obj-$(CONFIG_AUDITSYSCALL) += auditsc.o
>  obj-$(CONFIG_KPROBES) += kprobes.o
>  obj-$(CONFIG_SYSFS) += ksysfs.o
> diff --git a/kernel/audit.c b/kernel/audit.c
> index 9c13bf6..5941a07 100644
> --- a/kernel/audit.c
> +++ b/kernel/audit.c
> @@ -305,6 +305,7 @@ int kauditd_thread(void *dummy)
>  			remove_wait_queue(&kauditd_wait, &wait);
>  		}
>  	}
> +	return 0;
>  }
>  
>  /**

It looks like most of the changes you made to audit.c aren't included
here.

> diff --git a/kernel/auditsc.c b/kernel/auditsc.c
> index 3e46e1c..8f0a61c 100644
> --- a/kernel/auditsc.c
> +++ b/kernel/auditsc.c
> @@ -51,17 +51,15 @@
>  #include <linux/audit.h>
>  #include <linux/personality.h>
>  #include <linux/time.h>
> -#include <linux/kthread.h>
>  #include <linux/netlink.h>
>  #include <linux/compiler.h>
>  #include <asm/unistd.h>
>  #include <linux/security.h>
> +#include <linux/list.h>
>  
> -/* 0 = no checking
> -   1 = put_count checking
> -   2 = verbose put_count checking
> -*/
> -#define AUDIT_DEBUG 0
> +#include "audit.h"
> +
> +extern struct list_head audit_filter_list[];
>  
>  /* No syscall auditing will take place unless audit_enabled != 0. */
>  extern int audit_enabled;
> @@ -75,29 +73,6 @@ extern int audit_enabled;
>   * path_lookup. */
>  #define AUDIT_NAMES_RESERVED 7
>  
> -/* At task start time, the audit_state is set in the audit_context using
> -   a per-task filter.  At syscall entry, the audit_state is augmented by
> -   the syscall filter. */
> -enum audit_state {
> -	AUDIT_DISABLED,		/* Do not create per-task audit_context.
> -				 * No syscall-specific audit records can
> -				 * be generated. */
> -	AUDIT_SETUP_CONTEXT,	/* Create the per-task audit_context,
> -				 * but don't necessarily fill it in at
> -				 * syscall entry time (i.e., filter
> -				 * instead). */
> -	AUDIT_BUILD_CONTEXT,	/* Create the per-task audit_context,
> -				 * and always fill it in at syscall
> -				 * entry time.  This makes a full
> -				 * syscall record available if some
> -				 * other part of the kernel decides it
> -				 * should be recorded. */
> -	AUDIT_RECORD_CONTEXT	/* Create the per-task audit_context,
> -				 * always fill it in at syscall entry
> -				 * time, and always write out the audit
> -				 * record at syscall exit time.  */
> -};
> -
>  /* When fs/namei.c:getname() is called, we store the pointer in name and
>   * we don't let putname() free it (instead we free all of the saved
>   * pointers at syscall exit time).
> @@ -182,264 +157,6 @@ struct audit_context {
>  #endif
>  };
>  
> -				/* Public API */
> -/* 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. */
> -static struct list_head audit_filter_list[AUDIT_NR_FILTERS] = {
> -	LIST_HEAD_INIT(audit_filter_list[0]),
> -	LIST_HEAD_INIT(audit_filter_list[1]),
> -	LIST_HEAD_INIT(audit_filter_list[2]),
> -	LIST_HEAD_INIT(audit_filter_list[3]),
> -	LIST_HEAD_INIT(audit_filter_list[4]),
> -	LIST_HEAD_INIT(audit_filter_list[5]),
> -#if AUDIT_NR_FILTERS != 6
> -#error Fix audit_filter_list initialiser
> -#endif
> -};
> -
> -struct audit_entry {
> -	struct list_head  list;
> -	struct rcu_head   rcu;
> -	struct audit_rule rule;
> -};
> -
> -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)
> -{
> -	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];
> -	}
> -	for (i = 0; i < AUDIT_BITMASK_SIZE; i++) d->mask[i] = s->mask[i];
> -	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)
> -{
> -	int i;
> -
> -	if (a->flags != b->flags)
> -		return 1;
> -
> -	if (a->action != b->action)
> -		return 1;
> -
> -	if (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])
> -			return 1;
> -	}
> -
> -	for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
> -		if (a->mask[i] != b->mask[i])
> -			return 1;
> -
> -	return 0;
> -}
> -
> -/* 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)
> -{
> -	struct audit_entry  *entry;
> -	int i;
> -
> -	/* 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;
> -		}
> -	}
> -
> -	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);
> -	}
> -
> -	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);
> -}
> -
> -/* 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)
> -{
> -	struct audit_entry  *e;
> -
> -	/* 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)) {
> -			list_del_rcu(&e->list);
> -			call_rcu(&e->rcu, audit_free_rule);
> -			return 0;
> -		}
> -	}
> -	return -ENOENT;		/* No matching rule */
> -}
> -
> -static int audit_list_rules(void *_dest)
> -{
> -	int pid, seq;
> -	int *dest = _dest;
> -	struct audit_entry *entry;
> -	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(entry, &audit_filter_list[i], list)
> -			audit_send_reply(pid, seq, AUDIT_LIST, 0, 1,
> -					 &entry->rule, sizeof(entry->rule));
> -	}
> -	audit_send_reply(pid, seq, AUDIT_LIST, 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
> - * @pid: target pid for netlink audit messages
> - * @uid: target uid for netlink audit messages
> - * @seq: netlink audit message sequence (serial) number
> - * @data: payload data
> - * @loginuid: loginuid of sender
> - */
> -int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
> -							uid_t loginuid)
> -{
> -	struct task_struct *tsk;
> -	int *dest;
> -	int		   err = 0;
> -	unsigned listnr;
> -
> -	switch (type) {
> -	case AUDIT_LIST:
> -		/* 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)
> -			return -ENOMEM;
> -		dest[0] = pid;
> -		dest[1] = seq;
> -
> -		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;
> -		if (listnr >= AUDIT_NR_FILTERS)
> -			return -EINVAL;
> -
> -		err = audit_add_rule(data, &audit_filter_list[listnr]);
> -		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]);
> -		if (!err)
> -			audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
> -				  "auid=%u removed an audit rule\n", loginuid);
> -		break;
> -	default:
> -		return -EINVAL;
> -	}
> -
> -	return err;
> -}
> -
> -static int audit_comparator(const u32 left, const u32 op, const u32 right)
> -{
> -	switch (op) {
> -	case AUDIT_EQUAL:
> -		return (left == right);
> -	case AUDIT_NOT_EQUAL:
> -		return (left != right);
> -	case AUDIT_LESS_THAN:
> -		return (left < right);
> -	case AUDIT_LESS_THAN_OR_EQUAL:
> -		return (left <= right);
> -	case AUDIT_GREATER_THAN:
> -		return (left > right);
> -	case AUDIT_GREATER_THAN_OR_EQUAL:
> -		return (left >= right);
> -	default:
> -		return -EINVAL;
> -	}
> -}
>  
>  /* Compare a task_struct with an audit_rule.  Return 1 on match, 0
>   * otherwise. */
> @@ -612,95 +329,6 @@ static enum audit_state audit_filter_sys
>  	return AUDIT_BUILD_CONTEXT;
>  }
>  
> -static int audit_filter_user_rules(struct netlink_skb_parms *cb,
> -				   struct audit_rule *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];
> -		int result = 0;
> -
> -		switch (field) {
> -		case AUDIT_PID:
> -			result = audit_comparator(cb->creds.pid, op, value);
> -			break;
> -		case AUDIT_UID:
> -			result = audit_comparator(cb->creds.uid, op, value);
> -			break;
> -		case AUDIT_GID:
> -			result = audit_comparator(cb->creds.gid, op, value);
> -			break;
> -		case AUDIT_LOGINUID:
> -			result = audit_comparator(cb->loginuid, op, value);
> -			break;
> -		}
> -
> -		if (!result)
> -			return 0;
> -	}
> -	switch (rule->action) {
> -	case AUDIT_NEVER:    *state = AUDIT_DISABLED;	    break;
> -	case AUDIT_POSSIBLE: *state = AUDIT_BUILD_CONTEXT;  break;
> -	case AUDIT_ALWAYS:   *state = AUDIT_RECORD_CONTEXT; break;
> -	}
> -	return 1;
> -}
> -
> -int audit_filter_user(struct netlink_skb_parms *cb, int type)
> -{
> -	struct audit_entry *e;
> -	enum audit_state   state;
> -	int ret = 1;
> -
> -	rcu_read_lock();
> -	list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_USER], list) {
> -		if (audit_filter_user_rules(cb, &e->rule, &state)) {
> -			if (state == AUDIT_DISABLED)
> -				ret = 0;
> -			break;
> -		}
> -	}
> -	rcu_read_unlock();
> -
> -	return ret; /* Audit by default */
> -}
> -
> -int audit_filter_exclude(int type)
> -{
> -	struct audit_entry *e;
> -	int result = 0;
> -	
> -	rcu_read_lock();
> -	if (list_empty(&audit_filter_list[AUDIT_FILTER_EXCLUDE]))
> -		goto unlock_and_return;
> -
> -	list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_EXCLUDE],
> -				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); 
> -				if (!result)
> -					break;
> -			}
> -		}
> -		if (result)
> -			goto unlock_and_return;
> -	}
> -unlock_and_return:
> -	rcu_read_unlock();
> -	return result;
> -}
> -
> -
>  /* This should be called with task_lock() held. */
>  static inline struct audit_context *audit_get_context(struct task_struct *tsk,
>  						      int return_valid,
> 
> 
> -- 
> dwmw2
> 
> --
> Linux-audit mailing list
> Linux-audit at redhat.com
> https://www.redhat.com/mailman/listinfo/linux-audit
> 




More information about the Linux-audit mailing list