Commit 42017c2e authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Linus Torvalds

[PATCH] generic acl support for ->permission

Currently we every filesystem with Posix ACLs has it's own reimplemtation
of the generic permission checking code with additonal ACL support.  This
patch

- adds an optional callback to vfs_permission that filesystems can use
  for ACL support (and renames it to generic_permission because the old
  name was wrong - it wasn't like the other vfs_* functions at all)

- uses it in ext2, ext3 and jfs.  XFS will follow a little later as it's
  permission checking is burried under several layers of abstraction.

From: Dave Kleikamp <shaggy@austin.ibm.com>

  jfs doesn't currently set MS_POSIXACL (it doesn't require the acl mount
  option), so this test would fail here.  The patch below will set it.
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 98291a77
...@@ -7,8 +7,8 @@ or out of order NULL pointer checks in little used error paths). ...@@ -7,8 +7,8 @@ or out of order NULL pointer checks in little used error paths).
Version 1.21 Version 1.21
------------ ------------
Add new mount parm to control whether mode check (vfs_permission) is done on Add new mount parm to control whether mode check (generic_permission) is done
the client. If Unix extensions are enabled and the uids on the client on the client. If Unix extensions are enabled and the uids on the client
and server do not match, client permission checks are meaningless on and server do not match, client permission checks are meaningless on
server uids that do not exist on the client (this does not affect the server uids that do not exist on the client (this does not affect the
normal ACL check which occurs on the server). Fix default uid normal ACL check which occurs on the server). Fix default uid
......
...@@ -200,7 +200,7 @@ static int cifs_permission(struct inode * inode, int mask, struct nameidata *nd) ...@@ -200,7 +200,7 @@ static int cifs_permission(struct inode * inode, int mask, struct nameidata *nd)
on the client (above and beyond ACL on servers) for on the client (above and beyond ACL on servers) for
servers which do not support setting and viewing mode bits, servers which do not support setting and viewing mode bits,
so allowing client to check permissions is useful */ so allowing client to check permissions is useful */
return vfs_permission(inode, mask); return generic_permission(inode, mask, NULL);
} }
static kmem_cache_t *cifs_inode_cachep; static kmem_cache_t *cifs_inode_cachep;
......
...@@ -886,7 +886,7 @@ int prepare_binprm(struct linux_binprm *bprm) ...@@ -886,7 +886,7 @@ int prepare_binprm(struct linux_binprm *bprm)
mode = inode->i_mode; mode = inode->i_mode;
/* /*
* Check execute perms again - if the caller has CAP_DAC_OVERRIDE, * Check execute perms again - if the caller has CAP_DAC_OVERRIDE,
* vfs_permission lets a non-executable through * generic_permission lets a non-executable through
*/ */
if (!(mode & 0111)) /* with at least _one_ execute bit set */ if (!(mode & 0111)) /* with at least _one_ execute bit set */
return -EACCES; return -EACCES;
......
...@@ -280,60 +280,24 @@ ext2_set_acl(struct inode *inode, int type, struct posix_acl *acl) ...@@ -280,60 +280,24 @@ ext2_set_acl(struct inode *inode, int type, struct posix_acl *acl)
return error; return error;
} }
/* static int
* Inode operation permission(). ext2_check_acl(struct inode *inode, int mask)
*
* inode->i_sem: don't care
*/
int
ext2_permission(struct inode *inode, int mask, struct nameidata *nd)
{ {
int mode = inode->i_mode; struct posix_acl *acl = ext2_get_acl(inode, ACL_TYPE_ACCESS);
/* Nobody gets write access to a read-only fs */
if ((mask & MAY_WRITE) && IS_RDONLY(inode) &&
(S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
return -EROFS;
/* Nobody gets write access to an immutable file */
if ((mask & MAY_WRITE) && IS_IMMUTABLE(inode))
return -EACCES;
if (current->fsuid == inode->i_uid) {
mode >>= 6;
} else if (test_opt(inode->i_sb, POSIX_ACL)) {
struct posix_acl *acl;
/* The access ACL cannot grant access if the group class
permission bits don't contain all requested permissions. */
if (((mode >> 3) & mask & S_IRWXO) != mask)
goto check_groups;
acl = ext2_get_acl(inode, ACL_TYPE_ACCESS);
if (acl) { if (acl) {
int error = posix_acl_permission(inode, acl, mask); int error = posix_acl_permission(inode, acl, mask);
posix_acl_release(acl); posix_acl_release(acl);
if (error == -EACCES)
goto check_capabilities;
return error; return error;
} else
goto check_groups;
} else {
check_groups:
if (in_group_p(inode->i_gid))
mode >>= 3;
} }
if ((mode & mask & S_IRWXO) == mask)
return 0;
check_capabilities: return -EAGAIN;
/* Allowed to override Discretionary Access Control? */ }
if (!(mask & MAY_EXEC) ||
(inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode)) int
if (capable(CAP_DAC_OVERRIDE)) ext2_permission(struct inode *inode, int mask, struct nameidata *nd)
return 0; {
/* Read and search granted if capable(CAP_DAC_READ_SEARCH) */ return generic_permission(inode, mask, ext2_check_acl);
if (capable(CAP_DAC_READ_SEARCH) && ((mask == MAY_READ) ||
(S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE))))
return 0;
return -EACCES;
} }
/* /*
......
...@@ -285,60 +285,24 @@ ext3_set_acl(handle_t *handle, struct inode *inode, int type, ...@@ -285,60 +285,24 @@ ext3_set_acl(handle_t *handle, struct inode *inode, int type,
return error; return error;
} }
/* static int
* Inode operation permission(). ext3_check_acl(struct inode *inode, int mask)
*
* inode->i_sem: don't care
*/
int
ext3_permission(struct inode *inode, int mask, struct nameidata *nd)
{ {
int mode = inode->i_mode; struct posix_acl *acl = ext3_get_acl(inode, ACL_TYPE_ACCESS);
/* Nobody gets write access to a read-only fs */
if ((mask & MAY_WRITE) && IS_RDONLY(inode) &&
(S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
return -EROFS;
/* Nobody gets write access to an immutable file */
if ((mask & MAY_WRITE) && IS_IMMUTABLE(inode))
return -EACCES;
if (current->fsuid == inode->i_uid) {
mode >>= 6;
} else if (test_opt(inode->i_sb, POSIX_ACL)) {
struct posix_acl *acl;
/* The access ACL cannot grant access if the group class
permission bits don't contain all requested permissions. */
if (((mode >> 3) & mask & S_IRWXO) != mask)
goto check_groups;
acl = ext3_get_acl(inode, ACL_TYPE_ACCESS);
if (acl) { if (acl) {
int error = posix_acl_permission(inode, acl, mask); int error = posix_acl_permission(inode, acl, mask);
posix_acl_release(acl); posix_acl_release(acl);
if (error == -EACCES)
goto check_capabilities;
return error; return error;
} else
goto check_groups;
} else {
check_groups:
if (in_group_p(inode->i_gid))
mode >>= 3;
} }
if ((mode & mask & S_IRWXO) == mask)
return 0;
check_capabilities: return -EAGAIN;
/* Allowed to override Discretionary Access Control? */ }
if (!(mask & MAY_EXEC) ||
(inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode)) int
if (capable(CAP_DAC_OVERRIDE)) ext3_permission(struct inode *inode, int mask, struct nameidata *nd)
return 0; {
/* Read and search granted if capable(CAP_DAC_READ_SEARCH) */ return generic_permission(inode, mask, ext3_check_acl);
if (capable(CAP_DAC_READ_SEARCH) && ((mask == MAY_READ) ||
(S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE))))
return 0;
return -EACCES;
} }
/* /*
......
...@@ -517,7 +517,7 @@ static int hfs_permission(struct inode *inode, int mask, ...@@ -517,7 +517,7 @@ static int hfs_permission(struct inode *inode, int mask,
{ {
if (S_ISREG(inode->i_mode) && mask & MAY_EXEC) if (S_ISREG(inode->i_mode) && mask & MAY_EXEC)
return 0; return 0;
return vfs_permission(inode, mask); return generic_permission(inode, mask, NULL);
} }
static int hfs_file_open(struct inode *inode, struct file *file) static int hfs_file_open(struct inode *inode, struct file *file)
......
...@@ -260,7 +260,7 @@ static int hfsplus_permission(struct inode *inode, int mask, struct nameidata *n ...@@ -260,7 +260,7 @@ static int hfsplus_permission(struct inode *inode, int mask, struct nameidata *n
*/ */
if (S_ISREG(inode->i_mode) && mask & MAY_EXEC && !(inode->i_mode & 0111)) if (S_ISREG(inode->i_mode) && mask & MAY_EXEC && !(inode->i_mode & 0111))
return 0; return 0;
return vfs_permission(inode, mask); return generic_permission(inode, mask, NULL);
} }
......
...@@ -808,7 +808,7 @@ int hostfs_permission(struct inode *ino, int desired, struct nameidata *nd) ...@@ -808,7 +808,7 @@ int hostfs_permission(struct inode *ino, int desired, struct nameidata *nd)
if(name == NULL) return(-ENOMEM); if(name == NULL) return(-ENOMEM);
err = access_file(name, r, w, x); err = access_file(name, r, w, x);
kfree(name); kfree(name);
if(!err) err = vfs_permission(ino, desired); if(!err) err = generic_permission(ino, desired, NULL);
return(err); return(err);
} }
......
...@@ -123,88 +123,25 @@ static int jfs_set_acl(struct inode *inode, int type, struct posix_acl *acl) ...@@ -123,88 +123,25 @@ static int jfs_set_acl(struct inode *inode, int type, struct posix_acl *acl)
return rc; return rc;
} }
/* static int jfs_check_acl(struct inode *inode, int mask)
* jfs_permission()
*
* modified vfs_permission to check posix acl
*/
int jfs_permission(struct inode * inode, int mask, struct nameidata *nd)
{ {
umode_t mode = inode->i_mode;
struct jfs_inode_info *ji = JFS_IP(inode); struct jfs_inode_info *ji = JFS_IP(inode);
if (mask & MAY_WRITE) {
/*
* Nobody gets write access to a read-only fs.
*/
if (IS_RDONLY(inode) &&
(S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
return -EROFS;
/*
* Nobody gets write access to an immutable file.
*/
if (IS_IMMUTABLE(inode))
return -EACCES;
}
if (current->fsuid == inode->i_uid) {
mode >>= 6;
goto check_mode;
}
/*
* ACL can't contain additional permissions if the ACL_MASK entry
* is zero.
*/
if (!(mode & S_IRWXG))
goto check_groups;
if (ji->i_acl == JFS_ACL_NOT_CACHED) { if (ji->i_acl == JFS_ACL_NOT_CACHED) {
struct posix_acl *acl; struct posix_acl *acl = jfs_get_acl(inode, ACL_TYPE_ACCESS);
acl = jfs_get_acl(inode, ACL_TYPE_ACCESS);
if (IS_ERR(acl)) if (IS_ERR(acl))
return PTR_ERR(acl); return PTR_ERR(acl);
posix_acl_release(acl); posix_acl_release(acl);
} }
if (ji->i_acl) { if (ji->i_acl)
int rc = posix_acl_permission(inode, ji->i_acl, mask); return posix_acl_permission(inode, ji->i_acl, mask);
if (rc == -EACCES) return -EAGAIN;
goto check_capabilities; }
return rc;
}
check_groups:
if (in_group_p(inode->i_gid))
mode >>= 3;
check_mode:
/*
* If the DACs are ok we don't need any capability check.
*/
if (((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask))
return 0;
check_capabilities:
/*
* Read/write DACs are always overridable.
* Executable DACs are overridable if at least one exec bit is set.
*/
if (!(mask & MAY_EXEC) ||
(inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode))
if (capable(CAP_DAC_OVERRIDE))
return 0;
/*
* Searching includes executable on directories, else just read.
*/
if (mask == MAY_READ || (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE)))
if (capable(CAP_DAC_READ_SEARCH))
return 0;
return -EACCES; int jfs_permission(struct inode *inode, int mask, struct nameidata *nd)
{
return generic_permission(inode, mask, jfs_check_acl);
} }
int jfs_init_acl(struct inode *inode, struct inode *dir) int jfs_init_acl(struct inode *inode, struct inode *dir)
......
...@@ -403,6 +403,10 @@ static int jfs_fill_super(struct super_block *sb, void *data, int silent) ...@@ -403,6 +403,10 @@ static int jfs_fill_super(struct super_block *sb, void *data, int silent)
} }
sbi->flag = flag; sbi->flag = flag;
#ifdef CONFIG_JFS_POSIX_ACL
sb->s_flags |= MS_POSIXACL;
#endif
if (newLVSize) { if (newLVSize) {
printk(KERN_ERR "resize option for remount only\n"); printk(KERN_ERR "resize option for remount only\n");
return -EINVAL; return -EINVAL;
......
...@@ -152,15 +152,19 @@ char * getname(const char __user * filename) ...@@ -152,15 +152,19 @@ char * getname(const char __user * filename)
return result; return result;
} }
/* /**
* vfs_permission() * generic_permission - check for access rights on a Posix-like filesystem
* @inode: inode to check access rights for
* @mask: right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
* @check_acl: optional callback to check for Posix ACLs
* *
* is used to check for read/write/execute permissions on a file. * Used to check for read/write/execute permissions on a file.
* We use "fsuid" for this, letting us set arbitrary permissions * We use "fsuid" for this, letting us set arbitrary permissions
* for filesystem access without changing the "normal" uids which * for filesystem access without changing the "normal" uids which
* are used for other things.. * are used for other things..
*/ */
int vfs_permission(struct inode * inode, int mask) int generic_permission(struct inode *inode, int mask,
int (*check_acl)(struct inode *inode, int mask))
{ {
umode_t mode = inode->i_mode; umode_t mode = inode->i_mode;
...@@ -181,8 +185,18 @@ int vfs_permission(struct inode * inode, int mask) ...@@ -181,8 +185,18 @@ int vfs_permission(struct inode * inode, int mask)
if (current->fsuid == inode->i_uid) if (current->fsuid == inode->i_uid)
mode >>= 6; mode >>= 6;
else if (in_group_p(inode->i_gid)) else {
if (IS_POSIXACL(inode) && (mode & S_IRWXG) && check_acl) {
int error = check_acl(inode, mask);
if (error == -EACCES)
goto check_capabilities;
else if (error != -EAGAIN)
return error;
}
if (in_group_p(inode->i_gid))
mode >>= 3; mode >>= 3;
}
/* /*
* If the DACs are ok we don't need any capability check. * If the DACs are ok we don't need any capability check.
...@@ -190,6 +204,7 @@ int vfs_permission(struct inode * inode, int mask) ...@@ -190,6 +204,7 @@ int vfs_permission(struct inode * inode, int mask)
if (((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask)) if (((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask))
return 0; return 0;
check_capabilities:
/* /*
* Read/write DACs are always overridable. * Read/write DACs are always overridable.
* Executable DACs are overridable if at least one exec bit is set. * Executable DACs are overridable if at least one exec bit is set.
...@@ -220,7 +235,7 @@ int permission(struct inode * inode,int mask, struct nameidata *nd) ...@@ -220,7 +235,7 @@ int permission(struct inode * inode,int mask, struct nameidata *nd)
if (inode->i_op && inode->i_op->permission) if (inode->i_op && inode->i_op->permission)
retval = inode->i_op->permission(inode, submask, nd); retval = inode->i_op->permission(inode, submask, nd);
else else
retval = vfs_permission(inode, submask); retval = generic_permission(inode, submask, NULL);
if (retval) if (retval)
return retval; return retval;
...@@ -315,7 +330,7 @@ static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name, ...@@ -315,7 +330,7 @@ static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name,
/* /*
* Short-cut version of permission(), for calling by * Short-cut version of permission(), for calling by
* path_walk(), when dcache lock is held. Combines parts * path_walk(), when dcache lock is held. Combines parts
* of permission() and vfs_permission(), and tests ONLY for * of permission() and generic_permission(), and tests ONLY for
* MAY_EXEC permission. * MAY_EXEC permission.
* *
* If appropriate, check DAC only. If not appropriate, or * If appropriate, check DAC only. If not appropriate, or
...@@ -2456,7 +2471,7 @@ EXPORT_SYMBOL(vfs_follow_link); ...@@ -2456,7 +2471,7 @@ EXPORT_SYMBOL(vfs_follow_link);
EXPORT_SYMBOL(vfs_link); EXPORT_SYMBOL(vfs_link);
EXPORT_SYMBOL(vfs_mkdir); EXPORT_SYMBOL(vfs_mkdir);
EXPORT_SYMBOL(vfs_mknod); EXPORT_SYMBOL(vfs_mknod);
EXPORT_SYMBOL(vfs_permission); EXPORT_SYMBOL(generic_permission);
EXPORT_SYMBOL(vfs_readlink); EXPORT_SYMBOL(vfs_readlink);
EXPORT_SYMBOL(vfs_rename); EXPORT_SYMBOL(vfs_rename);
EXPORT_SYMBOL(vfs_rmdir); EXPORT_SYMBOL(vfs_rmdir);
......
...@@ -1600,7 +1600,7 @@ int nfs_permission(struct inode *inode, int mask, struct nameidata *nd) ...@@ -1600,7 +1600,7 @@ int nfs_permission(struct inode *inode, int mask, struct nameidata *nd)
return res; return res;
out_notsup: out_notsup:
nfs_revalidate_inode(NFS_SERVER(inode), inode); nfs_revalidate_inode(NFS_SERVER(inode), inode);
res = vfs_permission(inode, mask); res = generic_permission(inode, mask, NULL);
unlock_kernel(); unlock_kernel();
return res; return res;
} }
......
...@@ -473,7 +473,7 @@ static int proc_check_root(struct inode *inode) ...@@ -473,7 +473,7 @@ static int proc_check_root(struct inode *inode)
static int proc_permission(struct inode *inode, int mask, struct nameidata *nd) static int proc_permission(struct inode *inode, int mask, struct nameidata *nd)
{ {
if (vfs_permission(inode, mask) != 0) if (generic_permission(inode, mask, NULL) != 0)
return -EACCES; return -EACCES;
return proc_check_root(inode); return proc_check_root(inode);
} }
......
...@@ -1339,7 +1339,9 @@ extern sector_t bmap(struct inode *, sector_t); ...@@ -1339,7 +1339,9 @@ extern sector_t bmap(struct inode *, sector_t);
extern int setattr_mask(unsigned int); extern int setattr_mask(unsigned int);
extern int notify_change(struct dentry *, struct iattr *); extern int notify_change(struct dentry *, struct iattr *);
extern int permission(struct inode *, int, struct nameidata *); extern int permission(struct inode *, int, struct nameidata *);
extern int vfs_permission(struct inode *, int); extern int generic_permission(struct inode *, int,
int (*check_acl)(struct inode *, int));
extern int get_write_access(struct inode *); extern int get_write_access(struct inode *);
extern int deny_write_access(struct file *); extern int deny_write_access(struct file *);
static inline void put_write_access(struct inode * inode) static inline void put_write_access(struct inode * inode)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment