[RFC][PATCH 1/2] file system auditing

Timothy R. Chavez tinytim at us.ibm.com
Tue Apr 5 21:46:27 UTC 2005


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.

The patch has been split in two for RFC.

[PATCH 1/2]
The first patch consists of the file system hooks and appears at the bottom of 
this message preceded by brief explanations of each hook.  The quoted 
terminology is defined in the high-level overview of the design that's been 
included with the second patch in the next message.

[PATCH 2/2]
The second patch consists of the file system auditing implementation preceded 
by a high-level overview of the design.

The entire patch was diffed against linux-2.6.11-rc2-mm1

-tim

----

1.  Setup

Placement in fs/inode.c 

audit_inode_alloc()/audit_inode_free():
These hooks are responsible for allocating and deallocating inode audit data.

2.  Management

Management of "watches" starts at the dentry level.

Placement in fs/dcache.c

Initial setup:

d_instantiate()/d_splice_alias() :
These hooks cover initial audit setup for inodes that are newly created and 
for existing inodes when they are first looked up, prior to becoming 
accessible via the dcache.

Update:

__d_lookup():
This hook covers updating the inode audit data if necessary upon subsequent 
lookups when the inode is already accessible via dcache, both to reflect 
removal of "watches" and to handle changes in state since the initial setup.

d_move():
This hook covers resetting the inode audit data and updating it accordingly 
post-move.

Deletion:

dentry_iput():
This hook covers resetting the inode audit data if the dentry being deleted is 
at a "watch point".

3.  Notification

Any time we access a "watched" object we are auditable.  These hooks are used 
to notify the audit subsystem of relevant access.  

Placement in fs/namei.c.

Permissions:

permission()/exec_permission_lite():
Notify the audit subsystem when a "watched" object is consulted about 
permissions requirements.

Creation:

vfs_link()/symlink()/create()/mkdir()/mknod():
Notify the audit subsystem when an object has successfully manifested at a 
"watch point". 

Deletion:

may_delete() [vfs_unlink()/rmdir()]: 
Notify the audit subsystem when an object attempts to leave a "watch point".  
This hook appears in may_delete() after its been determined the object has an 
inode.

Note:  We are unable to use may_create() in a similar fashion to may_delete() 
because the object should not have an inode associated with it yet.

Rename:

may_delete(),vfs_rename_other()/rename_dir():
Notify the audit subsystem when an object has been moved out of a "watch 
point" using the may_delete() hook.  Notify the audit subsystem when an 
object already exists at our destination and our destination is a "watch 
point".  Notify the audit subsystem when an object moves in to a "watch 
point" using the vfs_rename_other/rename_dir() hooks.

diff -Nurp linux-2.6.12-rc2-mm1/fs/dcache.c linux-2.6.12-rc2-mm1~audit/fs/dcache.c
--- linux-2.6.12-rc2-mm1/fs/dcache.c	2005-04-05 14:05:16.000000000 -0500
+++ linux-2.6.12-rc2-mm1~audit/fs/dcache.c	2005-04-05 13:16:04.000000000 -0500
@@ -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/fs/inode.c linux-2.6.12-rc2-mm1~audit/fs/inode.c
--- linux-2.6.12-rc2-mm1/fs/inode.c	2005-04-05 14:06:28.000000000 -0500
+++ linux-2.6.12-rc2-mm1~audit/fs/inode.c	2005-04-05 13:16:04.000000000 -0500
@@ -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/fs/namei.c linux-2.6.12-rc2-mm1~audit/fs/namei.c
--- linux-2.6.12-rc2-mm1/fs/namei.c	2005-04-05 14:06:28.000000000 -0500
+++ linux-2.6.12-rc2-mm1~audit/fs/namei.c	2005-04-05 13:16:04.000000000 -0500
@@ -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)




More information about the Linux-audit mailing list