Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 650a10dc authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* 'audit.b63' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/audit-current:
  Fix rule eviction order for AUDIT_DIR
  Audit: clean up all op= output to include string quoting
  Audit: move audit_get_nd completely into audit_watch
  audit: seperate audit inode watches into a subfile
  Audit: clean up audit_receive_skb
  Audit: cleanup netlink mesg handling
  Audit: unify the printk of an skb when auditd not around
  Audit: dereferencing krule as if it were an audit_watch
  Audit: better estimation of execve record length
  Audit: fix audit watch use after free
parents 9937ac0c 916d7576
Loading
Loading
Loading
Loading
+3 −0
Original line number Original line Diff line number Diff line
@@ -599,6 +599,8 @@ extern void audit_log_untrustedstring(struct audit_buffer *ab,
extern void		    audit_log_d_path(struct audit_buffer *ab,
extern void		    audit_log_d_path(struct audit_buffer *ab,
					     const char *prefix,
					     const char *prefix,
					     struct path *path);
					     struct path *path);
extern void		    audit_log_key(struct audit_buffer *ab,
					  char *key);
extern void		    audit_log_lost(const char *message);
extern void		    audit_log_lost(const char *message);
extern int		    audit_update_lsm_rules(void);
extern int		    audit_update_lsm_rules(void);


@@ -621,6 +623,7 @@ extern int audit_enabled;
#define audit_log_n_untrustedstring(a,n,s) do { ; } while (0)
#define audit_log_n_untrustedstring(a,n,s) do { ; } while (0)
#define audit_log_untrustedstring(a,s) do { ; } while (0)
#define audit_log_untrustedstring(a,s) do { ; } while (0)
#define audit_log_d_path(b, p, d) do { ; } while (0)
#define audit_log_d_path(b, p, d) do { ; } while (0)
#define audit_log_key(b, k) do { ; } while (0)
#define audit_enabled 0
#define audit_enabled 0
#endif
#endif
#endif
#endif
+1 −1
Original line number Original line Diff line number Diff line
@@ -70,7 +70,7 @@ obj-$(CONFIG_RESOURCE_COUNTERS) += res_counter.o
obj-$(CONFIG_STOP_MACHINE) += stop_machine.o
obj-$(CONFIG_STOP_MACHINE) += stop_machine.o
obj-$(CONFIG_KPROBES_SANITY_TEST) += test_kprobes.o
obj-$(CONFIG_KPROBES_SANITY_TEST) += test_kprobes.o
obj-$(CONFIG_AUDIT) += audit.o auditfilter.o
obj-$(CONFIG_AUDIT) += audit.o auditfilter.o
obj-$(CONFIG_AUDITSYSCALL) += auditsc.o
obj-$(CONFIG_AUDITSYSCALL) += auditsc.o audit_watch.o
obj-$(CONFIG_GCOV_KERNEL) += gcov/
obj-$(CONFIG_GCOV_KERNEL) += gcov/
obj-$(CONFIG_AUDIT_TREE) += audit_tree.o
obj-$(CONFIG_AUDIT_TREE) += audit_tree.o
obj-$(CONFIG_KPROBES) += kprobes.o
obj-$(CONFIG_KPROBES) += kprobes.o
+65 −81
Original line number Original line Diff line number Diff line
@@ -115,9 +115,6 @@ static atomic_t audit_lost = ATOMIC_INIT(0);
/* The netlink socket. */
/* The netlink socket. */
static struct sock *audit_sock;
static struct sock *audit_sock;


/* Inotify handle. */
struct inotify_handle *audit_ih;

/* Hash for inode-based rules */
/* Hash for inode-based rules */
struct list_head audit_inode_hash[AUDIT_INODE_BUCKETS];
struct list_head audit_inode_hash[AUDIT_INODE_BUCKETS];


@@ -136,7 +133,7 @@ static DECLARE_WAIT_QUEUE_HEAD(kauditd_wait);
static DECLARE_WAIT_QUEUE_HEAD(audit_backlog_wait);
static DECLARE_WAIT_QUEUE_HEAD(audit_backlog_wait);


/* Serialize requests from userspace. */
/* Serialize requests from userspace. */
static DEFINE_MUTEX(audit_cmd_mutex);
DEFINE_MUTEX(audit_cmd_mutex);


/* AUDIT_BUFSIZ is the size of the temporary buffer used for formatting
/* AUDIT_BUFSIZ is the size of the temporary buffer used for formatting
 * audit records.  Since printk uses a 1024 byte buffer, this buffer
 * audit records.  Since printk uses a 1024 byte buffer, this buffer
@@ -375,6 +372,25 @@ static void audit_hold_skb(struct sk_buff *skb)
		kfree_skb(skb);
		kfree_skb(skb);
}
}


/*
 * For one reason or another this nlh isn't getting delivered to the userspace
 * audit daemon, just send it to printk.
 */
static void audit_printk_skb(struct sk_buff *skb)
{
	struct nlmsghdr *nlh = nlmsg_hdr(skb);
	char *data = NLMSG_DATA(nlh);

	if (nlh->nlmsg_type != AUDIT_EOE) {
		if (printk_ratelimit())
			printk(KERN_NOTICE "type=%d %s\n", nlh->nlmsg_type, data);
		else
			audit_log_lost("printk limit exceeded\n");
	}

	audit_hold_skb(skb);
}

static void kauditd_send_skb(struct sk_buff *skb)
static void kauditd_send_skb(struct sk_buff *skb)
{
{
	int err;
	int err;
@@ -427,14 +443,8 @@ static int kauditd_thread(void *dummy)
		if (skb) {
		if (skb) {
			if (audit_pid)
			if (audit_pid)
				kauditd_send_skb(skb);
				kauditd_send_skb(skb);
			else {
				if (printk_ratelimit())
					printk(KERN_NOTICE "%s\n", skb->data + NLMSG_SPACE(0));
			else
			else
					audit_log_lost("printk limit exceeded\n");
				audit_printk_skb(skb);

				audit_hold_skb(skb);
			}
		} else {
		} else {
			DECLARE_WAITQUEUE(wait, current);
			DECLARE_WAITQUEUE(wait, current);
			set_current_state(TASK_INTERRUPTIBLE);
			set_current_state(TASK_INTERRUPTIBLE);
@@ -495,42 +505,25 @@ int audit_send_list(void *_dest)
	return 0;
	return 0;
}
}


#ifdef CONFIG_AUDIT_TREE
static int prune_tree_thread(void *unused)
{
	mutex_lock(&audit_cmd_mutex);
	audit_prune_trees();
	mutex_unlock(&audit_cmd_mutex);
	return 0;
}

void audit_schedule_prune(void)
{
	kthread_run(prune_tree_thread, NULL, "audit_prune_tree");
}
#endif

struct sk_buff *audit_make_reply(int pid, int seq, int type, int done,
struct sk_buff *audit_make_reply(int pid, int seq, int type, int done,
				 int multi, void *payload, int size)
				 int multi, void *payload, int size)
{
{
	struct sk_buff	*skb;
	struct sk_buff	*skb;
	struct nlmsghdr	*nlh;
	struct nlmsghdr	*nlh;
	int		len = NLMSG_SPACE(size);
	void		*data;
	void		*data;
	int		flags = multi ? NLM_F_MULTI : 0;
	int		flags = multi ? NLM_F_MULTI : 0;
	int		t     = done  ? NLMSG_DONE  : type;
	int		t     = done  ? NLMSG_DONE  : type;


	skb = alloc_skb(len, GFP_KERNEL);
	skb = nlmsg_new(size, GFP_KERNEL);
	if (!skb)
	if (!skb)
		return NULL;
		return NULL;


	nlh		 = NLMSG_PUT(skb, pid, seq, t, size);
	nlh	= NLMSG_NEW(skb, pid, seq, t, size, flags);
	nlh->nlmsg_flags = flags;
	data	= NLMSG_DATA(nlh);
	data	= NLMSG_DATA(nlh);
	memcpy(data, payload, size);
	memcpy(data, payload, size);
	return skb;
	return skb;


nlmsg_failure:			/* Used by NLMSG_PUT */
nlmsg_failure:			/* Used by NLMSG_NEW */
	if (skb)
	if (skb)
		kfree_skb(skb);
		kfree_skb(skb);
	return NULL;
	return NULL;
@@ -926,28 +919,29 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
}
}


/*
/*
 * Get message from skb (based on rtnetlink_rcv_skb).  Each message is
 * Get message from skb.  Each message is processed by audit_receive_msg.
 * processed by audit_receive_msg.  Malformed skbs with wrong length are
 * Malformed skbs with wrong length are discarded silently.
 * discarded silently.
 */
 */
static void audit_receive_skb(struct sk_buff *skb)
static void audit_receive_skb(struct sk_buff *skb)
{
{
	int		err;
	struct nlmsghdr *nlh;
	struct nlmsghdr *nlh;
	u32		rlen;
	/*
	 * len MUST be signed for NLMSG_NEXT to be able to dec it below 0
	 * if the nlmsg_len was not aligned
	 */
	int len;
	int err;


	while (skb->len >= NLMSG_SPACE(0)) {
	nlh = nlmsg_hdr(skb);
	nlh = nlmsg_hdr(skb);
		if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len)
	len = skb->len;
			return;

		rlen = NLMSG_ALIGN(nlh->nlmsg_len);
	while (NLMSG_OK(nlh, len)) {
		if (rlen > skb->len)
		err = audit_receive_msg(skb, nlh);
			rlen = skb->len;
		/* if err or if this message says it wants a response */
		if ((err = audit_receive_msg(skb, nlh))) {
		if (err || (nlh->nlmsg_flags & NLM_F_ACK))
			netlink_ack(skb, nlh, err);
			netlink_ack(skb, nlh, err);
		} else if (nlh->nlmsg_flags & NLM_F_ACK)

			netlink_ack(skb, nlh, 0);
		nlh = NLMSG_NEXT(nlh, len);
		skb_pull(skb, rlen);
	}
	}
}
}


@@ -959,13 +953,6 @@ static void audit_receive(struct sk_buff *skb)
	mutex_unlock(&audit_cmd_mutex);
	mutex_unlock(&audit_cmd_mutex);
}
}


