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

Commit f368c07d authored by Amy Griffis's avatar Amy Griffis Committed by Al Viro
Browse files

[PATCH] audit: path-based rules

In this implementation, audit registers inotify watches on the parent
directories of paths specified in audit rules.  When audit's inotify
event handler is called, it updates any affected rules based on the
filesystem event.  If the parent directory is renamed, removed, or its
filesystem is unmounted, audit removes all rules referencing that
inotify watch.

To keep things simple, this implementation limits location-based
auditing to the directory entries in an existing directory.  Given
a path-based rule for /foo/bar/passwd, the following table applies:

    passwd modified -- audit event logged
    passwd replaced -- audit event logged, rules list updated
    bar renamed     -- rule removed
    foo renamed     -- untracked, meaning that the rule now applies to
		       the new location

Audit users typically want to have many rules referencing filesystem
objects, which can significantly impact filtering performance.  This
patch also adds an inode-number-based rule hash to mitigate this
situation.

The patch is relative to the audit git tree:
http://kernel.org/git/?p=linux/kernel/git/viro/audit-current.git;a=summary
and uses the inotify kernel API:
http://lkml.org/lkml/2006/6/1/145



Signed-off-by: default avatarAmy Griffis <amy.griffis@hp.com>
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 20ca73bc
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -165,6 +165,7 @@
#define AUDIT_INODE	102
#define AUDIT_INODE	102
#define AUDIT_EXIT	103
#define AUDIT_EXIT	103
#define AUDIT_SUCCESS   104	/* exit >= 0; value ignored */
#define AUDIT_SUCCESS   104	/* exit >= 0; value ignored */
#define AUDIT_WATCH	105


#define AUDIT_ARG0      200
#define AUDIT_ARG0      200
#define AUDIT_ARG1      (AUDIT_ARG0+1)
#define AUDIT_ARG1      (AUDIT_ARG0+1)
+2 −1
Original line number Original line Diff line number Diff line
@@ -182,7 +182,8 @@ config AUDITSYSCALL
	help
	help
	  Enable low-overhead system-call auditing infrastructure that
	  Enable low-overhead system-call auditing infrastructure that
	  can be used independently or with another kernel subsystem,
	  can be used independently or with another kernel subsystem,
	  such as SELinux.
	  such as SELinux.  To use audit's filesystem watch feature, please
	  ensure that INOTIFY is configured.


config IKCONFIG
config IKCONFIG
	bool "Kernel .config support"
	bool "Kernel .config support"
+33 −8
Original line number Original line Diff line number Diff line
@@ -56,6 +56,7 @@
#include <linux/skbuff.h>
#include <linux/skbuff.h>
#include <linux/netlink.h>
#include <linux/netlink.h>
#include <linux/selinux.h>
#include <linux/selinux.h>
#include <linux/inotify.h>


#include "audit.h"
#include "audit.h"


@@ -103,6 +104,12 @@ 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 */
struct list_head audit_inode_hash[AUDIT_INODE_BUCKETS];

/* The audit_freelist is a list of pre-allocated audit buffers (if more
/* The audit_freelist is a list of pre-allocated audit buffers (if more
 * than AUDIT_MAXFREE are in use, the audit buffer is freed instead of
 * than AUDIT_MAXFREE are in use, the audit buffer is freed instead of
 * being placed on the freelist). */
 * being placed on the freelist). */
@@ -115,10 +122,8 @@ static struct task_struct *kauditd_task;
static DECLARE_WAIT_QUEUE_HEAD(kauditd_wait);
static DECLARE_WAIT_QUEUE_HEAD(kauditd_wait);
static DECLARE_WAIT_QUEUE_HEAD(audit_backlog_wait);
static DECLARE_WAIT_QUEUE_HEAD(audit_backlog_wait);


/* The netlink socket is only to be read by 1 CPU, which lets us assume
/* Serialize requests from userspace. */
 * that list additions and deletions never happen simultaneously in
static DEFINE_MUTEX(audit_cmd_mutex);
 * auditsc.c */
