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

[RFC][PATCH] (#5, U1) prelim auditfs



This updated version of patch #5 fixes a problem (thanks Serge and
Mounir) that was discovered on Mounir's machine.  I guess there was
some problem with the forward declaration of an inline function
*blink* and it was breaking at compile time with a message that said
"Can't inline this.." and then subsequent, "The function you're trying
to use is unimplemented / has no body" -- Some quick and minor
rearranging of code was done to fix the problem.

-- 
- Timothy R. Chavez
diff -Nurp linux-2.6.10/Makefile linux-2.6.10-audit/Makefile
--- linux-2.6.10/Makefile	2004-12-24 15:35:01.000000000 -0600
+++ linux-2.6.10-audit/Makefile	2005-02-28 12:50:36.000000000 -0600
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 6
 SUBLEVEL = 10
-EXTRAVERSION =
+EXTRAVERSION =-auditfs-tc1-5
 NAME=Woozy Numbat
 
 # *DOCUMENTATION*
diff -Nurp linux-2.6.10/arch/i386/defconfig linux-2.6.10-audit/arch/i386/defconfig
--- linux-2.6.10/arch/i386/defconfig	2004-12-24 15:35:01.000000000 -0600
+++ linux-2.6.10-audit/arch/i386/defconfig	2005-02-22 09:04:45.000000000 -0600
@@ -23,6 +23,7 @@ CONFIG_POSIX_MQUEUE=y
 CONFIG_SYSCTL=y
 CONFIG_AUDIT=y
 CONFIG_AUDITSYSCALL=y
+CONFIG_AUDITFILESYSTEM=n
 CONFIG_LOG_BUF_SHIFT=15
 CONFIG_HOTPLUG=y
 # CONFIG_IKCONFIG is not set
diff -Nurp linux-2.6.10/fs/dcache.c linux-2.6.10-audit/fs/dcache.c
--- linux-2.6.10/fs/dcache.c	2004-12-24 15:34:00.000000000 -0600
+++ linux-2.6.10-audit/fs/dcache.c	2005-02-25 14:51:55.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 */
 
@@ -781,6 +782,7 @@ void d_instantiate(struct dentry *entry,
 	entry->d_inode = inode;
 	spin_unlock(&dcache_lock);
 	security_d_instantiate(entry, inode);
+	audit_watch(entry, 0);
 }
 
 /**
@@ -920,6 +922,7 @@ struct dentry *d_splice_alias(struct ino
 			security_d_instantiate(dentry, inode);
 			d_rehash(dentry);
 		}
+		audit_watch(dentry, 0);
 	} else
 		d_add(dentry, inode);
 	return new;
@@ -1027,6 +1030,8 @@ next:
  	}
  	rcu_read_unlock();
 
+	audit_watch(found, 0);
+
  	return found;
 }
 
@@ -1266,6 +1271,7 @@ already_unhashed:
 	spin_unlock(&dentry->d_lock);
 	write_sequnlock(&rename_lock);
 	spin_unlock(&dcache_lock);
+	audit_watch(dentry, 1);
 }
 
 /**
diff -Nurp linux-2.6.10/fs/inode.c linux-2.6.10-audit/fs/inode.c
--- linux-2.6.10/fs/inode.c	2004-12-24 15:35:40.000000000 -0600
+++ linux-2.6.10-audit/fs/inode.c	2005-02-28 13:03:45.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,6 +137,7 @@ static struct inode *alloc_inode(struct 
 		inode->i_rdev = 0;
 		inode->i_security = NULL;
 		inode->dirtied_when = 0;
+		audit_inode_alloc(inode);
 		if (security_inode_alloc(inode)) {
 			if (inode->i_sb->s_op->destroy_inode)
 				inode->i_sb->s_op->destroy_inode(inode);
@@ -175,6 +177,7 @@ void destroy_inode(struct inode *inode) 
 	if (inode_has_buffers(inode))
 		BUG();
 	security_inode_free(inode);
+	audit_inode_free(inode);
 	if (inode->i_sb->s_op->destroy_inode)
 		inode->i_sb->s_op->destroy_inode(inode);
 	else
diff -Nurp linux-2.6.10/fs/namei.c linux-2.6.10-audit/fs/namei.c
--- linux-2.6.10/fs/namei.c	2004-12-24 15:34:30.000000000 -0600
+++ linux-2.6.10-audit/fs/namei.c	2005-02-24 10:30:35.000000000 -0600
@@ -231,6 +231,8 @@ int permission(struct inode * inode,int 
 
 	/* Ordinary permission routines do not understand MAY_APPEND. */
 	submask = mask & ~MAY_APPEND;
