]> bbs.cooldavid.org Git - net-next-2.6.git/commitdiff
fsnotify: new fsnotify hooks and events types for access decisions
authorEric Paris <eparis@redhat.com>
Fri, 18 Dec 2009 02:24:34 +0000 (21:24 -0500)
committerEric Paris <eparis@redhat.com>
Wed, 28 Jul 2010 13:59:01 +0000 (09:59 -0400)
introduce a new fsnotify hook, fsnotify_perm(), which is called from the
security code.  This hook is used to allow fsnotify groups to make access
control decisions about events on the system.  We also must change the
generic fsnotify function to return an error code if we intend these hooks
to be in any way useful.

Signed-off-by: Eric Paris <eparis@redhat.com>
fs/notify/fsnotify.c
include/linux/fsnotify.h
include/linux/fsnotify_backend.h
include/linux/security.h
security/security.c

index c5adf833bf6a77d0ce7cb09a961952212e8c1bae..66826862789421ed0760317b4bff7f45e62dc3d9 100644 (file)
@@ -169,27 +169,22 @@ void __fsnotify_flush_ignored_mask(struct inode *inode, void *data, int data_is)
        }
 }
 
-static void send_to_group(struct fsnotify_group *group, struct inode *to_tell,
-                         struct vfsmount *mnt, __u32 mask, void *data,
-                         int data_is, u32 cookie, const unsigned char *file_name,
-                         struct fsnotify_event **event)
+static int send_to_group(struct fsnotify_group *group, struct inode *to_tell,
+                        struct vfsmount *mnt, __u32 mask, void *data,
+                        int data_is, u32 cookie, const unsigned char *file_name,
+                        struct fsnotify_event **event)
 {
        if (!group->ops->should_send_event(group, to_tell, mnt, mask,
                                           data, data_is))
-               return;
+               return 0;
        if (!*event) {
                *event = fsnotify_create_event(to_tell, mask, data,
                                                data_is, file_name,
                                                cookie, GFP_KERNEL);
-               /*
-                * shit, we OOM'd and now we can't tell, maybe
-                * someday someone else will want to do something
-                * here
-                */
                if (!*event)
-                       return;
+                       return -ENOMEM;
        }
-       group->ops->handle_event(group, *event);
+       return group->ops->handle_event(group, *event);
 }
 
 static bool needed_by_vfsmount(__u32 test_mask, struct vfsmount *mnt)
@@ -206,20 +201,20 @@ static bool needed_by_vfsmount(__u32 test_mask, struct vfsmount *mnt)
  * out to all of the registered fsnotify_group.  Those groups can then use the
  * notification event in whatever means they feel necessary.
  */
-void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
-             const unsigned char *file_name, u32 cookie)
+int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
+            const unsigned char *file_name, u32 cookie)
 {
        struct fsnotify_group *group;
        struct fsnotify_event *event = NULL;
        struct vfsmount *mnt = NULL;
-       int idx;
+       int idx, ret = 0;
        /* global tests shouldn't care about events on child only the specific event */
        __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD);
 
        /* if no fsnotify listeners, nothing to do */
        if (list_empty(&fsnotify_inode_groups) &&
            list_empty(&fsnotify_vfsmount_groups))
-                return;
+               return 0;
  
        if (mask & FS_MODIFY)
                __fsnotify_flush_ignored_mask(to_tell, data, data_is);
@@ -227,7 +222,7 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
        /* if none of the directed listeners or vfsmount listeners care */
        if (!(test_mask & fsnotify_inode_mask) &&
            !(test_mask & fsnotify_vfsmount_mask))
-                return;
+               return 0;
  
        if (data_is == FSNOTIFY_EVENT_PATH)
                mnt = ((struct path *)data)->mnt;
@@ -236,7 +231,7 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
         * listeners list cares, nothing to do */
        if (!(test_mask & to_tell->i_fsnotify_mask) &&
            !needed_by_vfsmount(test_mask, mnt))
-                return;
+               return 0;
 
        /*
         * SRCU!!  the groups list is very very much read only and the path is
@@ -248,20 +243,24 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
        if (test_mask & to_tell->i_fsnotify_mask) {
                list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list) {
                        if (test_mask & group->mask) {
-                               send_to_group(group, to_tell, NULL, mask, data, data_is,
-                                             cookie, file_name, &event);
+                               ret = send_to_group(group, to_tell, NULL, mask, data, data_is,
+                                                   cookie, file_name, &event);
+                               if (ret)
+                                       goto out;
                        }
                }
        }
        if (needed_by_vfsmount(test_mask, mnt)) {
                list_for_each_entry_rcu(group, &fsnotify_vfsmount_groups, vfsmount_group_list) {
                        if (test_mask & group->mask) {
-                               send_to_group(group, to_tell, mnt, mask, data, data_is,
-                                             cookie, file_name, &event);
+                               ret = send_to_group(group, to_tell, mnt, mask, data, data_is,
+                                                   cookie, file_name, &event);
+                               if (ret)
+                                       goto out;
                        }
                }
        }
-
+out:
        srcu_read_unlock(&fsnotify_grp_srcu, idx);
        /*
         * fsnotify_create_event() took a reference so the event can't be cleaned
@@ -269,6 +268,8 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
         */
        if (event)
                fsnotify_put_event(event);
