fsaudit patch against audit.53 RPM

David Woodhouse dwmw2 at infradead.org
Tue Jun 7 14:11:05 UTC 2005


On Mon, 2005-06-06 at 10:52 -0500, Timothy R. Chavez wrote:
> This patch includes David's Implicit unpinning patch.

It's really better if you don't do that. I'd already consolidated the
multitude of auditfs patches into one, so I had to dig out the original
patches and revert them before applying your patch and then rediffing
against a clean tree.

Please make sure you read patches carefully before you send them. There
was a lot of this kind of thing:

-               return 0;
+                return 0;

Moving audit_data_unhash() around also made the patch far harder to
comprehend, so I put it back where it was.

> Changelog:
> 
> * Removed a fundamental mathematical operation embedded in
> a debugging statement ;)

Sorry :)

> * Removed the local lock on inode audit data in favor of of a global
> auditfs_lock spinlock
> 
> * Added a list of watches that could be associated with any given
> inode in favor of a pointer to just one
> 
> * Attempt to remove watches first via the master watchlist and then
> by path lookup
> 
> * Remove watch from inode, master watchlist, and local watchlist 
> when removing watch via administrator action
> 
> * Altered record in kernel/auditsc.c to support watches per inode per
> record

You make no mention of adding a call to audit_filesystem_init(), and I
don't see why we need it, so I removed that.

audit.56 is building with this now...

