Commit c4ec54b40d33f8016fea970a383cc584dd0e6019

Authored by Eric Paris
1 parent d14f172948

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>

Showing 5 changed files with 68 additions and 30 deletions Side-by-side Diff

fs/notify/fsnotify.c
... ... @@ -169,27 +169,22 @@
169 169 }
170 170 }
171 171  
172   -static void send_to_group(struct fsnotify_group *group, struct inode *to_tell,
173   - struct vfsmount *mnt, __u32 mask, void *data,
174   - int data_is, u32 cookie, const unsigned char *file_name,
175   - struct fsnotify_event **event)
  172 +static int send_to_group(struct fsnotify_group *group, struct inode *to_tell,
  173 + struct vfsmount *mnt, __u32 mask, void *data,
  174 + int data_is, u32 cookie, const unsigned char *file_name,
  175 + struct fsnotify_event **event)
176 176 {
177 177 if (!group->ops->should_send_event(group, to_tell, mnt, mask,
178 178 data, data_is))
179   - return;
  179 + return 0;
180 180 if (!*event) {
181 181 *event = fsnotify_create_event(to_tell, mask, data,
182 182 data_is, file_name,
183 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 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  
195 190 static bool needed_by_vfsmount(__u32 test_mask, struct vfsmount *mnt)
196 191  
197 192  
... ... @@ -206,20 +201,20 @@
206 201 * out to all of the registered fsnotify_group. Those groups can then use the
207 202 * notification event in whatever means they feel necessary.
208 203 */
209   -void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
210   - const unsigned char *file_name, u32 cookie)
  204 +int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
  205 + const unsigned char *file_name, u32 cookie)
211 206 {
212 207 struct fsnotify_group *group;
213 208 struct fsnotify_event *event = NULL;
214 209 struct vfsmount *mnt = NULL;
215   - int idx;
  210 + int idx, ret = 0;
216 211 /* global tests shouldn't care about events on child only the specific event */
217 212 __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD);
218 213  
219 214 /* if no fsnotify listeners, nothing to do */
220 215 if (list_empty(&fsnotify_inode_groups) &&
221 216 list_empty(&fsnotify_vfsmount_groups))
222   - return;
  217 + return 0;
223 218  
224 219 if (mask & FS_MODIFY)
225 220 __fsnotify_flush_ignored_mask(to_tell, data, data_is);
... ... @@ -227,7 +222,7 @@
227 222 /* if none of the directed listeners or vfsmount listeners care */
228 223 if (!(test_mask & fsnotify_inode_mask) &&
229 224 !(test_mask & fsnotify_vfsmount_mask))
230   - return;
  225 + return 0;
231 226  
232 227 if (data_is == FSNOTIFY_EVENT_PATH)
233 228 mnt = ((struct path *)data)->mnt;
... ... @@ -236,7 +231,7 @@
236 231 * listeners list cares, nothing to do */
237 232 if (!(test_mask & to_tell->i_fsnotify_mask) &&
238 233 !needed_by_vfsmount(test_mask, mnt))
239   - return;
  234 + return 0;
240 235  
241 236 /*
242 237 * SRCU!! the groups list is very very much read only and the path is
243 238  
244 239  
... ... @@ -248,20 +243,24 @@
248 243 if (test_mask & to_tell->i_fsnotify_mask) {
249 244 list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list) {
250 245 if (test_mask & group->mask) {
251   - send_to_group(group, to_tell, NULL, mask, data, data_is,
252   - cookie, file_name, &event);
  246 + ret = send_to_group(group, to_tell, NULL, mask, data, data_is,
  247 + cookie, file_name, &event);
  248 + if (ret)
  249 + goto out;
253 250 }
254 251 }
255 252 }
256 253 if (needed_by_vfsmount(test_mask, mnt)) {
257 254 list_for_each_entry_rcu(group, &fsnotify_vfsmount_groups, vfsmount_group_list) {
258 255 if (test_mask & group->mask) {
259   - send_to_group(group, to_tell, mnt, mask, data, data_is,
260   - cookie, file_name, &event);
  256 + ret = send_to_group(group, to_tell, mnt, mask, data, data_is,
  257 + cookie, file_name, &event);
  258 + if (ret)
  259 + goto out;
261 260 }
262 261 }
263 262 }
264   -
  263 +out:
265 264 srcu_read_unlock(&fsnotify_grp_srcu, idx);
266 265 /*
267 266 * fsnotify_create_event() took a reference so the event can't be cleaned
... ... @@ -269,6 +268,8 @@
269 268 */
270 269 if (event)
271 270 fsnotify_put_event(event);
  271 +
  272 + return 0;
272 273 }
273 274 EXPORT_SYMBOL_GPL(fsnotify);
274 275  
include/linux/fsnotify.h
... ... @@ -34,6 +34,25 @@
34 34 __fsnotify_parent(path, dentry, mask);
35 35 }
36 36  
  37 +/* simple call site for access decisions */
  38 +static 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 57 * fsnotify_d_move - dentry has been moved
39 58 * Called with dcache_lock and dentry->d_lock held.
include/linux/fsnotify_backend.h
... ... @@ -41,6 +41,9 @@
41 41 #define FS_Q_OVERFLOW 0x00004000 /* Event queued overflowed */
42 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 47 #define FS_IN_ISDIR 0x40000000 /* event occurred against dir */
45 48 #define FS_IN_ONESHOT 0x80000000 /* only send event once */
46 49  
... ... @@ -282,8 +285,8 @@
282 285 /* called from the vfs helpers */
283 286  
284 287 /* main fsnotify call to send events */
285   -extern void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
286   - const unsigned char *name, u32 cookie);
  288 +extern int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
  289 + const unsigned char *name, u32 cookie);
287 290 extern void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask);
288 291 extern void __fsnotify_inode_delete(struct inode *inode);
289 292 extern void __fsnotify_vfsmount_delete(struct vfsmount *mnt);
... ... @@ -413,9 +416,11 @@
413 416  
414 417 #else
415 418  
416   -static inline void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
417   - const unsigned char *name, u32 cookie)
418   -{}
  419 +static inline int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
  420 + const unsigned char *name, u32 cookie)
  421 +{
  422 + return 0;
  423 +}
419 424  
420 425 static inline void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask)
421 426 {}
include/linux/security.h
... ... @@ -23,6 +23,7 @@
23 23 #define __LINUX_SECURITY_H
24 24  
25 25 #include <linux/fs.h>
  26 +#include <linux/fsnotify.h>
26 27 #include <linux/binfmts.h>
27 28 #include <linux/signal.h>
28 29 #include <linux/resource.h>
... ... @@ -620,7 +620,13 @@
620 620  
621 621 int 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  
626 632 int security_file_alloc(struct file *file)
... ... @@ -684,7 +690,13 @@
684 690  
685 691 int 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  
690 702 int security_task_create(unsigned long clone_flags)