+
+       return 0;
 }
 EXPORT_SYMBOL_GPL(fsnotify);
 
index b8cf161f5a6de329acf3abac9a0a29e7f1d82234..64efda9aae628c3d2ee3581fb351be9a36d6c902 100644 (file)
@@ -34,6 +34,25 @@ static inline void fsnotify_parent(struct path *path, struct dentry *dentry, __u
        __fsnotify_parent(path, dentry, mask);
 }
 
+/* simple call site for access decisions */
+static inline int fsnotify_perm(struct file *file, int mask)
+{
+       struct path *path = &file->f_path;
+       struct inode *inode = path->dentry->d_inode;
+       __u32 fsnotify_mask;
+
+       if (file->f_mode & FMODE_NONOTIFY)
+               return 0;
+       if (!(mask & (MAY_READ | MAY_OPEN)))
+               return 0;
+       if (mask & MAY_READ)
+               fsnotify_mask = FS_ACCESS_PERM;
+       if (mask & MAY_OPEN)
+               fsnotify_mask = FS_OPEN_PERM;
+
+       return fsnotify(inode, fsnotify_mask, path, FSNOTIFY_EVENT_PATH, NULL, 0);
+}
+
 /*
  * fsnotify_d_move - dentry has been moved
  * Called with dcache_lock and dentry->d_lock held.
index efe9ba321cf22a1ff1ca4e8a8d8bd746c507b532..c34728e7d8cb9cb5ef13f6518f441aa056be72a2 100644 (file)
@@ -41,6 +41,9 @@
 #define FS_Q_OVERFLOW          0x00004000      /* Event queued overflowed */
 #define FS_IN_IGNORED          0x00008000      /* last inotify event here */
 
+#define FS_OPEN_PERM           0x00010000      /* open event in an permission hook */
+#define FS_ACCESS_PERM         0x00020000      /* access event in a permissions hook */
+
 #define FS_IN_ISDIR            0x40000000      /* event occurred against dir */
 #define FS_IN_ONESHOT          0x80000000      /* only send event once */
 
@@ -282,8 +285,8 @@ struct fsnotify_mark {
 /* called from the vfs helpers */
 
 /* main fsnotify call to send events */
-extern void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
-                    const unsigned char *name, u32 cookie);
+extern int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
+                   const unsigned char *name, u32 cookie);
 extern void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask);
 extern void __fsnotify_inode_delete(struct inode *inode);
 extern void __fsnotify_vfsmount_delete(struct vfsmount *mnt);
@@ -413,9 +416,11 @@ extern int fsnotify_replace_event(struct fsnotify_event_holder *old_holder,
 
 #else
 
-static inline void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
-                           const unsigned char *name, u32 cookie)
-{}
+static inline int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
+                          const unsigned char *name, u32 cookie)
+{
+       return 0;
+}
 
 static inline void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask)
 {}
index 0c8819170463624c57f7c968771727eed1038691..24fc29540aa32ba311a71c1b3750b7b989f5acbf 100644 (file)
@@ -23,6 +23,7 @@
 #define __LINUX_SECURITY_H
 
 #include <linux/fs.h>
+#include <linux/fsnotify.h>
 #include <linux/binfmts.h>
 #include <linux/signal.h>
 #include <linux/resource.h>
index 351942a4ca0e79b0c21ccd35c31e442928792f47..f6ac27cd3452be28611b064702c689e59a12bb17 100644 (file)
@@ -620,7 +620,13 @@ void security_inode_getsecid(const struct inode *inode, u32 *secid)
 
 int security_file_permission(struct file *file, int mask)
 {
-       return security_ops->file_permission(file, mask);
+       int ret;
+
+       ret = security_ops->file_permission(file, mask);
+       if (ret)
+               return ret;
+
+       return fsnotify_perm(file, mask);
 }
 
 int security_file_alloc(struct file *file)
@@ -684,7 +690,13 @@ int security_file_receive(struct file *file)
 
 int security_dentry_open(struct file *file, const struct cred *cred)
 {
-       return security_ops->dentry_open(file, cred);
+       int ret;
+
+       ret = security_ops->dentry_open(file, cred);
+       if (ret)
+               return ret;
+
+       return fsnotify_perm(file, MAY_OPEN);
 }
 
 int security_task_create(unsigned long clone_flags)