aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Paris <eparis@redhat.com>2009-12-17 21:24:34 -0500
committerEric Paris <eparis@redhat.com>2010-07-28 09:59:01 -0400
commitc4ec54b40d33f8016fea970a383cc584dd0e6019 (patch)
tree8e8865170cf340d1e79dc379f56417588715b2c8
parentd14f1729483fad3a8817fbbcbd017678b7d1ad26 (diff)
fsnotify: new fsnotify hooks and events types for access decisions
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>
-rw-r--r--fs/notify/fsnotify.c47
-rw-r--r--include/linux/fsnotify.h19
-rw-r--r--include/linux/fsnotify_backend.h15
-rw-r--r--include/linux/security.h1
-rw-r--r--security/security.c16
5 files changed, 68 insertions, 30 deletions
diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c
index c5adf833bf6a..668268627894 100644
--- a/fs/notify/fsnotify.c
+++ b/fs/notify/fsnotify.c
@@ -169,27 +169,22 @@ void __fsnotify_flush_ignored_mask(struct inode *inode, void *data, int data_is)
169 } 169 }
170} 170}
171 171
172static void send_to_group(struct fsnotify_group *group, struct inode *to_tell, 172static int send_to_group(struct fsnotify_group *group, struct inode *to_tell,
173 struct vfsmount *mnt, __u32 mask, void *data, 173 struct vfsmount *mnt, __u32 mask, void *data,
174 int data_is, u32 cookie, const unsigned char *file_name, 174 int data_is, u32 cookie, const unsigned char *file_name,
175 struct fsnotify_event **event) 175 struct fsnotify_event **event)
176{ 176{
177 if (!group->ops->should_send_event(group, to_tell, mnt, mask, 177 if (!group->ops->should_send_event(group, to_tell, mnt, mask,
178 data, data_is)) 178 data, data_is))
179 return; 179 return 0;
180 if (!*event) { 180 if (!*event) {
181 *event = fsnotify_create_event(to_tell, mask, data, 181 *event = fsnotify_create_event(to_tell, mask, data,
182 data_is, file_name, 182 data_is, file_name,
183 cookie, GFP_KERNEL); 183 cookie, GFP_KERNEL);
184 /*
185 * shit, we OOM'd and now we can't tell, maybe
186 * someday someone else will want to do something
187 * here
188 */
189 if (!*event) 184 if (!*event)
190 return; 185 return -ENOMEM;
191 } 186 }
192 group->ops->handle_event(group, *event); 187 return group->ops->handle_event(group, *event);
193} 188}
194 189
195static bool needed_by_vfsmount(__u32 test_mask, struct vfsmount *mnt) 190static 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)
206 * out to all of the registered fsnotify_group. Those groups can then use the 201 * out to all of the registered fsnotify_group. Those groups can then use the
207 * notification event in whatever means they feel necessary. 202 * notification event in whatever means they feel necessary.
208 */ 203 */
209void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, 204int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
210 const unsigned char *file_name, u32 cookie) 205 const unsigned char *file_name, u32 cookie)
211{ 206{
212 struct fsnotify_group *group; 207 struct fsnotify_group *group;
213 struct fsnotify_event *event = NULL; 208 struct fsnotify_event *event = NULL;
214 struct vfsmount *mnt = NULL; 209 struct vfsmount *mnt = NULL;
215 int idx; 210 int idx, ret = 0;
216 /* global tests shouldn't care about events on child only the specific event */ 211 /* global tests shouldn't care about events on child only the specific event */
217 __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD); 212 __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD);
218 213
219 /* if no fsnotify listeners, nothing to do */ 214 /* if no fsnotify listeners, nothing to do */
220 if (list_empty(&fsnotify_inode_groups) && 215 if (list_empty(&fsnotify_inode_groups) &&
221 list_empty(&fsnotify_vfsmount_groups)) 216 list_empty(&fsnotify_vfsmount_groups))
222 return; 217 return 0;
223 218
224 if (mask & FS_MODIFY) 219 if (mask & FS_MODIFY)
225 __fsnotify_flush_ignored_mask(to_tell, data, data_is); 220 __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,
227 /* if none of the directed listeners or vfsmount listeners care */ 222 /* if none of the directed listeners or vfsmount listeners care */
228 if (!(test_mask & fsnotify_inode_mask) && 223 if (!(test_mask & fsnotify_inode_mask) &&
229 !(test_mask & fsnotify_vfsmount_mask)) 224 !(test_mask & fsnotify_vfsmount_mask))
230 return; 225 return 0;
231 226
232 if (data_is == FSNOTIFY_EVENT_PATH) 227 if (data_is == FSNOTIFY_EVENT_PATH)
233 mnt = ((struct path *)data)->mnt; 228 mnt = ((struct path *)data)->mnt;
@@ -236,7 +231,7 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
236 * listeners list cares, nothing to do */ 231 * listeners list cares, nothing to do */
237 if (!(test_mask & to_tell->i_fsnotify_mask) && 232 if (!(test_mask & to_tell->i_fsnotify_mask) &&
238 !needed_by_vfsmount(test_mask, mnt)) 233 !needed_by_vfsmount(test_mask, mnt))
239 return; 234 return 0;
240 235
241 /* 236 /*
242 * SRCU!! the groups list is very very much read only and the path is 237 * 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,
248 if (test_mask & to_tell->i_fsnotify_mask) { 243 if (test_mask & to_tell->i_fsnotify_mask) {
249 list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list) { 244 list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list) {
250 if (test_mask & group->mask) { 245 if (test_mask & group->mask) {
251 send_to_group(group, to_tell, NULL, mask, data, data_is, 246 ret = send_to_group(group, to_tell, NULL, mask, data, data_is,
252 cookie, file_name, &event); 247 cookie, file_name, &event);
248 if (ret)
249 goto out;
253 } 250 }
254 } 251 }
255 } 252 }
256 if (needed_by_vfsmount(test_mask, mnt)) { 253 if (needed_by_vfsmount(test_mask, mnt)) {
257 list_for_each_entry_rcu(group, &fsnotify_vfsmount_groups, vfsmount_group_list) { 254 list_for_each_entry_rcu(group, &fsnotify_vfsmount_groups, vfsmount_group_list) {
258 if (test_mask & group->mask) { 255 if (test_mask & group->mask) {
259 send_to_group(group, to_tell, mnt, mask, data, data_is, 256 ret = send_to_group(group, to_tell, mnt, mask, data, data_is,
260 cookie, file_name, &event); 257 cookie, file_name, &event);
258 if (ret)
259 goto out;
261 } 260 }
262 } 261 }
263 } 262 }
264 263out:
265 srcu_read_unlock(&fsnotify_grp_srcu, idx); 264 srcu_read_unlock(&fsnotify_grp_srcu, idx);
266 /* 265 /*
267 * fsnotify_create_event() took a reference so the event can't be cleaned 266 * 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,
269 */ 268 */
270 if (event) 269 if (event)
271 fsnotify_put_event(event); 270 fsnotify_put_event(event);
271
272 return 0;
272} 273}
273EXPORT_SYMBOL_GPL(fsnotify); 274EXPORT_SYMBOL_GPL(fsnotify);
274 275
diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h
index b8cf161f5a6d..64efda9aae62 100644
--- a/include/linux/fsnotify.h
+++ b/include/linux/fsnotify.h
@@ -34,6 +34,25 @@ static inline void fsnotify_parent(struct path *path, struct dentry *dentry, __u
34 __fsnotify_parent(path, dentry, mask); 34 __fsnotify_parent(path, dentry, mask);
35} 35}
36 36
37/* simple call site for access decisions */
38static inline int fsnotify_perm(struct file *file, int mask)
39{
40 struct path *path = &file->f_path;
41 struct inode *inode = path->dentry->d_inode;
42 __u32 fsnotify_mask;
43
44 if (file->f_mode & FMODE_NONOTIFY)
45 return 0;
46 if (!(mask & (MAY_READ | MAY_OPEN)))
47 return 0;
48 if (mask & MAY_READ)
49 fsnotify_mask = FS_ACCESS_PERM;
50 if (mask & MAY_OPEN)
51 fsnotify_mask = FS_OPEN_PERM;
52
53 return fsnotify(inode, fsnotify_mask, path, FSNOTIFY_EVENT_PATH, NULL, 0);
54}
55
37/* 56/*
38 * fsnotify_d_move - dentry has been moved 57 * fsnotify_d_move - dentry has been moved
39 * Called with dcache_lock and dentry->d_lock held. 58 * Called with dcache_lock and dentry->d_lock held.
diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h
index efe9ba321cf2..c34728e7d8cb 100644
--- a/include/linux/fsnotify_backend.h
+++ b/include/linux/fsnotify_backend.h
@@ -41,6 +41,9 @@
41#define FS_Q_OVERFLOW 0x00004000 /* Event queued overflowed */ 41#define FS_Q_OVERFLOW 0x00004000 /* Event queued overflowed */
42#define FS_IN_IGNORED 0x00008000 /* last inotify event here */ 42#define FS_IN_IGNORED 0x00008000 /* last inotify event here */
43 43
44#define FS_OPEN_PERM 0x00010000 /* open event in an permission hook */
45#define FS_ACCESS_PERM 0x00020000 /* access event in a permissions hook */
46
44#define FS_IN_ISDIR 0x40000000 /* event occurred against dir */ 47#define FS_IN_ISDIR 0x40000000 /* event occurred against dir */
45#define FS_IN_ONESHOT 0x80000000 /* only send event once */ 48#define FS_IN_ONESHOT 0x80000000 /* only send event once */
46 49
@@ -282,8 +285,8 @@ struct fsnotify_mark {
282/* called from the vfs helpers */ 285/* called from the vfs helpers */
283 286
284/* main fsnotify call to send events */ 287/* main fsnotify call to send events */
285extern void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, 288extern int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
286 const unsigned char *name, u32 cookie); 289 const unsigned char *name, u32 cookie);
287extern void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask); 290extern void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask);
288extern void __fsnotify_inode_delete(struct inode *inode); 291extern void __fsnotify_inode_delete(struct inode *inode);
289extern void __fsnotify_vfsmount_delete(struct vfsmount *mnt); 292extern void __fsnotify_vfsmount_delete(struct vfsmount *mnt);
@@ -413,9 +416,11 @@ extern int fsnotify_replace_event(struct fsnotify_event_holder *old_holder,
413 416
414#else 417#else
415 418
416static inline void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, 419static inline int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
417 const unsigned char *name, u32 cookie) 420 const unsigned char *name, u32 cookie)
418{} 421{
422 return 0;
423}
419 424
420static inline void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask) 425static inline void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask)
421{} 426{}
diff --git a/include/linux/security.h b/include/linux/security.h
index 0c8819170463..24fc29540aa3 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -23,6 +23,7 @@
23#define __LINUX_SECURITY_H 23#define __LINUX_SECURITY_H
24 24
25#include <linux/fs.h> 25#include <linux/fs.h>
26#include <linux/fsnotify.h>
26#include <linux/binfmts.h> 27#include <linux/binfmts.h>
27#include <linux/signal.h> 28#include <linux/signal.h>
28#include <linux/resource.h> 29#include <linux/resource.h>
diff --git a/security/security.c b/security/security.c
index 351942a4ca0e..f6ac27cd3452 100644
--- a/security/security.c
+++ b/security/security.c
@@ -620,7 +620,13 @@ void security_inode_getsecid(const struct inode *inode, u32 *secid)
620 620
621int security_file_permission(struct file *file, int mask) 621int security_file_permission(struct file *file, int mask)
622{ 622{
623 return security_ops->file_permission(file, mask); 623 int ret;
624
625 ret = security_ops->file_permission(file, mask);
626 if (ret)
627 return ret;
628
629 return fsnotify_perm(file, mask);
624} 630}
625 631
626int security_file_alloc(struct file *file) 632int security_file_alloc(struct file *file)
@@ -684,7 +690,13 @@ int security_file_receive(struct file *file)
684 690
685int security_dentry_open(struct file *file, const struct cred *cred) 691int security_dentry_open(struct file *file, const struct cred *cred)
686{ 692{
687 return security_ops->dentry_open(file, cred); 693 int ret;
694
695 ret = security_ops->dentry_open(file, cred);
696 if (ret)
697 return ret;
698
699 return fsnotify_perm(file, MAY_OPEN);
688} 700}
689 701
690int security_task_create(unsigned long clone_flags) 702int security_task_create(unsigned long clone_flags)