#ifdef CONFIG_AUDITSYSCALL
static const struct inotify_operations audit_inotify_ops = {
	.handle_event	= audit_handle_ievent,
	.destroy_watch	= audit_free_parent,
};
#endif

/* Initialize audit support at boot time. */
/* Initialize audit support at boot time. */
static int __init audit_init(void)
static int __init audit_init(void)
{
{
@@ -991,12 +978,6 @@ static int __init audit_init(void)


	audit_log(NULL, GFP_KERNEL, AUDIT_KERNEL, "initialized");
	audit_log(NULL, GFP_KERNEL, AUDIT_KERNEL, "initialized");


#ifdef CONFIG_AUDITSYSCALL
	audit_ih = inotify_init(&audit_inotify_ops);
	if (IS_ERR(audit_ih))
		audit_panic("cannot initialize inotify handle");
#endif

	for (i = 0; i < AUDIT_INODE_BUCKETS; i++)
	for (i = 0; i < AUDIT_INODE_BUCKETS; i++)
		INIT_LIST_HEAD(&audit_inode_hash[i]);
		INIT_LIST_HEAD(&audit_inode_hash[i]);


@@ -1070,18 +1051,20 @@ static struct audit_buffer * audit_buffer_alloc(struct audit_context *ctx,
			goto err;
			goto err;
	}
	}


	ab->skb = alloc_skb(AUDIT_BUFSIZ, gfp_mask);
	if (!ab->skb)
		goto err;

	ab->ctx = ctx;
	ab->ctx = ctx;
	ab->gfp_mask = gfp_mask;
	ab->gfp_mask = gfp_mask;
	nlh = (struct nlmsghdr *)skb_put(ab->skb, NLMSG_SPACE(0));

	nlh->nlmsg_type = type;
	ab->skb = nlmsg_new(AUDIT_BUFSIZ, gfp_mask);
	nlh->nlmsg_flags = 0;
	if (!ab->skb)
	nlh->nlmsg_pid = 0;
		goto nlmsg_failure;
	nlh->nlmsg_seq = 0;

	nlh = NLMSG_NEW(ab->skb, 0, 0, type, 0, 0);

	return ab;
	return ab;

nlmsg_failure:                  /* Used by NLMSG_NEW */
	kfree_skb(ab->skb);
	ab->skb = NULL;
err:
err:
	audit_buffer_free(ab);
	audit_buffer_free(ab);
	return NULL;
	return NULL;
@@ -1452,6 +1435,15 @@ void audit_log_d_path(struct audit_buffer *ab, const char *prefix,
	kfree(pathname);
	kfree(pathname);
}
}


