diff options
author | Miklos Szeredi <miklos@szeredi.hu> | 2008-07-31 07:41:58 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2008-10-23 05:13:25 -0400 |
commit | f696a3659fc4b3a3bf4bc83d9dbec5e5a2ffd929 (patch) | |
tree | 738adf557608f26e881a710839de12b7527549b6 | |
parent | 5cec56deb6d41b5b570306b17cd0b1590ebd0897 (diff) |
[PATCH] move executable checking into ->permission()
For execute permission on a regular files we need to check if file has
any execute bits at all, regardless of capabilites.
This check is normally performed by generic_permission() but was also
added to the case when the filesystem defines its own ->permission()
method. In the latter case the filesystem should be responsible for
performing this check.
Move the check from inode_permission() inside filesystems which are
not calling generic_permission().
Create a helper function execute_ok() that returns true if the inode
is a directory or if any execute bits are present in i_mode.
Also fix up the following code:
- coda control file is never executable
- sysctl files are never executable
- hfs_permission seems broken on MAY_EXEC, remove
- hfsplus_permission is eqivalent to generic_permission(), remove
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
-rw-r--r-- | fs/cifs/cifsfs.c | 9 | ||||
-rw-r--r-- | fs/coda/dir.c | 3 | ||||
-rw-r--r-- | fs/coda/pioctl.c | 2 | ||||
-rw-r--r-- | fs/hfs/inode.c | 8 | ||||
-rw-r--r-- | fs/hfsplus/inode.c | 13 | ||||
-rw-r--r-- | fs/namei.c | 21 | ||||
-rw-r--r-- | fs/nfs/dir.c | 3 | ||||
-rw-r--r-- | fs/proc/proc_sysctl.c | 10 | ||||
-rw-r--r-- | include/linux/fs.h | 5 |
9 files changed, 30 insertions, 44 deletions
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 89c64a8dcb99..84cc011a16e4 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c | |||
@@ -275,9 +275,12 @@ static int cifs_permission(struct inode *inode, int mask) | |||
275 | 275 | ||
276 | cifs_sb = CIFS_SB(inode->i_sb); | 276 | cifs_sb = CIFS_SB(inode->i_sb); |
277 | 277 | ||
278 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) | 278 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) { |
279 | return 0; | 279 | if ((mask & MAY_EXEC) && !execute_ok(inode)) |
280 | else /* file mode might have been restricted at mount time | 280 | return -EACCES; |
281 | else | ||
282 | return 0; | ||
283 | } else /* file mode might have been restricted at mount time | ||
281 | on the client (above and beyond ACL on servers) for | 284 | on the client (above and beyond ACL on servers) for |
282 | servers which do not support setting and viewing mode bits, | 285 | servers which do not support setting and viewing mode bits, |
283 | so allowing client to check permissions is useful */ | 286 | so allowing client to check permissions is useful */ |
diff --git a/fs/coda/dir.c b/fs/coda/dir.c index c5916228243c..75b1fa90b2cb 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c | |||
@@ -146,6 +146,9 @@ int coda_permission(struct inode *inode, int mask) | |||
146 | if (!mask) | 146 | if (!mask) |
147 | return 0; | 147 | return 0; |
148 | 148 | ||
149 | if ((mask & MAY_EXEC) && !execute_ok(inode)) | ||
150 | return -EACCES; | ||
151 | |||
149 | lock_kernel(); | 152 | lock_kernel(); |
150 | 153 | ||
151 | if (coda_cache_check(inode, mask)) | 154 | if (coda_cache_check(inode, mask)) |
diff --git a/fs/coda/pioctl.c b/fs/coda/pioctl.c index c51365422aa8..773f2ce9aa06 100644 --- a/fs/coda/pioctl.c +++ b/fs/coda/pioctl.c | |||
@@ -43,7 +43,7 @@ const struct file_operations coda_ioctl_operations = { | |||
43 | /* the coda pioctl inode ops */ | 43 | /* the coda pioctl inode ops */ |
44 | static int coda_ioctl_permission(struct inode *inode, int mask) | 44 | static int coda_ioctl_permission(struct inode *inode, int mask) |
45 | { | 45 | { |
46 | return 0; | 46 | return (mask & MAY_EXEC) ? -EACCES : 0; |
47 | } | 47 | } |
48 | 48 | ||
49 | static int coda_pioctl(struct inode * inode, struct file * filp, | 49 | static int coda_pioctl(struct inode * inode, struct file * filp, |
diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index 7e19835efa2e..c69b7ac75bf7 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c | |||
@@ -511,13 +511,6 @@ void hfs_clear_inode(struct inode *inode) | |||
511 | } | 511 | } |
512 | } | 512 | } |
513 | 513 | ||
514 | static int hfs_permission(struct inode *inode, int mask) | ||
515 | { | ||
516 | if (S_ISREG(inode->i_mode) && mask & MAY_EXEC) | ||
517 | return 0; | ||
518 | return generic_permission(inode, mask, NULL); | ||
519 | } | ||
520 | |||
521 | static int hfs_file_open(struct inode *inode, struct file *file) | 514 | static int hfs_file_open(struct inode *inode, struct file *file) |
522 | { | 515 | { |
523 | if (HFS_IS_RSRC(inode)) | 516 | if (HFS_IS_RSRC(inode)) |
@@ -616,7 +609,6 @@ static const struct inode_operations hfs_file_inode_operations = { | |||
616 | .lookup = hfs_file_lookup, | 609 | .lookup = hfs_file_lookup, |
617 | .truncate = hfs_file_truncate, | 610 | .truncate = hfs_file_truncate, |
618 | .setattr = hfs_inode_setattr, | 611 | .setattr = hfs_inode_setattr, |
619 | .permission = hfs_permission, | ||
620 | .setxattr = hfs_setxattr, | 612 | .setxattr = hfs_setxattr, |
621 | .getxattr = hfs_getxattr, | 613 | .getxattr = hfs_getxattr, |
622 | .listxattr = hfs_listxattr, | 614 | .listxattr = hfs_listxattr, |
diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index 963be644297a..b207f0e6fc22 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c | |||
@@ -238,18 +238,6 @@ static void hfsplus_set_perms(struct inode *inode, struct hfsplus_perm *perms) | |||
238 | perms->dev = cpu_to_be32(HFSPLUS_I(inode).dev); | 238 | perms->dev = cpu_to_be32(HFSPLUS_I(inode).dev); |
239 | } | 239 | } |
240 | 240 | ||
241 | static int hfsplus_permission(struct inode *inode, int mask) | ||
242 | { | ||
243 | /* MAY_EXEC is also used for lookup, if no x bit is set allow lookup, | ||
244 | * open_exec has the same test, so it's still not executable, if a x bit | ||
245 | * is set fall back to standard permission check. | ||
246 | */ | ||
247 | if (S_ISREG(inode->i_mode) && mask & MAY_EXEC && !(inode->i_mode & 0111)) | ||
248 | return 0; | ||
249 | return generic_permission(inode, mask, NULL); | ||
250 | } | ||
251 | |||
252 | |||
253 | static int hfsplus_file_open(struct inode *inode, struct file *file) | 241 | static int hfsplus_file_open(struct inode *inode, struct file *file) |
254 | { | 242 | { |
255 | if (HFSPLUS_IS_RSRC(inode)) | 243 | if (HFSPLUS_IS_RSRC(inode)) |
@@ -281,7 +269,6 @@ static int hfsplus_file_release(struct inode *inode, struct file *file) | |||
281 | static const struct inode_operations hfsplus_file_inode_operations = { | 269 | static const struct inode_operations hfsplus_file_inode_operations = { |
282 | .lookup = hfsplus_file_lookup, | 270 | .lookup = hfsplus_file_lookup, |
283 | .truncate = hfsplus_file_truncate, | 271 | .truncate = hfsplus_file_truncate, |
284 | .permission = hfsplus_permission, | ||
285 | .setxattr = hfsplus_setxattr, | 272 | .setxattr = hfsplus_setxattr, |
286 | .getxattr = hfsplus_getxattr, | 273 | .getxattr = hfsplus_getxattr, |
287 | .listxattr = hfsplus_listxattr, | 274 | .listxattr = hfsplus_listxattr, |
diff --git a/fs/namei.c b/fs/namei.c index 9e2a534383d9..09ce58e49e72 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -212,8 +212,7 @@ int generic_permission(struct inode *inode, int mask, | |||
212 | * Read/write DACs are always overridable. | 212 | * Read/write DACs are always overridable. |
213 | * Executable DACs are overridable if at least one exec bit is set. | 213 | * Executable DACs are overridable if at least one exec bit is set. |
214 | */ | 214 | */ |
215 | if (!(mask & MAY_EXEC) || | 215 | if (!(mask & MAY_EXEC) || execute_ok(inode)) |
216 | (inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode)) | ||
217 | if (capable(CAP_DAC_OVERRIDE)) | 216 | if (capable(CAP_DAC_OVERRIDE)) |
218 | return 0; | 217 | return 0; |
219 | 218 | ||
@@ -249,23 +248,11 @@ int inode_permission(struct inode *inode, int mask) | |||
249 | } | 248 | } |
250 | 249 | ||
251 | /* Ordinary permission routines do not understand MAY_APPEND. */ | 250 | /* Ordinary permission routines do not understand MAY_APPEND. */ |
252 | if (inode->i_op && inode->i_op->permission) { | 251 | if (inode->i_op && inode->i_op->permission) |
253 | retval = inode->i_op->permission(inode, mask); | 252 | retval = inode->i_op->permission(inode, mask); |
254 | if (!retval) { | 253 | else |
255 | /* | ||
256 | * Exec permission on a regular file is denied if none | ||
257 | * of the execute bits are set. | ||
258 | * | ||
259 | * This check should be done by the ->permission() | ||
260 | * method. | ||
261 | */ | ||
262 | if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode) && | ||
263 | !(inode->i_mode & S_IXUGO)) | ||
264 | return -EACCES; | ||
265 | } | ||
266 | } else { | ||
267 | retval = generic_permission(inode, mask, NULL); | 254 | retval = generic_permission(inode, mask, NULL); |
268 | } | 255 | |
269 | if (retval) | 256 | if (retval) |
270 | return retval; | 257 | return retval; |
271 | 258 | ||
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index c216c8786c51..3e64b98f3a93 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c | |||
@@ -1957,6 +1957,9 @@ force_lookup: | |||
1957 | } else | 1957 | } else |
1958 | res = PTR_ERR(cred); | 1958 | res = PTR_ERR(cred); |
1959 | out: | 1959 | out: |
1960 | if (!res && (mask & MAY_EXEC) && !execute_ok(inode)) | ||
1961 | res = -EACCES; | ||
1962 | |||
1960 | dfprintk(VFS, "NFS: permission(%s/%ld), mask=0x%x, res=%d\n", | 1963 | dfprintk(VFS, "NFS: permission(%s/%ld), mask=0x%x, res=%d\n", |
1961 | inode->i_sb->s_id, inode->i_ino, mask, res); | 1964 | inode->i_sb->s_id, inode->i_ino, mask, res); |
1962 | return res; | 1965 | return res; |
diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index 5fe210c09171..7b997754a25e 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c | |||
@@ -298,13 +298,19 @@ static int proc_sys_permission(struct inode *inode, int mask) | |||
298 | * sysctl entries that are not writeable, | 298 | * sysctl entries that are not writeable, |
299 | * are _NOT_ writeable, capabilities or not. | 299 | * are _NOT_ writeable, capabilities or not. |
300 | */ | 300 | */ |
301 | struct ctl_table_header *head = grab_header(inode); | 301 | struct ctl_table_header *head; |
302 | struct ctl_table *table = PROC_I(inode)->sysctl_entry; | 302 | struct ctl_table *table; |
303 | int error; | 303 | int error; |
304 | 304 | ||
305 | /* Executable files are not allowed under /proc/sys/ */ | ||
306 | if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode)) | ||
307 | return -EACCES; | ||
308 | |||
309 | head = grab_header(inode); | ||
305 | if (IS_ERR(head)) | 310 | if (IS_ERR(head)) |
306 | return PTR_ERR(head); | 311 | return PTR_ERR(head); |
307 | 312 | ||
313 | table = PROC_I(inode)->sysctl_entry; | ||
308 | if (!table) /* global root - r-xr-xr-x */ | 314 | if (!table) /* global root - r-xr-xr-x */ |
309 | error = mask & MAY_WRITE ? -EACCES : 0; | 315 | error = mask & MAY_WRITE ? -EACCES : 0; |
310 | else /* Use the permissions on the sysctl table entry */ | 316 | else /* Use the permissions on the sysctl table entry */ |
diff --git a/include/linux/fs.h b/include/linux/fs.h index 5f70aa62cf0f..025a4a251b64 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -1851,6 +1851,11 @@ extern int inode_permission(struct inode *, int); | |||
1851 | extern int generic_permission(struct inode *, int, | 1851 | extern int generic_permission(struct inode *, int, |
1852 | int (*check_acl)(struct inode *, int)); | 1852 | int (*check_acl)(struct inode *, int)); |
1853 | 1853 | ||
1854 | static inline bool execute_ok(struct inode *inode) | ||
1855 | { | ||
1856 | return (inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode); | ||
1857 | } | ||
1858 | |||
1854 | extern int get_write_access(struct inode *); | 1859 | extern int get_write_access(struct inode *); |
1855 | extern int deny_write_access(struct file *); | 1860 | extern int deny_write_access(struct file *); |
1856 | static inline void put_write_access(struct inode * inode) | 1861 | static inline void put_write_access(struct inode * inode) |