DEFINE_MUTEX(audit_netlink_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
@@ -373,8 +378,8 @@ int audit_send_list(void *_dest)
	struct sk_buff *skb;
	struct sk_buff *skb;


	/* wait for parent to finish and send an ACK */
	/* wait for parent to finish and send an ACK */
	mutex_lock(&audit_netlink_mutex);
	mutex_lock(&audit_cmd_mutex);
	mutex_unlock(&audit_netlink_mutex);
	mutex_unlock(&audit_cmd_mutex);


	while ((skb = __skb_dequeue(&dest->q)) != NULL)
	while ((skb = __skb_dequeue(&dest->q)) != NULL)
		netlink_unicast(audit_sock, skb, pid, 0);
		netlink_unicast(audit_sock, skb, pid, 0);
@@ -665,20 +670,30 @@ static void audit_receive(struct sock *sk, int length)
	struct sk_buff  *skb;
	struct sk_buff  *skb;
	unsigned int qlen;
	unsigned int qlen;


	mutex_lock(&audit_netlink_mutex);
	mutex_lock(&audit_cmd_mutex);


	for (qlen = skb_queue_len(&sk->sk_receive_queue); qlen; qlen--) {
	for (qlen = skb_queue_len(&sk->sk_receive_queue); qlen; qlen--) {
		skb = skb_dequeue(&sk->sk_receive_queue);
		skb = skb_dequeue(&sk->sk_receive_queue);
		audit_receive_skb(skb);
		audit_receive_skb(skb);
		kfree_skb(skb);
		kfree_skb(skb);
	}
	}
	mutex_unlock(&audit_netlink_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)
{
{
#ifdef CONFIG_AUDITSYSCALL
	int i;
#endif

	printk(KERN_INFO "audit: initializing netlink socket (%s)\n",
	printk(KERN_INFO "audit: initializing netlink socket (%s)\n",
	       audit_default ? "enabled" : "disabled");
	       audit_default ? "enabled" : "disabled");
	audit_sock = netlink_kernel_create(NETLINK_AUDIT, 0, audit_receive,
	audit_sock = netlink_kernel_create(NETLINK_AUDIT, 0, audit_receive,
@@ -697,6 +712,16 @@ static int __init audit_init(void)
	selinux_audit_set_callback(&selinux_audit_rule_update);
	selinux_audit_set_callback(&selinux_audit_rule_update);


	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");

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

	return 0;
	return 0;
}
}
__initcall(audit_init);
__initcall(audit_init);
+34 −4
Original line number Original line Diff line number Diff line
@@ -19,7 +19,6 @@
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
 */


#include <linux/mutex.h>
#include <linux/fs.h>
#include <linux/fs.h>
#include <linux/audit.h>
#include <linux/audit.h>
#include <linux/skbuff.h>
#include <linux/skbuff.h>
@@ -54,6 +53,18 @@ enum audit_state {
};
};


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

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_field {
struct audit_field {
	u32				type;
	u32				type;
	u32				val;
	u32				val;
@@ -71,6 +82,9 @@ struct audit_krule {
	u32			buflen; /* for data alloc on list rules */
	u32			buflen; /* for data alloc on list rules */
	u32			field_count;
	u32			field_count;
	struct audit_field	*fields;
	struct audit_field	*fields;
	struct audit_field	*inode_f; /* quick access to an inode field */
	struct audit_watch	*watch;	/* associated watch */
	struct list_head	rlist;	/* entry in audit_watch.rules list */
};
};


struct audit_entry {
struct audit_entry {
@@ -79,10 +93,18 @@ struct audit_entry {
	struct audit_krule	rule;
	struct audit_krule	rule;
};
};



extern int audit_pid;
extern int audit_pid;
extern int audit_comparator(const u32 left, const u32 op, const u32 right);


#define AUDIT_INODE_BUCKETS	32
extern struct list_head audit_inode_hash[AUDIT_INODE_BUCKETS];

static inline int audit_hash_ino(u32 ino)
{
	return (ino & (AUDIT_INODE_BUCKETS-1));
}

extern int audit_comparator(const u32 left, const u32 op, const u32 right);
extern int audit_compare_dname_path(const char *dname, const char *path);
extern struct sk_buff *	    audit_make_reply(int pid, int seq, int type,
extern struct sk_buff *	    audit_make_reply(int pid, int seq, int type,
					     int done, int multi,
					     int done, int multi,
					     void *payload, int size);
					     void *payload, int size);
@@ -91,7 +113,6 @@ extern void audit_send_reply(int pid, int seq, int type,
					     void *payload, int size);
					     void *payload, int size);
extern void		    audit_log_lost(const char *message);
extern void		    audit_log_lost(const char *message);
extern void		    audit_panic(const char *message);
extern void		    audit_panic(const char *message);
extern struct mutex audit_netlink_mutex;


struct audit_netlink_list {
struct audit_netlink_list {
	int pid;
	int pid;
@@ -100,6 +121,10 @@ struct audit_netlink_list {


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


struct inotify_watch;
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);


#ifdef CONFIG_AUDITSYSCALL
#ifdef CONFIG_AUDITSYSCALL
@@ -109,6 +134,11 @@ static inline void audit_signal_info(int sig, struct task_struct *t)
	if (unlikely(audit_pid && t->tgid == audit_pid))
	if (unlikely(audit_pid && t->tgid == audit_pid))
		__audit_signal_info(sig, t);
		__audit_signal_info(sig, t);
}
}
extern enum audit_state audit_filter_inodes(struct task_struct *,
					    struct audit_context *);
extern void audit_set_auditable(struct audit_context *);
#else
#else
#define audit_signal_info(s,t)
#define audit_signal_info(s,t)
#define audit_filter_inodes(t,c) AUDIT_DISABLED
#define audit_set_auditable(c)
#endif
#endif
+745 −40

File changed.

Preview size limit exceeded, changes collapsed.

Loading