void audit_log_key(struct audit_buffer *ab, char *key)
{
	audit_log_format(ab, " key=");
	if (key)
		audit_log_untrustedstring(ab, key);
	else
		audit_log_format(ab, "(null)");
}

/**
/**
 * audit_log_end - end one audit record
 * audit_log_end - end one audit record
 * @ab: the audit_buffer
 * @ab: the audit_buffer
@@ -1475,15 +1467,7 @@ void audit_log_end(struct audit_buffer *ab)
			skb_queue_tail(&audit_skb_queue, ab->skb);
			skb_queue_tail(&audit_skb_queue, ab->skb);
			wake_up_interruptible(&kauditd_wait);
			wake_up_interruptible(&kauditd_wait);
		} else {
		} else {
			if (nlh->nlmsg_type != AUDIT_EOE) {
			audit_printk_skb(ab->skb);
				if (printk_ratelimit()) {
					printk(KERN_NOTICE "type=%d %s\n",
						nlh->nlmsg_type,
						ab->skb->data + NLMSG_SPACE(0));
				} else
					audit_log_lost("printk limit exceeded\n");
			}
			audit_hold_skb(ab->skb);
		}
		}
		ab->skb = NULL;
		ab->skb = NULL;
	}
	}
+22 −21
Original line number Original line Diff line number Diff line
@@ -53,18 +53,7 @@ enum audit_state {
};
};


/* Rule lists */
/* Rule lists */
struct audit_parent;
struct audit_watch;

