Commit 0f45aa18 authored by David Woodhouse's avatar David Woodhouse
Browse files

AUDIT: Allow filtering of user messages



Turn the field from a bitmask to an enumeration and add a list to allow 
filtering of messages generated by userspace. We also define a list for 
file system watches in anticipation of that feature.

Signed-off-by: default avatarDavid Woodhouse <dwmw2@infradead.org>
parent 0107b3cf
Loading
Loading
Loading
Loading
+11 −4
Original line number Diff line number Diff line
@@ -75,10 +75,15 @@
#define AUDIT_KERNEL		2000	/* Asynchronous audit record. NOT A REQUEST. */

/* Rule flags */
#define AUDIT_PER_TASK 0x01	/* Apply rule at task creation (not syscall) */
#define AUDIT_AT_ENTRY 0x02	/* Apply rule at syscall entry */
#define AUDIT_AT_EXIT  0x04	/* Apply rule at syscall exit */
#define AUDIT_PREPEND  0x10	/* Prepend to front of list */
#define AUDIT_FILTER_USER	0x00	/* Apply rule to user-generated messages */
#define AUDIT_FILTER_TASK	0x01	/* Apply rule at task creation (not syscall) */
#define AUDIT_FILTER_ENTRY	0x02	/* Apply rule at syscall entry */
#define AUDIT_FILTER_WATCH	0x03	/* Apply rule to file system watches */
#define AUDIT_FILTER_EXIT	0x04	/* Apply rule at syscall exit */

#define AUDIT_NR_FILTERS	5

#define AUDIT_FILTER_PREPEND	0x10	/* Prepend to front of list */

/* Rule actions */
#define AUDIT_NEVER    0	/* Do not build context if rule matches */
@@ -230,6 +235,7 @@ extern int audit_socketcall(int nargs, unsigned long *args);
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 task_struct *tsk, int type);
#else
#define audit_alloc(t) ({ 0; })
#define audit_free(t) do { ; } while (0)
@@ -246,6 +252,7 @@ extern void audit_signal_info(int sig, struct task_struct *t);
#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(struct ({ 1; })
#endif

#ifdef CONFIG_AUDIT
+20 −16
Original line number Diff line number Diff line
@@ -107,13 +107,6 @@ static struct sk_buff_head audit_skb_queue;
static struct task_struct *kauditd_task;
static DECLARE_WAIT_QUEUE_HEAD(kauditd_wait);

/* 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 LIST_HEAD(audit_tsklist);
static LIST_HEAD(audit_entlist);
static LIST_HEAD(audit_extlist);

/* The netlink socket is only to be read by 1 CPU, which lets us assume
 * that list additions and deletions never happen simultaneously in
 * auditsc.c */
@@ -376,6 +369,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
	u16			msg_type = nlh->nlmsg_type;
	uid_t			loginuid; /* loginuid of sender */
	struct audit_sig_info   sig_data;
	struct task_struct *tsk;

	err = audit_netlink_ok(NETLINK_CB(skb).eff_cap, msg_type);
	if (err)
@@ -435,15 +429,25 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
		break;
	case AUDIT_USER:
	case AUDIT_FIRST_USER_MSG...AUDIT_LAST_USER_MSG:
		read_lock(&tasklist_lock);
		tsk = find_task_by_pid(pid);
		if (tsk)
			get_task_struct(tsk);
		read_unlock(&tasklist_lock);
		if (!tsk)
			return -ESRCH;

		if (audit_filter_user(tsk, msg_type)) {
			    ab = audit_log_start(NULL, msg_type);
		if (!ab)
			break;	/* audit_panic has been called */
			    if (ab) {
				    audit_log_format(ab,
				 "user pid=%d uid=%u auid=%u"
				 " msg='%.1024s'",
						     "user pid=%d uid=%u auid=%u msg='%.1024s'",
						     pid, uid, loginuid, (char *)data);
				    audit_set_pid(ab, pid);
				    audit_log_end(ab);
			    }
		}
		put_task_struct(tsk);
		break;
	case AUDIT_ADD:
	case AUDIT_DEL:
+54 −38
Original line number Diff line number Diff line
@@ -167,9 +167,16 @@ struct audit_context {
/* 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 LIST_HEAD(audit_tsklist);
static LIST_HEAD(audit_entlist);
static LIST_HEAD(audit_extlist);
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]),
#if AUDIT_NR_FILTERS != 5
#error Fix audit_filter_list initialiser
#endif
};

struct audit_entry {
	struct list_head  list;
@@ -210,16 +217,15 @@ static int audit_compare_rule(struct audit_rule *a, struct audit_rule *b)
/* 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_entry *entry,
static inline void audit_add_rule(struct audit_entry *entry,
				  struct list_head *list)
{
	if (entry->rule.flags & AUDIT_PREPEND) {
		entry->rule.flags &= ~AUDIT_PREPEND;
	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 void audit_free_rule(struct rcu_head *head)
@@ -245,7 +251,7 @@ static inline int audit_del_rule(struct audit_rule *rule,
			return 0;
		}
	}
	return -EFAULT;		/* No matching rule */
	return -ENOENT;		/* No matching rule */
}