--- linux-2.6.9/fs/namei.c	2005-06-02 18:11:38.000000000 +0100
+++ linux-2.6.9/fs/namei.c	2005-06-07 09:59:18.000000000 +0100
@@ -2117,7 +2117,7 @@ int vfs_rename_other(struct inode *old_d
 	error = audit_notify_watch(old_dentry->d_inode, MAY_WRITE);
 	if (!error)
 		security_inode_post_rename(old_dir, old_dentry, new_dir, new_dentry);
-	
+
 	if (target)
 		up(&target->i_sem);
 	dput(new_dentry);
--- linux-2.6.9/include/linux/audit.h	2005-06-03 16:28:49.000000000 +0100
+++ linux-2.6.9/include/linux/audit.h	2005-06-07 09:59:18.000000000 +0100
@@ -219,6 +219,7 @@ struct audit_watch {
 	atomic_t		w_count;
 	struct hlist_node 	w_node;		/* per-directory list	      */
 	struct hlist_node	w_master;	/* Master watch list	      */
+	struct hlist_node	w_watched;	/* Watches on inode	      */
 	dev_t			w_dev;		/* Superblock device	      */
 	__u32			w_perms;	/* Permissions filtering      */
 	char			*w_name;	/* Watch point beneath parent */
@@ -230,9 +231,8 @@ struct audit_inode_data {
 	int			count;
 	struct audit_inode_data *next_hash;	/* Watch data hash table      */
 	struct inode		*inode;		/* Inode to which it belongs  */
-	struct audit_watch	*watch;		/* Watch for this inode	      */
+	struct hlist_head	watches;	/* List of watches on inode   */
 	struct hlist_head 	watchlist;	/* Watches for children       */
-	rwlock_t		lock;
 };
 
 
@@ -241,6 +241,11 @@ struct audit_sig_info {
 	pid_t		pid;
 };
 
+struct audit_watch_info {
+	struct hlist_node node;
+	struct audit_watch *watch;
+};
+
 struct audit_buffer;
 struct audit_context;
 struct inode;
@@ -303,7 +308,7 @@ extern void audit_update_watch(struct de
 extern void audit_watch_put(struct audit_watch *watch);
 extern struct audit_watch *audit_watch_get(struct audit_watch *watch);
 extern int audit_notify_watch(struct inode *inode, int mask);
-extern int auditfs_attach_wdata(struct inode *inode, struct audit_watch *watch,
+extern int auditfs_attach_wdata(struct inode *inode, struct hlist_head *watches,
 				int mask);
 #else
 #define audit_filesystem_init() ({ 0; })
--- linux-2.6.9/kernel/audit.c	2005-05-26 12:25:41.000000000 +0100
+++ linux-2.6.9/kernel/audit.c	2005-06-07 14:28:34.000000000 +0100
@@ -565,7 +565,6 @@ int __init audit_init(void)
 	audit_pid  = 0;
 
 	audit_initialized = 1;
-	audit_filesystem_init();
 	audit_enabled = audit_default;
 	audit_log(NULL, AUDIT_KERNEL, "initialized");
 	return 0;
--- linux-2.6.9/kernel/auditfs.c	2005-06-07 14:32:08.000000000 +0100
+++ linux-2.6.9/kernel/auditfs.c	2005-06-07 14:39:24.000000000 +0100
@@ -38,7 +38,7 @@
 #include <linux/module.h>
 #include <asm/uaccess.h>
 
-#if 0
+#if 1
 #define dprintk(...) do { } while(0)
 #define __print_symbol(x, y) do { } while(0)
 #else
@@ -51,15 +51,15 @@ extern int audit_enabled;
 
 static kmem_cache_t *audit_watch_cache;
 
-/* Read-heavy list */
 static HLIST_HEAD(master_watchlist);
-static spinlock_t master_watchlist_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t auditfs_lock = SPIN_LOCK_UNLOCKED;
 
 struct audit_skb_list {
 	struct hlist_node list;
 	void *memblk;
 	size_t size;
 };
+
 extern spinlock_t inode_lock;
 
 static int audit_nr_watches;
@@ -72,6 +72,8 @@ static int auditfs_cache_buckets = 16384
 module_param(auditfs_cache_buckets, int, 0);
 MODULE_PARM_DESC(auditfs_cache_buckets, "Number of auditfs cache entries to allocate (default 16384)\n");
 
+static void audit_data_put(struct audit_inode_data *data);
+
 static int audit_data_pool_grow(void)
 {
 	struct audit_inode_data *new;
@@ -93,11 +95,11 @@ static int audit_data_pool_grow(void)
 	spin_unlock(&auditfs_hash_lock);
 	return 0;
 }
-
 static void audit_data_pool_shrink(void)
 {
 	spin_lock(&auditfs_hash_lock);
 	audit_nr_watches--;
+
 	while (audit_pool_size > audit_nr_watches + 1) {
 		struct audit_inode_data *old = audit_data_pool;
 		audit_data_pool = old->next_hash;
@@ -139,8 +141,7 @@ static struct audit_inode_data *audit_da
 		dprintk("allocate from pool. %d left\n", audit_pool_size);
 
 		INIT_HLIST_HEAD(&ret->watchlist);
-		ret->watch = NULL;
-		ret->lock = RW_LOCK_UNLOCKED;
+		INIT_HLIST_HEAD(&ret->watches);
 		ret->inode = inode;
 		ret->next_hash = *list;
 		ret->count = 2;
@@ -152,7 +153,7 @@ static struct audit_inode_data *audit_da
 	}
 	if (ret) {
 		dprintk("Got audit data %p for inode %p (%lu), count++ now %d. From %p: ", 
-		       ret, ret->inode, ret->inode->i_ino, ret->count, __builtin_return_address(0));
+			ret, ret->inode, ret->inode->i_ino, ret->count, __builtin_return_address(0));
 		__print_symbol("%s\n", __builtin_return_address(0));
 	}
  out:
@@ -161,14 +162,17 @@ static struct audit_inode_data *audit_da
 	return ret;
 }
 
-static inline struct audit_watch *audit_watch_fetch(const char *name,
+/* Private Interface */
+
+/* Caller should be holding auditfs_lock */
+static inline struct audit_watch *audit_fetch_watch(const char *name,
 						    struct audit_inode_data *data)
 {
 	struct audit_watch *watch, *ret = NULL;
 	struct hlist_node *pos;
 
 	hlist_for_each_entry(watch, pos, &data->watchlist, w_node)
-		if(!strcmp(watch->w_name, name)) {
+		if (!strcmp(watch->w_name, name)) {
 			ret = audit_watch_get(watch);
 			break;
 		}
@@ -176,15 +180,15 @@ static inline struct audit_watch *audit_
 	return ret;
 }
 
-static inline struct audit_watch *audit_watch_fetch_lock(const char *name,
+static inline struct audit_watch *audit_fetch_watch_lock(const char *name,
 							 struct audit_inode_data *data)
 {
 	struct audit_watch *ret = NULL;
 
 	if (name && data) {
-		read_lock(&data->lock);
-		ret = audit_watch_fetch(name, data);
-		read_unlock(&data->lock);
+		spin_lock(&auditfs_lock);
+		ret = audit_fetch_watch(name, data);
+		spin_unlock(&auditfs_lock);
 	}
 
 	return ret;
@@ -203,10 +207,6 @@ static inline struct audit_watch *audit_
 	return watch;
 }
 
-/*
- * If a watch has been removed from a watchlist, we promptly unpin the
- * dentry that was being watched, as it is no longer important to us.
- */
 static inline void audit_watch_free(struct audit_watch *watch)
 {
 	if (watch) {
@@ -291,95 +291,16 @@ audit_to_transport_exit:
 	return t;
 }
 
-/*
- * Because we're protected by audit_netlink_sem, there will never be a race on
- * current insertions of the same watch.  However, we do lock both the master
- * watchlist and the local watchlist to ensure that insertions and removals of
- * watches are serialized.
- */
-static inline int audit_create_watch(const char *path,
-				     const char *name,
-				     const char *filterkey,
-				     __u32 perms, dev_t dev,
-				     struct audit_inode_data *data)
-{
-	int ret;
-	struct audit_watch *watch;
-
-	ret = -EEXIST;
-	watch = audit_watch_fetch_lock(name, data);
-	if (watch) {
-		audit_watch_put(watch);
-		goto audit_create_watch_exit;
-	}
-
-	ret = -ENOMEM;
-	watch = audit_watch_alloc();
-	if (!watch)
-		goto audit_create_watch_exit;
-
-	watch->w_path = kmalloc(strlen(path)+1, GFP_KERNEL);
-	if (!watch->w_path)
-		goto audit_create_watch_fail;
-	strcpy(watch->w_path, path);
-
-	watch->w_name = kmalloc(strlen(name)+1, GFP_KERNEL);
-	if (!watch->w_name)
-		goto audit_create_watch_fail;
-	strcpy(watch->w_name, name);
-
-	if (filterkey) {
-		watch->w_filterkey = kmalloc(strlen(filterkey)+1, GFP_KERNEL);
-		if (!watch->w_filterkey)
-			goto audit_create_watch_fail;
-		strcpy(watch->w_filterkey, filterkey);
-	}
-
-	watch->w_dev = dev;
-	watch->w_perms = perms;
-
-	audit_watch_get(watch);
-
-	write_lock(&data->lock);
-	hlist_add_head(&watch->w_node, &data->watchlist);
-	spin_lock(&master_watchlist_lock);
-	hlist_add_head(&watch->w_master, &master_watchlist);
-	spin_unlock(&master_watchlist_lock);
-	write_unlock(&data->lock);
-	return 0;
-
- audit_create_watch_fail:
-	audit_watch_put(watch);
-
- audit_create_watch_exit:
-	return ret;
-}
-
-/*
- * There's three ways we can arrive at audit_destroy_watch.  Two of the ways
- * may cause contention with one another, while the third may not.  They are:
- *
- * 1)  An administrator has explicitly requested a watch removal
- * 2)  A directory with an "active" watchlist is rename()'ed
- * 3)  The inode is in the process of being destroyed
- *
- * In ways 1) and 2) the data->lock must be held to safely enter.  We use
- * the audit_master_watchlist spinlock to protect against removal of two
- * consecutive entries concurrently in the master watchlist.
- *
- * If way 3) occurs there will no longer be any reachable references to the
- * inode, thus there is only one way to remove this watch, and no need to
- * hold the data->lock.
- */
 static inline void audit_destroy_watch(struct audit_watch *watch)
 {
 	if (watch) {
-		hlist_del_init(&watch->w_node);
-		spin_lock(&master_watchlist_lock);
+		hlist_del_init(&watch->w_watched);
 		hlist_del_init(&watch->w_master);
-		spin_unlock(&master_watchlist_lock);
+		hlist_del_init(&watch->w_node);
+		audit_watch_put(watch);
 		audit_watch_put(watch);
 		audit_watch_put(watch);
+
 	}
 }
 
@@ -394,7 +315,6 @@ static inline void audit_drain_watchlist
 	}
 }
 
-/* Must be called with auditfs_hash_lock held */
 static void audit_data_unhash(struct audit_inode_data *data)
 {
 	int h = hash_ptr(data->inode, auditfs_hash_bits);
@@ -412,7 +332,6 @@ static void audit_data_unhash(struct aud
 	data->inode = NULL;
 }
 
-
 static void audit_data_put(struct audit_inode_data *data)
 {
 	if (!data)
@@ -425,7 +344,7 @@ static void audit_data_put(struct audit_
 	__print_symbol("%s\n", __builtin_return_address(0));
 
 	if (data->count == 1 && data->inode && 
-	    !data->watch && hlist_empty(&data->watchlist)) {
+	    hlist_empty(&data->watches) && hlist_empty(&data->watchlist)) {
 		dprintk("Last put.\n");
 		data->count--;
 	}
@@ -459,6 +378,7 @@ static inline int audit_insert_watch(str
 	int ret;
 	struct nameidata nd;
 	struct audit_inode_data *pdata;
+	struct audit_watch *lookup;
 
 	/* Grow the pool by two -- one for the watch itself, and
 	   one for the parent directory */
@@ -473,21 +393,34 @@ static inline int audit_insert_watch(str
 	if (nd.last_type != LAST_NORM || !nd.last.name)
 		goto release;
 
-	ret = -ENOMEM;
 	pdata = audit_data_get(nd.dentry->d_inode, 1);
 	if (!pdata)
 		goto put_pdata;
 
-	ret = audit_create_watch(watch->w_path,
-				 nd.last.name,
-				 watch->w_filterkey,
-				 watch->w_perms,
-				 nd.dentry->d_inode->i_sb->s_dev,
-				 pdata);
-	if (ret < 0)
+	ret = -EEXIST;
+	lookup = audit_fetch_watch_lock(nd.last.name, pdata);
+	if (lookup) {
+		audit_watch_put(lookup);
+		goto put_pdata;
+	}
+
+	ret = -ENOMEM;
+	watch->w_name = kmalloc(strlen(nd.last.name)+1, GFP_KERNEL);
+	if (!watch->w_name)
 		goto put_pdata;
+	strcpy(watch->w_name, nd.last.name);
+
+	watch->w_dev = nd.dentry->d_inode->i_sb->s_dev;
+
+	ret = 0;
+	audit_watch_get(watch);
+	spin_lock(&auditfs_lock);
+	hlist_add_head(&watch->w_node, &pdata->watchlist);
+	hlist_add_head(&watch->w_master, &master_watchlist);
+	spin_unlock(&auditfs_lock);
+
+	audit_log(NULL, AUDIT_CONFIG_CHANGE, "auid=%u inserted watch", loginuid);
 
-	audit_log(NULL, AUDIT_CONFIG_CHANGE, "auid=%u inserted watch\n", loginuid);
 	/* __d_lookup will attach the audit data, if nd.last exists. */
 	dput(d_lookup(nd.dentry, &nd.last));
 
@@ -504,10 +437,21 @@ static inline int audit_insert_watch(str
 
 static inline int audit_remove_watch(struct audit_watch *watch, uid_t loginuid)
 {
-	int ret;
+	int ret = 0;
 	struct nameidata nd;
-	struct audit_inode_data *data;
-	struct audit_watch *real;
+	struct audit_inode_data *data = NULL;
+	struct audit_watch *real, *this;
+	struct hlist_node *pos, *tmp;
+
+	/* Let's try removing via the master watchlist first */
+	spin_lock(&auditfs_lock);
+	hlist_for_each_entry_safe(this, pos, tmp, &master_watchlist, w_master)
+		if (!strcmp(this->w_path, watch->w_path)) {
+			audit_destroy_watch(this);
+			spin_unlock(&auditfs_lock);
+			goto audit_remove_watch_exit;
+		}
+	spin_unlock(&auditfs_lock);
 
 	ret = path_lookup(watch->w_path, LOOKUP_PARENT, &nd);
 	if (ret < 0)
@@ -521,26 +465,47 @@ static inline int audit_remove_watch(str
 	if (!data)
 		goto audit_remove_watch_release;
 
-	write_lock(&data->lock);
-	real = audit_watch_fetch(nd.last.name, data);
+	spin_lock(&auditfs_lock);
+	real = audit_fetch_watch(nd.last.name, data);
 	if (!real) {
-		write_unlock(&data->lock);
+		spin_unlock(&auditfs_lock);
 		goto audit_remove_watch_release;
 	}
 	audit_destroy_watch(real);
+	spin_unlock(&auditfs_lock);
 	audit_watch_put(real);
-	audit_log(NULL, AUDIT_CONFIG_CHANGE, "auid=%u removed watch\n", loginuid);
-	write_unlock(&data->lock);
-	audit_data_put(data);
-	audit_data_pool_shrink();
+
 	ret = 0;
 
 audit_remove_watch_release:
 	path_release(&nd);
 audit_remove_watch_exit:
+	audit_data_put(data);
+	if (!ret) {
+		audit_log(NULL, AUDIT_CONFIG_CHANGE, "auid=%u removed watch", loginuid);
+		audit_data_pool_shrink();
+	}
+
 	return ret;
 }
 
+
+/* Caller should be holding auditfs_lock */
+struct audit_watch *audit_is_watched(struct audit_watch *watch,
+				     struct audit_inode_data *data)
+{
+	struct audit_watch *watched;
+	struct hlist_node *pos;
+
+	if (watch && data) {
+		hlist_for_each_entry(watched, pos, &data->watches, w_watched)
+			if (!strcmp(watch->w_name, watched->w_name))
+				return audit_watch_get(watched);
+	}
+
+	return NULL;
+}
+
 struct audit_watch *audit_watch_get(struct audit_watch *watch)
 {
 	int new;
@@ -583,8 +548,9 @@ void audit_watch_put(struct audit_watch 
  */
 void audit_update_watch(struct dentry *dentry, int remove)
 {
-	struct audit_watch *watch = NULL;
+	struct audit_watch *me, *this, *watch;
 	struct audit_inode_data *data, *parent;
+	struct hlist_node *pos, *tmp;
 
 	if (likely(!audit_enabled))
 		return;
@@ -601,7 +567,7 @@ void audit_update_watch(struct dentry *d
 	if (!parent)
 		return;
 
-	watch = audit_watch_fetch_lock(dentry->d_name.name, parent);
+	watch = audit_fetch_watch_lock(dentry->d_name.name, parent);
 
 	/* Fetch audit data, using the preallocated one from the watch if
 	   there is actually a relevant watch and the inode didn't already
@@ -613,27 +579,29 @@ void audit_update_watch(struct dentry *d
 	if (!data)
 		goto put_watch;
 
-	write_lock(&data->lock);
+	spin_lock(&auditfs_lock);
 	if (remove) {
-		/* If the inode was being watched for _this_ pathname, clear the watch */
-		/* FIXME: If there was _another_ path to the same inode which was 
-		   supposed to be watched, we ought to continue watching. */
-		if (watch && data->watch && 
-		    !strcmp(watch->w_name, data->watch->w_name)) {
-			audit_watch_put(data->watch);
-			data->watch = NULL;
+		me = audit_is_watched(watch, data);
+		if (me) {
+			hlist_del_init(&me->w_watched);
+			audit_watch_put(me);
+		}
+	} else {
+		hlist_for_each_entry_safe(this, pos, tmp, &data->watches, w_watched)
+			if (hlist_unhashed(&this->w_node)) {
+				hlist_del(&this->w_watched);
+				audit_watch_put(this);
+			}
+		me = audit_is_watched(watch, data);
+		if (!me && watch) {
+			audit_watch_get(watch);
+			hlist_add_head(&watch->w_watched, &data->watches);
 		}
-	} else if (!data->watch) {
-		/* Inode wasn't watched before. Maybe it is now */
-		data->watch = audit_watch_get(watch);
-	} else if (hlist_unhashed(&data->watch->w_node)) {
-		/* Old watch is dead now. Drop it and add new one .*/
-		audit_watch_put(data->watch);
-		data->watch = audit_watch_get(watch);
+		audit_watch_put(me);
 	}
-	/* FIXME: If inode's i_audit is no longer used, clear it. */
-	write_unlock(&data->lock);
+	spin_unlock(&auditfs_lock);
 	audit_data_put(data);
+
  put_watch:
 	audit_watch_put(watch);
 	audit_data_put(parent);
@@ -689,19 +657,18 @@ int audit_list_watches(int pid, int seq)
 
  restart:
 	INIT_HLIST_HEAD(&skb_list);
-	spin_lock(&master_watchlist_lock);
+	spin_lock(&auditfs_lock);
 
 	hlist_for_each_entry(watch, pos, &master_watchlist, w_master) {
 		audit_watch_get(watch);
-		spin_unlock(&master_watchlist_lock);
+		spin_unlock(&auditfs_lock);
 		entry = audit_to_skb(watch);
 		if (IS_ERR(entry)) {
 			ret = PTR_ERR(entry);
-			spin_unlock(&master_watchlist_lock);
 			goto audit_list_watches_fail;
 		}
 		hlist_add_head(&entry->list, &skb_list);
-		spin_lock(&master_watchlist_lock);
+		spin_lock(&auditfs_lock);
 		if (hlist_unhashed(&watch->w_master)) {
 			/* This watch was removed from the list while we 
 			   pondered it. We could play tricks to find how far
@@ -720,7 +687,7 @@ int audit_list_watches(int pid, int seq)
 		}
 		audit_watch_put(watch);
 	}
-	spin_unlock(&master_watchlist_lock);
+	spin_unlock(&auditfs_lock);
 
 	hlist_for_each_entry_safe(entry, pos, tmp, &skb_list, list) {
 		audit_send_reply(pid, seq, AUDIT_WATCH_LIST, 0, 1, 
@@ -757,7 +724,7 @@ int audit_receive_watch(int type, int pi
 	if (req->fklen > AUDIT_FILTERKEY_MAX)
 		goto audit_receive_watch_exit;
 
-	if ( payload[req->fklen] != '/')
+	if (payload[req->fklen] != '/')
 		goto audit_receive_watch_exit;
 
 	if (req->perms > (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND))
@@ -779,13 +746,17 @@ int audit_receive_watch(int type, int pi
 		ret = -EINVAL;
 	}
 
+	if (ret < 0 || type == AUDIT_WATCH_REM)
+		audit_watch_put(watch);
+
 audit_receive_watch_exit:
-	audit_watch_free(watch);
 	return ret;
 }
 
 void audit_inode_free(struct inode *inode)
 {
+	struct audit_watch *watch;
+	struct hlist_node *pos, *tmp;
 	struct audit_inode_data *data = audit_data_get(inode, 0);
 
 	if (inode->i_sb->s_dev == MKDEV(3,6) || inode->i_sb->s_dev == MKDEV(3,9))
@@ -795,10 +766,13 @@ void audit_inode_free(struct inode *inod
 		spin_lock(&auditfs_hash_lock);
 		audit_data_unhash(data);
 		spin_unlock(&auditfs_hash_lock);
-		
+
 		audit_drain_watchlist(data);
-		audit_watch_put(data->watch);
-		data->watch = NULL;
+		/* Release all our references to any watches we may have on us */
+        	hlist_for_each_entry_safe(watch, pos, tmp, &data->watches, w_watched) {
+			hlist_del(&watch->w_watched);
+                	audit_watch_put(watch);
+        	}
 		audit_data_put(data);
 	}
 }
@@ -847,7 +821,6 @@ int audit_notify_watch(struct inode *ino
 {
 	int ret = 0;
 	struct audit_inode_data *data;
-	struct audit_watch *watch = NULL;
 
 	if (likely(!audit_enabled))
 		return 0;
@@ -859,34 +832,13 @@ int audit_notify_watch(struct inode *ino
 	if (!data)
 		return 0;
 
-	/* 
-	 * Won't be able to drop i_audit->watch reference
-	 * before we obtain our own reference
-	 */
-	read_lock(&data->lock);
-	watch = audit_watch_get(data->watch);
-	read_unlock(&data->lock);
-	if (!watch)
+	if (hlist_empty(&data->watches))
 		goto out;
 
-	/* FIXME: Make audit_remove_watch actually remove it */
-	if (hlist_unhashed(&watch->w_node)) {
-		dprintk("Hm. Unhashed watch\n");
-		data->watch = NULL;
-		audit_watch_put(watch);
-		goto fail;
-	}
-
-	if (mask && (watch->w_perms && !(watch->w_perms & mask)))
-		goto fail;
+	ret = auditfs_attach_wdata(inode, &data->watches, mask);
 
-	ret = auditfs_attach_wdata(inode, watch, mask);
-
-	if (ret) {
-	fail:
-		audit_watch_put(watch);
-	}
- out:
+out:
 	audit_data_put(data);
 	return ret;
 }
+
--- linux-2.6.9/kernel/auditsc.c	2005-06-06 15:08:01.000000000 +0100
+++ linux-2.6.9/kernel/auditsc.c	2005-06-07 10:04:45.000000000 +0100
@@ -131,7 +131,7 @@ struct audit_aux_data_path {
 
 struct audit_aux_data_watched {
 	struct audit_aux_data	link;
-	struct audit_watch	*watch;
+	struct hlist_head	watches;
 	unsigned long		ino;
 	int			mask;
 	uid_t			uid;
@@ -576,6 +576,8 @@ static inline void audit_free_names(stru
 static inline void audit_free_aux(struct audit_context *context)
 {
 	struct audit_aux_data *aux;
+	struct audit_watch_info *winfo;
+	struct hlist_node *pos, *tmp;
 
 	while ((aux = context->aux)) {
 		switch(aux->type) {
@@ -586,7 +588,11 @@ static inline void audit_free_aux(struct
 			break; }
 		case AUDIT_FS_WATCH: {
 			struct audit_aux_data_watched *axi = (void *)aux;
-			audit_watch_put(axi->watch);
+			hlist_for_each_entry_safe(winfo, pos, tmp, &axi->watches, node) {
+				audit_watch_put(winfo->watch);
+				hlist_del(&winfo->node);
+				kfree(winfo);
+                        }
 			break; }
 		}
 		
@@ -702,6 +708,8 @@ static void audit_log_exit(struct audit_
 	int i;
 	struct audit_buffer *ab;
 	struct audit_aux_data *aux;
+	struct audit_watch_info *winfo;
+	struct hlist_node *pos;
 
 	ab = audit_log_start(context, AUDIT_SYSCALL);
 	if (!ab)
@@ -768,17 +776,26 @@ static void audit_log_exit(struct audit_
 
 		case AUDIT_FS_WATCH: {
 			struct audit_aux_data_watched *axi = (void *)aux;
-			audit_log_format(ab, " watch=");
-			audit_log_untrustedstring(ab, axi->watch->w_name);
+			struct audit_buffer *sub_ab;
 			audit_log_format(ab,
-					 " filterkey=%s perm=%u perm_mask=%d"
-					 " inode=%lu inode_uid=%u inode_gid=%u"
+					 "inode=%lu inode_uid=%u inode_gid=%u"
 					 " inode_dev=%02x:%02x inode_rdev=%02x:%02x",
-					 axi->watch->w_filterkey,
-					 axi->watch->w_perms,
-					 axi->mask, axi->ino, axi->uid, axi->gid,
+					 axi->ino, axi->uid, axi->gid,
 					 MAJOR(axi->dev), MINOR(axi->dev),
 					 MAJOR(axi->rdev), MINOR(axi->rdev));
+			hlist_for_each_entry(winfo, pos, &axi->watches, node) {
+				sub_ab = audit_log_start(context, AUDIT_FS_WATCH);
+				if (!sub_ab)
+					return;		/* audit_panic has been called */
+				audit_log_format(sub_ab, "watch_inode=%lu", axi->ino);
+				audit_log_format(sub_ab, " watch=");
+				audit_log_untrustedstring(sub_ab, winfo->watch->w_name);
+				audit_log_format(sub_ab,
+						 " filterkey=%s perm=%u perm_mask=%u",
+						 winfo->watch->w_filterkey,
+						 winfo->watch->w_perms, axi->mask);
+				audit_log_end(sub_ab);
+			}
 			break; }
 		}
 		audit_log_end(ab);
@@ -1198,11 +1215,14 @@ void audit_signal_info(int sig, struct t
 #ifdef CONFIG_AUDITFILESYSTEM
 /* This has to be here instead of in auditfs.c, because it needs to
    see the audit context */
-int auditfs_attach_wdata(struct inode *inode, struct audit_watch *watch,
+int auditfs_attach_wdata(struct inode *inode, struct hlist_head *watches,
 			 int mask)
 {
 	struct audit_context *context = current->audit_context;
 	struct audit_aux_data_watched *ax;
+	struct audit_watch *watch;
+	struct audit_watch_info *this, *winfo;
+	struct hlist_node *pos, *tmp;
 
 	ax = kmalloc(sizeof(*ax), GFP_KERNEL);
 	if (!ax)
@@ -1211,7 +1231,18 @@ int auditfs_attach_wdata(struct inode *i
 	if (context->in_syscall && !context->auditable)
 		context->auditable = 1;
 
-	ax->watch = watch;
+	INIT_HLIST_HEAD(&ax->watches);
+
+	hlist_for_each_entry(watch, pos, watches, w_watched) {
+		winfo = kmalloc(sizeof(struct audit_watch_info), GFP_KERNEL);
+		if (!winfo)
+			goto auditfs_attach_wdata_fail;
+ 		if (mask && (watch->w_perms && !(watch->w_perms&mask)))
+			continue;
+		winfo->watch = audit_watch_get(watch);
+		hlist_add_head(&winfo->node, &ax->watches);
+	}
+
 	ax->mask = mask;
 	ax->ino = inode->i_ino;
 	ax->uid = inode->i_uid;
@@ -1224,5 +1255,14 @@ int auditfs_attach_wdata(struct inode *i
 	context->aux = (void *)ax;
 
 	return 0;
+
+auditfs_attach_wdata_fail:
+	hlist_for_each_entry_safe(this, pos, tmp, &ax->watches, node) {
+		hlist_del(&this->node);
+		audit_watch_put(this->watch);
+		kfree(this);
+	}
+
+	return -ENOMEM;
 }
 #endif /* CONFIG_AUDITFILESYSTEM */


-- 
dwmw2




More information about the Linux-audit mailing list