struct audit_watch {
	atomic_t		count;	/* reference count */
	char			*path;	/* insertion path */
	dev_t			dev;	/* associated superblock device */
	unsigned long		ino;	/* associated inode number */
	struct audit_parent	*parent; /* associated parent */
	struct list_head	wlist;	/* entry in parent->watches list */
	struct list_head	rules;	/* associated rules */
};

struct audit_tree;
struct audit_tree;
struct audit_chunk;
struct audit_chunk;


@@ -108,19 +97,28 @@ struct audit_netlink_list {


int audit_send_list(void *);
int audit_send_list(void *);


struct inotify_watch;
/* Inotify handle */
extern struct inotify_handle *audit_ih;

extern void audit_free_parent(struct inotify_watch *);
extern void audit_handle_ievent(struct inotify_watch *, u32, u32, u32,
				const char *, struct inode *);
extern int selinux_audit_rule_update(void);
extern int selinux_audit_rule_update(void);


extern struct mutex audit_filter_mutex;
extern struct mutex audit_filter_mutex;
extern void audit_free_rule_rcu(struct rcu_head *);
extern void audit_free_rule_rcu(struct rcu_head *);
extern struct list_head audit_filter_list[];
extern struct list_head audit_filter_list[];


/* audit watch functions */
extern unsigned long audit_watch_inode(struct audit_watch *watch);
extern dev_t audit_watch_dev(struct audit_watch *watch);
extern void audit_put_watch(struct audit_watch *watch);
extern void audit_get_watch(struct audit_watch *watch);
extern int audit_to_watch(struct audit_krule *krule, char *path, int len, u32 op);
extern int audit_add_watch(struct audit_krule *krule);
extern void audit_remove_watch(struct audit_watch *watch);
extern void audit_remove_watch_rule(struct audit_krule *krule, struct list_head *list);
extern void audit_inotify_unregister(struct list_head *in_list);
extern char *audit_watch_path(struct audit_watch *watch);
extern struct list_head *audit_watch_rules(struct audit_watch *watch);

extern struct audit_entry *audit_dupe_rule(struct audit_krule *old,
					   struct audit_watch *watch);

#ifdef CONFIG_AUDIT_TREE
#ifdef CONFIG_AUDIT_TREE
extern struct audit_chunk *audit_tree_lookup(const struct inode *);
extern struct audit_chunk *audit_tree_lookup(const struct inode *);
extern void audit_put_chunk(struct audit_chunk *);
extern void audit_put_chunk(struct audit_chunk *);
@@ -130,10 +128,9 @@ extern int audit_add_tree_rule(struct audit_krule *);
extern int audit_remove_tree_rule(struct audit_krule *);
extern int audit_remove_tree_rule(struct audit_krule *);
extern void audit_trim_trees(void);
extern void audit_trim_trees(void);
extern int audit_tag_tree(char *old, char *new);
extern int audit_tag_tree(char *old, char *new);
extern void audit_schedule_prune(void);
extern void audit_prune_trees(void);
extern const char *audit_tree_path(struct audit_tree *);
extern const char *audit_tree_path(struct audit_tree *);
extern void audit_put_tree(struct audit_tree *);
extern void audit_put_tree(struct audit_tree *);
extern void audit_kill_trees(struct list_head *);
#else
#else
#define audit_remove_tree_rule(rule) BUG()
#define audit_remove_tree_rule(rule) BUG()
#define audit_add_tree_rule(rule) -EINVAL
#define audit_add_tree_rule(rule) -EINVAL
@@ -142,6 +139,7 @@ extern void audit_put_tree(struct audit_tree *);
#define audit_put_tree(tree) (void)0
#define audit_put_tree(tree) (void)0
#define audit_tag_tree(old, new) -EINVAL
#define audit_tag_tree(old, new) -EINVAL
#define audit_tree_path(rule) ""	/* never called */
#define audit_tree_path(rule) ""	/* never called */
#define audit_kill_trees(list) BUG()
#endif
#endif


extern char *audit_unpack_string(void **, size_t *, size_t);
extern char *audit_unpack_string(void **, size_t *, size_t);
@@ -160,7 +158,10 @@ static inline int audit_signal_info(int sig, struct task_struct *t)
	return 0;
	return 0;
}
}
extern void audit_filter_inodes(struct task_struct *, struct audit_context *);
extern void audit_filter_inodes(struct task_struct *, struct audit_context *);
extern struct list_head *audit_killed_trees(void);
#else
#else
#define audit_signal_info(s,t) AUDIT_DISABLED
#define audit_signal_info(s,t) AUDIT_DISABLED
#define audit_filter_inodes(t,c) AUDIT_DISABLED
#define audit_filter_inodes(t,c) AUDIT_DISABLED
#endif
#endif

