Make 'check_acl()' a first-class filesystem op
This is stage one in flattening out the callchains for the common permission testing. Rather than have most filesystem implement their own inode->i_op->permission function that just calls back down to the VFS layers 'generic_permission()' with the per-filesystem ACL checking function, the filesystem can just expose its 'check_acl' function directly, and let the VFS layer do everything for it. This is all just preparatory - no filesystem actually enables this yet. Reviewed-by: James Morris <jmorris@namei.org> Acked-by: Serge Hallyn <serue@us.ibm.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
74
fs/namei.c
74
fs/namei.c
@@ -169,6 +169,36 @@ void putname(const char *name)
|
|||||||
EXPORT_SYMBOL(putname);
|
EXPORT_SYMBOL(putname);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This does basic POSIX ACL permission checking
|
||||||
|
*/
|
||||||
|
static int acl_permission_check(struct inode *inode, int mask,
|
||||||
|
int (*check_acl)(struct inode *inode, int mask))
|
||||||
|
{
|
||||||
|
umode_t mode = inode->i_mode;
|
||||||
|
|
||||||
|
mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
|
||||||
|
|
||||||
|
if (current_fsuid() == inode->i_uid)
|
||||||
|
mode >>= 6;
|
||||||
|
else {
|
||||||
|
if (IS_POSIXACL(inode) && (mode & S_IRWXG) && check_acl) {
|
||||||
|
int error = check_acl(inode, mask);
|
||||||
|
if (error != -EAGAIN)
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_group_p(inode->i_gid))
|
||||||
|
mode >>= 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the DACs are ok we don't need any capability check.
|
||||||
|
*/
|
||||||
|
if ((mask & ~mode) == 0)
|
||||||
|
return 0;
|
||||||
|
return -EACCES;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* generic_permission - check for access rights on a Posix-like filesystem
|
* generic_permission - check for access rights on a Posix-like filesystem
|
||||||
@@ -184,32 +214,15 @@ EXPORT_SYMBOL(putname);
|
|||||||
int generic_permission(struct inode *inode, int mask,
|
int generic_permission(struct inode *inode, int mask,
|
||||||
int (*check_acl)(struct inode *inode, int mask))
|
int (*check_acl)(struct inode *inode, int mask))
|
||||||
{
|
{
|
||||||
umode_t mode = inode->i_mode;
|
int ret;
|
||||||
|
|
||||||
mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
|
|
||||||
|
|
||||||
if (current_fsuid() == inode->i_uid)
|
|
||||||
mode >>= 6;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the DACs are ok we don't need any capability check.
|
* Do the basic POSIX ACL permission checks.
|
||||||
*/
|
*/
|
||||||
if ((mask & ~mode) == 0)
|
ret = acl_permission_check(inode, mask, check_acl);
|
||||||
return 0;
|
if (ret != -EACCES)
|
||||||
|
return ret;
|
||||||
|
|
||||||
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.
|
||||||
@@ -262,7 +275,7 @@ int inode_permission(struct inode *inode, int mask)
|
|||||||
if (inode->i_op->permission)
|
if (inode->i_op->permission)
|
||||||
retval = inode->i_op->permission(inode, mask);
|
retval = inode->i_op->permission(inode, mask);
|
||||||
else
|
else
|
||||||
retval = generic_permission(inode, mask, NULL);
|
retval = generic_permission(inode, mask, inode->i_op->check_acl);
|
||||||
|
|
||||||
if (retval)
|
if (retval)
|
||||||
return retval;
|
return retval;
|
||||||
@@ -432,27 +445,22 @@ static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name,
|
|||||||
*/
|
*/
|
||||||
static int exec_permission_lite(struct inode *inode)
|
static int exec_permission_lite(struct inode *inode)
|
||||||
{
|
{
|
||||||
umode_t mode = inode->i_mode;
|
int ret;
|
||||||
|
|
||||||
if (inode->i_op->permission) {
|
if (inode->i_op->permission) {
|
||||||
int ret = inode->i_op->permission(inode, MAY_EXEC);
|
ret = inode->i_op->permission(inode, MAY_EXEC);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
goto ok;
|
goto ok;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
ret = acl_permission_check(inode, MAY_EXEC, inode->i_op->check_acl);
|
||||||
if (current_fsuid() == inode->i_uid)
|
if (!ret)
|
||||||
mode >>= 6;
|
|
||||||
else if (in_group_p(inode->i_gid))
|
|
||||||
mode >>= 3;
|
|
||||||
|
|
||||||
if (mode & MAY_EXEC)
|
|
||||||
goto ok;
|
goto ok;
|
||||||
|
|
||||||
if (capable(CAP_DAC_OVERRIDE) || capable(CAP_DAC_READ_SEARCH))
|
if (capable(CAP_DAC_OVERRIDE) || capable(CAP_DAC_READ_SEARCH))
|
||||||
goto ok;
|
goto ok;
|
||||||
|
|
||||||
return -EACCES;
|
return ret;
|
||||||
ok:
|
ok:
|
||||||
return security_inode_permission(inode, MAY_EXEC);
|
return security_inode_permission(inode, MAY_EXEC);
|
||||||
}
|
}
|
||||||
|
@@ -1528,6 +1528,7 @@ struct inode_operations {
|
|||||||
void (*put_link) (struct dentry *, struct nameidata *, void *);
|
void (*put_link) (struct dentry *, struct nameidata *, void *);
|
||||||
void (*truncate) (struct inode *);
|
void (*truncate) (struct inode *);
|
||||||
int (*permission) (struct inode *, int);
|
int (*permission) (struct inode *, int);
|
||||||
|
int (*check_acl)(struct inode *, int);
|
||||||
int (*setattr) (struct dentry *, struct iattr *);
|
int (*setattr) (struct dentry *, struct iattr *);
|
||||||
int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
|
int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
|
||||||
int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
|
int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
|
||||||
|
Reference in New Issue
Block a user