[RFC][PATCH] fswatch + audit + inotify status patch
by Timothy R. Chavez
Ahoy,
Here's an update with a quick blurb in the form of a patch to just
peruse and perhaps comment on to keep things going...
The major work being done _right now_ is two-fold.
First fold:
Merging in the common and useful parts of Inotify and audit into a
cohesive, generic system. You'll see this work in fs/fswatch.c and
include/linux/fswatch.h mostly. Currently the framework is trying to
support both statically allocated, embedded watches and dynamically
allocated stand-alone watches (though I've not really tested the
dynamically allocated aspect of the system yet). I'm thinking about
ditching the dynamically allocated watches... but perhaps this feature
makes the framework more robust or at the very least, appealing?
Second fold:
The conversion of fs/auditfs.c into using this framework. I think I am mostly
there. In the generic watches struct we have two callback functions, one
that's called when a watch is accessed and its user wants to be notified of
that action and the other is called when the watch is destroyed and needs to
know how to clean up. This may need to expand into an array of callbacks so
that each fswatch hook can call a custom-taylored callback function instead
of one.
The major pieces I'm working on this week (and will hopefully finish):
* Finish up (debugging) watch insertions.
* To test and debug watch removals.
* To test and debug the watch destroy callback.
The major pieces I will be working on this weekend and next week:
* Modifying the list traversal code such that it works with the new
(and improved?) auditfs way of doing things.
* Finish the "movement" and "change" actions.
Future work:
Then there's the necessity to change dnotify and inotify to use this new
framework which I would be interested in recruiting help for. I anticipate
some work around making it so this system interfaces nicely with Inotify
without Inotify having to make any drastic (or more drastic) changes then
auditfs has made.
Once I've completed the initial fswatch framework and adapted auditfs to
use it, I'll provide a more detailed description of the fswatch framework and
CC those people (external) to this list that might be interested in this
milestone.
Warning: This might not be the cleanest patch (and is a work-in-progress)
-tim
fswatch work
---
commit 812607888b862038eb4df745ea9fe0706df92252
tree 16ba8dde73a7bf4dd73b0b3093ca2d220bd7cd48
parent 39299d9d15c41cbdd7c7009967cd35afaf34d8fa
author root <root(a)liltux.austin.ibm.com> Wed, 20 Jul 2011 03:10:24 -0500
committer root <root(a)liltux.austin.ibm.com> Wed, 20 Jul 2011 03:10:24 -0500
fs/Kconfig | 6
fs/Makefile | 1
fs/attr.c | 4
fs/compat.c | 6
fs/dcache.c | 8 +
fs/file_table.c | 4
fs/fswatch.c | 672 +++++++++++++++++++++++++++++++++++++++++++
fs/inode.c | 9 -
fs/namei.c | 23 +
fs/nfsd/vfs.c | 8 -
fs/open.c | 6
fs/read_write.c | 10 -
fs/sysfs/file.c | 5
fs/xattr.c | 4
include/linux/audit.h | 55 ++++
include/linux/dnotify.h | 101 ++++++
include/linux/fs.h | 2
include/linux/fswatch.h | 118 ++++++++
init/Kconfig | 10 +
kernel/Makefile | 1
kernel/audit.c | 23 +
kernel/auditfs.c | 329 +++++++++++++++++++++
kernel/auditsc.c | 117 +++++++
security/selinux/nlmsgtab.c | 3
24 files changed, 1481 insertions(+), 44 deletions(-)
diff --git a/fs/Kconfig b/fs/Kconfig
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -408,6 +408,12 @@ config QUOTACTL
depends on XFS_QUOTA || QUOTA
default y
+config FSWATCH
+ bool "Generic file watches"
+ help
+ This is a generic framework for monitoring file system
+ activity.
+
config DNOTIFY
bool "Dnotify support" if EMBEDDED
default y
diff --git a/fs/Makefile b/fs/Makefile
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_QFMT_V1) += quota_v1.o
obj-$(CONFIG_QFMT_V2) += quota_v2.o
obj-$(CONFIG_QUOTACTL) += quota.o
+obj-$(CONFIG_FSWATCH) += fswatch.o
obj-$(CONFIG_DNOTIFY) += dnotify.o
obj-$(CONFIG_PROC_FS) += proc/
diff --git a/fs/attr.c b/fs/attr.c
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -10,7 +10,7 @@
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/smp_lock.h>
-#include <linux/fsnotify.h>
+#include <linux/fswatch.h>
#include <linux/fcntl.h>
#include <linux/quotaops.h>
#include <linux/security.h>
@@ -175,7 +175,7 @@ int notify_change(struct dentry * dentry
up_write(&dentry->d_inode->i_alloc_sem);
if (!error)
- fsnotify_change(dentry, ia_valid);
+ fswatch_notify_change(dentry, ia_valid);
return error;
}
diff --git a/fs/compat.c b/fs/compat.c
--- a/fs/compat.c
+++ b/fs/compat.c
@@ -37,7 +37,7 @@
#include <linux/ctype.h>
#include <linux/module.h>
#include <linux/dirent.h>
-#include <linux/fsnotify.h>
+#include <linux/fswatch.h>
#include <linux/highuid.h>
#include <linux/sunrpc/svc.h>
#include <linux/nfsd/nfsd.h>
@@ -1310,9 +1310,9 @@ out:
if ((ret + (type == READ)) > 0) {
struct dentry *dentry = file->f_dentry;
if (type == READ)
- fsnotify_access(dentry);
+ fswatch_notify_access(dentry);
else
- fsnotify_modify(dentry);
+ fswatch_notify_modify(dentry);
}
return ret;
}
diff --git a/fs/dcache.c b/fs/dcache.c
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -32,6 +32,7 @@
#include <linux/seqlock.h>
#include <linux/swap.h>
#include <linux/bootmem.h>
+#include <linux/fswatch.h>
/* #define DCACHE_DEBUG 1 */
@@ -97,6 +98,7 @@ static inline void dentry_iput(struct de
{
struct inode *inode = dentry->d_inode;
if (inode) {
+ fswatch_update_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;
+ fswatch_update_watch(entry, 0);
spin_unlock(&dcache_lock);
security_d_instantiate(entry, inode);
}
@@ -987,6 +990,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;
+ fswatch_update_watch(dentry, 0);
spin_unlock(&dcache_lock);
security_d_instantiate(dentry, inode);
d_rehash(dentry);
@@ -1090,6 +1094,7 @@ struct dentry * __d_lookup(struct dentry
if (!d_unhashed(dentry)) {
atomic_inc(&dentry->d_count);
found = dentry;
+ fswatch_update_watch(dentry, 0);
}
spin_unlock(&dentry->d_lock);
break;
@@ -1299,6 +1304,8 @@ void d_move(struct dentry * dentry, stru
spin_lock(&target->d_lock);
}
+ fswatch_update_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 +1339,7 @@ already_unhashed:
list_add(&target->d_child, &target->d_parent->d_subdirs);
}
+ fswatch_update_watch(dentry, 0);
list_add(&dentry->d_child, &dentry->d_parent->d_subdirs);
spin_unlock(&target->d_lock);
spin_unlock(&dentry->d_lock);
diff --git a/fs/file_table.c b/fs/file_table.c
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -16,7 +16,7 @@
#include <linux/eventpoll.h>
#include <linux/mount.h>
#include <linux/cdev.h>
-#include <linux/fsnotify.h>
+#include <linux/fswatch.h>
/* sysctl tunables... */
struct files_stat_struct files_stat = {
@@ -128,7 +128,7 @@ void fastcall __fput(struct file *file)
might_sleep();
- fsnotify_close(file);
+ fswatch_notify_close(file);
/*
* The function eventpoll_release() should be the first called
* in the file cleanup chain.
diff --git a/fs/fswatch.c b/fs/fswatch.c
new file mode 100644
--- /dev/null
+++ b/fs/fswatch.c
@@ -0,0 +1,672 @@
+#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/hash.h>
+#include <linux/slab.h>
+#include <linux/fswatch.h>
+#include <linux/module.h>
+#include <asm/uaccess.h>
+
+/* Locks */
+static spinlock_t fswatch_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t watch_hash_lock = SPIN_LOCK_UNLOCKED;
+extern spinlock_t inode_lock;
+
+/* Slab */
+static kmem_cache_t *watch_cache;
+
+/* Hash table */
+static int fswatch_nr_watches;
+static int fswatch_pool_size;
+static struct watch_inode_data *fswatch_data_pool;
+static struct watch_inode_data **fswatch_hash_table;
+static int fswatch_hash_bits;
+static int fswatch_cache_buckets = 16384;
+module_param(fswatch_cache_buckets, int, 0);
+MODULE_PARM_DESC(fswatch_cache_buckets,
+ "Number of watch cache entries to alloc (default 16384)\n");
+
+static void fswatch_put_data(struct watch_inode_data *data);
+
+static int fswatch_data_pool_grow(void)
+{
+ struct watch_inode_data *new;
+
+ new = kmalloc(sizeof(*new), GFP_KERNEL);
+ if (!new)
+ return -ENOMEM;
+ new->next_hash = kmalloc(sizeof(*new), GFP_KERNEL);
+ if (!new->next_hash) {
+ kfree(new);
+ return -ENOMEM;
+ }
+
+ spin_lock(&watch_hash_lock);
+ new->next_hash->next_hash = fswatch_data_pool;
+ fswatch_data_pool = new;
+ fswatch_nr_watches++;
+ fswatch_pool_size += 2;
+ spin_unlock(&watch_hash_lock);
+ return 0;
+}
+
+static void fswatch_data_pool_shrink(void)
+{
+ spin_lock(&watch_hash_lock);
+ fswatch_nr_watches--;
+
+ while (fswatch_pool_size > fswatch_nr_watches + 1) {
+ struct watch_inode_data *old = fswatch_data_pool;
+ fswatch_data_pool = old->next_hash;
+ fswatch_pool_size--;
+ kfree(old);
+ }
+ spin_unlock(&watch_hash_lock);
+}
+
+static struct watch_inode_data *fswatch_get_data(struct inode *inode, int allocate)
+{
+ struct watch_inode_data **list;
+ struct watch_inode_data *ret = NULL;
+ int h;
+
+ spin_lock(&watch_hash_lock);
+
+ /* I_WATCH bit can only be changed under watch_hash_lock, so no need
+ to lock inode_lock (on all known hardware) */
+ if (!allocate && !(inode->i_state & I_WATCH))
+ goto out;
+
+ h = hash_ptr(inode, fswatch_hash_bits);
+ list = &fswatch_hash_table[h];
+
+ while (*list && (unsigned long)((*list)->inode) < (unsigned long)inode)
+ list = &(*list)->next_hash;
+ if (*list && (*list)->inode == inode)
+ ret = *list;
+
+ if (ret) {
+ ret->count++;
+ } else if (allocate) {
+ ret = fswatch_data_pool;
+ fswatch_data_pool = ret->next_hash;
+ fswatch_pool_size--;
+
+ INIT_HLIST_HEAD(&ret->watchlist);
+ INIT_HLIST_HEAD(&ret->watches);
+ ret->inode = inode;
+ ret->next_hash = *list;
+ ret->count = 2;
+ *list = ret;
+
+ spin_lock(&inode_lock);
+ inode->i_state |= I_WATCH;
+ spin_unlock(&inode_lock);
+ }
+ out:
+ spin_unlock(&watch_hash_lock);
+
+ return ret;
+}
+
+static inline struct watch *fswatch_fetch_watch(const char *name,
+ struct watch_inode_data *data,
+ uint user)
+{
+ struct watch *watch, *ret = NULL;
+ struct hlist_node *pos;
+
+ hlist_for_each_entry(watch, pos, &data->watchlist, w_node)
+ if (!strcmp(watch->w_name, name)) {
+ ret = fswatch_get_watch(watch);
+ break;
+ }
+
+ return ret;
+}
+
+static inline struct watch *fswatch_fetch_watch_lock(const char *name,
+ struct watch_inode_data *data,
+ uint user)
+{
+ struct watch *ret = NULL;
+
+ if (name && data) {
+ spin_lock(&fswatch_lock);
+ ret = fswatch_fetch_watch(name, data, user);
+ spin_unlock(&fswatch_lock);
+ }
+
+ return ret;
+}
+
+struct watch *fswatch_create_watch(char *name, uint user, uint mask,
+ void (*notify)(struct watch *, struct inode *, uint),
+ void (*destroy)(struct watch *))
+{
+ struct watch *watch;
+
+ watch = kmem_cache_alloc(watch_cache, GFP_KERNEL);
+ if (watch) {
+ memset(watch, 0, sizeof(*watch));
+ /* We take the first reference when this watch is inserted
+ * into a list */
+ atomic_set(&watch->w_count, 0);
+ }
+
+ return watch;
+}
+
+void fswatch_free_watch(struct watch *watch)
+{
+ BUG_ON(!watch);
+ BUG_ON(!hlist_unhashed(&watch->w_node));
+ BUG_ON(!hlist_unhashed(&watch->w_watched));
+
+ kfree(watch->w_name);
+ if (watch->w_destroy)
+ watch->w_destroy(watch);
+ else
+ kmem_cache_free(watch_cache, watch);
+}
+
+static inline void fswatch_destroy_watch(struct watch *watch)
+{
+ BUG_ON(!watch);
+
+ if (!hlist_unhashed(&watch->w_watched)) {
+ hlist_del_init(&watch->w_watched);
+ fswatch_put_watch(watch);
+ }
+
+ if (!hlist_unhashed(&watch->w_node)) {
+ hlist_del_init(&watch->w_node);
+ fswatch_put_watch(watch);
+ }
+}
+
+static inline void fswatch_drain_watchlist(struct watch_inode_data *data)
+{
+ struct watch *watch;
+ struct hlist_node *pos, *tmp;
+
+ spin_lock(&fswatch_lock);
+ hlist_for_each_entry_safe(watch, pos, tmp, &data->watchlist, w_node) {
+ fswatch_destroy_watch(watch);
+ fswatch_data_pool_shrink();
+ /* Add notification that fswatch was removed implicitly */
+ }
+ spin_unlock(&fswatch_lock);
+}
+
+static void fswatch_data_unhash(struct watch_inode_data *data)
+{
+ int h = hash_ptr(data->inode, fswatch_hash_bits);
+ struct watch_inode_data **list = &fswatch_hash_table[h];
+
+ while (*list && (unsigned long)((*list)->inode) < (unsigned long)data->inode)
+ list = &(*list)->next_hash;
+
+ BUG_ON(*list != data);
+ *list = data->next_hash;
+
+ spin_lock(&inode_lock);
+ data->inode->i_state &= ~I_WATCH;
+ spin_unlock(&inode_lock);
+ data->inode = NULL;
+}
+
+static void fswatch_put_data(struct watch_inode_data *data)
+{
+ if (!data)
+ return;
+
+ spin_lock(&watch_hash_lock);
+ data->count--;
+
+ if (data->count == 1 && data->inode &&
+ hlist_empty(&data->watches) && hlist_empty(&data->watchlist)) {
+ data->count--;
+ }
+
+ if (!data->count) {
+ /* We are last user. Remove it from the hash table to
+ disassociate it from its inode */
+ if (data->inode)
+ fswatch_data_unhash(data);
+ spin_unlock(&watch_hash_lock);
+
+ fswatch_drain_watchlist(data);
+
+ spin_lock(&watch_hash_lock);
+ /* Check whether to free it or return it to the pool */
+ if (fswatch_nr_watches > fswatch_pool_size) {
+ data->next_hash = fswatch_data_pool;
+ fswatch_data_pool = data;
+ fswatch_pool_size++;
+ } else {
+ kfree(data);
+ }
+ }
+ spin_unlock(&watch_hash_lock);
+}
+
+/*
+ * The update hook is responsible for watching and unwatching d_inodes during
+ * their lifetimes in dcache. Each d_inode being watched is pinned in memory.
+ * As soon as a d_inode becomes unwatched (ie: dentry is destroyed, watch is
+ * unhashed / removed from watchlist, dentry is moved out of watch path).
+ *
+ * Hook appears in fs/dcache.c:
+ * d_move(),
+ * dentry_iput(),
+ * d_instantiate(),
+ * d_splice_alias()
+ * __d_lookup()
+ */
+void fswatch_update_watch(struct dentry *dentry, int remove)
+{
+ struct watch *this;
+ struct watch *watch;
+ struct watch_inode_data *data = NULL;
+ struct watch_inode_data *parent = NULL;
+ struct hlist_node *wpos;
+ struct hlist_node *tpos;
+ struct hlist_node *tmp;
+
+ if (!dentry || !dentry->d_inode)
+ return;
+
+ if (!dentry->d_parent || !dentry->d_parent->d_inode)
+ return;
+
+ /* If there's no watch data on the parent inode, then there can
+ be no watches to add or remove */
+ parent = fswatch_get_data(dentry->d_parent->d_inode, 0);
+ if (!parent)
+ return;
+
+ spin_lock(&fswatch_lock);
+ /* Must update every watch here */
+ hlist_for_each_entry(watch, wpos, &parent->watchlist, w_node) {
+ watch = fswatch_get_watch(watch);
+ spin_unlock(&fswatch_lock);
+ if (strcmp(watch->w_name, dentry->d_name.name)) {
+ fswatch_put_watch(watch);
+ spin_lock(&fswatch_lock);
+ continue;
+ }
+ /* Fetch watch data, using the preallocated one from the watch
+ if there is actually a relevant watch and the inode didn't
+ already have any fswatch data */
+ data = fswatch_get_data(dentry->d_inode, !!watch);
+
+ /* If there's no data, then there wasn't a watch either.
+ Nothing to see here; move along */
+ if (!data) {
+ fswatch_put_watch(watch);
+ goto put_fswatch;
+ }
+
+ spin_lock(&fswatch_lock);
+ if (remove) {
+ if (watch && !hlist_unhashed(&watch->w_watched)) {
+ hlist_del_init(&watch->w_watched);
+ fswatch_put_watch(watch);
+ }
+ } else {
+ hlist_for_each_entry_safe(this, tpos, tmp, &data->watches, w_watched) {
+ if (hlist_unhashed(&this->w_node)) {
+ hlist_del(&this->w_watched);
+ fswatch_put_watch(this);
+ }
+ }
+ if (watch && hlist_unhashed(&watch->w_watched)) {
+ hlist_add_head(&watch->w_watched, &data->watches);
+ fswatch_get_watch(watch);
+ }
+ }
+ fswatch_put_watch(watch);
+ fswatch_put_data(data);
+
+ }
+ spin_unlock(&fswatch_lock);
+
+put_fswatch:
+ fswatch_put_data(parent);
+}
+
+int fswatch_add_watch(char *path, struct watch *watch)
+{
+ int ret;
+ struct nameidata nd;
+ struct watch_inode_data *pdata;
+ struct watch *lookup;
+
+ /* Grow the pool by two -- one for the fswatch itself, and
+ one for the parent directory */
+ if (fswatch_data_pool_grow())
+ return -ENOMEM;
+
+ ret = path_lookup(path, LOOKUP_PARENT, &nd);
+ if (ret < 0)
+ goto out;
+
+ ret = -EPERM;
+ if (nd.last_type != LAST_NORM || !nd.last.name)
+ goto release;
+
+ ret = -ENOMEM;
+ watch->w_name = kmalloc(strlen(nd.last.name)+1, GFP_KERNEL);
+ if (!watch->w_name)
+ goto release;
+
+ strcpy(watch->w_name, nd.last.name);
+ watch->w_dev = nd.dentry->d_inode->i_sb->s_dev;
+
+ pdata = fswatch_get_data(nd.dentry->d_inode, 1);
+ if (!pdata)
+ goto put_pdata;
+
+ ret = -EEXIST;
+ lookup = fswatch_fetch_watch_lock(nd.last.name, pdata, watch->w_user);
+ if (lookup) {
+ fswatch_put_watch(lookup);
+ goto put_pdata;
+ }
+
+ ret = 0;
+ spin_lock(&fswatch_lock);
+ fswatch_get_watch(watch);
+ hlist_add_head(&watch->w_node, &pdata->watchlist);
+ spin_unlock(&fswatch_lock);
+
+ /* __d_lookup will attach the audit data, if nd.last exists. */
+ dput(d_lookup(nd.dentry, &nd.last));
+
+put_pdata:
+ fswatch_put_data(pdata);
+release:
+ path_release(&nd);
+out:
+ if (ret)
+ fswatch_data_pool_shrink();
+ return ret;
+}
+
+int fswatch_rem_watch(char *path, uint user)
+{
+ int ret = 0;
+ struct nameidata nd;
+ struct watch_inode_data *pdata = NULL;
+ struct watch *watch;
+
+ ret = path_lookup(path, LOOKUP_PARENT, &nd);
+ if (ret < 0)
+ goto out;
+
+ ret = -ENOENT;
+ if (nd.last_type != LAST_NORM || !nd.last.name)
+ goto release;
+
+ pdata = fswatch_get_data(nd.dentry->d_inode, 0);
+ if (!pdata)
+ goto release;
+
+ spin_lock(&fswatch_lock);
+ watch = fswatch_fetch_watch(nd.last.name, pdata, user);
+ if (!watch) {
+ spin_unlock(&fswatch_lock);
+ goto release;
+ }
+
+ ret = 0;
+ fswatch_destroy_watch(watch);
+ fswatch_put_watch(watch);
+ spin_unlock(&fswatch_lock);
+
+release:
+ path_release(&nd);
+out:
+ fswatch_put_data(pdata);
+ if (!ret)
+ fswatch_data_pool_shrink();
+ return ret;
+}
+
+struct watch *fswatch_get_watch(struct watch *watch)
+{
+ int new;
+
+ BUG_ON(!watch);
+
+ new = atomic_inc_return(&watch->w_count);
+
+ return watch;
+}
+
+void fswatch_put_watch(struct watch *watch)
+{
+ int new;
+
+ BUG_ON(!watch);
+
+ new = atomic_dec_return(&watch->w_count);
+ if (!new)
+ fswatch_free_watch(watch);
+}
+
+void fswatch_inode_free(struct inode *inode)
+{
+ struct watch *watch;
+ struct hlist_node *pos, *tmp;
+ struct watch_inode_data *data = fswatch_get_data(inode, 0);
+
+ if (data) {
+ spin_lock(&watch_hash_lock);
+ fswatch_data_unhash(data);
+ spin_unlock(&watch_hash_lock);
+
+ fswatch_drain_watchlist(data);
+ /* Release all our references to any watches we may have on us */
+ spin_lock(&fswatch_lock);
+ hlist_for_each_entry_safe(watch, pos, tmp, &data->watches, w_watched) {
+ hlist_del(&watch->w_watched);
+ fswatch_put_watch(watch);
+ }
+ spin_unlock(&fswatch_lock);
+ fswatch_put_data(data);
+ }
+}
+
+int fswatch_init(void)
+{
+
+ watch_cache = kmem_cache_create("watch_cache", sizeof(struct watch),
+ 0, 0, NULL, NULL);
+ if (!watch_cache)
+ goto fswatch_init_fail;
+
+ /* Set up hash table for inode objects */
+ fswatch_hash_bits = long_log2(fswatch_cache_buckets);
+ if (fswatch_cache_buckets != (1 << fswatch_hash_bits)) {
+ fswatch_hash_bits++;
+ fswatch_cache_buckets = 1 << fswatch_hash_bits;
+ printk(KERN_NOTICE
+ "%s: fswatch_cache_buckets set to %d (bits %d)\n",
+ __FUNCTION__, fswatch_cache_buckets, fswatch_hash_bits);
+ }
+
+ fswatch_hash_table = kmalloc(fswatch_cache_buckets * sizeof(void *), GFP_KERNEL);
+
+ if (!fswatch_hash_table) {
+ printk(KERN_NOTICE "No memory to initialize fswatch cache.\n");
+ goto fswatch_init_fail;
+ }
+
+ memset(fswatch_hash_table, 0, fswatch_cache_buckets * sizeof(void *));
+
+ return 0;
+
+fswatch_init_fail:
+ kmem_cache_destroy(watch_cache);
+ return -ENOMEM;
+}
+
+
+/*
+ * Notify all users with watches on this inode, interested in this action
+ */
+void fswatch_notify_users(struct inode *inode, const void *aux, uint action)
+{
+ struct watch *watch;
+ struct watch_inode_data *data = NULL;
+ struct hlist_node *pos;
+
+ data = fswatch_get_data(inode, 0);
+ if (!data)
+ return;
+
+ spin_lock(&fswatch_lock);
+ hlist_for_each_entry(watch, pos, &data->watches, w_watched) {
+ fswatch_get_watch(watch);
+ spin_unlock(&fswatch_lock);
+ if (!watch->w_mask || watch->w_mask&action)
+ watch->w_notify(watch, aux, inode, action);
+ fswatch_put_watch(watch);
+ spin_lock(&fswatch_lock);
+ }
+ spin_unlock(&fswatch_lock);
+}
+
+/*
+ * fswatch_notify_move - file old_name at old_dir was moved to new_name at new_dir
+ */
+
+/*
+ * fswatch_notify_unlink - file was unlinked
+ */
+void fswatch_notify_unlink(struct dentry *dentry, struct inode *dir)
+{
+ struct inode *inode = dentry->d_inode;
+ const char *name = dentry->d_name.name;
+
+ fswatch_notify_users(inode, name, FSW_DELETE_SELF);
+ fswatch_notify_users(dir, name, FSW_DELETE);
+}
+
+/*
+ * fswatch_notify_rmdir - directory was removed
+ */
+void fswatch_notify_rmdir(struct dentry *dentry, struct inode *inode,
+ struct inode *dir)
+{
+ const char *name = dentry->d_name.name;
+
+ fswatch_notify_users(inode, name, FSW_DELETE_SELF);
+ fswatch_notify_users(dir, name, FSW_DELETE);
+ return;
+}
+
+/*
+ * fswatch_notify_create - 'name' was linked in
+ */
+void fswatch_notify_create(struct inode *inode, const char *name)
+{
+ fswatch_notify_users(inode, name, FSW_CREATE);
+ return;
+}
+
+/*
+ * fswatch_notify_mkdir - directory 'name' was created
+ */
+void fswatch_notify_mkdir(struct inode *inode, const char *name)
+{
+ fswatch_notify_users(inode, name, FSW_CREATE);
+ return;
+}
+
+/*
+ * fswatch_notify_access - file was read
+ */
+void fswatch_notify_access(struct dentry *dentry)
+{
+ struct inode *inode = dentry->d_inode;
+ const char *name = dentry->d_name.name;
+
+ fswatch_notify_users(inode, name, FSW_ACCESS);
+ return;
+}
+EXPORT_SYMBOL_GPL(fswatch_notify_access);
+
+/*
+ * fswatch_notify_modify - file was modified
+ */
+void fswatch_notify_modify(struct dentry *dentry)
+{
+ struct inode *inode = dentry->d_inode;
+ const char *name = dentry->d_name.name;
+
+ fswatch_notify_users(inode, name, FSW_MODIFY);
+ return;
+}
+EXPORT_SYMBOL_GPL(fswatch_notify_modify);
+
+/*
+ * fswatch_notify_open - file was opened
+ */
+void fswatch_notify_open(struct dentry *dentry, int mask)
+{
+ struct inode *inode = dentry->d_inode;
+ const char *name = dentry->d_name.name;
+
+ fswatch_notify_users(inode, name, FSW_OPEN);
+ return;
+}
+
+/*
+ * fswatch_notify_close - file was closed
+ */
+void fswatch_notify_close(struct file *file)
+{
+ struct dentry *dentry = file->f_dentry;
+ struct inode *inode = dentry->d_inode;
+ struct inode *parent = dentry->d_parent->d_inode;
+ const char *name = dentry->d_name.name;
+
+ if (file->f_mode & FMODE_WRITE) {
+ fswatch_notify_users(inode, name, FSW_CLOSE_WRITE);
+ fswatch_notify_users(parent, name, FSW_CLOSE_WRITE);
+ } else {
+ fswatch_notify_users(inode, name, FSW_CLOSE_NOWRITE);
+ fswatch_notify_users(parent, name, FSW_CLOSE_NOWRITE);
+ }
+ return;
+}
+
+/*
+ * fswatch_notify_xattr - extended attributes were changed
+ */
+void fswatch_notify_xattr(struct dentry *dentry)
+{
+ struct inode *inode = dentry->d_inode;
+ struct inode *parent = dentry->d_parent->d_inode;
+ const char *name = dentry->d_name.name;
+
+ fswatch_notify_users(inode, name, FSW_ATTRIB);
+ fswatch_notify_users(parent, name, FSW_ATTRIB);
+ return;
+}
+
+/*
+ * fswatch_notify_change - notify_change event. file was modified and/or metadata
+ * was changed.
+ */
+void fswatch_notify_change(struct dentry *dentry, unsigned int ia_valid)
+{
+ return;
+}
diff --git a/fs/inode.c b/fs/inode.c
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -22,6 +22,8 @@
#include <linux/cdev.h>
#include <linux/bootmem.h>
#include <linux/inotify.h>
+#include <linux/fswatch.h>
+#include <linux/audit.h>
/*
* This is needed for the following functions:
@@ -173,6 +175,7 @@ void destroy_inode(struct inode *inode)
{
if (inode_has_buffers(inode))
BUG();
+ fswatch_inode_free(inode);
security_inode_free(inode);
if (inode->i_sb->s_op->destroy_inode)
inode->i_sb->s_op->destroy_inode(inode);
@@ -262,7 +265,7 @@ void clear_inode(struct inode *inode)
bd_forget(inode);
if (inode->i_cdev)
cd_forget(inode);
- inode->i_state = I_CLEAR;
+ inode->i_state = I_CLEAR | (inode->i_state & I_WATCH);
}
EXPORT_SYMBOL(clear_inode);
@@ -1051,7 +1054,7 @@ void generic_delete_inode(struct inode *
hlist_del_init(&inode->i_hash);
spin_unlock(&inode_lock);
wake_up_inode(inode);
- if (inode->i_state != I_CLEAR)
+ if ((inode->i_state & ~I_WATCH) != I_CLEAR)
BUG();
destroy_inode(inode);
}
@@ -1363,6 +1366,8 @@ void __init inode_init(unsigned long mem
inode_cachep = kmem_cache_create("inode_cache", sizeof(struct inode),
0, SLAB_RECLAIM_ACCOUNT|SLAB_PANIC, init_once, NULL);
set_shrinker(DEFAULT_SEEKS, shrink_icache_memory);
+ fswatch_init();
+ audit_filesystem_init();
/* Hash may have been set up in inode_init_early */
if (!hashdist)
diff --git a/fs/namei.c b/fs/namei.c
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -21,7 +21,7 @@
#include <linux/namei.h>
#include <linux/quotaops.h>
#include <linux/pagemap.h>
-#include <linux/fsnotify.h>
+#include <linux/fswatch.h>
#include <linux/smp_lock.h>
#include <linux/personality.h>
#include <linux/security.h>
@@ -1312,7 +1312,7 @@ int vfs_create(struct inode *dir, struct
DQUOT_INIT(dir);
error = dir->i_op->create(dir, dentry, mode, nd);
if (!error) {
- fsnotify_create(dir, dentry->d_name.name);
+ fswatch_notify_create(dir, dentry->d_name.name);
security_inode_post_create(dir, dentry, mode);
}
return error;
@@ -1637,7 +1637,7 @@ int vfs_mknod(struct inode *dir, struct
DQUOT_INIT(dir);
error = dir->i_op->mknod(dir, dentry, mode, dev);
if (!error) {
- fsnotify_create(dir, dentry->d_name.name);
+ fswatch_notify_create(dir, dentry->d_name.name);
security_inode_post_mknod(dir, dentry, mode, dev);
}
return error;
@@ -1710,7 +1710,7 @@ int vfs_mkdir(struct inode *dir, struct
DQUOT_INIT(dir);
error = dir->i_op->mkdir(dir, dentry, mode);
if (!error) {
- fsnotify_mkdir(dir, dentry->d_name.name);
+ fswatch_notify_mkdir(dir, dentry->d_name.name);
security_inode_post_mkdir(dir,dentry, mode);
}
return error;
@@ -1801,7 +1801,7 @@ int vfs_rmdir(struct inode *dir, struct
}
up(&dentry->d_inode->i_sem);
if (!error) {
- fsnotify_rmdir(dentry, dentry->d_inode, dir);
+ fswatch_notify_rmdir(dentry, dentry->d_inode, dir);
d_delete(dentry);
}
dput(dentry);
@@ -1874,7 +1874,7 @@ int vfs_unlink(struct inode *dir, struct
/* We don't d_delete() NFS sillyrenamed files--they still exist. */
if (!error && !(dentry->d_flags & DCACHE_NFSFS_RENAMED)) {
- fsnotify_unlink(dentry, dir);
+ fswatch_notify_unlink(dentry, dir);
d_delete(dentry);
}
@@ -1951,7 +1951,7 @@ int vfs_symlink(struct inode *dir, struc
DQUOT_INIT(dir);
error = dir->i_op->symlink(dir, dentry, oldname);
if (!error) {
- fsnotify_create(dir, dentry->d_name.name);
+ fswatch_notify_create(dir, dentry->d_name.name);
security_inode_post_symlink(dir, dentry, oldname);
}
return error;
@@ -2024,7 +2024,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) {
- fsnotify_create(dir, new_dentry->d_name.name);
+ fswatch_notify_create(dir, new_dentry->d_name.name);
security_inode_post_link(old_dentry, dir, new_dentry);
}
return error;
@@ -2188,7 +2188,6 @@ int vfs_rename(struct inode *old_dir, st
{
int error;
int is_dir = S_ISDIR(old_dentry->d_inode->i_mode);
- const char *old_name;
if (old_dentry->d_inode == new_dentry->d_inode)
return 0;
@@ -2210,17 +2209,13 @@ int vfs_rename(struct inode *old_dir, st
DQUOT_INIT(old_dir);
DQUOT_INIT(new_dir);
- old_name = fsnotify_oldname_init(old_dentry->d_name.name);
-
if (is_dir)
error = vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry);
else
error = vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry);
if (!error) {
- const char *new_name = old_dentry->d_name.name;
- fsnotify_move(old_dir, new_dir, old_name, new_name, is_dir);
+ //fswatch_notify_move(old_dir, new_dir, old_name, new_name, is_dir);
}
- fsnotify_oldname_free(old_name);
return error;
}
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -45,7 +45,7 @@
#endif /* CONFIG_NFSD_V3 */
#include <linux/nfsd/nfsfh.h>
#include <linux/quotaops.h>
-#include <linux/fsnotify.h>
+#include <linux/fswatch.h>
#include <linux/posix_acl.h>
#include <linux/posix_acl_xattr.h>
#ifdef CONFIG_NFSD_V4
@@ -860,8 +860,8 @@ nfsd_vfs_read(struct svc_rqst *rqstp, st
nfsdstats.io_read += err;
*count = err;
err = 0;
- fsnotify_access(file->f_dentry);
- } else
+ fswatch_notify_access(file->f_dentry);
+ } else
err = nfserrno(err);
out:
return err;
@@ -916,7 +916,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, s
set_fs(oldfs);
if (err >= 0) {
nfsdstats.io_write += cnt;
- fsnotify_modify(file->f_dentry);
+ fswatch_notify_modify(file->f_dentry);
}
/* clear setuid/setgid flag after write */
diff --git a/fs/open.c b/fs/open.c
--- a/fs/open.c
+++ b/fs/open.c
@@ -10,7 +10,7 @@
#include <linux/file.h>
#include <linux/smp_lock.h>
#include <linux/quotaops.h>
-#include <linux/fsnotify.h>
+#include <linux/fswatch.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/tty.h>
@@ -951,7 +951,7 @@ asmlinkage long sys_open(const char __us
put_unused_fd(fd);
fd = PTR_ERR(f);
} else {
- fsnotify_open(f->f_dentry);
+ fswatch_notify_open(f->f_dentry, mode);
fd_install(fd, f);
}
}
@@ -990,7 +990,7 @@ int filp_close(struct file *filp, fl_own
if (filp->f_op && filp->f_op->flush)
retval = filp->f_op->flush(filp);
- dnotify_flush(filp, id);
+ //dnotify_flush(filp, id);
locks_remove_posix(filp, id);
fput(filp);
return retval;
diff --git a/fs/read_write.c b/fs/read_write.c
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -10,7 +10,7 @@
#include <linux/file.h>
#include <linux/uio.h>
#include <linux/smp_lock.h>
-#include <linux/fsnotify.h>
+#include <linux/fswatch.h>
#include <linux/security.h>
#include <linux/module.h>
#include <linux/syscalls.h>
@@ -252,7 +252,7 @@ ssize_t vfs_read(struct file *file, char
else
ret = do_sync_read(file, buf, count, pos);
if (ret > 0) {
- fsnotify_access(file->f_dentry);
+ fswatch_notify_access(file->f_dentry);
current->rchar += ret;
}
current->syscr++;
@@ -303,7 +303,7 @@ ssize_t vfs_write(struct file *file, con
else
ret = do_sync_write(file, buf, count, pos);
if (ret > 0) {
- fsnotify_modify(file->f_dentry);
+ fswatch_notify_modify(file->f_dentry);
current->wchar += ret;
}
current->syscw++;
@@ -541,9 +541,9 @@ out:
kfree(iov);
if ((ret + (type == READ)) > 0) {
if (type == READ)
- fsnotify_access(file->f_dentry);
+ fswatch_notify_access(file->f_dentry);
else
- fsnotify_modify(file->f_dentry);
+ fswatch_notify_modify(file->f_dentry);
}
return ret;
Efault:
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c
--- a/fs/sysfs/file.c
+++ b/fs/sysfs/file.c
@@ -3,8 +3,9 @@
*/
#include <linux/module.h>
-#include <linux/fsnotify.h>
#include <linux/kobject.h>
+#include <linux/fswatch.h>
+#include <linux/dnotify.h>
#include <linux/namei.h>
#include <asm/uaccess.h>
#include <asm/semaphore.h>
@@ -405,7 +406,7 @@ int sysfs_update_file(struct kobject * k
if (victim->d_inode &&
(victim->d_parent->d_inode == dir->d_inode)) {
victim->d_inode->i_mtime = CURRENT_TIME;
- fsnotify_modify(victim);
+ fswatch_notify_modify(victim);
/**
* Drop reference from initial sysfs_get_dentry().
diff --git a/fs/xattr.c b/fs/xattr.c
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -16,7 +16,7 @@
#include <linux/security.h>
#include <linux/syscalls.h>
#include <linux/module.h>
-#include <linux/fsnotify.h>
+#include <linux/fswatch.h>
#include <asm/uaccess.h>
/*
@@ -59,7 +59,7 @@ setxattr(struct dentry *d, char __user *
goto out;
error = d->d_inode->i_op->setxattr(d, kname, kvalue, size, flags);
if (!error) {
- fsnotify_xattr(d);
+ fswatch_notify_xattr(d);
security_inode_post_setxattr(d, kname, kvalue, size, flags);
}
out:
diff --git a/include/linux/audit.h b/include/linux/audit.h
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -24,8 +24,15 @@
#ifndef _LINUX_AUDIT_H_
#define _LINUX_AUDIT_H_
+#ifdef __KERNEL__
#include <linux/sched.h>
#include <linux/elf.h>
+#include <linux/fswatch.h>
+
+struct hlist_head;
+struct hlist_node;
+struct dentry;
+#endif
/* The netlink messages for the audit system is divided into blocks:
* 1000 - 1099 are for commanding the audit system
@@ -68,6 +75,7 @@
#define AUDIT_CONFIG_CHANGE 1305 /* Audit system configuration change */
#define AUDIT_SOCKADDR 1306 /* sockaddr copied as syscall arg */
#define AUDIT_CWD 1307 /* Current working directory */
+#define AUDIT_FS_INODE 1308 /* File system inode */
#define AUDIT_AVC 1400 /* SE Linux avc denial or grant */
#define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */
@@ -175,6 +183,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
+
struct audit_status {
__u32 mask; /* Bit mask for valid entries */
__u32 enabled; /* 1 = enabled, 0 = disabled */
@@ -195,13 +206,42 @@ struct audit_rule { /* for AUDIT_LIST,
__u32 values[AUDIT_MAX_FIELDS];
};
+/* Structure to transport watch data to and from the kernel */
+
+struct watch_transport {
+ __u32 dev_major;
+ __u32 dev_minor;
+ __u32 actions;
+ __u32 valid;
+ __u32 pathlen;
+ __u32 fklen;
+ char buf[0];
+};
+
+struct watch;
+
#ifdef __KERNEL__
+struct audit_watch {
+ char * au_name;
+ char * au_path;
+ char * au_filterkey;
+ uint32_t au_actions;
+ dev_t au_dev;
+ struct watch au_watch;
+ struct hlist_node au_master;
+};
struct audit_sig_info {
uid_t uid;
pid_t pid;
};
+struct audit_watch_info {
+ struct hlist_node node;
+ struct watch *watch;
+ uint action;
+};
+
struct audit_buffer;
struct audit_context;
struct inode;
@@ -248,6 +288,7 @@ extern int audit_filter_user(struct netl
#define audit_inode(n,i,f) do { ; } while (0)
#define audit_receive_filter(t,p,u,s,d,l) ({ -EOPNOTSUPP; })
#define auditsc_get_stamp(c,t,s) do { BUG(); } while (0)
+#define audit_receive_filter(t,p,u,s,d,l) ({ -EOPNOTSUPP; })
#define audit_get_loginuid(c) ({ -1; })
#define audit_ipc_perms(q,u,g,m) ({ 0; })
#define audit_socketcall(n,a) ({ 0; })
@@ -257,6 +298,20 @@ extern int audit_filter_user(struct netl
#define audit_filter_user(cb,t) ({ 1; })
#endif
+#ifdef CONFIG_AUDITFILESYSTEM
+extern int audit_filesystem_init(void);
+extern int audit_list_watches(int pid, int seq);
+extern int audit_receive_watch(int type, int pid, int uid, int seq,
+ struct watch_transport *req, uid_t loginuid);
+extern void auditfs_attach_wdata(struct watch *, const void *, struct inode *, uint);
+#else
+#define audit_filesystem_init(t) ({ 0; })
+#define audit_list_watches(p,s) ({ -EOPNOTSUPP; })
+#define audit_receive_watch(t,p,u,s,r,l) ({ -EOPNOTSUPP; })
+#define audit_update_watch(d,r) do { ; } while (0)
+#define audit_notify_watch(i,m) do { ; } while(0)
+#endif
+
#ifdef CONFIG_AUDIT
/* These are defined in audit.c */
/* Public API */
diff --git a/include/linux/dnotify.h b/include/linux/dnotify.h
--- a/include/linux/dnotify.h
+++ b/include/linux/dnotify.h
@@ -33,8 +33,109 @@ static inline void inode_dir_notify(stru
__inode_dir_notify(inode, event);
}
+static inline void dnotify_move(struct inode *old_dir, struct inode *new_dir,
+ const char *old_name, const char *new_name,
+ int isdir)
+{
+ if (old_dir == new_dir)
+ inode_dir_notify(old_dir, DN_RENAME);
+ else {
+ inode_dir_notify(old_dir, DN_DELETE);
+ inode_dir_notify(new_dir, DN_CREATE);
+ }
+}
+
+static inline void dnotify_unlink(struct dentry *dentry, struct inode *dir)
+{
+ inode_dir_notify(dir, DN_DELETE);
+}
+
+static inline void dnotify_rmdir(struct dentry *dentry, struct inode *inode,
+ struct inode *dir)
+{
+ inode_dir_notify(dir, DN_DELETE);
+}
+
+static inline void dnotify_create(struct inode *inode, const char *name)
+{
+ inode_dir_notify(inode, DN_CREATE);
+}
+
+static inline void dnotify_mkdir(struct inode *inode, const char *name)
+{
+ inode_dir_notify(inode, DN_CREATE);
+}
+
+static inline void dnotify_access(struct dentry *dentry)
+{
+ dnotify_parent(dentry, DN_ACCESS);
+}
+
+static inline void dnotify_modify(struct dentry *dentry)
+{
+ dnotify_parent(dentry, DN_MODIFY);
+}
+
+static inline void dnotify_change(struct dentry *dentry, unsigned int ia_valid)
+{
+ int dn_mask = 0;
+
+ if (ia_valid & ATTR_UID)
+ dn_mask |= DN_ATTRIB;
+ if (ia_valid & ATTR_GID)
+ dn_mask |= DN_ATTRIB;
+ if (ia_valid & ATTR_SIZE)
+ dn_mask |= DN_MODIFY;
+ /* both times implies a utime(s) call */
+ if ((ia_valid & (ATTR_ATIME | ATTR_MTIME)) == (ATTR_ATIME | ATTR_MTIME))
+ dn_mask |= DN_ATTRIB;
+ else if (ia_valid & ATTR_ATIME)
+ dn_mask |= DN_ACCESS;
+ else if (ia_valid & ATTR_MTIME)
+ dn_mask |= DN_MODIFY;
+ if (ia_valid & ATTR_MODE)
+ dn_mask |= DN_ATTRIB;
+
+ if (dn_mask)
+ dnotify_parent(dentry, dn_mask);
+}
#else
+static inline void dnotify_move(struct inode *old_dir, struct inode *new_dir,
+ const char *old_name, const char *new_name,
+ int isdir)
+{
+}
+
+static inline void dnotify_unlink(struct dentry *dentry, struct inode *dir)
+{
+}
+
+static inline void dnotify_rmdir(struct dentry *dentry, struct inode *inode,
+ struct inode *dir)
+{
+}
+
+static inline void dnotify_create(struct inode *inode, const char *name)
+{
+}
+
+static inline void dnotify_mkdir(struct inode *inode, const char *name)
+{
+}
+
+static inline void dnotify_access(struct dentry *dentry)
+{
+}
+
+static inline void dnotify_modify(struct dentry *dentry)
+{
+}
+
+static inline void dnotify_change(struct dentry *dentry, unsigned int ia_valid)
+{
+}
+
static inline void __inode_dir_notify(struct inode *inode, unsigned long event)
{
}
diff --git a/include/linux/fs.h b/include/linux/fs.h
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -227,6 +227,7 @@ struct poll_table_struct;
struct kstatfs;
struct vm_area_struct;
struct vfsmount;
+struct audit_inode_data;
/* Used to be a macro which just called the function, now just a function */
extern void update_atime (struct inode *);
@@ -1053,6 +1054,7 @@ struct super_operations {
#define I_CLEAR 32
#define I_NEW 64
#define I_WILL_FREE 128
+#define I_WATCH 256
#define I_DIRTY (I_DIRTY_SYNC | I_DIRTY_DATASYNC | I_DIRTY_PAGES)
diff --git a/include/linux/fswatch.h b/include/linux/fswatch.h
new file mode 100644
--- /dev/null
+++ b/include/linux/fswatch.h
@@ -0,0 +1,118 @@
+#ifndef _FSWATCH_H
+#define _FSWATCH_H
+
+#include <linux/list.h>
+
+#define FSW_USER_AUDIT 0x0001
+#define FSW_USER_INOTIFY 0x0002
+
+#define FSW_ACCESS 0x00000001 /* File was accessed */
+#define FSW_MODIFY 0x00000002 /* File was modified */
+#define FSW_ATTRIB 0x00000004 /* Metadata changed */
+#define FSW_CLOSE_WRITE 0x00000008 /* Writtable file was closed */
+#define FSW_CLOSE_NOWRITE 0x00000010 /* Unwrittable file closed */
+#define FSW_OPEN 0x00000020 /* File was opened */
+#define FSW_MOVED_FROM 0x00000040 /* File was moved from X */
+#define FSW_MOVED_TO 0x00000080 /* File was moved to Y */
+#define FSW_CREATE 0x00000100 /* Subfile was created */
+#define FSW_DELETE 0x00000200 /* Subfile was deleted */
+#define FSW_DELETE_SELF 0x00000400 /* Self was deleted */
+
+struct inode;
+
+struct watch {
+ atomic_t w_count;
+ uint w_mask; /* Action mask */
+ uint w_user; /* Watch user */
+ struct hlist_node w_node; /* Per-directory list */
+ struct hlist_node w_watched; /* Per-inode list */
+ dev_t w_dev; /* Watch device */
+ char *w_name; /* Watch's name */
+ void (*w_destroy)(struct watch *);
+ void (*w_notify)(struct watch *, const void *, struct inode *, uint);
+};
+
+struct watch_inode_data {
+ int count;
+ struct watch_inode_data *next_hash; /* Watch data hash table */
+ struct inode *inode; /* Inode to which it belongs */
+ struct hlist_head watches; /* Watches on us */
+ struct hlist_head watchlist; /* Watches on children */
+};
+
+/* User */
+int fswatch_add_watch(char *, struct watch *);
+int fswatch_rem_watch(char *, uint user);
+void fswatch_put_watch(struct watch *);
+struct watch *fswatch_get_watch(struct watch *);
+struct watch *fswatch_alloc_watch(char *, char *, uint, uint,
+ void (*)(struct watch *),
+ void (*)(struct watch *, const void *, struct inode *, uint));
+
+/* Internals */
+int fswatch_init(void);
+void fswatch_inode_free(struct inode *);
+void fswatch_update_watch(struct dentry *, int);
+
+/* Hooks */
+#ifdef CONFIG_FSWATCH
+/* FIXME: fswatch_notify_move */
+const char *fswatch_notify_oldname_init(const char *);
+void fswatch_notify_oldname_free(const char *);
+void fswatch_notify_unlink(struct dentry *, struct inode *);
+void fswatch_notify_rmdir(struct dentry *, struct inode *, struct inode *);
+void fswatch_notify_create(struct inode *, const char *);
+void fswatch_notify_mkdir(struct inode *, const char *);
+void fswatch_notify_access(struct dentry *dentry);
+void fswatch_notify_modify(struct dentry *dentry);
+void fswatch_notify_open(struct dentry *dentry, int mask);
+void fswatch_notify_close(struct file *file);
+void fswatch_notify_xattr(struct dentry *dentry);
+void fswatch_notify_change(struct dentry *dentry, unsigned int ia_valid);
+void fswatch_notify_perms(struct inode *, int mask);
+#else
+/* FIXME: fswatch_notify_move */
+static inline const char *fswatch_notify_oldname_init(const char *)
+{
+ return NULL;
+}
+static inline void fswatch_notify_oldname_free(const char *)
+{
+}
+static inline void fswatch_notify_unlink(struct dentry *, struct inode *)
+{
+}
+static inline void fswatch_notify_rmdir(struct dentry *, struct inode *, struct inode *)
+{
+{
+static inline void fswatch_notify_create(struct inode *, const char *)
+{
+}
+static inline void fswatch_notify_mkdir(struct inode *, const char *)
+{
+}
+static inline void fswatch_notify_access(struct dentry *dentry)
+{
+}
+static inline void fswatch_notify_modify(struct dentry *dentry)
+{
+}
+static inline void fswatch_notify_open(struct dentry *dentry, int mask)
+{
+}
+static inline void fswatch_notify_close(struct file *file)
+{
+}
+static inline void fswatch_notify_xattr(struct dentry *dentry)
+{
+}
+static inline void fswatch_notify_change(struct dentry *dentry, unsigned int ia_valid)
+{
+}
+static inline void fswatch_notify_perms(struct inode *, int mask)
+{
+}
+#endif /* CONFIG_FSWATCH */
+
+#endif /* _FSWATCH_H */
+
diff --git a/init/Kconfig b/init/Kconfig
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -181,6 +181,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 && FSWATCH
+ 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 --git a/kernel/Makefile b/kernel/Makefile
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -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_GENERIC_HARDIRQS) += irq/
diff --git a/kernel/audit.c b/kernel/audit.c
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -111,8 +111,13 @@ static DECLARE_WAIT_QUEUE_HEAD(kauditd_w
static DECLARE_WAIT_QUEUE_HEAD(audit_backlog_wait);
/* The netlink socket is only to be read by 1 CPU, which lets us assume
- * that list additions and deletions never happen simultaneously in
- * auditsc.c */
+ * that list additions and deletions, and watch insertions never happen
+ * simultaneiously in auditsc.c and auditfs.c respectively.
+ *
+ * Even though there can only be one watch removal via netlink at a time,
+ * there is still a chance of watch removal via a hook. In this case, the
+ * semaphore is not enough.
+ */
DECLARE_MUTEX(audit_netlink_sem);
/* AUDIT_BUFSIZ is the size of the temporary buffer used for formatting
@@ -348,6 +353,9 @@ static int audit_netlink_ok(kernel_cap_t
case AUDIT_SET:
case AUDIT_ADD:
case AUDIT_DEL:
+ case AUDIT_WATCH_LIST:
+ case AUDIT_WATCH_INS:
+ case AUDIT_WATCH_REM:
case AUDIT_SIGNAL_INFO:
if (!cap_raised(eff_cap, CAP_AUDIT_CONTROL))
err = -EPERM;
@@ -464,6 +472,17 @@ static int audit_receive_msg(struct sk_b
audit_send_reply(NETLINK_CB(skb).pid, seq, AUDIT_SIGNAL_INFO,
0, 0, &sig_data, sizeof(sig_data));
break;
+ case AUDIT_WATCH_LIST:
+ err = audit_list_watches(pid, seq);
+ break;
+ case AUDIT_WATCH_INS:
+ case AUDIT_WATCH_REM:
+ if (nlh->nlmsg_len < sizeof(struct watch_transport))
+ return -EINVAL;
+ err = audit_receive_watch(nlh->nlmsg_type,
+ NETLINK_CB(skb).pid,
+ uid, seq, data, loginuid);
+ break;
default:
err = -EINVAL;
break;
diff --git a/kernel/auditfs.c b/kernel/auditfs.c
new file mode 100644
--- /dev/null
+++ b/kernel/auditfs.c
@@ -0,0 +1,329 @@
+/* auditfs.c -- Filesystem auditing support
+ * Implements filesystem auditing support, depends on kernel/auditsc.c
+ *
+ * Copyright 2005 International Business Machines Corp. (IBM)
+ * Copyright 2005 Red Hat, Inc.
+ *
+ * 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(a)us.ibm.com>
+ * David Woodhouse <dwmw2(a)infradead.org>
+ */
+
+#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/hash.h>
+#include <linux/slab.h>
+#include <linux/audit.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+#include <linux/fswatch.h>
+#include <asm/uaccess.h>
+
+
+extern int audit_enabled;
+
+spinlock_t auditfs_lock = SPIN_LOCK_UNLOCKED;
+
+static kmem_cache_t *audit_watch_cache;
+static HLIST_HEAD(master_watchlist);
+
+struct audit_skb_list {
+ struct hlist_node list;
+ void *memblk;
+ size_t size;
+};
+
+static inline struct audit_watch *audit_watch_alloc(void)
+{
+ struct audit_watch *watch;
+
+ watch = kmem_cache_alloc(audit_watch_cache, GFP_KERNEL);
+ if (watch)
+ memset(watch, 0, sizeof(*watch));
+
+ return watch;
+}
+
+static inline void audit_free_watch(struct audit_watch *w)
+{
+ BUG_ON(!w);
+ BUG_ON(!hlist_unhashed(&w->au_master));
+
+ kfree(w->au_name);
+ kfree(w->au_path);
+ kfree(w->au_filterkey);
+
+}
+
+static void audit_free_watch_fn(struct watch *w)
+{
+ struct audit_watch *watch;
+
+ BUG_ON(!w);
+
+ watch = container_of(w, struct audit_watch, au_watch);
+ if (watch) {
+ spin_lock(&auditfs_lock);
+ hlist_del_init(&watch->au_master);
+ audit_free_watch(watch);
+ spin_unlock(&auditfs_lock);
+ }
+}
+
+
+/* Convert a watch_transport structure into a kernel audit_watch structure. */
+static inline struct audit_watch *audit_to_watch(void *memblk)
+{
+ unsigned int offset;
+ struct watch_transport *t;
+ struct audit_watch *watch;
+
+ watch = audit_watch_alloc();
+ if (!watch)
+ goto audit_to_watch_fail;
+
+ t = memblk;
+
+ watch->au_actions = t->actions;
+
+ offset = sizeof(struct watch_transport);
+ watch->au_filterkey = kmalloc(t->fklen+1, GFP_KERNEL);
+ if (!watch->au_filterkey)
+ goto audit_to_watch_fail;
+ watch->au_filterkey[t->fklen] = 0;
+ memcpy(watch->au_filterkey, memblk + offset, t->fklen);
+
+ offset += t->fklen;
+ watch->au_path = kmalloc(t->pathlen+1, GFP_KERNEL);
+ if (!watch->au_path)
+ goto audit_to_watch_fail;
+ watch->au_path[t->pathlen] = 0;
+ memcpy(watch->au_path, memblk + offset, t->pathlen);
+
+ return watch;
+
+audit_to_watch_fail:
+ audit_free_watch(watch);
+ return NULL;
+}
+
+/*
+ * Convert a kernel audit_watch structure into a watch_transport structure.
+ * We do this to send watch information back to user space.
+ */
+static inline void *audit_to_transport(struct audit_watch *watch, size_t size)
+{
+ struct watch_transport *t;
+ char *p;
+
+ t = kmalloc(size, GFP_KERNEL);
+ if (!t)
+ goto audit_to_transport_exit;
+
+ memset(t, 0, sizeof(*t));
+
+ t->dev_major = MAJOR(watch->au_dev);
+ t->dev_minor = MINOR(watch->au_dev);
+ t->actions = watch->au_actions;
+ t->pathlen = strlen(watch->au_path) + 1;
+
+ p = (char *)&t[1];
+
+ if (watch->au_filterkey) {
+ t->fklen = strlen(watch->au_filterkey) + 1;
+ memcpy(p, watch->au_filterkey, t->fklen);
+ p += t->fklen;
+ }
+ memcpy(p, watch->au_path, t->pathlen);
+
+audit_to_transport_exit:
+ return t;
+}
+
+static inline int audit_insert_watch(struct audit_watch *w, uid_t loginuid)
+{
+ int ret;
+
+
+ w->au_watch.w_user = FSW_USER_AUDIT;
+ w->au_watch.w_notify = auditfs_attach_wdata;
+ w->au_watch.w_destroy = audit_free_watch_fn;
+
+ ret = fswatch_add_watch(w->au_path, &w->au_watch);
+ if (ret) {
+ spin_lock(&auditfs_lock);
+ hlist_add_head(&w->au_master, &master_watchlist);
+ spin_unlock(&auditfs_lock);
+ audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
+ "auid=%u inserted watch", loginuid);
+ }
+
+ return ret;
+}
+
+static inline int audit_remove_watch(struct audit_watch *w, uid_t loginuid)
+{
+ int ret = 0;
+ struct audit_watch *this;
+ struct hlist_node *pos, *tmp;
+
+ spin_lock(&auditfs_lock);
+ hlist_for_each_entry_safe(this, pos, tmp, &master_watchlist, au_master)
+ if (!strcmp(this->au_path, w->au_path)) {
+ hlist_del_init(&this->au_master);
+ break;
+ }
+ spin_unlock(&auditfs_lock);
+
+ ret = fswatch_rem_watch(w->au_path, FSW_USER_AUDIT);
+ if (ret)
+ audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
+ "auid=%u inserted watch", loginuid);
+ audit_free_watch(w);
+
+ return ret;
+}
+
+/* Convert a watch to a audit_skb_list */
+struct audit_skb_list *audit_to_skb(struct audit_watch *watch)
+{
+ size_t size;
+ void *memblk;
+ struct audit_skb_list *entry;
+
+ /* We must include space for both "\0" */
+ size = sizeof(struct watch_transport) + strlen(watch->au_path) +
+ strlen(watch->au_filterkey) + 2;
+
+ entry = ERR_PTR(-ENOMEM);
+ memblk = audit_to_transport(watch, size);
+ if (!memblk)
+ goto audit_queue_watch_exit;
+
+ entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry) {
+ entry = ERR_PTR(-ENOMEM);
+ goto audit_queue_watch_exit;
+ }
+
+ entry->memblk = memblk;
+ entry->size = size;
+
+audit_queue_watch_exit:
+ return entry;
+}
+
+/*
+ * Read the "master watchlist" which is a watchlist of all watches in the
+ * file system and send it to user space. There will never be concurrent
+ * readers of this list.
+ *
+ * The reference to watch will not be put back during a read upon a
+ * watch removal, until after we're done reading. So, the potential
+ * for the rug being pulled out from under us is NIL.
+ *
+ * This list is only a "snapshot in time". It is not gospel.
+ */
+static int audit_list_watches_fn(void *_dest)
+{
+ return 0;
+}
+
+int audit_list_watches(int pid, int seq)
+{
+ struct task_struct *tsk;
+ int *dest = kmalloc(2 * sizeof(int), GFP_KERNEL);
+ if (!dest)
+ return -ENOMEM;
+ dest[0] = pid;
+ dest[1] = seq;
+
+ tsk = kthread_run(audit_list_watches_fn, dest, "audit_list_watches");
+ if (IS_ERR(tsk)) {
+ kfree(dest);
+ return PTR_ERR(tsk);
+ }
+
+ return 0;
+}
+
+int audit_receive_watch(int type, int pid, int uid, int seq,
+ struct watch_transport *req, uid_t loginuid)
+{
+ int ret = 0;
+ struct audit_watch *w = NULL;
+ char *payload = (char *)&req[1];
+
+ ret = -ENAMETOOLONG;
+ if (req->pathlen >= PATH_MAX)
+ goto audit_receive_watch_exit;
+
+ if (req->fklen >= AUDIT_FILTERKEY_MAX)
+ goto audit_receive_watch_exit;
+
+ ret = -EINVAL;
+ if (req->pathlen == 0)
+ goto audit_receive_watch_exit;
+
+ if (payload[req->fklen] != '/')
+ goto audit_receive_watch_exit;
+
+ /*FIXME: Make sure "actions" are valid */
+
+ ret = -ENOMEM;
+ w = audit_to_watch(req);
+ if (!w)
+ goto audit_receive_watch_exit;
+
+ switch (type) {
+ case AUDIT_WATCH_INS:
+ ret = audit_insert_watch(w, loginuid);
+ break;
+ case AUDIT_WATCH_REM:
+ ret = audit_remove_watch(w, loginuid);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ if (ret < 0)
+ audit_free_watch(w);
+
+audit_receive_watch_exit:
+ return ret;
+}
+
+/* Can't handle error */
+int audit_filesystem_init(void)
+{
+ audit_watch_cache =
+ kmem_cache_create("audit_watch_cache",
+ sizeof(struct audit_watch), 0, 0, NULL, NULL);
+ if (!audit_watch_cache) {
+ kmem_cache_destroy(audit_watch_cache);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -41,6 +41,7 @@
#include <linux/time.h>
#include <linux/kthread.h>
#include <linux/netlink.h>
+#include <linux/fswatch.h>
#include <linux/compiler.h>
#include <asm/unistd.h>
@@ -135,6 +136,18 @@ struct audit_aux_data_path {
struct vfsmount *mnt;
};
+struct audit_aux_data_watched {
+ struct audit_aux_data link;
+ struct hlist_head watches;
+ uint action;
+ ulong ino;
+ umode_t mode;
+ uid_t uid;
+ gid_t gid;
+ dev_t dev;
+ dev_t rdev;
+};
+
/* The per-task audit context. */
struct audit_context {
int in_syscall; /* 1 if task is in a syscall */
@@ -665,13 +678,26 @@ 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)) {
- if (aux->type == AUDIT_AVC_PATH) {
+ switch(aux->type) {
+ case AUDIT_AVC_PATH: {
struct audit_aux_data_path *axi = (void *)aux;
dput(axi->dentry);
mntput(axi->mnt);
+ break; }
+ case AUDIT_FS_INODE: {
+ struct audit_aux_data_watched *axi = (void *)aux;
+ hlist_for_each_entry_safe(winfo, pos, tmp, &axi->watches, node) {
+ fswatch_put_watch(winfo->watch);
+ hlist_del(&winfo->node);
+ kfree(winfo);
+ }
+ break; }
}
+
context->aux = aux->next;
kfree(aux);
}
@@ -784,6 +810,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, gfp_mask, AUDIT_SYSCALL);
if (!ab)
@@ -849,6 +877,30 @@ static void audit_log_exit(struct audit_
audit_log_d_path(ab, "path=", axi->dentry, axi->mnt);
break; }
+ case AUDIT_FS_INODE: {
+ struct audit_aux_data_watched *axi = (void *)aux;
+ struct audit_watch *au_watch;
+ struct audit_buffer *sub_ab;
+ audit_log_format(ab,
+ "inode=%lu mode=%u uid=%u gid=%u "
+ "dev=%02x:%02x rdev=%02x:%02x",
+ axi->ino, axi->mode, 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) {
+ au_watch = container_of(winfo->watch, struct audit_watch, au_watch);
+ sub_ab = audit_log_start(context, GFP_KERNEL, AUDIT_FS_WATCH);
+ if (!sub_ab)
+ return; /* audit_panic has been called */
+ audit_log_format(sub_ab, "watch=%lu", axi->ino);
+ audit_log_format(sub_ab, " name=");
+ audit_log_untrustedstring(sub_ab, winfo->watch->w_name);
+ audit_log_format(sub_ab,
+ " filterkey=%s action=%d",
+ au_watch->au_filterkey, winfo->action);
+ audit_log_end(sub_ab);
+ }
+ break; }
}
audit_log_end(ab);
}
@@ -865,9 +917,8 @@ static void audit_log_exit(struct audit_
if (!ab)
continue; /* audit_panic has been called */
- audit_log_format(ab, "item=%d", i);
if (context->names[i].name) {
- audit_log_format(ab, " name=");
+ audit_log_format(ab, "name=");
audit_log_untrustedstring(ab, context->names[i].name);
}
audit_log_format(ab, " flags=%x\n", context->names[i].flags);
@@ -1272,3 +1323,63 @@ void audit_signal_info(int sig, struct t
}
}
+#ifdef CONFIG_AUDITFILESYSTEM
+extern spinlock_t auditfs_lock;
+
+/* This has to be here instead of in auditfs.c, because it needs to
+ see the audit context */
+void auditfs_attach_wdata(struct watch *w, const void *name,
+ struct inode *inode, uint action)
+{
+ int disabled;
+ struct audit_context *ctx = current->audit_context;
+ struct audit_aux_data_watched *ax;
+ struct audit_watch_info *this, *winfo;
+ struct hlist_node *pos, *tmp;
+
+ printk("BEEP!\n");
+
+ if (!ctx || (w->w_user != FSW_USER_AUDIT))
+ return;
+
+ ax = kmalloc(sizeof(*ax), GFP_KERNEL);
+ if (!ax)
+ return;
+
+ disabled = audit_filter_syscall(current, ctx,
+ &audit_filter_list[AUDIT_FILTER_WATCH]);
+ if (ctx->in_syscall && !ctx->auditable && AUDIT_DISABLED != disabled)
+ ctx->auditable = 1;
+
+ INIT_HLIST_HEAD(&ax->watches);
+
+ winfo = kmalloc(sizeof(struct audit_watch_info), GFP_KERNEL);
+ if (!winfo)
+ goto auditfs_attach_wdata_fail;
+ winfo->watch = fswatch_get_watch(w);
+ winfo->action = action;
+ hlist_add_head(&winfo->node, &ax->watches);
+
+ ax->mode = inode->i_mode;
+ 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_FS_INODE;
+ ax->link.next = ctx->aux;
+ ctx->aux = (void *)ax;
+
+ return;
+
+auditfs_attach_wdata_fail:
+ hlist_for_each_entry_safe(this, pos, tmp, &ax->watches, node) {
+ hlist_del(&this->node);
+ fswatch_put_watch(this->watch);
+ kfree(this);
+ }
+ kfree(ax);
+}
+
+#endif /* CONFIG_AUDITFILESYSTEM */
diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c
--- a/security/selinux/nlmsgtab.c
+++ b/security/selinux/nlmsgtab.c
@@ -100,6 +100,9 @@ static struct nlmsg_perm nlmsg_audit_per
{ AUDIT_DEL, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
{ AUDIT_USER, NETLINK_AUDIT_SOCKET__NLMSG_RELAY },
{ AUDIT_SIGNAL_INFO, NETLINK_AUDIT_SOCKET__NLMSG_READ },
+ { AUDIT_WATCH_INS, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
+ { AUDIT_WATCH_REM, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
+ { AUDIT_WATCH_LIST, NETLINK_AUDIT_SOCKET__NLMSG_READPRIV },
};
19 years, 5 months
Re: at, cron: wrong auid
by Debora Velarde
Debora Velarde/Austin/IBM wrote on 07/19/2005 11:16:47 AM:
> Hi All,
>
> We were asked to modify our 'at' and 'crontab' testcases so that the
> job being run contained a syscall. Then we needed to verify that
> the correct audit record was generated for that syscall. In doing
> so, I see that the audit record for the syscall executed by the job,
> contains "auid=0", rather than "auid=500" which is the user I
> initially logged in with.
>
> I asked Klaus if this behavior is valid. His reply, "The syscall
> audit record needs to have the auid of the user on whose
> behalf the job is executing, for example auid=500, *not* 0."
>
> -debbie
My apologies, this was a testcase problem. auid is fine.
-debbie
19 years, 5 months
audit.80 kernel
by David Woodhouse
* Sat Jul 16 2005 David Woodhouse <dwmw2(a)redhat.com> audit.80
- Fix assertion in refcounting code
--
dwmw2
19 years, 5 months
audit.79 kernel
by David Woodhouse
* Sat Jul 16 2005 David Woodhouse <dwmw2(a)redhat.com> audit.79
- Defer serial number generation until it's needed to avoid contention
--
dwmw2
19 years, 5 months
at, cron: wrong auid
by Debora Velarde
Hi All,
We were asked to modify our 'at' and 'crontab' testcases so that the job
being run contained a syscall. Then we needed to verify that the correct
audit record was generated for that syscall. In doing so, I see that the
audit record for the syscall executed by the job, contains "auid=0", rather
than "auid=500" which is the user I initially logged in with.
I asked Klaus if this behavior is valid. His reply, "The syscall audit
record needs to have the auid of the user on whose
behalf the job is executing, for example auid=500, *not* 0."
-debbie
19 years, 5 months
CAPP auditable events
by Amy Griffis
Hello,
I'm interested in defining a set of audit rules/watches that, when
loaded, cause audit to generate the set of auditable events required
by CAPP (CAPP, pp. 19-21).
I've consulted a variety of sources, including the CAPP specification
itself, the LAuS design document, and the LAuS filter.conf file
provided with our CAPP certification RPM. From that, I have a
configuration I believe to be fairly complete. However, the sources
seem to be in conflict on some parts, and none are a definitive
technical specification.
Is there a follow-on to the CAPP spec that provides a definitive
technical specification of the auditable events for linux 2.6; for
instance, by listing the specific system calls?
Thanks,
Amy
19 years, 5 months
Re: splinter work on generic fs notification framework
by Amy Griffis
On Thu, Jul 14, 2005 at 02:49:32PM -0500, Timothy R. Chavez wrote:
> On Thursday 14 July 2005 13:50, you wrote:
> > Hi Tim,
> >
> > On Tue, Jul 12, 2005 at 02:18:20PM -0500, Timothy R. Chavez wrote:
> > > diff --exclude=.git -Nurp audit-2.6/include/linux/watch.h audit-2.6.git-fsnotify/include/linux/watch.h
> > > --- audit-2.6/include/linux/watch.h
> > > +++ audit-2.6.git-fsnotify/include/linux/watch.h
> > > @@ -0,0 +1,57 @@
> > > +#ifndef _WATCH_H
> > > +#define _WATCH_H
> > > +
> > > +struct 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 beneath parent */
> > > + char *w_path; /* Insertion path */
> > > + char *w_filterkey; /* An arbitrary filtering key */
> > > + void (*w_func); /* Callback function */
> > > +};
> >
> > Looking at this structure, I'm wondering if it is sufficient for
> > inotify. Inotify's watches are associated with an instance of an
> > inotify device. How were you planning to account for that?
>
> Hi Amy,
>
> That structure is incomplete :(
Any progress here?
> Inotify will account for that... there's some trickery being that
> will let systems like audit and Inotify use their own specialized
> watches, but embed the fswatch into them as that is the common
> piece.
Yes, that's one way to do it.
Do you have any updates to the design that you could post to this
list?
Thanks,
Amy
19 years, 5 months
Idiocy in audit_serial()
by David Woodhouse
Someone reported livelock on a 16-way itanic box, in audit_serial().
Looking at it, that doesn't really surprise me much. An audit.78 kernel
is building with this...
--- linux-2.6.9/kernel/audit.c~ 2005-06-30 17:45:22.000000000 +0100
+++ linux-2.6.9/kernel/audit.c 2005-07-15 08:45:38.000000000 +0100
@@ -678,26 +678,25 @@ err:
* (timestamp,serial) tuple is unique for each syscall and is live from
* syscall entry to syscall exit.
*
- * Atomic values are only guaranteed to be 24-bit, so we count down.
- *
* NOTE: Another possibility is to store the formatted records off the
* audit context (for those records that have a context), and emit them
* all at syscall exit. However, this could delay the reporting of
* significant errors until syscall exit (or never, if the system
* halts). */
+
unsigned int audit_serial(void)
{
- static atomic_t serial = ATOMIC_INIT(0xffffff);
- unsigned int a, b;
+ static spinlock_t serial_lock = SPIN_LOCK_UNLOCKED;
+ static unsigned int serial = 0;
+
+ unsigned long flags;
+ unsigned int ret;
- do {
- a = atomic_read(&serial);
- if (atomic_dec_and_test(&serial))
- atomic_set(&serial, 0xffffff);
- b = atomic_read(&serial);
- } while (b != a - 1);
+ spin_lock_irqsave(&serial_lock, flags);
+ ret = serial++;
+ spin_unlock_irqrestore(&serial_lock, flags);
- return 0xffffff - b;
+ return ret;
}
static inline void audit_get_stamp(struct audit_context *ctx,
--
dwmw2
19 years, 5 months
Re: Audit records on screen
by Kris Wilson
We found that auditd was not running on the systems where we were seeing
the records on the screen and believe that this is the correct behavior in
this case.
19 years, 5 months
Re: Audit records on screen
by Kris Wilson
We are seeing the following audit records frequently displaying on the
xSeries and opteron consoles. We don't see these when accessing remotely.
audit(1121277961.150:13073995): user pid=13666 uid=0 auid=500 msg='PAM
accounting: user=root exe="/usr/sbin/crond" (hostname=?, addr=?,
terminal=cron result=Success)'
audit(1121277961.151:13074064): login pid=13666 uid=0 old auid=500 new
auid=0
audit(1121277961.151:13074070): user pid=13666 uid=0 auid=0 msg='PAM
session open: user=root exe="/usr/sbin/crond" (hostname=?, addr=?,
terminal=cron result=Success)'
audit(1121277961.151:13074079): user pid=13666 uid=0 auid=0 msg='PAM
setcred: user=root exe="/usr/sbin/crond" (hostname=?, addr=?,
terminal=cron result=Success)'
audit(1121277961.156:13074258): user pid=13666 uid=0 auid=0 msg='PAM
setcred: user=root exe="/usr/sbin/crond" (hostname=?, addr=?,
terminal=cron result=Success)'
audit(1121277961.157:13074277): user pid=13666 uid=0 auid=0 msg='PAM
session close: user=root exe="/usr/sbin/crond" (hostname=?, addr=?,
terminal=cron result=Success)'
P.S. I hope this is not arriving as HTML; had to reinstall my desktop -
have set default to plain text but who knows....
19 years, 5 months