extern struct mutex audit_cmd_mutex;
+55 −11
Original line number Original line Diff line number Diff line
@@ -2,6 +2,7 @@
#include <linux/inotify.h>
#include <linux/inotify.h>
#include <linux/namei.h>
#include <linux/namei.h>
#include <linux/mount.h>
#include <linux/mount.h>
#include <linux/kthread.h>


struct audit_tree;
struct audit_tree;
struct audit_chunk;
struct audit_chunk;
@@ -441,13 +442,11 @@ static void kill_rules(struct audit_tree *tree)
		if (rule->tree) {
		if (rule->tree) {
			/* not a half-baked one */
			/* not a half-baked one */
			ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE);
			ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE);
			audit_log_format(ab, "op=remove rule dir=");
			audit_log_format(ab, "op=");
			audit_log_string(ab, "remove rule");
			audit_log_format(ab, " dir=");
			audit_log_untrustedstring(ab, rule->tree->pathname);
			audit_log_untrustedstring(ab, rule->tree->pathname);
			if (rule->filterkey) {
			audit_log_key(ab, rule->filterkey);
				audit_log_format(ab, " key=");
				audit_log_untrustedstring(ab, rule->filterkey);
			} else
				audit_log_format(ab, " key=(null)");
			audit_log_format(ab, " list=%d res=1", rule->listnr);
			audit_log_format(ab, " list=%d res=1", rule->listnr);
			audit_log_end(ab);
			audit_log_end(ab);
			rule->tree = NULL;
			rule->tree = NULL;