+
+	audit_notify_watch(inode, mask);
 
 	if (inode->i_op && inode->i_op->permission)
 		retval = inode->i_op->permission(inode, submask, nd);
@@ -342,6 +344,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;
 
@@ -1704,6 +1708,9 @@ int vfs_rmdir(struct inode *dir, struct 
 	if (error)
 		return error;
 
+	if (dentry)
+		audit_notify_watch(dentry->d_inode, 0);
+
 	if (!dir->i_op || !dir->i_op->rmdir)
 		return -EPERM;
 
diff -Nurp linux-2.6.10/include/linux/audit.h linux-2.6.10-audit/include/linux/audit.h
--- linux-2.6.10/include/linux/audit.h	2004-12-24 15:34:57.000000000 -0600
+++ linux-2.6.10-audit/include/linux/audit.h	2005-02-24 20:18:34.000000000 -0600
@@ -25,14 +25,16 @@
 #define _LINUX_AUDIT_H_
 
 /* 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) */
@@ -74,7 +76,7 @@
 #define AUDIT_DEVMINOR	101
 #define AUDIT_INODE	102
 #define AUDIT_EXIT	103
-#define AUDIT_SUCCESS   104	/* exit >= 0; value ignored */
+#define AUDIT_SUCCESS	104	/* exit >= 0; value ignored */
 
 #define AUDIT_ARG0      200
 #define AUDIT_ARG1      (AUDIT_ARG0+1)
@@ -91,11 +93,15 @@
 #define AUDIT_STATUS_PID		0x0004
 #define AUDIT_STATUS_RATE_LIMIT		0x0008
 #define AUDIT_STATUS_BACKLOG_LIMIT	0x0010
+#define AUDIT_STATUS_FSENABLED		0x0020
 				/* Failure-to-log actions */
 #define AUDIT_FAIL_SILENT	0
 #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;
@@ -106,6 +112,7 @@ struct audit_message {
 struct audit_status {
 	__u32		mask;		/* Bit mask for valid entries */
 	__u32		enabled;	/* 1 = enabled, 0 = disbaled */
+	__u32		fs_enabled;	/* 1 = fs auditing on, 0 = off */
 	__u32		failure;	/* Failure-to-log action */
 	__u32		pid;		/* pid of auditd process */
 	__u32		rate_limit;	/* messages rate limit (per second) */
@@ -129,6 +136,29 @@ struct audit_rule {		/* for AUDIT_LIST, 
 	__u32		values[AUDIT_MAX_FIELDS];
 };
 
+struct audit_watch {
+	int	namelen;
+	int 	fklen;
+	char	*name;
+	char	*filterkey;
+	__u32	perms;
+};
+
+struct audit_data {
+	struct audit_wentry	*wentry;
+	struct list_head 	watchlist;
+	rwlock_t 		watchlist_lock;
+	atomic_t		count;
+};
+
+struct audit_wentry {
+	struct list_head 	w_list;
+	atomic_t 		w_count;
+	struct audit_data 	*w_data;
+	struct audit_watch	*w_watch;
+	int			w_valid;
+};
+
 #ifdef __KERNEL__
 
 #ifdef CONFIG_AUDIT
@@ -137,6 +167,8 @@ struct audit_context;
 #endif
 
 #ifdef CONFIG_AUDITSYSCALL
+struct inode;
+
 /* These are defined in auditsc.c */
 				/* Public API */
 extern int  audit_alloc(struct task_struct *task);
@@ -155,6 +187,7 @@ extern int  audit_receive_filter(int typ
 extern void audit_get_stamp(struct audit_context *ctx,
 			    struct timespec *t, int *serial);
 extern int  audit_set_loginuid(struct audit_context *ctx, uid_t loginuid);
+extern int audit_notify_watch(struct inode *inode, int mask);
 #else
 #define audit_alloc(t) ({ 0; })
 #define audit_free(t) do { ; } while (0)
@@ -163,8 +196,28 @@ extern int  audit_set_loginuid(struct au
 #define audit_getname(n) do { ; } while (0)
 #define audit_putname(n) do { ; } while (0)
 #define audit_inode(n,i,d) do { ; } while (0)
+#define audit_notify_watch(i,m) ({ 0; })
 #endif
 
+#ifdef CONFIG_AUDITFILESYSTEM
+extern void audit_receive_watch(int type, int pid, int uid, int seq,
+			       struct audit_watch *req);
+extern void audit_filesystem_init(void);
+extern void audit_inode_alloc(struct inode *inode);
+extern void audit_inode_free(struct inode *inode);
+extern void audit_watch(struct dentry *dentry, int drain);
+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) do { ; } while(0)
+#define audit_filesystem_init() do { ; } while(0)
+#define audit_inode_alloc(i) do { ; } while(0)
+#define audit_inode_free(i) do { ; } while(0)
+#define audit_watch(d,d) do { ; } while (0)
+#define audit_watch_put(w) do { ; } while(0)
+#define audit_watch_get(w) ({ 0; })
+#endif
+
 #ifdef CONFIG_AUDIT
 /* These are defined in audit.c */
 				/* Public API */
diff -Nurp linux-2.6.10/include/linux/fs.h linux-2.6.10-audit/include/linux/fs.h
--- linux-2.6.10/include/linux/fs.h	2004-12-24 15:34:27.000000000 -0600
+++ linux-2.6.10-audit/include/linux/fs.h	2005-02-22 09:04:45.000000000 -0600
@@ -18,6 +18,7 @@
 #include <linux/cache.h>
 #include <linux/prio_tree.h>
 #include <linux/kobject.h>
+#include <linux/audit.h>
 #include <asm/atomic.h>
 
 struct iovec;
@@ -223,7 +224,6 @@ extern int dir_notify_enable;
 
 #include <linux/list.h>
 #include <linux/radix-tree.h>
-#include <linux/audit.h>
 #include <linux/init.h>
 #include <asm/semaphore.h>
 #include <asm/byteorder.h>
@@ -459,6 +459,9 @@ struct inode {
 #ifdef CONFIG_QUOTA
 	struct dquot		*i_dquot[MAXQUOTAS];
 #endif
+#ifdef CONFIG_AUDITFILESYSTEM
+	struct audit_data	*i_audit;
+#endif
 	/* These three should probably be a union */
 	struct list_head	i_devices;
 	struct pipe_inode_info	*i_pipe;
diff -Nurp linux-2.6.10/init/Kconfig linux-2.6.10-audit/init/Kconfig
--- linux-2.6.10/init/Kconfig	2004-12-24 15:35:24.000000000 -0600
+++ linux-2.6.10-audit/init/Kconfig	2005-02-22 10:05:37.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.10/kernel/Makefile linux-2.6.10-audit/kernel/Makefile
--- linux-2.6.10/kernel/Makefile	2004-12-24 15:34:26.000000000 -0600
+++ linux-2.6.10-audit/kernel/Makefile	2005-02-22 09:04:45.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.10/kernel/audit.c linux-2.6.10-audit/kernel/audit.c
--- linux-2.6.10/kernel/audit.c	2004-12-24 15:35:50.000000000 -0600
+++ linux-2.6.10-audit/kernel/audit.c	2005-02-28 13:10:21.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:
@@ -60,6 +61,9 @@ static int	audit_initialized;
 /* No syscall auditing will take place unless audit_enabled != 0. */
 int		audit_enabled;
 
+/* No filesystem auditing will take place unless audit_fsenabled != 0 */
+int		auditfs_enabled = 1;
+
 /* Default state when kernel boots without any parameters. */
 static int	audit_default;
 
@@ -260,6 +264,17 @@ int audit_set_enabled(int state)
 	return old;
 }
 
+int audit_set_fsenabled(int state)
+{
+	int old		= auditfs_enabled;
+	if (state != 0 && state != 1)
+		return -EINVAL;
+	auditfs_enabled = state;
+	audit_log(current->audit_context, "auditfs_enabled=%d old=%d",
+		auditfs_enabled, old);
+	return old;
+}
+
 int audit_set_failure(int state)
 {
 	int old		 = audit_failure;
@@ -317,6 +332,7 @@ static int audit_receive_msg(struct sk_b
 	switch (nlh->nlmsg_type) {
 	case AUDIT_GET:
 		status_set.enabled	 = audit_enabled;
+		status_set.fs_enabled	 = auditfs_enabled;
 		status_set.failure	 = audit_failure;
 		status_set.pid		 = audit_pid;
 		status_set.rate_limit	 = audit_rate_limit;
@@ -334,6 +350,10 @@ static int audit_receive_msg(struct sk_b
 			err = audit_set_enabled(status_get->enabled);
 			if (err < 0) return err;
 		}
+		if (status_get->mask & AUDIT_STATUS_FSENABLED) {
+			err = audit_set_fsenabled(status_get->fs_enabled);
+			if (err < 0) return err;
+		}
 		if (status_get->mask & AUDIT_STATUS_FAILURE) {
 			err = audit_set_failure(status_get->failure);
 			if (err < 0) return err;
@@ -394,6 +414,15 @@ static int audit_receive_msg(struct sk_b
 		err = -EOPNOTSUPP;
 #endif
 		break;
+#ifdef CONFIG_AUDITFILESYSTEM
+	case AUDIT_WATCH_INS:
+	case AUDIT_WATCH_REM:
+		audit_receive_watch(nlh->nlmsg_type, pid, uid, seq,
+			       	    data);
+#else
+		err = -EOPNOTSUPP;
+#endif
+		break;
 	default:
 		err = -EINVAL;
 		break;
@@ -533,6 +562,7 @@ int __init audit_init(void)
 
 	audit_initialized = 1;
 	audit_enabled = audit_default;
+	audit_filesystem_init();
 	audit_log(NULL, "initialized");
 	return 0;
 }
diff -Nurp linux-2.6.10/kernel/auditfs.c linux-2.6.10-audit/kernel/auditfs.c
--- linux-2.6.10/kernel/auditfs.c	1969-12-31 17:00:00.000000000 -0700
+++ linux-2.6.10-audit/kernel/auditfs.c	2005-02-28 17:27:09.000000000 -0600
@@ -0,0 +1,559 @@
+/* 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>
+
+kmem_cache_t *audit_watch_cache;
+kmem_cache_t *audit_wentry_cache;
+kmem_cache_t *audit_data_cache;
+
+
+/* Private Interface */
+
+static void audit_data_put(struct audit_data *data);
+void audit_wentry_put(struct audit_wentry *wentry);
+
+/*
+ * Caller must hold i_audit->watchlist_lock
+ */
+static inline void audit_destroy_wentry(struct audit_wentry *wentry)
+{
+	list_del_init(&wentry->w_list);
+	wentry->w_valid = 0;
+	audit_data_put(wentry->w_data);
+	audit_wentry_put(wentry);
+}
+
+/*
+ * Caller must hold i_audit->watchlist_lock
+ */
+static
+inline struct audit_wentry *audit_wentry_fetch(const char *name,
+					       struct audit_data *data)
+{
+	struct audit_wentry *wentry, *ret = NULL;
+
+	if (name && data) {
+		list_for_each_entry(wentry, &data->watchlist, w_list) {
+			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)
+{
+	unsigned long flags;
+	struct audit_wentry *wentry, *ret = NULL;
+
+	if (name && data) {
+		read_lock_irqsave(&data->watchlist_lock, flags);
+		list_for_each_entry(wentry, &data->watchlist, w_list)
+		    if (!strcmp(wentry->w_watch->name, name)) {
+			ret = audit_wentry_get(wentry);
+			break;
+		}
+		read_unlock_irqrestore(&data->watchlist_lock, flags);
+	}
+
+	return ret;
+}
+
+static inline struct audit_data *audit_data_alloc(void)
+{
+	struct audit_data *data;
+
+	data = kmem_cache_alloc(audit_data_cache, GFP_KERNEL);
+	if (data) {
+		data->wentry = NULL;
+		atomic_set(&data->count, 1);
+		INIT_LIST_HEAD(&data->watchlist);
+		data->watchlist_lock = RW_LOCK_UNLOCKED;
+
+	}
+
+	return data;
+}
+
+/*
+ * Caller must hold i_audit->watchlist_lock
+ */
+static inline void audit_drain_watchlist(struct audit_data *data)
+{
+	struct audit_wentry *wentry, *tmp;
+
+	if (data) {
+		list_for_each_entry_safe(wentry, tmp, &data->watchlist,
+					 w_list)
+		    audit_destroy_wentry(wentry);
+	}
+}
+
+static inline void audit_drain_watchlist_lock(struct audit_data *data)
+{
+	unsigned long flags;
+	struct audit_wentry *wentry, *tmp;
+
+	if (data) {
+		write_lock_irqsave(&data->watchlist_lock, flags);
+
+		list_for_each_entry_safe(wentry, tmp, &data->watchlist,
+					 w_list)
+		    audit_destroy_wentry(wentry);
+
+		write_unlock_irqrestore(&data->watchlist_lock, flags);
+	}
+}
+
+static inline void audit_data_free(struct audit_data *data)
+{
+	unsigned long flags;
+
+	if (!data)
+		return;
+
+	write_lock_irqsave(&data->watchlist_lock, flags);
+
+	audit_drain_watchlist(data);
+
+	if (data->wentry) {
+		audit_wentry_put(data->wentry);
+		data->wentry = NULL;
+	}
+
+	write_unlock_irqrestore(&data->watchlist_lock, flags);
+
+	kmem_cache_free(audit_data_cache, data);
+}
+
+static struct audit_data *audit_data_get(struct audit_data *data)
+{
+	if (data) {
+		BUG_ON(!atomic_read(&data->count));
+		atomic_inc(&data->count);
+	}
+
+	return data;
+}
+
+static void audit_data_put(struct audit_data *data)
+{
+	if (data && atomic_dec_and_test(&data->count))
+		audit_data_free(data);
+}
+
+
+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,
+					      unsigned int perms)
+{
+	struct audit_watch *err = NULL;
+	struct audit_watch *watch = NULL;
+
+	/* Must have a name */
+	if (!name || strlen(name) > PATH_MAX) {
+		err = ERR_PTR(-EINVAL);
+		goto audit_create_watch_fail;
+	}
+
+	/* Filterkey's are optional */
+	if (filterkey && strlen(filterkey) > AUDIT_FILTERKEY_MAX) {
+		err = ERR_PTR(-EINVAL);
+		goto audit_create_watch_fail;
+	}
+
+	if (perms > 15) {
+		err = ERR_PTR(-EINVAL);
+		goto audit_create_watch_fail;
+	}
+
+	watch = audit_watch_alloc();
+	if (watch) {
+		watch->namelen = strlen(name) + 1;
+		watch->name = kmalloc(watch->namelen, GFP_KERNEL);
+		if (!watch->name) {
+			err = ERR_PTR(-ENOMEM);
+			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) {
+				err = ERR_PTR(-ENOMEM);
+				goto audit_create_watch_fail;
+			}
+			strcpy(watch->filterkey, filterkey);
+		}
+
+		watch->perms = perms;
+
+		goto audit_create_watch_exit;
+	}
+
+	err = ERR_PTR(-ENOMEM);
+
+      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_valid = 0;
+		wentry->w_watch = NULL;
+		wentry->w_data = 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 int audit_create_wentry(const char *name,
+			       const char *filterkey,
+			       unsigned int perms, struct audit_data *data)
+{
+	int ret = 0;
+	unsigned long flags;
+	struct audit_wentry *wentry = NULL;
+	struct audit_wentry *new_wentry = NULL;
+
+	/* This first reference belongs to the watchlist */
+	new_wentry = audit_wentry_alloc();
+	if (!new_wentry) {
+		ret = -ENOMEM;
+		goto audit_create_wentry_fail;
+	}
+
+	new_wentry->w_data = audit_data_alloc();
+	if (!new_wentry->w_data) {
+		ret = -ENOMEM;
+		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;
+	}
+
+	new_wentry->w_data->wentry = audit_wentry_get(new_wentry);
+
+	write_lock_irqsave(&data->watchlist_lock, flags);
+
+	wentry = audit_wentry_fetch(name, data);
+	if (!wentry) {
+		list_add(&new_wentry->w_list, &data->watchlist);
+		new_wentry->w_valid = 1;
+		write_unlock_irqrestore(&data->watchlist_lock, flags);
+		goto audit_create_wentry_exit;
+	}
+	audit_wentry_put(wentry);
+
+	write_unlock_irqrestore(&data->watchlist_lock, flags);
+
+	ret = -EEXIST;
+	audit_wentry_put(new_wentry->w_data->wentry);
+	new_wentry->w_data->wentry = NULL;
+
+      audit_create_wentry_fail:
+	audit_wentry_put(new_wentry);
+	if (!data->wentry && list_empty(&data->watchlist))
+		audit_data_put(data);
+      audit_create_wentry_exit:
+	return ret;
+}
+
+static int audit_insert_watch(struct audit_watch *req)
+{
+	int ret;
+	char *path = NULL;
+	char *filterkey = NULL;
+	struct nameidata nd;
+
+	path = getname(req->name);
+	if (IS_ERR(req->name)) {
+		ret = PTR_ERR(req->name);
+		goto audit_insert_watch_exit;
+	}
+
+	if (req->fklen) {
+		filterkey = kmalloc(req->fklen, GFP_KERNEL);
+		if (!filterkey) {
+			ret = -ENOMEM;
+			goto audit_insert_watch_exit;
+		}
+		strncpy(filterkey, req->filterkey, req->fklen);
+	}
+
+	ret = path_lookup(path, LOOKUP_PARENT | LOOKUP_FOLLOW, &nd);
+	if (ret < 0)
+		goto audit_insert_watch_release;
+
+	if (nd.last_type != LAST_NORM) {
+		ret = -EPERM;
+		goto audit_insert_watch_release;
+	}
+
+	/* Allocate space on our parent to hold the watch, if space
+	 * does not already exist
+	 */
+	if (!nd.dentry->d_inode->i_audit) {
+		nd.dentry->d_inode->i_audit = audit_data_alloc();
+		if (!nd.dentry->d_inode->i_audit) {
+			ret = -ENOMEM;
+			goto audit_insert_watch_release;
+		}
+	}
+
+	ret = audit_create_wentry(nd.last.name, filterkey, req->perms,
+				  nd.dentry->d_inode->i_audit);
+
+      audit_insert_watch_release:
+	path_release(&nd);
+      audit_insert_watch_exit:
+	putname(path);
+	kfree(filterkey);
+	return ret;
+}
+
+static int audit_remove_watch(struct audit_watch *req)
+{
+	int ret;
+	unsigned long flags;
+	char *path = NULL;
+	struct nameidata nd;
+	struct audit_data *data;
+	struct audit_wentry *wentry;
+
+	path = getname(req->name);
+	if (IS_ERR(req->name)) {
+		ret = PTR_ERR(req->name);
+		goto audit_remove_watch_exit;
+	}
+
+	if (!path || (path && strlen(path) > PATH_MAX)) {
+		ret = -EINVAL;
+		goto audit_remove_watch_exit;
+	}
+
+	ret = path_lookup(path, LOOKUP_PARENT | LOOKUP_FOLLOW, &nd);
+	if (ret < 0)
+		goto audit_remove_watch_release;
+
+	data = nd.dentry->d_inode->i_audit;
+	if (!data) {
+		ret = -EACCES;
+		goto audit_remove_watch_release;
+	}
+
+	write_lock_irqsave(&data->watchlist_lock, flags);
+
+	wentry = audit_wentry_fetch(nd.last.name, data);
+	if (!wentry) {
+		ret = -EACCES;
+		write_unlock_irqrestore(&data->watchlist_lock, flags);
+		goto audit_remove_watch_release;
+	}
+	audit_destroy_wentry(wentry);
+	audit_wentry_put(wentry);
+
+	write_unlock_irqrestore(&data->watchlist_lock, flags);
+
+	if (!data->wentry && list_empty(&data->watchlist)) {
+		audit_data_put(data);
+		nd.dentry->d_inode->i_audit = NULL;
+	}
+
+      audit_remove_watch_release:
+	path_release(&nd);
+      audit_remove_watch_exit:
+	putname(path);
+	return ret;
+}
+
+/* Public interface */
+
+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);
+}
+
+/*
+ * Hook appears in: fs/dcache.c:d_instantiate(), d_move(), and d_splice_alias()
+ */
+void audit_watch(struct dentry *dentry, int drain)
+{
+	struct audit_wentry *wentry;
+	struct audit_data *data;
+
+	if (!dentry || !dentry->d_inode)
+		return;
+
+	if (!dentry->d_parent || !dentry->d_parent->d_inode)
+		return;
+
+	wentry = audit_wentry_fetch_lock(dentry->d_name.name,
+					 dentry->d_parent->d_inode->i_audit);
+
+	data = dentry->d_inode->i_audit;
+	if (data) {
+		if (drain)
+			audit_drain_watchlist_lock(data);
+		if (wentry && !list_empty(&data->watchlist)) {
+			audit_data_put(wentry->w_data);
+			wentry->w_data = audit_data_get(data);
+			audit_wentry_put(wentry->w_data->wentry);
+			wentry->w_data->wentry = audit_wentry_get(wentry);
+			audit_data_put(data);
+			dentry->d_inode->i_audit =
+				audit_data_get(wentry->w_data);
+		} else if (wentry && !data->wentry->w_valid) {
+			audit_data_put(data);
+			dentry->d_inode->i_audit =
+				audit_data_get(wentry->w_data);
+		}
+	} else if (wentry)
+		dentry->d_inode->i_audit = audit_data_get(wentry->w_data);
+
+	audit_wentry_put(wentry);
+}
+
+void audit_receive_watch(int type, int pid, int uid, int seq,
+			 struct audit_watch *req)
+{
+	int ret;
+
+	switch (type) {
+	case AUDIT_WATCH_INS:
+		ret = audit_insert_watch(req);
+		break;
+	case AUDIT_WATCH_REM:
+		ret = audit_remove_watch(req);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	audit_send_reply(pid, seq, type, 0, 1, &ret, sizeof(int));
+}
+
+void audit_inode_alloc(struct inode *inode)
+{
+	if (!inode)
+		return;
+
+	inode->i_audit = NULL;
+}
+
+void audit_inode_free(struct inode *inode)
+{
+	if (!inode || !inode->i_audit)
+		return;
+
+	audit_data_put(inode->i_audit);
+	inode->i_audit = NULL;
+}
+
+void audit_filesystem_init()
+{
+	audit_watch_cache =
+	    kmem_cache_create("audit_watch_cache",
+			      sizeof(struct audit_watch), 0, 0,
+			      NULL, NULL);
+	audit_wentry_cache =
+	    kmem_cache_create("audit_wentry_cache",
+			      sizeof(struct audit_wentry), 0, 0,
+			      NULL, NULL);
+	audit_data_cache =
+	    kmem_cache_create("audit_data_cache",
+			      sizeof(struct audit_data), 0, 0, NULL, NULL);
+}
diff -Nurp linux-2.6.10/kernel/auditsc.c linux-2.6.10-audit/kernel/auditsc.c
--- linux-2.6.10/kernel/auditsc.c	2004-12-24 15:35:24.000000000 -0600
+++ linux-2.6.10-audit/kernel/auditsc.c	2005-02-27 18:13:53.000000000 -0600
@@ -19,6 +19,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>
  *
  * Many of the ideas implemented here are from Stephen C. Tweedie,
  * especially the idea of avoiding a copy by using getname.
@@ -49,6 +50,11 @@
 /* No syscall auditing will take place unless audit_enabled != 0. */
 extern int audit_enabled;
 
+/* No syscall will collect information about auditable file/dirs
+ * unless auditfs_enabled != 0
+ */
+extern int auditfs_enabled;
+
 /* AUDIT_NAMES is the number of slots we reserve in the audit_context
  * for saving names from getname(). */
 #define AUDIT_NAMES    20
@@ -113,6 +119,10 @@ struct audit_context {
 	uid_t		    uid, euid, suid, fsuid;
 	gid_t		    gid, egid, sgid, fsgid;
 	unsigned long	    personality;
+	struct list_head    trail; /* The list of watched files/dirs that were
+				    * accessed and determined to be valid and
+				    * unfiltered in this audit_context
+				    */
 
 #if AUDIT_DEBUG
 	int		    put_count;
@@ -134,6 +144,21 @@ struct audit_entry {
 	struct audit_rule rule;
 };
 
+/* The structure that stores information about files/directories being
+ * watched in the filesystem, that the syscall accessed.
+ */
+
+struct audit_file {
+	struct audit_wentry *wentry;
+	struct list_head list;
+	unsigned long ino;
+	umode_t mode;
+	uid_t uid;
+	gid_t gid;
+	dev_t rdev;
+	int mask;
+};
+
 /* Check to see if two rules are identical.  It is called from
  * audit_del_rule during AUDIT_DEL. */
 static int audit_compare_rule(struct audit_rule *a, struct audit_rule *b)
@@ -506,6 +531,37 @@ static inline void audit_free_names(stru
 	context->name_count = 0;
 }
 
+static struct audit_file *audit_file_alloc(void)
+{
+	struct audit_file *file;
+
+	file = kmalloc(sizeof(struct audit_file), GFP_KERNEL);
+
+	if (file)
+		file->wentry = NULL;
+
+	return file;
+}
+
+static void audit_file_free(struct audit_file *file)
+{
+	if (file) {
+		audit_wentry_put(file->wentry);
+		file->wentry = NULL;
+		kfree(file);
+	}
+}
+
+static inline void audit_free_files(struct audit_context *context)
+{
+	struct audit_file *file, *tmp;
+
+	list_for_each_entry_safe(file, tmp, &context->trail, list) {
+	        list_del(&file->list);
+		audit_file_free(file);
+	}
+}
+
 static inline void audit_zero_context(struct audit_context *context,
 				      enum audit_state state)
 {
@@ -514,6 +570,7 @@ static inline void audit_zero_context(st
 	memset(context, 0, sizeof(*context));
 	context->state      = state;
 	context->loginuid   = loginuid;
+	INIT_LIST_HEAD(&context->trail);
 }
 
 static inline struct audit_context *audit_alloc_context(enum audit_state state)
@@ -572,6 +629,7 @@ static inline void audit_free_context(st
 			       context->name_count, count);
 		}
 		audit_free_names(context);
+		audit_free_files(context);
 		kfree(context);
 		context  = previous;
 	} while (context);
@@ -582,6 +640,7 @@ static inline void audit_free_context(st
 static void audit_log_exit(struct audit_context *context)
 {
 	int i;
+	struct audit_file *file;
 	struct audit_buffer *ab;
 
 	ab = audit_log_start(context);
@@ -591,7 +650,7 @@ static void audit_log_exit(struct audit_
 	if (context->personality != PER_LINUX)
 		audit_log_format(ab, " per=%lx", context->personality);
 	if (context->return_valid)
-		audit_log_format(ab, " exit=%u", context->return_code);
+		audit_log_format(ab, " exit=%d",  context->return_code);
 	audit_log_format(ab,
 		  " a0=%lx a1=%lx a2=%lx a3=%lx items=%d"
 		  " pid=%d loginuid=%d uid=%d gid=%d"
@@ -628,6 +687,27 @@ static void audit_log_exit(struct audit_
 					 MINOR(context->names[i].rdev));
 		audit_log_end(ab);
 	}
+
+	if (!auditfs_enabled)
+		return;
+
+	list_for_each_entry(file, &context->trail, list) {
+		ab = audit_log_start(context);
+		if (!ab)
+			continue;
+
+		/* Do we need more information? */
+		audit_log_format(ab,
+			"name=%s filter_key=%s perm_mask=%u perm=%d inode=%lu "
+			"inode_mode=%d inode_uid=%d inode_gid=%d "
+			"inode_dev=%02x:%02x",
+			file->wentry->w_watch->name,
+			file->wentry->w_watch->filterkey,
+			file->wentry->w_watch->perms,
+			file->mask, file->ino, file->mode, file->uid,
+			file->gid, MAJOR(file->rdev), MINOR(file->rdev));
+		audit_log_end(ab);
+	}
 }
 
 /* Free a per-task audit context.  Called from copy_process and
@@ -791,6 +871,7 @@ void audit_syscall_exit(struct task_stru
 		tsk->audit_context = new_context;
 	} else {
 		audit_free_names(context);
+		audit_free_files(context);
 		audit_zero_context(context, context->state);
 		tsk->audit_context = context;
 	}
@@ -914,3 +995,64 @@ int audit_set_loginuid(struct audit_cont
 	}
 	return 0;
 }
+
+/* If file/dir has an audit_context and has filesystem auditing
+ * turned on, then if this inode is being watched, collect info
+ * about it.
+ *
+ * Hook located in: fs/namie.c:permission()
+ */
+
+int audit_notify_watch(struct inode *inode, int mask)
+{
+	int ret = 0;
+	struct audit_context *context;
+	struct audit_file *file;
+
+	/* Do we care about filesystem auditing? */
+	if (!auditfs_enabled)
+		goto audit_notify_watch_exit;
+
+
+	/* Do we have a context and are we in a syscall? */
+	context = current->audit_context;
+	if (!context || !context->in_syscall)
+		goto audit_notify_watch_exit;
+
+	/* Are we being watched? */
+	if (inode && (!inode->i_audit || !inode->i_audit->wentry))
+		goto audit_notify_watch_exit;
+
+	/* If we're pointing at an invalid watchlist entry ignore us */
+	if (!inode->i_audit->wentry->w_valid)
+		goto audit_notify_watch_exit;
+
+	/* Permissions (READ/WRITE/EXEC/APPEND) filter.
+	 * If mask || w_watch->perms == 0, no filtering, audit on all
+	 */
+	if (!mask || (inode->i_audit->wentry->w_watch->perms &&
+	    !(inode->i_audit->wentry->w_watch->perms & mask))) {
+	    	audit_free_files(context);
+		goto audit_notify_watch_exit;
+	}
+
+	/* Setup */
+	file = audit_file_alloc();
+	if (!file) {
+		ret = -ENOMEM;
+		goto audit_notify_watch_exit;
+	}
+
+	file->wentry = audit_wentry_get(inode->i_audit->wentry);
+	file->ino = inode->i_ino;
+	file->uid = inode->i_uid;
+	file->gid = inode->i_gid;
+	file->rdev = inode->i_rdev;
+	file->mask = mask;
+
+	list_add(&file->list, &context->trail);
+
+	audit_notify_watch_exit:
+	return ret;
+}
+

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