/* Copy rule from user-space to kernel-space.  Called during
@@ -260,6 +266,8 @@ static int audit_copy_rule(struct audit_rule *d, struct audit_rule *s)
		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;
@@ -275,23 +283,20 @@ static int audit_copy_rule(struct audit_rule *d, struct audit_rule *s)
int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
							uid_t loginuid)
{
	u32		   flags;
	struct audit_entry *entry;
	int		   err = 0;
	int i;
	unsigned listnr;

	switch (type) {
	case AUDIT_LIST:
		/* The *_rcu iterators not needed here because we are
		   always called with audit_netlink_sem held. */
		list_for_each_entry(entry, &audit_tsklist, list)
			audit_send_reply(pid, seq, AUDIT_LIST, 0, 1,
					 &entry->rule, sizeof(entry->rule));
		list_for_each_entry(entry, &audit_entlist, list)
			audit_send_reply(pid, seq, AUDIT_LIST, 0, 1,
					 &entry->rule, sizeof(entry->rule));
		list_for_each_entry(entry, &audit_extlist, list)
		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);
		break;
	case AUDIT_ADD:
@@ -301,24 +306,18 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
			kfree(entry);
			return -EINVAL;
		}
		flags = entry->rule.flags;
		if (!err && (flags & AUDIT_PER_TASK))
			err = audit_add_rule(entry, &audit_tsklist);
		if (!err && (flags & AUDIT_AT_ENTRY))
			err = audit_add_rule(entry, &audit_entlist);
		if (!err && (flags & AUDIT_AT_EXIT))
			err = audit_add_rule(entry, &audit_extlist);
		listnr = entry->rule.flags & ~AUDIT_FILTER_PREPEND;
		audit_add_rule(entry, &audit_filter_list[listnr]);
		audit_log(NULL, AUDIT_CONFIG_CHANGE, 
				"auid=%u added an audit rule\n", loginuid);
		break;
	case AUDIT_DEL:
		flags =((struct audit_rule *)data)->flags;
		if (!err && (flags & AUDIT_PER_TASK))
			err = audit_del_rule(data, &audit_tsklist);
		if (!err && (flags & AUDIT_AT_ENTRY))
			err = audit_del_rule(data, &audit_entlist);
		if (!err && (flags & AUDIT_AT_EXIT))
			err = audit_del_rule(data, &audit_extlist);
		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, AUDIT_CONFIG_CHANGE,
				  "auid=%u removed an audit rule\n", loginuid);
		break;
@@ -454,7 +453,7 @@ static enum audit_state audit_filter_task(struct task_struct *tsk)
	enum audit_state   state;

	rcu_read_lock();
	list_for_each_entry_rcu(e, &audit_tsklist, list) {
	list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TASK], list) {
		if (audit_filter_rules(tsk, &e->rule, NULL, &state)) {
			rcu_read_unlock();
			return state;
@@ -490,6 +489,23 @@ static enum audit_state audit_filter_syscall(struct task_struct *tsk,
	return AUDIT_BUILD_CONTEXT;
}

int audit_filter_user(struct task_struct *tsk, int type)
{
	struct audit_entry *e;
	enum audit_state   state;

	rcu_read_lock();
	list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_USER], list) {
		if (audit_filter_rules(tsk, &e->rule, NULL, &state)) {
			rcu_read_unlock();
			return state != AUDIT_DISABLED;
		}
	}
	rcu_read_unlock();
	return 1; /* Audit by default */

}

/* This should be called with task_lock() held. */
static inline struct audit_context *audit_get_context(struct task_struct *tsk,
						      int return_valid,
@@ -504,7 +520,7 @@ static inline struct audit_context *audit_get_context(struct task_struct *tsk,

	if (context->in_syscall && !context->auditable) {
		enum audit_state state;
		state = audit_filter_syscall(tsk, context, &audit_extlist);
		state = audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_EXIT]);
		if (state == AUDIT_RECORD_CONTEXT)
			context->auditable = 1;
	}
@@ -876,7 +892,7 @@ void audit_syscall_entry(struct task_struct *tsk, int arch, int major,

	state = context->state;
	if (state == AUDIT_SETUP_CONTEXT || state == AUDIT_BUILD_CONTEXT)
		state = audit_filter_syscall(tsk, context, &audit_entlist);
		state = audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_ENTRY]);
	if (likely(state == AUDIT_DISABLED))
		return;