@@ -519,6 +518,8 @@ static void trim_marked(struct audit_tree *tree)
	}
	}
}
}


static void audit_schedule_prune(void);

/* called with audit_filter_mutex */
/* called with audit_filter_mutex */
int audit_remove_tree_rule(struct audit_krule *rule)
int audit_remove_tree_rule(struct audit_krule *rule)
{
{
@@ -824,10 +825,11 @@ int audit_tag_tree(char *old, char *new)


/*
/*
 * That gets run when evict_chunk() ends up needing to kill audit_tree.
 * That gets run when evict_chunk() ends up needing to kill audit_tree.
 * Runs from a separate thread, with audit_cmd_mutex held.
 * Runs from a separate thread.
 */
 */
void audit_prune_trees(void)
static int prune_tree_thread(void *unused)
{
{
	mutex_lock(&audit_cmd_mutex);
	mutex_lock(&audit_filter_mutex);
	mutex_lock(&audit_filter_mutex);


	while (!list_empty(&prune_list)) {
	while (!list_empty(&prune_list)) {
@@ -844,6 +846,40 @@ void audit_prune_trees(void)
	}
	}


	mutex_unlock(&audit_filter_mutex);
	mutex_unlock(&audit_filter_mutex);
	mutex_unlock(&audit_cmd_mutex);
	return 0;
}

static void audit_schedule_prune(void)
{
	kthread_run(prune_tree_thread, NULL, "audit_prune_tree");
}

/*
 * ... and that one is done if evict_chunk() decides to delay until the end
 * of syscall.  Runs synchronously.
 */
void audit_kill_trees(struct list_head *list)
{
	mutex_lock(&audit_cmd_mutex);
	mutex_lock(&audit_filter_mutex);

	while (!list_empty(list)) {
		struct audit_tree *victim;

		victim = list_entry(list->next, struct audit_tree, list);
		kill_rules(victim);
		list_del_init(&victim->list);

		mutex_unlock(&audit_filter_mutex);

		prune_one(victim);

		mutex_lock(&audit_filter_mutex);
	}

	mutex_unlock(&audit_filter_mutex);
	mutex_unlock(&audit_cmd_mutex);
}
}


/*
/*
@@ -854,6 +890,8 @@ void audit_prune_trees(void)
static void evict_chunk(struct audit_chunk *chunk)
static void evict_chunk(struct audit_chunk *chunk)
{
{
	struct audit_tree *owner;
	struct audit_tree *owner;
	struct list_head *postponed = audit_killed_trees();
	int need_prune = 0;
	int n;
	int n;


	if (chunk->dead)
	if (chunk->dead)
@@ -869,15 +907,21 @@ static void evict_chunk(struct audit_chunk *chunk)
		owner->root = NULL;
		owner->root = NULL;
		list_del_init(&owner->same_root);
		list_del_init(&owner->same_root);
		spin_unlock(&hash_lock);
		spin_unlock(&hash_lock);
		if (!postponed) {
			kill_rules(owner);
			kill_rules(owner);
			list_move(&owner->list, &prune_list);
			list_move(&owner->list, &prune_list);
		audit_schedule_prune();
			need_prune = 1;
		} else {
			list_move(&owner->list, postponed);
		}
		spin_lock(&hash_lock);
		spin_lock(&hash_lock);
	}
	}
	list_del_rcu(&chunk->hash);
	list_del_rcu(&chunk->hash);
	for (n = 0; n < chunk->count; n++)
	for (n = 0; n < chunk->count; n++)
		list_del_init(&chunk->owners[n].list);
		list_del_init(&chunk->owners[n].list);
	spin_unlock(&hash_lock);
	spin_unlock(&hash_lock);
	if (need_prune)
		audit_schedule_prune();
	mutex_unlock(&audit_filter_mutex);
	mutex_unlock(&audit_filter_mutex);
}
}


Loading