[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[RFC][PATCH] (#6 U1) the latest incarnation



Hello,

This is the latest patch. Quite a bit has changed and a lot more of the complexity has been stripped away. I've added back the hooks for d_instantiate() and d_splice_alias() for good form and rethought the positioning of the d_move() hooks. Also, I've done quite a bit with the locking. Now, [admittedly] I'm a novice, so the reader-writer locking stradegy I've used is probably not the best for performance -- especially since I've hooked __d_lookup() and will hit a write_lock() when I enter audit_attach_watch() (formly called audit_watch()).

Perhaps the locking can eventually change to using RCU and what not, but I'm pressed for time and have limited knowledge.

To save space in the inode struct, I've kept the i_audit as a pointer, rather then statically allocating memory for it. Now, all inode's get i_audit data allocated to them upon creation, and freed upon deletion. Is it reasonable to keep it this way? I am using kmalloc for this allocation, so I do believe I'm still wasting space, but I suppose I can change this back to audit_data slab allocations -- I just didn't know of a good way of creating the cache outside of the audit_inode_alloc() function itself.

I'll get cracking on the "list all watches" feature.

This is a patch against 2.6.11 for immediate gratification (and testing). David, I'll try to get you a patch to the the bitkeeper tree asap.

-tim

diff -Nurp linux-2.6.11/fs/dcache.c linux-2.6.11-audit/fs/dcache.c
--- linux-2.6.11/fs/dcache.c	2005-03-02 01:37:48.000000000 -0600
+++ linux-2.6.11-audit/fs/dcache.c	2005-03-23 13:53:28.000000000 -0600
@@ -32,6 +32,7 @@
 #include <linux/seqlock.h>
 #include <linux/swap.h>
 #include <linux/bootmem.h>
+#include <linux/audit.h>
 
 /* #define DCACHE_DEBUG 1 */
 
@@ -798,6 +799,7 @@ void d_instantiate(struct dentry *entry,
 	if (inode)
 		list_add(&entry->d_alias, &inode->i_dentry);
 	entry->d_inode = inode;
+	audit_attach_watch(entry, 0);
 	spin_unlock(&dcache_lock);
 	security_d_instantiate(entry, inode);
 }
@@ -974,6 +976,7 @@ struct dentry *d_splice_alias(struct ino
 		new = __d_find_alias(inode, 1);
 		if (new) {
 			BUG_ON(!(new->d_flags & DCACHE_DISCONNECTED));
+			audit_attach_watch(new, 0);
 			spin_unlock(&dcache_lock);
 			security_d_instantiate(new, inode);
 			d_rehash(dentry);
@@ -983,6 +986,7 @@ struct dentry *d_splice_alias(struct ino
 			/* d_instantiate takes dcache_lock, so we do it by hand */
 			list_add(&dentry->d_alias, &inode->i_dentry);
 			dentry->d_inode = inode;
+			audit_attach_watch(dentry, 0);
 			spin_unlock(&dcache_lock);
 			security_d_instantiate(dentry, inode);
 			d_rehash(dentry);
@@ -1086,6 +1090,7 @@ struct dentry * __d_lookup(struct dentry
 		if (!d_unhashed(dentry)) {
 			atomic_inc(&dentry->d_count);
 			found = dentry;
+			audit_attach_watch(found, 0);
 		}
 		spin_unlock(&dentry->d_lock);
 		break;
@@ -1166,6 +1171,7 @@ void d_delete(struct dentry * dentry)
 	 */
 	spin_lock(&dcache_lock);
 	spin_lock(&dentry->d_lock);
+	audit_attach_watch(dentry, 1);
 	if (atomic_read(&dentry->d_count) == 1) {
 		dentry_iput(dentry);
 		return;
@@ -1295,6 +1301,8 @@ void d_move(struct dentry * dentry, stru
 		spin_lock(&target->d_lock);
 	}
 
+	audit_attach_watch(dentry, 1);
+
 	/* Move the dentry to the target hash queue, if on different bucket */
 	if (dentry->d_flags & DCACHE_UNHASHED)
 		goto already_unhashed;
@@ -1328,6 +1336,7 @@ already_unhashed:
 		list_add(&target->d_child, &target->d_parent->d_subdirs);
 	}
 
+	audit_attach_watch(dentry, 0);
 	list_add(&dentry->d_child, &dentry->d_parent->d_subdirs);
 	spin_unlock(&target->d_lock);
 	spin_unlock(&dentry->d_lock);
diff -Nurp linux-2.6.11/fs/inode.c linux-2.6.11-audit/fs/inode.c
--- linux-2.6.11/fs/inode.c	2005-03-02 01:38:33.000000000 -0600
+++ linux-2.6.11-audit/fs/inode.c	2005-03-22 17:07:04.000000000 -0600
@@ -21,6 +21,7 @@
 #include <linux/pagemap.h>
 #include <linux/cdev.h>
 #include <linux/bootmem.h>
+#include <linux/audit.h>
 
 /*
  * This is needed for the following functions:
@@ -136,7 +137,7 @@ static struct inode *alloc_inode(struct 
 		inode->i_rdev = 0;
 		inode->i_security = NULL;
 		inode->dirtied_when = 0;
-		if (security_inode_alloc(inode)) {
+		if (audit_inode_alloc(inode) || security_inode_alloc(inode)) {
 			if (inode->i_sb->s_op->destroy_inode)
 				inode->i_sb->s_op->destroy_inode(inode);
 			else
@@ -174,6 +175,7 @@ void destroy_inode(struct inode *inode) 
 {
 	if (inode_has_buffers(inode))
 		BUG();
+	audit_inode_free(inode);
 	security_inode_free(inode);
 	if (inode->i_sb->s_op->destroy_inode)
 		inode->i_sb->s_op->destroy_inode(inode);
diff -Nurp linux-2.6.11/fs/namei.c linux-2.6.11-audit/fs/namei.c
--- linux-2.6.11/fs/namei.c	2005-03-02 01:37:55.000000000 -0600
+++ linux-2.6.11-audit/fs/namei.c	2005-03-16 16:37:08.000000000 -0600
@@ -214,6 +214,8 @@ int permission(struct inode *inode, int 
 {
 	int retval, submask;
 
+	audit_notify_watch(inode, mask);
+
 	if (mask & MAY_WRITE) {
 		umode_t mode = inode->i_mode;
 
@@ -344,6 +346,8 @@ static inline int exec_permission_lite(s
 {
 	umode_t	mode = inode->i_mode;
 
+	audit_notify_watch(inode, MAY_EXEC);
+
 	if (inode->i_op && inode->i_op->permission)
 		return -EAGAIN;
 
@@ -1128,6 +1132,8 @@ static inline int may_delete(struct inod
 
 	BUG_ON(victim->d_parent->d_inode != dir);
 
+	audit_notify_watch(victim->d_inode, 0);
+
 	error = permission(dir,MAY_WRITE | MAY_EXEC, NULL);
 	if (error)
 		return error;
diff -Nurp linux-2.6.11/include/linux/audit.h linux-2.6.11-audit/include/linux/audit.h
--- linux-2.6.11/include/linux/audit.h	2005-03-02 01:38:09.000000000 -0600
+++ linux-2.6.11-audit/include/linux/audit.h	2005-03-22 17:13:33.000000000 -0600
@@ -24,15 +24,23 @@
 #ifndef _LINUX_AUDIT_H_
 #define _LINUX_AUDIT_H_
 
+#ifdef __KERNEL__
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <asm/atomic.h>
+#endif
+
 /* Request and reply types */
-#define AUDIT_GET      1000	/* Get status */
-#define AUDIT_SET      1001	/* Set status (enable/disable/auditd) */
-#define AUDIT_LIST     1002	/* List filtering rules */
-#define AUDIT_ADD      1003	/* Add filtering rule */
-#define AUDIT_DEL      1004	/* Delete filtering rule */
-#define AUDIT_USER     1005	/* Send a message from user-space */
-#define AUDIT_LOGIN    1006     /* Define the login id and informaiton */
-#define AUDIT_KERNEL   2000	/* Asynchronous audit record. NOT A REQUEST. */
+#define AUDIT_GET      	1000	/* Get status */
+#define AUDIT_SET      	1001	/* Set status (enable/disable/auditd) */
+#define AUDIT_LIST     	1002	/* List filtering rules */
+#define AUDIT_ADD      	1003	/* Add filtering rule */
+#define AUDIT_DEL      	1004	/* Delete filtering rule */
+#define AUDIT_USER     	1005	/* Send a message from user-space */
+#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_KERNEL   	2000	/* Asynchronous audit record. NOT A REQUEST. */
 
 /* Rule flags */
 #define AUDIT_PER_TASK 0x01	/* Apply rule at task creation (not syscall) */
@@ -96,6 +104,9 @@
 #define AUDIT_FAIL_PRINTK	1
 #define AUDIT_FAIL_PANIC	2
 
+/* 32 byte max key size */
+#define AUDIT_FILTERKEY_MAX	32
+
 #ifndef __KERNEL__
 struct audit_message {
 	struct nlmsghdr nlh;
@@ -123,8 +134,28 @@ struct audit_rule {		/* for AUDIT_LIST, 
 	__u32		values[AUDIT_MAX_FIELDS];
 };
 
+struct audit_watch {
+	__u32	namelen;
+	__u32 	fklen;
+	char	*name;
+	char	*filterkey;
+	__u32	perms;
+};
+
 #ifdef __KERNEL__
 
+struct audit_data {
+	struct audit_wentry	*wentry;
+	struct hlist_head 	watchlist;
+	rwlock_t		lock;
+};
+
+struct audit_wentry {
+	struct hlist_node 	w_node;
+	struct audit_watch	*w_watch;
+	atomic_t		w_count;
+};
+
 #ifdef CONFIG_AUDIT
 struct audit_buffer;
 struct audit_context;
@@ -161,6 +192,27 @@ extern uid_t audit_get_loginuid(struct a
 #define audit_get_loginuid(c) ({ -1; })
 #endif
 
+#ifdef CONFIG_AUDITFILESYSTEM
+extern int audit_receive_watch(int type, int pid, int uid, int seq,
+			       struct audit_watch *req);
+extern int audit_filesystem_init(void);
+extern int audit_inode_alloc(struct inode *inode);
+extern void audit_inode_free(struct inode *inode);
+extern void audit_attach_watch(struct dentry *dentry, int remove);
+extern void audit_notify_watch(struct inode *inode, int mask);
+extern void audit_wentry_put(struct audit_wentry *wentry);
+extern struct audit_wentry *audit_wentry_get(struct audit_wentry *wentry);
+#else
+#define audit_receive_watch(t,p,u,s,r) ({ -EOPNOTSUPP; })
+#define audit_filesystem_init() ({ 0; })
+#define audit_inode_alloc(i) ({ 0; })
+#define audit_inode_free(i) do { ; } while(0)
+#define audit_attach_watch(d,r) do { ; } while (0)
+#define audit_notify_watch(i,m) do { ; } while(0)
+#define audit_wentry_put(w) do { ; } while(0)
+#define audit_wentry_get(w) ({ 0; })
+#endif
+
 #ifdef CONFIG_AUDIT
 /* These are defined in audit.c */
 				/* Public API */
@@ -175,6 +227,10 @@ extern void		    audit_log_format(struct
 extern void		    audit_log_end(struct audit_buffer *ab);
 extern void		    audit_log_end_fast(struct audit_buffer *ab);
 extern void		    audit_log_end_irq(struct audit_buffer *ab);
+extern void		    audit_log_hex(struct audit_buffer *ab, const unsigned char *buf,
+					  size_t len);
+extern void		    audit_log_untrustedstring(struct audit_buffer *ab,
+						      const char *string);
 extern void		    audit_log_d_path(struct audit_buffer *ab,
 					     const char *prefix,
 					     struct dentry *dentry,
@@ -197,6 +253,8 @@ extern void		    audit_log_lost(const ch
 #define audit_log_end(b) do { ; } while (0)
 #define audit_log_end_fast(b) do { ; } while (0)
 #define audit_log_end_irq(b) do { ; } while (0)
+#define audit_log_hex(a,b,l) do { ; } while (0)
+#define audit_log_untrustedstring(a,s) do { ; } while (0)
 #define audit_log_d_path(b,p,d,v) do { ; } while (0)
 #define audit_set_rate_limit(l) do { ; } while (0)
 #define audit_set_backlog_limit(l) do { ; } while (0)
diff -Nurp linux-2.6.11/include/linux/fs.h linux-2.6.11-audit/include/linux/fs.h
--- linux-2.6.11/include/linux/fs.h	2005-03-02 01:37:50.000000000 -0600
+++ linux-2.6.11-audit/include/linux/fs.h	2005-03-23 13:55:45.000000000 -0600
@@ -471,6 +471,9 @@ struct inode {
 	struct dnotify_struct	*i_dnotify; /* for directory notifications */
 #endif
 
+#ifdef CONFIG_AUDITFILESYSTEM
+	struct audit_data	*i_audit;
+#endif
 	unsigned long		i_state;
 	unsigned long		dirtied_when;	/* jiffies of first dirtying */
 
diff -Nurp linux-2.6.11/init/Kconfig linux-2.6.11-audit/init/Kconfig
--- linux-2.6.11/init/Kconfig	2005-03-02 01:38:19.000000000 -0600
+++ linux-2.6.11-audit/init/Kconfig	2005-03-04 16:20:29.000000000 -0600
@@ -174,6 +174,17 @@ config AUDITSYSCALL
 	  can be used independently or with another kernel subsystem,
 	  such as SELinux.
 
+config AUDITFILESYSTEM
+	bool "Enable filesystem auditing support"
+	depends on AUDITSYSCALL
+	default n
+	help
+	  Generate audit records for regular files or directories that
+	  are being watched for access by audited syscalls.  To insert
+	  and remove watch points into the filesystem you may use the
+	  auditctl program provided with auditd.  For more information,
+	  'man auditctl'
+
 config LOG_BUF_SHIFT
 	int "Kernel log buffer size (16 => 64KB, 17 => 128KB)" if DEBUG_KERNEL
 	range 12 21
diff -Nurp linux-2.6.11/kernel/Makefile linux-2.6.11-audit/kernel/Makefile
--- linux-2.6.11/kernel/Makefile	2005-03-02 01:37:50.000000000 -0600
+++ linux-2.6.11-audit/kernel/Makefile	2005-03-04 16:20:29.000000000 -0600
@@ -23,6 +23,7 @@ obj-$(CONFIG_IKCONFIG_PROC) += configs.o
 obj-$(CONFIG_STOP_MACHINE) += stop_machine.o
 obj-$(CONFIG_AUDIT) += audit.o
 obj-$(CONFIG_AUDITSYSCALL) += auditsc.o
+obj-$(CONFIG_AUDITFILESYSTEM) += auditfs.o
 obj-$(CONFIG_KPROBES) += kprobes.o
 obj-$(CONFIG_SYSFS) += ksysfs.o
 obj-$(CONFIG_GENERIC_HARDIRQS) += irq/
diff -Nurp linux-2.6.11/kernel/audit.c linux-2.6.11-audit/kernel/audit.c
--- linux-2.6.11/kernel/audit.c	2005-03-02 01:38:33.000000000 -0600
+++ linux-2.6.11-audit/kernel/audit.c	2005-03-18 11:08:32.000000000 -0600
@@ -20,6 +20,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
  * Written by Rickard E. (Rik) Faith <faith redhat com>
+ * Modified by Timothy R. Chavez <chavezt us ibm com>
  *
  * Goals: 1) Integrate fully with SELinux.
  *	  2) Minimal run-time overhead:
@@ -319,6 +320,8 @@ static int audit_netlink_ok(kernel_cap_t
 	case AUDIT_SET:
 	case AUDIT_ADD:
 	case AUDIT_DEL:
+	case AUDIT_WATCH_INS:
+	case AUDIT_WATCH_REM:
 		if (!cap_raised(eff_cap, CAP_AUDIT_CONTROL))
 			err = -EPERM;
 		break;
@@ -413,6 +416,14 @@ static int audit_receive_msg(struct sk_b
 		err = -EOPNOTSUPP;
 #endif
 		break;
+	case AUDIT_WATCH_INS:
+	case AUDIT_WATCH_REM:
+		if (nlh->nlmsg_len < sizeof(struct audit_watch))
+			return -EINVAL;
+		err = audit_receive_watch(nlh->nlmsg_type,
+					  NETLINK_CB(skb).pid,
+					  uid, seq, data);
+		break;
 	default:
 		err = -EINVAL;
 		break;
@@ -557,6 +568,7 @@ int __init audit_init(void)
 
 	audit_initialized = 1;
 	audit_enabled = audit_default;
+	audit_filesystem_init();
 	audit_log(NULL, "initialized");
 	return 0;
 }
@@ -717,6 +729,28 @@ void audit_log_format(struct audit_buffe
 	va_end(args);
 }
 
+void audit_log_hex(struct audit_buffer *ab, const unsigned char *buf, size_t len)
+{
+	int i;
+
+	for (i=0; i<len; i++)
+		audit_log_format(ab, "%02x", buf[i]);
+}
+
+void audit_log_untrustedstring(struct audit_buffer *ab, const char *string)
+{
+	const unsigned char *p = string;
+
+	while (*p) {
+		if (*p == '"' || *p == ' ' || *p < 0x20 || *p > 0x7f) {
+			audit_log_hex(ab, string, strlen(string));
+			return;
+		}
+		p++;
+	}
+	audit_log_format(ab, "\"%s\"", string);
+}
+
 /* This is a helper-function to print the d_path without using a static
  * buffer or allocating another buffer in addition to the one in
  * audit_buffer. */
diff -Nurp linux-2.6.11/kernel/auditfs.c linux-2.6.11-audit/kernel/auditfs.c
--- linux-2.6.11/kernel/auditfs.c	1969-12-31 17:00:00.000000000 -0700
+++ linux-2.6.11-audit/kernel/auditfs.c	2005-03-23 13:39:38.000000000 -0600
@@ -0,0 +1,491 @@
+/* auditfs.c -- Filesystem auditing support -*- linux-c -*-
+ * Implements filesystem auditing support, depends on kernel/auditsc.c
+ *
+ * Copyright 2005 International Business Machines Corp. (IBM)
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307  USA
+ *
+ * Written by Timothy R. Chavez <chavezt us ibm com>
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/namei.h>
+#include <linux/mount.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/audit.h>
+#include <asm/uaccess.h>
+
+kmem_cache_t *audit_watch_cache;
+kmem_cache_t *audit_wentry_cache;
+
+/* Not implemented yet */
+LIST_HEAD(audit_global_watchlist);
+
+/* Private Interface */
+
+static inline struct audit_wentry *audit_wentry_fetch(const char *name,
+						      struct audit_data *data)
+{
+	struct audit_wentry *wentry, *ret = NULL;
+	struct hlist_node *pos;
+
+	hlist_for_each_entry(wentry, pos, &data->watchlist, w_node)
+		if(!strcmp(wentry->w_watch->name, name)) {
+			ret = audit_wentry_get(wentry);
+			break;
+		}
+
+	return ret;
+}
+
+static inline struct audit_wentry *audit_wentry_fetch_lock(const char *name,
+						      struct audit_data *data)
+{
+	struct audit_wentry *ret = NULL;
+
+	if (name && data) {
+		read_lock(&data->lock);
+		ret = audit_wentry_fetch(name, data);
+		read_unlock(&data->lock);
+	}
+
+	return ret;
+}
+
+static inline struct audit_watch *audit_watch_alloc(void)
+{
+	struct audit_watch *watch;
+
+	watch = kmem_cache_alloc(audit_watch_cache, GFP_KERNEL);
+	if (watch) {
+		watch->namelen = 0;
+		watch->fklen = 0;
+		watch->name = NULL;
+		watch->filterkey = NULL;
+		watch->perms = 0;
+	}
+
+	return watch;
+}
+
+static inline void audit_watch_free(struct audit_watch *watch)
+{
+	if (watch) {
+		kfree(watch->name);
+		kfree(watch->filterkey);
+		kmem_cache_free(audit_watch_cache, watch);
+	}
+}
+
+static struct audit_watch *audit_create_watch(const char *name,
+					      const char *filterkey,
+					      __u32 perms)
+{
+	struct audit_watch *err = NULL;
+	struct audit_watch *watch = NULL;
+
+	err = ERR_PTR(-ENOMEM);
+	watch = audit_watch_alloc();
+	if (watch) {
+		watch->namelen = strlen(name) + 1;
+		watch->name = kmalloc(watch->namelen, GFP_KERNEL);
+		if (!watch->name)
+			goto audit_create_watch_fail;
+		strcpy(watch->name, name);
+
+		if (filterkey) {
+			watch->fklen = strlen(filterkey) + 1;
+			watch->filterkey = kmalloc(watch->fklen, GFP_KERNEL);
+			if (!watch->filterkey)
+				goto audit_create_watch_fail;
+			strcpy(watch->filterkey, filterkey);
+		}
+
+		watch->perms = perms;
+
+		goto audit_create_watch_exit;
+	}
+
+
+audit_create_watch_fail:
+	audit_watch_free(watch);
+	watch = err;
+audit_create_watch_exit:
+	return watch;
+}
+
+static inline struct audit_wentry *audit_wentry_alloc(void)
+{
+	struct audit_wentry *wentry;
+
+	wentry = kmem_cache_alloc(audit_wentry_cache, GFP_KERNEL);
+	if (wentry) {
+		atomic_set(&wentry->w_count, 1);
+		wentry->w_watch = NULL;
+	}
+
+	return wentry;
+}
+
+static inline void audit_wentry_free(struct audit_wentry *wentry)
+{
+	if (wentry) {
+		audit_watch_free(wentry->w_watch);
+		kmem_cache_free(audit_wentry_cache, wentry);
+	}
+}
+
+static inline void audit_destroy_wentry(struct audit_wentry *wentry)
+{
+	if (wentry) {
+		hlist_del_init(&wentry->w_node);
+		audit_wentry_put(wentry);
+	}
+}
+
+struct audit_wentry *audit_wentry_get(struct audit_wentry *wentry)
+{
+	if (wentry) {
+		BUG_ON(!atomic_read(&wentry->w_count));
+		atomic_inc(&wentry->w_count);
+	}
+
+	return wentry;
+}
+
+void audit_wentry_put(struct audit_wentry *wentry)
+{
+	if (wentry && atomic_dec_and_test(&wentry->w_count))
+		audit_wentry_free(wentry);
+}
+
+/* The only time the new wentry gets updated is when it is inaccessible
+ * (out of the list).
+ */
+static int audit_create_wentry(const char *name,
+			       const char *filterkey,
+			       __u32 perms, struct audit_data *data)
+{
+	int ret;
+	struct audit_wentry *wentry = NULL;
+	struct audit_wentry *new_wentry = NULL;
+
+	ret = -ENOMEM;
+	new_wentry = audit_wentry_alloc();
+	if (!new_wentry)
+		goto audit_create_wentry_fail;
+
+	new_wentry->w_watch = audit_create_watch(name, filterkey, perms);
+	if (IS_ERR(new_wentry->w_watch)) {
+		ret = PTR_ERR(new_wentry->w_watch);
+		new_wentry->w_watch = NULL;
+		goto audit_create_wentry_fail;
+	}
+
+	ret = 0;
+	write_lock(&data->lock);
+	wentry = audit_wentry_fetch(name, data);
+	if (!wentry) {
+		hlist_add_head(&new_wentry->w_node, &data->watchlist);
+		write_unlock(&data->lock);
+		goto audit_create_wentry_exit;
+	}
+	audit_wentry_put(wentry);
+	write_unlock(&data->lock);
+
+	ret = -EEXIST;
+
+audit_create_wentry_fail:
+	audit_wentry_put(new_wentry);
+audit_create_wentry_exit:
+	return ret;
+}
+
+/* Caller must hold data->lock */
+
+static inline void audit_drain_watchlist(struct audit_data *data)
+{
+	struct audit_wentry *wentry;
+	struct hlist_node *pos, *buf;
+
+	hlist_for_each_entry_safe(wentry, pos, buf, &data->watchlist, w_node)
+		audit_destroy_wentry(wentry);
+}
+
+static inline struct audit_data *audit_data_alloc(void)
+{
+	struct audit_data *data;
+
+	data = kmalloc(sizeof(struct audit_data), GFP_KERNEL);
+	if (data) {
+		data->wentry = NULL;
+		INIT_HLIST_HEAD(&data->watchlist);
+		data->lock = RW_LOCK_UNLOCKED;
+	}
+
+	return data;
+}
+
+static inline void audit_data_free(struct audit_data *data)
+{
+	if (data) {
+		write_lock(&data->lock);
+		audit_drain_watchlist(data);
+		audit_wentry_put(data->wentry);
+		write_unlock(&data->lock);
+		kfree(data);
+	}
+}
+
+static int audit_insert_watch(char *path, char *filterkey, __u32 perms)
+{
+	int ret;
+	struct nameidata nd;
+
+	ret = path_lookup(path, LOOKUP_PARENT | LOOKUP_FOLLOW, &nd);
+	if (ret < 0)
+		goto audit_insert_watch_release;
+
+	ret = -EPERM;
+	if (nd.last_type != LAST_NORM || !nd.last.name)
+		goto audit_insert_watch_release;
+
+	ret = audit_create_wentry(nd.last.name, filterkey, perms,
+				  nd.dentry->d_inode->i_audit);
+audit_insert_watch_release:
+	path_release(&nd);
+	return ret;
+}
+
+static int audit_remove_watch(const char *path)
+{
+	int ret;
+	struct nameidata nd;
+	struct audit_data *data;
+	struct audit_wentry *wentry;
+
+	ret = path_lookup(path, LOOKUP_PARENT | LOOKUP_FOLLOW, &nd);
+	if (ret < 0)
+		goto audit_remove_watch_release;
+
+	ret = -EPERM;
+	if (nd.last_type != LAST_NORM || !nd.last.name)
+		goto audit_remove_watch_release;
+
+	data = nd.dentry->d_inode->i_audit;
+
+	write_lock(&data->lock);
+	wentry = audit_wentry_fetch(nd.last.name, data);
+	if (!wentry) {
+		write_unlock(&data->lock);
+		goto audit_remove_watch_release;
+	}
+	audit_destroy_wentry(wentry);
+	audit_wentry_put(wentry);
+	write_unlock(&data->lock);
+
+	ret = 0;
+
+audit_remove_watch_release:
+	path_release(&nd);
+	return ret;
+}
+
+/*
+ * Hook appears in fs/dcache.c:
+ *	d_move(),
+ * 	d_delete(),
+ *	d_instantiate(),
+ *	d_splice_alias()
+ *	__d_lookup() (Hm, a lot of write-locking overhead)
+ */
+void audit_attach_watch(struct dentry *dentry, int remove)
+{
+	struct audit_wentry *wentry;
+	struct audit_data *data, *parent;
+
+	if (!dentry || !dentry->d_inode)
+		return;
+
+	if (!dentry->d_parent || !dentry->d_parent->d_inode)
+		return;
+
+	data = dentry->d_inode->i_audit;
+	parent = dentry->d_parent->d_inode->i_audit;
+
+	write_lock(&data->lock);
+	wentry = audit_wentry_fetch_lock(dentry->d_name.name, parent);
+	/* FIXME: long watchlist == too much spinning? */
+	if (remove) {
+		audit_drain_watchlist(data);
+		if (wentry && data->wentry) {
+			if (!strcmp(wentry->w_watch->name,
+				    data->wentry->w_watch->name)) {
+				audit_wentry_put(data->wentry);
+				dentry->d_inode->i_audit->wentry = NULL;
+			}
+		}
+	} else if (!data->wentry || hlist_unhashed(&data->wentry->w_node)) {
+		audit_wentry_put(data->wentry);
+		dentry->d_inode->i_audit->wentry = audit_wentry_get(wentry);
+	}
+	audit_wentry_put(wentry);
+	write_unlock(&data->lock);
+}
+
+/*
+ *
+ * Hook appears in:
+ * fs/namie.c:permission(), exec_permission_lite(), vfs_unlink/rmdir
+ *
+ */
+
+void audit_notify_watch(struct inode *inode, int mask)
+{
+	struct audit_wentry *wentry;
+	struct audit_buffer *ab;
+
+	if (!inode)
+		return;
+
+	wentry = audit_wentry_get(inode->i_audit->wentry);
+	if (!wentry)
+		return;
+
+	if (!mask || (wentry->w_watch->perms && !(wentry->w_watch->perms&mask)))
+		return;
+
+	ab = audit_log_start(current->audit_context);
+	if (!ab)
+		return;
+
+	audit_log_format(ab, "name=");
+	audit_log_untrustedstring(ab, wentry->w_watch->name);
+	audit_log_format(ab,
+			 " filterkey=%s perm=%u perm_mask=%d inode=%lu"
+			 " inode_uid=%d inode_gid=%d inode_dev=%02x:%02x"
+			 " inode_rdev=%02x:%02x",
+			 wentry->w_watch->filterkey, wentry->w_watch->perms,
+			 mask, inode->i_ino, inode->i_uid, inode->i_gid,
+			 MAJOR(inode->i_sb->s_dev), MINOR(inode->i_sb->s_dev),
+			 MAJOR(inode->i_rdev), MINOR(inode->i_rdev)
+			);
+	audit_log_end(ab);
+	audit_wentry_put(wentry);
+}
+
+int audit_receive_watch(int type, int pid, int uid, int seq,
+			struct audit_watch *req)
+{
+	int watch_err, ret = 0;
+	char *name = NULL;
+	char *filterkey = NULL;
+
+	name = getname(req->name);
+	if (IS_ERR(req->name)) {
+		watch_err = PTR_ERR(req->name);
+		goto audit_receive_watch_err;
+	}
+
+	watch_err = -EINVAL;
+	if (!name || strlen(name) + 1 > PATH_MAX)
+		goto audit_receive_watch_err;
+
+	/* Includes terminating '\0' */
+	if (req->fklen > AUDIT_FILTERKEY_MAX)
+		goto audit_receive_watch_err;
+
+	if (req->perms > (MAY_READ | MAY_WRITE | MAY_EXEC | MAY_APPEND))
+		goto audit_receive_watch_err;
+
+	if (req->fklen) {
+		ret = -ENOMEM;
+		filterkey = kmalloc(req->fklen, GFP_KERNEL);
+		if (!filterkey)
+			goto audit_receive_watch_exit;
+
+		ret = strncpy_from_user(filterkey, req->filterkey, req->fklen);
+		if (ret < 0)
+			goto audit_receive_watch_exit;
+	}
+
+	switch (type) {
+	case AUDIT_WATCH_INS:
+		watch_err = audit_insert_watch(name, filterkey, req->perms);
+		break;
+	case AUDIT_WATCH_REM:
+		watch_err = audit_remove_watch(name);
+		break;
+	}
+
+audit_receive_watch_err:
+	audit_send_reply(pid, seq, type, 0, 1, &watch_err, sizeof(int));
+
+audit_receive_watch_exit:
+	putname(name);
+	kfree(filterkey);
+	return ret;
+}
+
+int audit_inode_alloc(struct inode *inode)
+{
+	if (inode) {
+		inode->i_audit = audit_data_alloc();
+		if (!inode->i_audit)
+			return -ENOMEM;
+	}
+
+	return 0;
+}
+
+void audit_inode_free(struct inode *inode)
+{
+	if (inode)
+		audit_data_free(inode->i_audit);
+}
+
+int audit_filesystem_init()
+{
+	int ret = 0;
+
+	audit_watch_cache =
+	    kmem_cache_create("audit_watch_cache",
+			      sizeof(struct audit_watch), 0, 0, NULL, NULL);
+	if (!audit_watch_cache)
+		goto audit_filesystem_init_fail;
+
+	audit_wentry_cache =
+	    kmem_cache_create("audit_wentry_cache",
+			      sizeof(struct audit_wentry), 0, 0, NULL, NULL);
+	if (!audit_wentry_cache)
+		goto audit_filesystem_init_fail;
+
+	goto audit_filesystem_init_exit;
+
+audit_filesystem_init_fail:
+	ret = -ENOMEM;
+	kmem_cache_destroy(audit_watch_cache);
+	kmem_cache_destroy(audit_wentry_cache);
+audit_filesystem_init_exit:
+	return ret;
+
+}
diff -Nurp linux-2.6.11/kernel/auditsc.c linux-2.6.11-audit/kernel/auditsc.c
--- linux-2.6.11/kernel/auditsc.c	2005-03-02 01:38:17.000000000 -0600
+++ linux-2.6.11-audit/kernel/auditsc.c	2005-03-18 15:57:59.000000000 -0600
@@ -612,9 +612,11 @@ static void audit_log_exit(struct audit_
 		if (!ab)
 			continue; /* audit_panic has been called */
 		audit_log_format(ab, "item=%d", i);
-		if (context->names[i].name)
-			audit_log_format(ab, " name=%s",
-					 context->names[i].name);
+		if (context->names[i].name) {
+			audit_log_format(ab, " name=");
+			audit_log_untrustedstring(ab, context->names[i].name);
+		}
+
 		if (context->names[i].ino != (unsigned long)-1)
 			audit_log_format(ab, " inode=%lu",
 					 context->names[i].ino);
diff -Nurp linux-2.6.11/security/selinux/nlmsgtab.c linux-2.6.11-audit/security/selinux/nlmsgtab.c
--- linux-2.6.11/security/selinux/nlmsgtab.c	2005-03-02 01:38:19.000000000 -0600
+++ linux-2.6.11-audit/security/selinux/nlmsgtab.c	2005-03-16 10:58:39.000000000 -0600
@@ -98,6 +98,8 @@ static struct nlmsg_perm nlmsg_audit_per
 	{ AUDIT_DEL,		NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
 	{ AUDIT_USER,		NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
 	{ AUDIT_LOGIN,		NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
+	{ AUDIT_WATCH_INS,	NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
+	{ AUDIT_WATCH_REM,	NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
 };
 
 

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]