linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC][PATCH] (#2) file system auditing
@ 2005-04-16 10:06 Timothy R. Chavez
  2005-04-20  7:00 ` Maneesh Soni
  0 siblings, 1 reply; 3+ messages in thread
From: Timothy R. Chavez @ 2005-04-16 10:06 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: Stephen Smalley, Chris Wright, David Woodhouse, maneesh

Hello,

The audit subsystem is currently incapable of auditing a file system object 
based on its location and name.  This is critical for auditing well-defined 
and security-relevant locations such as /etc/shadow, where the file is 
re-created on each transaction, and cannot rely on the (device, inode)-based 
filters to ensure persistence of auditing across transactions. This patch adds 
the necessary functionality to the audit subsystem and VFS to support file 
system auditing in which an object is audited based on its location and name.  
This work is being done to make the audit subsystem compliant with Common 
Criteria's Controlled Access Protection Profile (CAPP) specification.

This is the second (#2) RFC on linux-fsdevel.

I've made some updates to the file system auditing code.  The file system 
hooks remain unchanged, but I would still appreciate feedback.  Since the
audit patchset was temporarily removed from the mm tree, the patch, which 
appears at the bottom of this message, was diffed against linux-2.6.12-rc2-mm1
again.

The most notable updates/changes are as follows:

1.

Updated handling of watch data between user and kernel.  Introduced a new 
struct, audit_transport, which is used as a common means by the kernel and
user to send audit_watch information via netlink to each other.

2.

A master watchlist was added.  I've added a global linked list that keeps 
tabs on all the watches in the file system.  This list is currently used to 
send back to the user space a list of all the watches.  The path used to add 
the watch and the device the watch was added on are now stored with the 
watch.  Before sending the watch back to user space the path is walked and 
checked for the watch.  If the path can be walked from this [view of the] 
file system and the watch is found, the watch is returned as 'valid' 
otherwise it is returned as 'invalid'.

3.  

A bug was fixed by which one could create a hardlink to a file, watch the 
file, and access the hardlink first, subverting the audit subsystem.  Now, 
when a watch is inserted, the inode is updated ASAP.

-tim

diff -Nurp linux-2.6.12-rc2-mm1~orig/fs/dcache.c linux-2.6.12-rc2-mm1~audit/fs/dcache.c
--- linux-2.6.12-rc2-mm1~orig/fs/dcache.c	2005-04-11 14:14:36.000000000 +0000
+++ linux-2.6.12-rc2-mm1~audit/fs/dcache.c	2005-04-05 18:16:04.000000000 +0000
@@ -32,6 +32,7 @@
 #include <linux/seqlock.h>
 #include <linux/swap.h>
 #include <linux/bootmem.h>
+#include <linux/audit.h>
 
 /* #define DCACHE_DEBUG 1 */
 
@@ -97,6 +98,7 @@ static inline void dentry_iput(struct de
 {
 	struct inode *inode = dentry->d_inode;
 	if (inode) {
+		audit_attach_watch(dentry, 1);
 		dentry->d_inode = NULL;
 		list_del_init(&dentry->d_alias);
 		spin_unlock(&dentry->d_lock);
@@ -802,6 +804,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);
 }
@@ -978,6 +981,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);
@@ -987,6 +991,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);
@@ -1090,6 +1095,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;
@@ -1299,6 +1305,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;
@@ -1332,6 +1340,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.12-rc2-mm1~orig/fs/inode.c linux-2.6.12-rc2-mm1~audit/fs/inode.c
--- linux-2.6.12-rc2-mm1~orig/fs/inode.c	2005-04-11 14:14:37.000000000 +0000
+++ linux-2.6.12-rc2-mm1~audit/fs/inode.c	2005-04-05 18:16:04.000000000 +0000
@@ -22,6 +22,7 @@
 #include <linux/cdev.h>
 #include <linux/bootmem.h>
 #include <linux/inotify.h>
+#include <linux/audit.h>
 
 /*
  * This is needed for the following functions:
@@ -140,9 +141,11 @@ static struct inode *alloc_inode(struct 
 		inode->i_bdev = NULL;
 		inode->i_cdev = NULL;
 		inode->i_rdev = 0;
+		inode->i_audit = NULL;
 		inode->i_security = NULL;
 		inode->dirtied_when = 0;
-		if (security_inode_alloc(inode)) {
+		if (audit_inode_alloc(inode) || security_inode_alloc(inode)) {
+			audit_inode_free(inode);
 			if (inode->i_sb->s_op->destroy_inode)
 				inode->i_sb->s_op->destroy_inode(inode);
 			else
@@ -180,6 +183,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.12-rc2-mm1~orig/fs/namei.c linux-2.6.12-rc2-mm1~audit/fs/namei.c
--- linux-2.6.12-rc2-mm1~orig/fs/namei.c	2005-04-11 14:14:36.000000000 +0000
+++ linux-2.6.12-rc2-mm1~audit/fs/namei.c	2005-04-05 18:16:04.000000000 +0000
@@ -225,6 +225,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;
 
@@ -358,6 +360,8 @@ static inline int exec_permission_lite(s
 	if (inode->i_op && inode->i_op->permission)
 		return -EAGAIN;
 
+	audit_notify_watch(inode, MAY_EXEC);
+
 	if (current->fsuid == inode->i_uid)
 		mode >>= 6;
 	else if (in_group_p(inode->i_gid))
@@ -1172,6 +1176,8 @@ static inline int may_delete(struct inod
 
 	BUG_ON(victim->d_parent->d_inode != dir);
 
+	audit_notify_watch(victim->d_inode, MAY_WRITE);
+
 	error = permission(dir,MAY_WRITE | MAY_EXEC, NULL);
 	if (error)
 		return error;
@@ -1296,6 +1302,7 @@ int vfs_create(struct inode *dir, struct
 	DQUOT_INIT(dir);
 	error = dir->i_op->create(dir, dentry, mode, nd);
 	if (!error) {
+		audit_notify_watch(dentry->d_inode, MAY_WRITE);
 		fsnotify_create(dir, dentry->d_name.name);
 		security_inode_post_create(dir, dentry, mode);
 	}
@@ -1601,6 +1608,7 @@ int vfs_mknod(struct inode *dir, struct 
 	DQUOT_INIT(dir);
 	error = dir->i_op->mknod(dir, dentry, mode, dev);
 	if (!error) {
+		audit_notify_watch(dentry->d_inode, MAY_WRITE);
 		fsnotify_create(dir, dentry->d_name.name);
 		security_inode_post_mknod(dir, dentry, mode, dev);
 	}
@@ -1674,6 +1682,7 @@ int vfs_mkdir(struct inode *dir, struct 
 	DQUOT_INIT(dir);
 	error = dir->i_op->mkdir(dir, dentry, mode);
 	if (!error) {
+		audit_notify_watch(dentry->d_inode, MAY_WRITE);
 		fsnotify_mkdir(dir, dentry->d_name.name);
 		security_inode_post_mkdir(dir,dentry, mode);
 	}
@@ -1915,6 +1924,7 @@ int vfs_symlink(struct inode *dir, struc
 	DQUOT_INIT(dir);
 	error = dir->i_op->symlink(dir, dentry, oldname);
 	if (!error) {
+		audit_notify_watch(dentry->d_inode, MAY_WRITE);
 		fsnotify_create(dir, dentry->d_name.name);
 		security_inode_post_symlink(dir, dentry, oldname);
 	}
@@ -1988,6 +1998,7 @@ int vfs_link(struct dentry *old_dentry, 
 	error = dir->i_op->link(old_dentry, dir, new_dentry);
 	up(&old_dentry->d_inode->i_sem);
 	if (!error) {
+		audit_notify_watch(new_dentry->d_inode, MAY_WRITE);
 		fsnotify_create(dir, new_dentry->d_name.name);
 		security_inode_post_link(old_dentry, dir, new_dentry);
 	}
@@ -2111,6 +2122,7 @@ int vfs_rename_dir(struct inode *old_dir
 	}
 	if (!error) {
 		d_move(old_dentry,new_dentry);
+		audit_notify_watch(old_dentry->d_inode, MAY_WRITE);
 		security_inode_post_rename(old_dir, old_dentry,
 					   new_dir, new_dentry);
 	}
@@ -2139,6 +2151,7 @@ int vfs_rename_other(struct inode *old_d
 		/* The following d_move() should become unconditional */
 		if (!(old_dir->i_sb->s_type->fs_flags & FS_ODD_RENAME))
 			d_move(old_dentry, new_dentry);
+		audit_notify_watch(old_dentry->d_inode, MAY_WRITE);
 		security_inode_post_rename(old_dir, old_dentry, new_dir, new_dentry);
 	}
 	if (target)
diff -Nurp linux-2.6.12-rc2-mm1~orig/include/linux/audit.h linux-2.6.12-rc2-mm1~audit/include/linux/audit.h
--- linux-2.6.12-rc2-mm1~orig/include/linux/audit.h	2005-04-11 14:15:03.000000000 +0000
+++ linux-2.6.12-rc2-mm1~audit/include/linux/audit.h	2005-04-11 14:16:25.000000000 +0000
@@ -24,18 +24,27 @@
 #ifndef _LINUX_AUDIT_H_
 #define _LINUX_AUDIT_H_
 
+#ifdef __KERNEL__
 #include <linux/sched.h>
 #include <linux/elf.h>
 
+#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 informaiton */
+#define AUDIT_WATCH_INS		1007 /* Insert file/dir watch entry */
+#define AUDIT_WATCH_REM		1008 /* Remove file/dir watch entry */
+#define AUDIT_WATCH_LIST	1009 /* List all watches */
+#define AUDIT_KERNEL		2000 /* Asynchronous audit record. NOT A REQUEST. */
 
 /* Rule flags */
 #define AUDIT_PER_TASK 0x01	/* Apply rule at task creation (not syscall) */
@@ -132,6 +141,9 @@
 #define AUDIT_ARCH_V850		(EM_V850|__AUDIT_ARCH_LE)
 #define AUDIT_ARCH_X86_64	(EM_X86_64|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE)
 
+/* 32 byte max key size */
+#define AUDIT_FILTERKEY_MAX	32
+
 #ifndef __KERNEL__
 struct audit_message {
 	struct nlmsghdr nlh;
@@ -159,8 +171,41 @@ struct audit_rule {		/* for AUDIT_LIST, 
 	__u32		values[AUDIT_MAX_FIELDS];
 };
 
+/* Structure to transport watch data to and from the kernel */
+
+struct audit_transport {
+	__u32 dev;
+	__u32 perms;
+	__u32 valid;
+	__u32 pathlen;
+	__u32 fklen;
+	char buf[0];
+};
+
 #ifdef __KERNEL__
 
+struct audit_data {
+	struct audit_wentry	*wentry;
+	struct hlist_head 	watchlist;
+	rwlock_t		lock;
+};
+
+struct audit_watch {
+	dev_t	dev;		/* Superblock device	      */
+	__u32	perms;		/* Permissions filtering      */
+	char	*name;		/* Watch point beneath parent */
+	char 	*path;		/* Insertion path             */
+	char	*filterkey;	/* An arbitrary filtering key */
+};
+
+struct audit_wentry {
+	struct hlist_node 	w_node;
+	struct hlist_node	w_master;
+	struct audit_watch	*w_watch;
+	atomic_t		w_count;
+
+};
+
 struct audit_buffer;
 struct audit_context;
 struct inode;
@@ -190,16 +235,40 @@ extern void audit_get_stamp(struct audit
 extern int  audit_set_loginuid(struct audit_context *ctx, uid_t loginuid);
 extern uid_t audit_get_loginuid(struct audit_context *ctx);
 extern int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode);
+extern int audit_notify_watch(struct inode *inode, int mask);
 #else
 #define audit_alloc(t) ({ 0; })
 #define audit_free(t) do { ; } while (0)
 #define audit_syscall_entry(t,ta,a,b,c,d,e) do { ; } while (0)
 #define audit_syscall_exit(t,f,r) do { ; } while (0)
+#define audit_receive_filter(t,p,u,s,d) ({ -EOPNOTSUPP; })
 #define audit_getname(n) do { ; } while (0)
 #define audit_putname(n) do { ; } while (0)
 #define audit_inode(n,i) do { ; } while (0)
 #define audit_get_loginuid(c) ({ -1; })
 #define audit_ipc_perms(q,u,g,m) ({ 0; })
+#define audit_notify_watch(i,m) ({ 0; })
+#endif
+
+#ifdef CONFIG_AUDITFILESYSTEM
+extern int audit_list_watches(int pid, int seq);
+extern int audit_receive_watch(int type, int pid, int uid, int seq,
+			       struct audit_transport *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_wentry_put(struct audit_wentry *wentry);
+extern struct audit_wentry *audit_wentry_get(struct audit_wentry *wentry);
+#else
+#define audit_list_watches(p,s) ({ -EOPNOTSUPP; })
+#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_wentry_put(w) do { ; } while(0)
+#define audit_wentry_get(w) ({ 0; })
 #endif
 
 #ifdef CONFIG_AUDIT
diff -Nurp linux-2.6.12-rc2-mm1~orig/include/linux/fs.h linux-2.6.12-rc2-mm1~audit/include/linux/fs.h
--- linux-2.6.12-rc2-mm1~orig/include/linux/fs.h	2005-04-11 14:15:02.000000000 +0000
+++ linux-2.6.12-rc2-mm1~audit/include/linux/fs.h	2005-04-05 21:11:48.000000000 +0000
@@ -214,6 +214,7 @@ extern int dir_notify_enable;
 #include <linux/prio_tree.h>
 #include <linux/init.h>
 #include <linux/sched.h>
+#include <linux/audit.h>
 
 #include <asm/atomic.h>
 #include <asm/semaphore.h>
@@ -477,6 +478,7 @@ struct inode {
 	struct semaphore	inotify_sem;	/* protects the watches list */
 #endif
 
+	struct audit_data	*i_audit;
 	unsigned long		i_state;
 	unsigned long		dirtied_when;	/* jiffies of first dirtying */
 
diff -Nurp linux-2.6.12-rc2-mm1~orig/init/Kconfig linux-2.6.12-rc2-mm1~audit/init/Kconfig
--- linux-2.6.12-rc2-mm1~orig/init/Kconfig	2005-04-11 14:15:38.000000000 +0000
+++ linux-2.6.12-rc2-mm1~audit/init/Kconfig	2005-04-05 18:16:26.000000000 +0000
@@ -198,6 +198,16 @@ config AUDITSYSCALL
 	  can be used independently or with another kernel subsystem,
 	  such as SELinux.
 
+config AUDITFILESYSTEM
+	bool "Enable file system auditing support"
+	depends on AUDITSYSCALL
+	default n
+	help
+	  Enable file system auditing for regular files and directories.
+	  When a targeted file or directory is accessed, an audit record
+	  is generated describing the inode accessed, how it was accessed,
+	  and by whom (ie: pid and system call).
+
 config HOTPLUG
 	bool "Support for hot-pluggable devices" if !ARCH_S390
 	default ARCH_S390
diff -Nurp linux-2.6.12-rc2-mm1~orig/kernel/Makefile linux-2.6.12-rc2-mm1~audit/kernel/Makefile
--- linux-2.6.12-rc2-mm1~orig/kernel/Makefile	2005-04-11 14:15:35.000000000 +0000
+++ linux-2.6.12-rc2-mm1~audit/kernel/Makefile	2005-04-05 18:21:20.000000000 +0000
@@ -25,6 +25,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_DETECT_SOFTLOCKUP) += softlockup.o
diff -Nurp linux-2.6.12-rc2-mm1~orig/kernel/audit.c linux-2.6.12-rc2-mm1~audit/kernel/audit.c
--- linux-2.6.12-rc2-mm1~orig/kernel/audit.c	2005-04-11 14:15:36.000000000 +0000
+++ linux-2.6.12-rc2-mm1~audit/kernel/audit.c	2005-04-08 16:12:29.000000000 +0000
@@ -322,6 +322,9 @@ 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:
+	case AUDIT_WATCH_LIST:
 		if (!cap_raised(eff_cap, CAP_AUDIT_CONTROL))
 			err = -EPERM;
 		break;
@@ -409,12 +412,18 @@ static int audit_receive_msg(struct sk_b
 			return -EINVAL;
 		/* fallthrough */
 	case AUDIT_LIST:
-#ifdef CONFIG_AUDITSYSCALL
 		err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid,
 					   uid, seq, data);
-#else
-		err = -EOPNOTSUPP;
-#endif
+	case AUDIT_WATCH_LIST:
+		err = audit_list_watches(NETLINK_CB(skb).pid, seq);
+		break;
+	case AUDIT_WATCH_INS:
+	case AUDIT_WATCH_REM:
+		if (nlh->nlmsg_len < sizeof(struct audit_transport))
+			return -EINVAL;
+		err = audit_receive_watch(nlh->nlmsg_type,
+					  NETLINK_CB(skb).pid,
+					  uid, seq, data);
 		break;
 	default:
 		err = -EINVAL;
@@ -560,6 +569,7 @@ static 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.12-rc2-mm1~orig/kernel/auditfs.c linux-2.6.12-rc2-mm1~audit/kernel/auditfs.c
--- linux-2.6.12-rc2-mm1~orig/kernel/auditfs.c	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.12-rc2-mm1~audit/kernel/auditfs.c	2005-04-12 10:26:35.000000000 +0000
@@ -0,0 +1,560 @@
+/* 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;
+
+spinlock_t audit_master_watchlist_lock;
+HLIST_HEAD(audit_master_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->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 inline struct audit_watch *audit_create_watch(const char *path,
+						     const char *name,
+						     const char *filterkey,
+						     __u32 perms, dev_t dev)
+{
+	struct audit_watch *err = NULL;
+	struct audit_watch *watch = NULL;
+
+	err = ERR_PTR(-ENOMEM);
+	watch = audit_watch_alloc();
+	if (watch) {
+		watch->path = kmalloc(strlen(path)+1, GFP_KERNEL);
+		if (!watch->path)
+			goto audit_create_watch_fail;
+		strcpy(watch->path, path);
+		watch->name = kmalloc(strlen(name)+1, GFP_KERNEL);
+		if (!watch->name)
+			goto audit_create_watch_fail;
+		strcpy(watch->name, name);
+
+		if (filterkey) {
+			watch->filterkey = kmalloc(strlen(filterkey)+1,
+						   GFP_KERNEL);
+			if (!watch->filterkey)
+				goto audit_create_watch_fail;
+			strcpy(watch->filterkey, filterkey);
+		}
+
+		watch->dev = dev;
+		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_master_insert(struct audit_wentry *wentry)
+{
+	wentry = audit_wentry_get(wentry);
+	if (wentry) {
+		spin_lock(&audit_master_watchlist_lock);
+		hlist_add_head(&wentry->w_master, &audit_master_watchlist);
+		spin_unlock(&audit_master_watchlist_lock);
+	}
+}
+
+/*
+ * Remove entry from the master watchlist.  To ensure synchronized removal
+ * from both the directory watchlist and master watchlist, watchlist_lock
+ * should first be held so that audit_master_remove cannot be called on
+ * the same wentry.
+ */
+
+static inline void audit_master_remove(struct audit_wentry *wentry)
+{
+	if (wentry) {
+		spin_lock(&audit_master_watchlist_lock);
+		hlist_del_init(&wentry->w_master);
+		audit_wentry_put(wentry);
+		spin_unlock(&audit_master_watchlist_lock);
+	}
+}
+
+
+/* The only time the new wentry gets updated is when it is inaccessible
+ * (out of the list).
+ */
+static inline int audit_create_wentry(const char *path,
+				      const char *name,
+				      const char *filterkey,
+				      __u32 perms, dev_t dev,
+				      struct audit_data *data)
+{
+	int ret;
+	struct audit_wentry *wentry = NULL;
+	struct audit_wentry *new = NULL;
+
+	ret = -ENOMEM;
+	new = audit_wentry_alloc();
+	if (!new)
+		goto audit_create_wentry_fail;
+
+	new->w_watch = audit_create_watch(path, name, filterkey, perms, dev);
+	if (IS_ERR(new->w_watch)) {
+		ret = PTR_ERR(new->w_watch);
+		new->w_watch = NULL;
+		goto audit_create_wentry_fail;
+	}
+
+	ret = 0;
+	write_lock(&data->lock);
+	wentry = audit_wentry_fetch(name, data);
+	if (!wentry) {
+		audit_master_insert(new);
+		hlist_add_head(&new->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);
+audit_create_wentry_exit:
+	return ret;
+}
+
+/* Caller must hold watchlist_lock */
+
+static inline void audit_destroy_wentry(struct audit_wentry *wentry)
+{
+	if (wentry) {
+		audit_master_remove(wentry);
+		hlist_del_init(&wentry->w_node);
+		audit_wentry_put(wentry);
+	}
+}
+
+
+/* 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 inline 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_exit;
+
+	ret = -EPERM;
+	if (nd.last_type != LAST_NORM || !nd.last.name)
+		goto audit_insert_watch_release;
+
+	ret = audit_create_wentry(path, nd.last.name, filterkey, perms,
+				  nd.dentry->d_inode->i_sb->s_dev,
+				  nd.dentry->d_inode->i_audit);
+	/* __d_lookup will attach the audit data, if nd.last.name exists. */
+	d_lookup(nd.dentry, &nd.last);
+
+audit_insert_watch_release:
+	path_release(&nd);
+audit_insert_watch_exit:
+	return ret;
+}
+
+static inline int audit_remove_watch(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_exit;
+
+	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);
+audit_remove_watch_exit:
+	return ret;
+}
+
+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_move(),
+ * 	d_delete(),
+ *	d_instantiate(),
+ *	d_splice_alias()
+ *	__d_lookup()
+ */
+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;
+
+	wentry = audit_wentry_fetch_lock(dentry->d_name.name, parent);
+
+	write_lock(&data->lock);
+	/* 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);
+}
+
+int audit_send_watch(int pid, int seq, struct audit_watch *watch)
+{
+	int ret;
+	void *memblk;
+	unsigned int offset;
+	unsigned int total;
+	struct audit_data *data;
+	struct audit_wentry *wentry;
+	struct audit_transport req;
+	struct nameidata nd;
+
+	req.valid = 0;
+	req.dev = watch->dev;
+	req.perms = watch->perms;
+	req.pathlen = strlen(watch->path) + 1;
+	req.fklen = strlen(watch->filterkey) + 1;
+
+	ret = -ENOMEM;
+	total = sizeof(req) + req.pathlen + req.fklen;
+	memblk = kmalloc(total, GFP_KERNEL);
+	if (!memblk)
+		goto audit_send_watch_exit;
+
+	/* See if path to watch is valid */
+	ret = path_lookup(watch->path, LOOKUP_PARENT|LOOKUP_FOLLOW, &nd);
+	if (!ret) {
+		data = nd.dentry->d_inode->i_audit;
+		wentry = audit_wentry_fetch_lock(watch->name, data);
+		if (wentry && nd.dentry->d_inode->i_sb->s_dev == watch->dev) {
+			req.valid = 1;
+			audit_wentry_put(wentry);
+		}
+		path_release(&nd);
+	}
+
+	/* Payload */
+	memcpy(memblk, &req, sizeof(req));
+	offset = total - req.fklen;
+	memcpy(memblk + offset, watch->filterkey, req.fklen);
+	offset = offset - req.pathlen;
+	memcpy(memblk + offset, watch->path, req.pathlen);
+
+	audit_send_reply(pid, seq, AUDIT_WATCH_LIST, 0, 1, memblk, total);
+
+	kfree(memblk);
+
+audit_send_watch_exit:
+	return ret;
+}
+
+int audit_list_watches(int pid, int seq)
+{
+	int ret = 0;
+	struct audit_wentry *wentry;
+	struct hlist_node *pos;
+
+	spin_lock(&audit_master_watchlist_lock);
+	hlist_for_each_entry(wentry, pos, &audit_master_watchlist, w_master) {
+		ret = audit_send_watch(pid, seq, wentry->w_watch);
+		if (ret < 0)
+			break;
+	}
+	spin_unlock(&audit_master_watchlist_lock);
+
+	audit_send_reply(pid, seq, AUDIT_WATCH_LIST, 1, 1, NULL, 0);
+
+	return ret;
+}
+
+int audit_receive_watch(int type, int pid, int uid, int seq,
+			struct audit_transport *req)
+{
+	int ret = 0;
+	char *path = NULL;
+	char *filterkey = NULL;
+
+	ret = -ENOMEM;
+	path = kmalloc(req->pathlen, GFP_KERNEL);
+	if (!path)
+		goto audit_receive_watch_exit;
+	strncpy(path, req->buf, req->pathlen);
+
+	if (req->fklen) {
+		filterkey = kmalloc(req->fklen, GFP_KERNEL);
+		if (!filterkey)
+			goto audit_receive_watch_exit;
+	}
+	strncpy(filterkey, req->buf+req->pathlen, req->fklen);
+
+	ret = -EINVAL;
+	if (!path || strlen(path) + 1 > PATH_MAX)
+		goto audit_receive_watch_exit;
+
+	/* Includes terminating '\0' */
+	if (req->fklen > AUDIT_FILTERKEY_MAX)
+		goto audit_receive_watch_exit;
+
+	if (req->perms > (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND))
+		goto audit_receive_watch_exit;
+
+	switch (type) {
+	case AUDIT_WATCH_INS:
+		ret = audit_insert_watch(path, filterkey, req->perms);
+		break;
+	case AUDIT_WATCH_REM:
+		ret = audit_remove_watch(path);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+audit_receive_watch_exit:
+	kfree(path);
+	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.12-rc2-mm1~orig/kernel/auditsc.c linux-2.6.12-rc2-mm1~audit/kernel/auditsc.c
--- linux-2.6.12-rc2-mm1~orig/kernel/auditsc.c	2005-04-11 14:15:36.000000000 +0000
+++ linux-2.6.12-rc2-mm1~audit/kernel/auditsc.c	2005-04-08 16:12:23.000000000 +0000
@@ -102,6 +102,7 @@ struct audit_aux_data {
 };
 
 #define AUDIT_AUX_IPCPERM	0
+#define AUDIT_AUX_WATCH		1
 
 struct audit_aux_data_ipcctl {
 	struct audit_aux_data	d;
@@ -112,6 +113,16 @@ struct audit_aux_data_ipcctl {
 	mode_t			mode;
 };
 
+struct audit_aux_data_watched {
+	struct audit_aux_data	link;
+	struct audit_wentry 	*wentry;
+	unsigned long		ino;
+	int			mask;
+	uid_t			uid;
+	gid_t			gid;
+	dev_t			dev;
+	dev_t			rdev;
+};
 
 /* The per-task audit context. */
 struct audit_context {
@@ -665,6 +676,24 @@ static void audit_log_exit(struct audit_
 			audit_log_format(ab, 
 					 " qbytes=%lx uid=%d gid=%d mode=%x",
 					 axi->qbytes, axi->uid, axi->gid, axi->mode);
+			break;
+			}
+
+		case AUDIT_AUX_WATCH: {
+			struct audit_aux_data_watched *axi = (void *)aux;
+			audit_log_format(ab, " name=");
+			audit_log_untrustedstring(ab, axi->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",
+				axi->wentry->w_watch->filterkey,
+				axi->wentry->w_watch->perms,
+				axi->mask, axi->ino, axi->uid, axi->gid,
+				MAJOR(axi->dev), MINOR(axi->dev),
+				MAJOR(axi->rdev), MINOR(axi->rdev));
+				audit_wentry_put(axi->wentry);
+			break;
 			}
 		}
 		audit_log_end(ab);
@@ -1024,3 +1053,52 @@ int audit_ipc_perms(unsigned long qbytes
 	context->aux = (void *)ax;
 	return 0;
 }
+
+int audit_notify_watch(struct inode *inode, int mask)
+{
+	int ret = 0;
+	struct audit_context *context = current->audit_context;
+	struct audit_aux_data_watched *ax;
+	struct audit_wentry *wentry = NULL;
+
+	if (likely(!context))
+		goto audit_notify_watch_fail;
+
+	if (!inode)
+		goto audit_notify_watch_fail;
+
+	wentry = audit_wentry_get(inode->i_audit->wentry);
+	if (!wentry)
+		goto audit_notify_watch_fail;
+
+	if (mask && (wentry->w_watch->perms && !(wentry->w_watch->perms&mask)))
+		goto audit_notify_watch_fail;
+
+	ret = -ENOMEM;
+	ax = kmalloc(sizeof(*ax), GFP_KERNEL);
+	if (!ax)
+		goto audit_notify_watch_fail;
+
+	ret = 0;
+	if (context->in_syscall && !context->auditable)
+		context->auditable = 1;
+
+	ax->wentry = wentry;
+	ax->mask = mask;
+	ax->ino = inode->i_ino;
+	ax->uid = inode->i_uid;
+	ax->gid = inode->i_gid;
+	ax->dev = inode->i_sb->s_dev;
+	ax->rdev = inode->i_rdev;
+
+	ax->link.type = AUDIT_AUX_WATCH;
+	ax->link.next = context->aux;
+	context->aux = (void *)ax;
+
+	goto audit_notify_watch_exit;
+
+audit_notify_watch_fail:
+	audit_wentry_put(wentry);
+audit_notify_watch_exit:
+	return ret;
+}
diff -Nurp linux-2.6.12-rc2-mm1~orig/security/selinux/nlmsgtab.c linux-2.6.12-rc2-mm1~audit/security/selinux/nlmsgtab.c
--- linux-2.6.12-rc2-mm1~orig/security/selinux/nlmsgtab.c	2005-04-11 14:15:36.000000000 +0000
+++ linux-2.6.12-rc2-mm1~audit/security/selinux/nlmsgtab.c	2005-04-05 22:58:51.000000000 +0000
@@ -98,6 +98,9 @@ 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 },
+	{ AUDIT_WATCH_LIST,	NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
 };
 

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [RFC][PATCH] (#2) file system auditing
  2005-04-16 10:06 [RFC][PATCH] (#2) file system auditing Timothy R. Chavez
@ 2005-04-20  7:00 ` Maneesh Soni
  2005-04-26 20:23   ` Timothy R. Chavez
  0 siblings, 1 reply; 3+ messages in thread
From: Maneesh Soni @ 2005-04-20  7:00 UTC (permalink / raw)
  To: Timothy R. Chavez
  Cc: linux-fsdevel, Stephen Smalley, Chris Wright, David Woodhouse

On Sat, Apr 16, 2005 at 10:06:52AM +0000, Timothy R. Chavez wrote:
> Hello,
> 
> The audit subsystem is currently incapable of auditing a file system object 
> based on its location and name.  This is critical for auditing well-defined 
> and security-relevant locations such as /etc/shadow, where the file is 
> re-created on each transaction, and cannot rely on the (device, inode)-based 
> filters to ensure persistence of auditing across transactions. This patch adds 
> the necessary functionality to the audit subsystem and VFS to support file 
> system auditing in which an object is audited based on its location and name.  
> This work is being done to make the audit subsystem compliant with Common 
> Criteria's Controlled Access Protection Profile (CAPP) specification.
> 
> This is the second (#2) RFC on linux-fsdevel.
> 

Please see the comments interspread below.

Thanks
Maneesh


PS: Sorry to miss earlier versions.


[..]
> @@ -987,6 +991,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);
> @@ -1090,6 +1095,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;

Are you sure about hooking __d_lookup()? I am not getting why would one want
to watch every dcache lookup. IIUC, you probaly want to watch every fs object
entering the dcache, so IMO, the right place is real_lookup() and not the 
__d_lookup() which is performance sensitive code.

[..]
> +
> +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);
> +	}
> +}

I think you forgot, kfree(watch->path) here.

[..]
> +static inline 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_exit;
> +
> +	ret = -EPERM;
> +	if (nd.last_type != LAST_NORM || !nd.last.name)
> +		goto audit_insert_watch_release;
> +
> +	ret = audit_create_wentry(path, nd.last.name, filterkey, perms,
> +				  nd.dentry->d_inode->i_sb->s_dev,
> +				  nd.dentry->d_inode->i_audit);
> +	/* __d_lookup will attach the audit data, if nd.last.name exists. */
> +	d_lookup(nd.dentry, &nd.last);

Do you mean, if nd.last.name exists in dcache() or exists in the filesystem?

[..]
> +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;
> +
> +	wentry = audit_wentry_fetch_lock(dentry->d_name.name, parent);
> +
> +	write_lock(&data->lock);
> +	/* 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);

why not just, data->wentry = audit_wentry_get(wentry);

[..]
> +int audit_list_watches(int pid, int seq)
> +{
> +	int ret = 0;
> +	struct audit_wentry *wentry;
> +	struct hlist_node *pos;
> +
> +	spin_lock(&audit_master_watchlist_lock);
> +	hlist_for_each_entry(wentry, pos, &audit_master_watchlist, w_master) {
> +		ret = audit_send_watch(pid, seq, wentry->w_watch);
> +		if (ret < 0)
> +			break;
> +	}
> +	spin_unlock(&audit_master_watchlist_lock);
> +
> +	audit_send_reply(pid, seq, AUDIT_WATCH_LIST, 1, 1, NULL, 0);
> +
> +	return ret;
> +}
> +

Looks like there is lock heirarachy issue here between 
audit_master_watchlist_lock and data->lock(). 

audit_list_watches()---> audit_master_watchlist_lock
  audit_send_watch()
    audit_wentry_fetch_lock()---> data->lock

audit_insert_watch()
  audit_create_wentry()---> data->lock
    audit_master_insert()---> audit_master_watchlist_lock

OR

audit_attach_watch()---> data->lock
  audit_drain_watchlist()
    audit_destroy_watch()
      audit_master_remove()---> audit_master_watchlist_lock

> +int audit_receive_watch(int type, int pid, int uid, int seq,
> +			struct audit_transport *req)
> +{
> +	int ret = 0;
> +	char *path = NULL;
> +	char *filterkey = NULL;
> +
> +	ret = -ENOMEM;
> +	path = kmalloc(req->pathlen, GFP_KERNEL);
> +	if (!path)
> +		goto audit_receive_watch_exit;
> +	strncpy(path, req->buf, req->pathlen);
> +
> +	if (req->fklen) {
> +		filterkey = kmalloc(req->fklen, GFP_KERNEL);
> +		if (!filterkey)
> +			goto audit_receive_watch_exit;
> +	}
> +	strncpy(filterkey, req->buf+req->pathlen, req->fklen);
> +
> +	ret = -EINVAL;
> +	if (!path || strlen(path) + 1 > PATH_MAX)
> +		goto audit_receive_watch_exit;
> +
> +	/* Includes terminating '\0' */
> +	if (req->fklen > AUDIT_FILTERKEY_MAX)
> +		goto audit_receive_watch_exit;
> +
> +	if (req->perms > (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND))
> +		goto audit_receive_watch_exit;
> +
> +	switch (type) {
> +	case AUDIT_WATCH_INS:
> +		ret = audit_insert_watch(path, filterkey, req->perms);
> +		break;
> +	case AUDIT_WATCH_REM:
> +		ret = audit_remove_watch(path);
> +		break;
> +	default:
> +		ret = -EINVAL;
> +	}
> +
> +audit_receive_watch_exit:
> +	kfree(path);
> +	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.12-rc2-mm1~orig/kernel/auditsc.c linux-2.6.12-rc2-mm1~audit/kernel/auditsc.c
> --- linux-2.6.12-rc2-mm1~orig/kernel/auditsc.c	2005-04-11 14:15:36.000000000 +0000
> +++ linux-2.6.12-rc2-mm1~audit/kernel/auditsc.c	2005-04-08 16:12:23.000000000 +0000
> @@ -102,6 +102,7 @@ struct audit_aux_data {
>  };
>  
>  #define AUDIT_AUX_IPCPERM	0
> +#define AUDIT_AUX_WATCH		1
>  
>  struct audit_aux_data_ipcctl {
>  	struct audit_aux_data	d;
> @@ -112,6 +113,16 @@ struct audit_aux_data_ipcctl {
>  	mode_t			mode;
>  };
>  
> +struct audit_aux_data_watched {
> +	struct audit_aux_data	link;
> +	struct audit_wentry 	*wentry;
> +	unsigned long		ino;
> +	int			mask;
> +	uid_t			uid;
> +	gid_t			gid;
> +	dev_t			dev;
> +	dev_t			rdev;
> +};
>  
>  /* The per-task audit context. */
>  struct audit_context {
> @@ -665,6 +676,24 @@ static void audit_log_exit(struct audit_
>  			audit_log_format(ab, 
>  					 " qbytes=%lx uid=%d gid=%d mode=%x",
>  					 axi->qbytes, axi->uid, axi->gid, axi->mode);
> +			break;
> +			}
> +
> +		case AUDIT_AUX_WATCH: {
> +			struct audit_aux_data_watched *axi = (void *)aux;
> +			audit_log_format(ab, " name=");
> +			audit_log_untrustedstring(ab, axi->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",
> +				axi->wentry->w_watch->filterkey,
> +				axi->wentry->w_watch->perms,
> +				axi->mask, axi->ino, axi->uid, axi->gid,
> +				MAJOR(axi->dev), MINOR(axi->dev),
> +				MAJOR(axi->rdev), MINOR(axi->rdev));
> +				audit_wentry_put(axi->wentry);
> +			break;
>  			}
>  		}
>  		audit_log_end(ab);
> @@ -1024,3 +1053,52 @@ int audit_ipc_perms(unsigned long qbytes
>  	context->aux = (void *)ax;
>  	return 0;
>  }
> +
> +int audit_notify_watch(struct inode *inode, int mask)
> +{
> +	int ret = 0;
> +	struct audit_context *context = current->audit_context;
> +	struct audit_aux_data_watched *ax;
> +	struct audit_wentry *wentry = NULL;
> +
> +	if (likely(!context))
> +		goto audit_notify_watch_fail;
> +
> +	if (!inode)
> +		goto audit_notify_watch_fail;
> +
> +	wentry = audit_wentry_get(inode->i_audit->wentry);
> +	if (!wentry)
> +		goto audit_notify_watch_fail;
> +
> +	if (mask && (wentry->w_watch->perms && !(wentry->w_watch->perms&mask)))
> +		goto audit_notify_watch_fail;
> +
> +	ret = -ENOMEM;
> +	ax = kmalloc(sizeof(*ax), GFP_KERNEL);
> +	if (!ax)
> +		goto audit_notify_watch_fail;
> +
> +	ret = 0;
> +	if (context->in_syscall && !context->auditable)
> +		context->auditable = 1;
> +
> +	ax->wentry = wentry;
> +	ax->mask = mask;
> +	ax->ino = inode->i_ino;
> +	ax->uid = inode->i_uid;
> +	ax->gid = inode->i_gid;
> +	ax->dev = inode->i_sb->s_dev;
> +	ax->rdev = inode->i_rdev;
> +
> +	ax->link.type = AUDIT_AUX_WATCH;
> +	ax->link.next = context->aux;
> +	context->aux = (void *)ax;
> +
> +	goto audit_notify_watch_exit;
> +
> +audit_notify_watch_fail:
> +	audit_wentry_put(wentry);
> +audit_notify_watch_exit:
> +	return ret;
> +}
> diff -Nurp linux-2.6.12-rc2-mm1~orig/security/selinux/nlmsgtab.c linux-2.6.12-rc2-mm1~audit/security/selinux/nlmsgtab.c
> --- linux-2.6.12-rc2-mm1~orig/security/selinux/nlmsgtab.c	2005-04-11 14:15:36.000000000 +0000
> +++ linux-2.6.12-rc2-mm1~audit/security/selinux/nlmsgtab.c	2005-04-05 22:58:51.000000000 +0000
> @@ -98,6 +98,9 @@ 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 },
> +	{ AUDIT_WATCH_LIST,	NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
>  };
>  

-- 
Maneesh Soni
Linux Technology Center, 
IBM India Software Labs,
Bangalore, India
email: maneesh@in.ibm.com
Phone: 91-80-25044990

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [RFC][PATCH] (#2) file system auditing
  2005-04-20  7:00 ` Maneesh Soni
@ 2005-04-26 20:23   ` Timothy R. Chavez
  0 siblings, 0 replies; 3+ messages in thread
From: Timothy R. Chavez @ 2005-04-26 20:23 UTC (permalink / raw)
  To: maneesh; +Cc: David Woodhouse, Chris Wright, Stephen Smalley, linux-fsdevel

On Wed, 2005-04-20 at 12:30 +0530, Maneesh Soni wrote: 
> On Sat, Apr 16, 2005 at 10:06:52AM +0000, Timothy R. Chavez wrote:
> > Hello,
> > 
> > The audit subsystem is currently incapable of auditing a file system object 
> > based on its location and name.  This is critical for auditing well-defined 
> > and security-relevant locations such as /etc/shadow, where the file is 
> > re-created on each transaction, and cannot rely on the (device, inode)-based 
> > filters to ensure persistence of auditing across transactions. This patch adds 
> > the necessary functionality to the audit subsystem and VFS to support file 
> > system auditing in which an object is audited based on its location and name.  
> > This work is being done to make the audit subsystem compliant with Common 
> > Criteria's Controlled Access Protection Profile (CAPP) specification.
> > 
> > This is the second (#2) RFC on linux-fsdevel.
> > 
> 
> Please see the comments interspread below.
> 

Hi Maneesh,

Thank you for the comments.  I apologize for the late response.  My
responses are embedded below.  Also, I've updated the patch to
incorporate much of the feedback I've received from you and others in
addition to some fixes for various bugs that I carelessly introduced
between the first and second patch I submitted to linux-fsdevel.  I'll
submit this patch for RFC (noting the changes) momentarily to
linux-fsdevel.

-tim

> Thanks
> Maneesh
> 
> 
> PS: Sorry to miss earlier versions.
> 
> 
> [..]
> > @@ -987,6 +991,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);
> > @@ -1090,6 +1095,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;
> 
> Are you sure about hooking __d_lookup()? I am not getting why would one want
> to watch every dcache lookup. IIUC, you probaly want to watch every fs object
> entering the dcache, so IMO, the right place is real_lookup() and not the 
> __d_lookup() which is performance sensitive code.

Well the purpose of using __d_lookup is to make updates to the inode's
audit data, not to watch every dcache lookup.  The __d_lookup hook is
strategic for getting us to a place where we can determine whether or
not the inode's audit data needs updating.  When we remove a watch from
the file system, all we're doing is unhashing it from the local watch
list and decrementing its reference count.  An object that's pointing at
an unhashed watchlist entry is no longer watched, so it has to drop its
reference to the watchlist entry, reset its pointer to NULL, or in the
case of a hard link, poll its current location for a watch it can attach
too (Note: A hard link can be implicitly watched by linking to a watched
file.  Should the hard link itself exist in a watched location, it
retains its original watch until the inode becomes unwatched.  The hard
link then has the oppurtunity to be watched at its own location).  

It doesn't really seem to me that real_lookup() is suited for the
purpose stated above.  However, I do realize that having to potentially
waiting for two locks in the __d_lookup routine is not a good thing.
Perhaps a better stradegy, in this regard, can be devised.

> 
> [..]
> > +
> > +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);
> > +	}
> > +}
> 
> I think you forgot, kfree(watch->path) here.

Yep.  Good eye, thank you.

> 
> [..]
> > +static inline int audit_insert_watch(char *path, char *filterkey, __u32 perms)
> > +{
> > +	int ret;
> > +	struct nameidata nd;
> > +
> > +	ret = path_lookup(path, LOOKUP_PARENT|LOOKUP_FOLLOW, &nd);

Note: I've discarded the LOOKUP_FOLLOWS

> > +	if (ret < 0)
> > +		goto audit_insert_watch_exit;
> > +
> > +	ret = -EPERM;
> > +	if (nd.last_type != LAST_NORM || !nd.last.name)
> > +		goto audit_insert_watch_release;
> > +
> > +	ret = audit_create_wentry(path, nd.last.name, filterkey, perms,
> > +				  nd.dentry->d_inode->i_sb->s_dev,
> > +				  nd.dentry->d_inode->i_audit);
> > +	/* __d_lookup will attach the audit data, if nd.last.name exists. */
> > +	d_lookup(nd.dentry, &nd.last);
> 
> Do you mean, if nd.last.name exists in dcache() or exists in the filesystem?

Dcache.  Is my thinking flawed?  I'm under the impression that all the
relevant dentries I need will be in the dcache.  The reason I called
d_lookup here is because d_lookup will call __d_lookup which has the
audit_attach_watch() hook.  I know this is not too obvious, but to call
the hook directly from here would require I do the d_lookup anyway (to
get the dentry).

> 
> [..]
> > +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;
> > +
> > +	wentry = audit_wentry_fetch_lock(dentry->d_name.name, parent);
> > +
> > +	write_lock(&data->lock);
> > +	/* 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);
> 
> why not just, data->wentry = audit_wentry_get(wentry);

Oops this one slipped by me on this latest update, I'll add this to the
list of things I have to do..  Thanks

> 
> [..]
> > +int audit_list_watches(int pid, int seq)
> > +{
> > +	int ret = 0;
> > +	struct audit_wentry *wentry;
> > +	struct hlist_node *pos;
> > +
> > +	spin_lock(&audit_master_watchlist_lock);
> > +	hlist_for_each_entry(wentry, pos, &audit_master_watchlist, w_master) {
> > +		ret = audit_send_watch(pid, seq, wentry->w_watch);
> > +		if (ret < 0)
> > +			break;
> > +	}
> > +	spin_unlock(&audit_master_watchlist_lock);
> > +
> > +	audit_send_reply(pid, seq, AUDIT_WATCH_LIST, 1, 1, NULL, 0);
> > +
> > +	return ret;
> > +}
> > +
> 
> Looks like there is lock heirarachy issue here between 
> audit_master_watchlist_lock and data->lock(). 
> 
> audit_list_watches()---> audit_master_watchlist_lock
>   audit_send_watch()
>     audit_wentry_fetch_lock()---> data->lock
> 
> audit_insert_watch()
>   audit_create_wentry()---> data->lock
>     audit_master_insert()---> audit_master_watchlist_lock
> 
> OR
> 
> audit_attach_watch()---> data->lock
>   audit_drain_watchlist()
>     audit_destroy_watch()
>       audit_master_remove()---> audit_master_watchlist_lock
> 

Yes, this was definately an issue (and there was a problem with deadlock
on SMP).  I believe I resolved this by making the locking much tighter
(ie: audit_master_watchlist locking no longer spans function calls and
conditionals).

Thanks once again for your comments.

> > +int audit_receive_watch(int type, int pid, int uid, int seq,
> > +			struct audit_transport *req)
> > +{
> > +	int ret = 0;
> > +	char *path = NULL;
> > +	char *filterkey = NULL;
> > +
> > +	ret = -ENOMEM;
> > +	path = kmalloc(req->pathlen, GFP_KERNEL);
> > +	if (!path)
> > +		goto audit_receive_watch_exit;
> > +	strncpy(path, req->buf, req->pathlen);
> > +
> > +	if (req->fklen) {
> > +		filterkey = kmalloc(req->fklen, GFP_KERNEL);
> > +		if (!filterkey)
> > +			goto audit_receive_watch_exit;
> > +	}
> > +	strncpy(filterkey, req->buf+req->pathlen, req->fklen);
> > +
> > +	ret = -EINVAL;
> > +	if (!path || strlen(path) + 1 > PATH_MAX)
> > +		goto audit_receive_watch_exit;
> > +
> > +	/* Includes terminating '\0' */
> > +	if (req->fklen > AUDIT_FILTERKEY_MAX)
> > +		goto audit_receive_watch_exit;
> > +
> > +	if (req->perms > (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND))
> > +		goto audit_receive_watch_exit;
> > +
> > +	switch (type) {
> > +	case AUDIT_WATCH_INS:
> > +		ret = audit_insert_watch(path, filterkey, req->perms);
> > +		break;
> > +	case AUDIT_WATCH_REM:
> > +		ret = audit_remove_watch(path);
> > +		break;
> > +	default:
> > +		ret = -EINVAL;
> > +	}
> > +
> > +audit_receive_watch_exit:
> > +	kfree(path);
> > +	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.12-rc2-mm1~orig/kernel/auditsc.c linux-2.6.12-rc2-mm1~audit/kernel/auditsc.c
> > --- linux-2.6.12-rc2-mm1~orig/kernel/auditsc.c	2005-04-11 14:15:36.000000000 +0000
> > +++ linux-2.6.12-rc2-mm1~audit/kernel/auditsc.c	2005-04-08 16:12:23.000000000 +0000
> > @@ -102,6 +102,7 @@ struct audit_aux_data {
> >  };
> >  
> >  #define AUDIT_AUX_IPCPERM	0
> > +#define AUDIT_AUX_WATCH		1
> >  
> >  struct audit_aux_data_ipcctl {
> >  	struct audit_aux_data	d;
> > @@ -112,6 +113,16 @@ struct audit_aux_data_ipcctl {
> >  	mode_t			mode;
> >  };
> >  
> > +struct audit_aux_data_watched {
> > +	struct audit_aux_data	link;
> > +	struct audit_wentry 	*wentry;
> > +	unsigned long		ino;
> > +	int			mask;
> > +	uid_t			uid;
> > +	gid_t			gid;
> > +	dev_t			dev;
> > +	dev_t			rdev;
> > +};
> >  
> >  /* The per-task audit context. */
> >  struct audit_context {
> > @@ -665,6 +676,24 @@ static void audit_log_exit(struct audit_
> >  			audit_log_format(ab, 
> >  					 " qbytes=%lx uid=%d gid=%d mode=%x",
> >  					 axi->qbytes, axi->uid, axi->gid, axi->mode);
> > +			break;
> > +			}
> > +
> > +		case AUDIT_AUX_WATCH: {
> > +			struct audit_aux_data_watched *axi = (void *)aux;
> > +			audit_log_format(ab, " name=");
> > +			audit_log_untrustedstring(ab, axi->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",
> > +				axi->wentry->w_watch->filterkey,
> > +				axi->wentry->w_watch->perms,
> > +				axi->mask, axi->ino, axi->uid, axi->gid,
> > +				MAJOR(axi->dev), MINOR(axi->dev),
> > +				MAJOR(axi->rdev), MINOR(axi->rdev));
> > +				audit_wentry_put(axi->wentry);
> > +			break;
> >  			}
> >  		}
> >  		audit_log_end(ab);
> > @@ -1024,3 +1053,52 @@ int audit_ipc_perms(unsigned long qbytes
> >  	context->aux = (void *)ax;
> >  	return 0;
> >  }
> > +
> > +int audit_notify_watch(struct inode *inode, int mask)
> > +{
> > +	int ret = 0;
> > +	struct audit_context *context = current->audit_context;
> > +	struct audit_aux_data_watched *ax;
> > +	struct audit_wentry *wentry = NULL;
> > +
> > +	if (likely(!context))
> > +		goto audit_notify_watch_fail;
> > +
> > +	if (!inode)
> > +		goto audit_notify_watch_fail;
> > +
> > +	wentry = audit_wentry_get(inode->i_audit->wentry);
> > +	if (!wentry)
> > +		goto audit_notify_watch_fail;
> > +
> > +	if (mask && (wentry->w_watch->perms && !(wentry->w_watch->perms&mask)))
> > +		goto audit_notify_watch_fail;
> > +
> > +	ret = -ENOMEM;
> > +	ax = kmalloc(sizeof(*ax), GFP_KERNEL);
> > +	if (!ax)
> > +		goto audit_notify_watch_fail;
> > +
> > +	ret = 0;
> > +	if (context->in_syscall && !context->auditable)
> > +		context->auditable = 1;
> > +
> > +	ax->wentry = wentry;
> > +	ax->mask = mask;
> > +	ax->ino = inode->i_ino;
> > +	ax->uid = inode->i_uid;
> > +	ax->gid = inode->i_gid;
> > +	ax->dev = inode->i_sb->s_dev;
> > +	ax->rdev = inode->i_rdev;
> > +
> > +	ax->link.type = AUDIT_AUX_WATCH;
> > +	ax->link.next = context->aux;
> > +	context->aux = (void *)ax;
> > +
> > +	goto audit_notify_watch_exit;
> > +
> > +audit_notify_watch_fail:
> > +	audit_wentry_put(wentry);
> > +audit_notify_watch_exit:
> > +	return ret;
> > +}
> > diff -Nurp linux-2.6.12-rc2-mm1~orig/security/selinux/nlmsgtab.c linux-2.6.12-rc2-mm1~audit/security/selinux/nlmsgtab.c
> > --- linux-2.6.12-rc2-mm1~orig/security/selinux/nlmsgtab.c	2005-04-11 14:15:36.000000000 +0000
> > +++ linux-2.6.12-rc2-mm1~audit/security/selinux/nlmsgtab.c	2005-04-05 22:58:51.000000000 +0000
> > @@ -98,6 +98,9 @@ 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 },
> > +	{ AUDIT_WATCH_LIST,	NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
> >  };
> >  
> 


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2005-04-26 20:22 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-04-16 10:06 [RFC][PATCH] (#2) file system auditing Timothy R. Chavez
2005-04-20  7:00 ` Maneesh Soni
2005-04-26 20:23   ` Timothy R. Chavez

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).