namei: make permission helpers idmapped mount aware
The two helpers inode_permission() and generic_permission() are used by the vfs to perform basic permission checking by verifying that the caller is privileged over an inode. In order to handle idmapped mounts we extend the two helpers with an additional user namespace argument. On idmapped mounts the two helpers will make sure to map the inode according to the mount's user namespace and then peform identical permission checks to inode_permission() and generic_permission(). If the initial user namespace is passed nothing changes so non-idmapped mounts will see identical behavior as before. Link: https://lore.kernel.org/r/20210121131959.646623-6-christian.brauner@ubuntu.com Cc: Christoph Hellwig <hch@lst.de> Cc: David Howells <dhowells@redhat.com> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: linux-fsdevel@vger.kernel.org Reviewed-by: Christoph Hellwig <hch@lst.de> Reviewed-by: James Morris <jamorris@linux.microsoft.com> Acked-by: Serge Hallyn <serge@hallyn.com> Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
This commit is contained in:
Родитель
0558c1bf5a
Коммит
47291baa8d
|
@ -244,7 +244,8 @@ int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **de
|
|||
return -EPERM;
|
||||
|
||||
if (!inode_owner_or_capable(inode)) {
|
||||
error = inode_permission(inode, MAY_WRITE);
|
||||
error = inode_permission(&init_user_ns, inode,
|
||||
MAY_WRITE);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
|
|
@ -9889,7 +9889,7 @@ static int btrfs_permission(struct inode *inode, int mask)
|
|||
if (BTRFS_I(inode)->flags & BTRFS_INODE_READONLY)
|
||||
return -EACCES;
|
||||
}
|
||||
return generic_permission(inode, mask);
|
||||
return generic_permission(&init_user_ns, inode, mask);
|
||||
}
|
||||
|
||||
static int btrfs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||
|
|
|
@ -922,7 +922,7 @@ static int btrfs_may_delete(struct inode *dir, struct dentry *victim, int isdir)
|
|||
BUG_ON(d_inode(victim->d_parent) != dir);
|
||||
audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE);
|
||||
|
||||
error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
|
||||
error = inode_permission(&init_user_ns, dir, MAY_WRITE | MAY_EXEC);
|
||||
if (error)
|
||||
return error;
|
||||
if (IS_APPEND(dir))
|
||||
|
@ -951,7 +951,7 @@ static inline int btrfs_may_create(struct inode *dir, struct dentry *child)
|
|||
return -EEXIST;
|
||||
if (IS_DEADDIR(dir))
|
||||
return -ENOENT;
|
||||
return inode_permission(dir, MAY_WRITE | MAY_EXEC);
|
||||
return inode_permission(&init_user_ns, dir, MAY_WRITE | MAY_EXEC);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2538,7 +2538,8 @@ static int btrfs_search_path_in_tree_user(struct inode *inode,
|
|||
ret = PTR_ERR(temp_inode);
|
||||
goto out_put;
|
||||
}
|
||||
ret = inode_permission(temp_inode, MAY_READ | MAY_EXEC);
|
||||
ret = inode_permission(&init_user_ns, temp_inode,
|
||||
MAY_READ | MAY_EXEC);
|
||||
iput(temp_inode);
|
||||
if (ret) {
|
||||
ret = -EACCES;
|
||||
|
@ -3068,7 +3069,8 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
|
|||
if (root == dest)
|
||||
goto out_dput;
|
||||
|
||||
err = inode_permission(inode, MAY_WRITE | MAY_EXEC);
|
||||
err = inode_permission(&init_user_ns, inode,
|
||||
MAY_WRITE | MAY_EXEC);
|
||||
if (err)
|
||||
goto out_dput;
|
||||
}
|
||||
|
@ -3139,7 +3141,7 @@ static int btrfs_ioctl_defrag(struct file *file, void __user *argp)
|
|||
* running and allows defrag on files open in read-only mode.
|
||||
*/
|
||||
if (!capable(CAP_SYS_ADMIN) &&
|
||||
inode_permission(inode, MAY_WRITE)) {
|
||||
inode_permission(&init_user_ns, inode, MAY_WRITE)) {
|
||||
ret = -EPERM;
|
||||
goto out;
|
||||
}
|
||||
|
|
|
@ -2331,7 +2331,7 @@ int ceph_permission(struct inode *inode, int mask)
|
|||
err = ceph_do_getattr(inode, CEPH_CAP_AUTH_SHARED, false);
|
||||
|
||||
if (!err)
|
||||
err = generic_permission(inode, mask);
|
||||
err = generic_permission(&init_user_ns, inode, mask);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
|
@ -320,7 +320,7 @@ static int cifs_permission(struct inode *inode, int mask)
|
|||
on the client (above and beyond ACL on servers) for
|
||||
servers which do not support setting and viewing mode bits,
|
||||
so allowing client to check permissions is useful */
|
||||
return generic_permission(inode, mask);
|
||||
return generic_permission(&init_user_ns, inode, mask);
|
||||
}
|
||||
|
||||
static struct kmem_cache *cifs_inode_cachep;
|
||||
|
|
|
@ -197,7 +197,8 @@ int configfs_symlink(struct inode *dir, struct dentry *dentry, const char *symna
|
|||
if (dentry->d_inode || d_unhashed(dentry))
|
||||
ret = -EEXIST;
|
||||
else
|
||||
ret = inode_permission(dir, MAY_WRITE | MAY_EXEC);
|
||||
ret = inode_permission(&init_user_ns, dir,
|
||||
MAY_WRITE | MAY_EXEC);
|
||||
if (!ret)
|
||||
ret = type->ct_item_ops->allow_link(parent_item, target_item);
|
||||
if (!ret) {
|
||||
|
|
|
@ -864,7 +864,8 @@ int ecryptfs_truncate(struct dentry *dentry, loff_t new_length)
|
|||
static int
|
||||
ecryptfs_permission(struct inode *inode, int mask)
|
||||
{
|
||||
return inode_permission(ecryptfs_inode_to_lower(inode), mask);
|
||||
return inode_permission(&init_user_ns,
|
||||
ecryptfs_inode_to_lower(inode), mask);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1404,7 +1404,7 @@ EXPORT_SYMBOL(begin_new_exec);
|
|||
void would_dump(struct linux_binprm *bprm, struct file *file)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
if (inode_permission(inode, MAY_READ) < 0) {
|
||||
if (inode_permission(&init_user_ns, inode, MAY_READ) < 0) {
|
||||
struct user_namespace *old, *user_ns;
|
||||
bprm->interp_flags |= BINPRM_FLAGS_ENFORCE_NONDUMP;
|
||||
|
||||
|
|
|
@ -1280,7 +1280,7 @@ static int fuse_permission(struct inode *inode, int mask)
|
|||
}
|
||||
|
||||
if (fc->default_permissions) {
|
||||
err = generic_permission(inode, mask);
|
||||
err = generic_permission(&init_user_ns, inode, mask);
|
||||
|
||||
/* If permission is denied, try to refresh file
|
||||
attributes. This is also needed, because the root
|
||||
|
@ -1288,7 +1288,8 @@ static int fuse_permission(struct inode *inode, int mask)
|
|||
if (err == -EACCES && !refreshed) {
|
||||
err = fuse_perm_getattr(inode, mask);
|
||||
if (!err)
|
||||
err = generic_permission(inode, mask);
|
||||
err = generic_permission(&init_user_ns,
|
||||
inode, mask);
|
||||
}
|
||||
|
||||
/* Note: the opposite of the above test does not
|
||||
|
|
|
@ -1852,7 +1852,7 @@ int gfs2_permission(struct inode *inode, int mask)
|
|||
if ((mask & MAY_WRITE) && IS_IMMUTABLE(inode))
|
||||
error = -EPERM;
|
||||
else
|
||||
error = generic_permission(inode, mask);
|
||||
error = generic_permission(&init_user_ns, inode, mask);
|
||||
if (gfs2_holder_initialized(&i_gh))
|
||||
gfs2_glock_dq_uninit(&i_gh);
|
||||
|
||||
|
|
|
@ -779,7 +779,7 @@ static int hostfs_permission(struct inode *ino, int desired)
|
|||
err = access_file(name, r, w, x);
|
||||
__putname(name);
|
||||
if (!err)
|
||||
err = generic_permission(ino, desired);
|
||||
err = generic_permission(&init_user_ns, ino, desired);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
|
@ -285,7 +285,7 @@ int kernfs_iop_permission(struct inode *inode, int mask)
|
|||
kernfs_refresh_inode(kn, inode);
|
||||
mutex_unlock(&kernfs_mutex);
|
||||
|
||||
return generic_permission(inode, mask);
|
||||
return generic_permission(&init_user_ns, inode, mask);
|
||||
}
|
||||
|
||||
int kernfs_xattr_get(struct kernfs_node *kn, const char *name,
|
||||
|
|
|
@ -1318,9 +1318,14 @@ static ssize_t empty_dir_listxattr(struct dentry *dentry, char *list, size_t siz
|
|||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static int empty_dir_permission(struct inode *inode, int mask)
|
||||
{
|
||||
return generic_permission(&init_user_ns, inode, mask);
|
||||
}
|
||||
|
||||
static const struct inode_operations empty_dir_inode_operations = {
|
||||
.lookup = empty_dir_lookup,
|
||||
.permission = generic_permission,
|
||||
.permission = empty_dir_permission,
|
||||
.setattr = empty_dir_setattr,
|
||||
.getattr = empty_dir_getattr,
|
||||
.listxattr = empty_dir_listxattr,
|
||||
|
|
121
fs/namei.c
121
fs/namei.c
|
@ -259,7 +259,24 @@ void putname(struct filename *name)
|
|||
__putname(name);
|
||||
}
|
||||
|
||||
static int check_acl(struct inode *inode, int mask)
|
||||
/**
|
||||
* check_acl - perform ACL permission checking
|
||||
* @mnt_userns: user namespace of the mount the inode was found from
|
||||
* @inode: inode to check permissions on
|
||||
* @mask: right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC ...)
|
||||
*
|
||||
* This function performs the ACL permission checking. Since this function
|
||||
* retrieve POSIX acls it needs to know whether it is called from a blocking or
|
||||
* non-blocking context and thus cares about the MAY_NOT_BLOCK bit.
|
||||
*
|
||||
* If the inode has been found through an idmapped mount the user namespace of
|
||||
* the vfsmount must be passed through @mnt_userns. This function will then take
|
||||
* care to map the inode according to @mnt_userns before checking permissions.
|
||||
* On non-idmapped mounts or if permission checking is to be performed on the
|
||||
* raw inode simply passs init_user_ns.
|
||||
*/
|
||||
static int check_acl(struct user_namespace *mnt_userns,
|
||||
struct inode *inode, int mask)
|
||||
{
|
||||
#ifdef CONFIG_FS_POSIX_ACL
|
||||
struct posix_acl *acl;
|
||||
|
@ -271,14 +288,14 @@ static int check_acl(struct inode *inode, int mask)
|
|||
/* no ->get_acl() calls in RCU mode... */
|
||||
if (is_uncached_acl(acl))
|
||||
return -ECHILD;
|
||||
return posix_acl_permission(inode, acl, mask);
|
||||
return posix_acl_permission(mnt_userns, inode, acl, mask);
|
||||
}
|
||||
|
||||
acl = get_acl(inode, ACL_TYPE_ACCESS);
|
||||
if (IS_ERR(acl))
|
||||
return PTR_ERR(acl);
|
||||
if (acl) {
|
||||
int error = posix_acl_permission(inode, acl, mask);
|
||||
int error = posix_acl_permission(mnt_userns, inode, acl, mask);
|
||||
posix_acl_release(acl);
|
||||
return error;
|
||||
}
|
||||
|
@ -287,18 +304,31 @@ static int check_acl(struct inode *inode, int mask)
|
|||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/*
|
||||
* This does the basic UNIX permission checking.
|
||||
/**
|
||||
* acl_permission_check - perform basic UNIX permission checking
|
||||
* @mnt_userns: user namespace of the mount the inode was found from
|
||||
* @inode: inode to check permissions on
|
||||
* @mask: right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC ...)
|
||||
*
|
||||
* Note that the POSIX ACL check cares about the MAY_NOT_BLOCK bit,
|
||||
* for RCU walking.
|
||||
* This function performs the basic UNIX permission checking. Since this
|
||||
* function may retrieve POSIX acls it needs to know whether it is called from a
|
||||
* blocking or non-blocking context and thus cares about the MAY_NOT_BLOCK bit.
|
||||
*
|
||||
* If the inode has been found through an idmapped mount the user namespace of
|
||||
* the vfsmount must be passed through @mnt_userns. This function will then take
|
||||
* care to map the inode according to @mnt_userns before checking permissions.
|
||||
* On non-idmapped mounts or if permission checking is to be performed on the
|
||||
* raw inode simply passs init_user_ns.
|
||||
*/
|
||||
static int acl_permission_check(struct inode *inode, int mask)
|
||||
static int acl_permission_check(struct user_namespace *mnt_userns,
|
||||
struct inode *inode, int mask)
|
||||
{
|
||||
unsigned int mode = inode->i_mode;
|
||||
kuid_t i_uid;
|
||||
|
||||
/* Are we the owner? If so, ACL's don't matter */
|
||||
if (likely(uid_eq(current_fsuid(), inode->i_uid))) {
|
||||
i_uid = i_uid_into_mnt(mnt_userns, inode);
|
||||
if (likely(uid_eq(current_fsuid(), i_uid))) {
|
||||
mask &= 7;
|
||||
mode >>= 6;
|
||||
return (mask & ~mode) ? -EACCES : 0;
|
||||
|
@ -306,7 +336,7 @@ static int acl_permission_check(struct inode *inode, int mask)
|
|||
|
||||
/* Do we have ACL's? */
|
||||
if (IS_POSIXACL(inode) && (mode & S_IRWXG)) {
|
||||
int error = check_acl(inode, mask);
|
||||
int error = check_acl(mnt_userns, inode, mask);
|
||||
if (error != -EAGAIN)
|
||||
return error;
|
||||
}
|
||||
|
@ -320,7 +350,8 @@ static int acl_permission_check(struct inode *inode, int mask)
|
|||
* about? Need to check group ownership if so.
|
||||
*/
|
||||
if (mask & (mode ^ (mode >> 3))) {
|
||||
if (in_group_p(inode->i_gid))
|
||||
kgid_t kgid = i_gid_into_mnt(mnt_userns, inode);
|
||||
if (in_group_p(kgid))
|
||||
mode >>= 3;
|
||||
}
|
||||
|
||||
|
@ -330,6 +361,7 @@ static int acl_permission_check(struct inode *inode, int mask)
|
|||
|
||||
/**
|
||||
* generic_permission - check for access rights on a Posix-like filesystem
|
||||
* @mnt_userns: user namespace of the mount the inode was found from
|
||||
* @inode: inode to check access rights for
|
||||
* @mask: right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC,
|
||||
* %MAY_NOT_BLOCK ...)
|
||||
|
@ -342,25 +374,32 @@ static int acl_permission_check(struct inode *inode, int mask)
|
|||
* generic_permission is rcu-walk aware. It returns -ECHILD in case an rcu-walk
|
||||
* request cannot be satisfied (eg. requires blocking or too much complexity).
|
||||
* It would then be called again in ref-walk mode.
|
||||
*
|
||||
* If the inode has been found through an idmapped mount the user namespace of
|
||||
* the vfsmount must be passed through @mnt_userns. This function will then take
|
||||
* care to map the inode according to @mnt_userns before checking permissions.
|
||||
* On non-idmapped mounts or if permission checking is to be performed on the
|
||||
* raw inode simply passs init_user_ns.
|
||||
*/
|
||||
int generic_permission(struct inode *inode, int mask)
|
||||
int generic_permission(struct user_namespace *mnt_userns, struct inode *inode,
|
||||
int mask)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Do the basic permission checks.
|
||||
*/
|
||||
ret = acl_permission_check(inode, mask);
|
||||
ret = acl_permission_check(mnt_userns, inode, mask);
|
||||
if (ret != -EACCES)
|
||||
return ret;
|
||||
|
||||
if (S_ISDIR(inode->i_mode)) {
|
||||
/* DACs are overridable for directories */
|
||||
if (!(mask & MAY_WRITE))
|
||||
if (capable_wrt_inode_uidgid(&init_user_ns, inode,
|
||||
if (capable_wrt_inode_uidgid(mnt_userns, inode,
|
||||
CAP_DAC_READ_SEARCH))
|
||||
return 0;
|
||||
if (capable_wrt_inode_uidgid(&init_user_ns, inode,
|
||||
if (capable_wrt_inode_uidgid(mnt_userns, inode,
|
||||
CAP_DAC_OVERRIDE))
|
||||
return 0;
|
||||
return -EACCES;
|
||||
|
@ -371,7 +410,7 @@ int generic_permission(struct inode *inode, int mask)
|
|||
*/
|
||||
mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
|
||||
if (mask == MAY_READ)
|
||||
if (capable_wrt_inode_uidgid(&init_user_ns, inode,
|
||||
if (capable_wrt_inode_uidgid(mnt_userns, inode,
|
||||
CAP_DAC_READ_SEARCH))
|
||||
return 0;
|
||||
/*
|
||||
|
@ -380,7 +419,7 @@ int generic_permission(struct inode *inode, int mask)
|
|||
* at least one exec bit set.
|
||||
*/
|
||||
if (!(mask & MAY_EXEC) || (inode->i_mode & S_IXUGO))
|
||||
if (capable_wrt_inode_uidgid(&init_user_ns, inode,
|
||||
if (capable_wrt_inode_uidgid(mnt_userns, inode,
|
||||
CAP_DAC_OVERRIDE))
|
||||
return 0;
|
||||
|
||||
|
@ -388,13 +427,19 @@ int generic_permission(struct inode *inode, int mask)
|
|||
}
|
||||
EXPORT_SYMBOL(generic_permission);
|
||||
|
||||
/*
|
||||
/**
|
||||
* do_inode_permission - UNIX permission checking
|
||||
* @mnt_userns: user namespace of the mount the inode was found from
|
||||
* @inode: inode to check permissions on
|
||||
* @mask: right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC ...)
|
||||
*
|
||||
* We _really_ want to just do "generic_permission()" without
|
||||
* even looking at the inode->i_op values. So we keep a cache
|
||||
* flag in inode->i_opflags, that says "this has not special
|
||||
* permission function, use the fast case".
|
||||
*/
|
||||
static inline int do_inode_permission(struct inode *inode, int mask)
|
||||
static inline int do_inode_permission(struct user_namespace *mnt_userns,
|
||||
struct inode *inode, int mask)
|
||||
{
|
||||
if (unlikely(!(inode->i_opflags & IOP_FASTPERM))) {
|
||||
if (likely(inode->i_op->permission))
|
||||
|
@ -405,7 +450,7 @@ static inline int do_inode_permission(struct inode *inode, int mask)
|
|||
inode->i_opflags |= IOP_FASTPERM;
|
||||
spin_unlock(&inode->i_lock);
|
||||
}
|
||||
return generic_permission(inode, mask);
|
||||
return generic_permission(mnt_userns, inode, mask);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -430,8 +475,9 @@ static int sb_permission(struct super_block *sb, struct inode *inode, int mask)
|
|||
|
||||
/**
|
||||
* inode_permission - Check for access rights to a given inode
|
||||
* @inode: Inode to check permission on
|
||||
* @mask: Right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
|
||||
* @mnt_userns: User namespace of the mount the inode was found from
|
||||
* @inode: Inode to check permission on
|
||||
* @mask: Right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
|
||||
*
|
||||
* Check for read/write/execute permissions on an inode. We use fs[ug]id for
|
||||
* this, letting us set arbitrary permissions for filesystem access without
|
||||
|
@ -439,7 +485,8 @@ static int sb_permission(struct super_block *sb, struct inode *inode, int mask)
|
|||
*
|
||||
* When checking for MAY_APPEND, MAY_WRITE must also be set in @mask.
|
||||
*/
|
||||
int inode_permission(struct inode *inode, int mask)
|
||||
int inode_permission(struct user_namespace *mnt_userns,
|
||||
struct inode *inode, int mask)
|
||||
{
|
||||
int retval;
|
||||
|
||||
|
@ -463,7 +510,7 @@ int inode_permission(struct inode *inode, int mask)
|
|||
return -EACCES;
|
||||
}
|
||||
|
||||
retval = do_inode_permission(inode, mask);
|
||||
retval = do_inode_permission(mnt_userns, inode, mask);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
|
@ -1009,7 +1056,7 @@ static bool safe_hardlink_source(struct inode *inode)
|
|||
return false;
|
||||
|
||||
/* Hardlinking to unreadable or unwritable sources is dangerous. */
|
||||
if (inode_permission(inode, MAY_READ | MAY_WRITE))
|
||||
if (inode_permission(&init_user_ns, inode, MAY_READ | MAY_WRITE))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
@ -1569,13 +1616,14 @@ static struct dentry *lookup_slow(const struct qstr *name,
|
|||
static inline int may_lookup(struct nameidata *nd)
|
||||
{
|
||||
if (nd->flags & LOOKUP_RCU) {
|
||||
int err = inode_permission(nd->inode, MAY_EXEC|MAY_NOT_BLOCK);
|
||||
int err = inode_permission(&init_user_ns, nd->inode,
|
||||
MAY_EXEC | MAY_NOT_BLOCK);
|
||||
if (err != -ECHILD)
|
||||
return err;
|
||||
if (unlazy_walk(nd))
|
||||
return -ECHILD;
|
||||
}
|
||||
return inode_permission(nd->inode, MAY_EXEC);
|
||||
return inode_permission(&init_user_ns, nd->inode, MAY_EXEC);
|
||||
}
|
||||
|
||||
static int reserve_stack(struct nameidata *nd, struct path *link, unsigned seq)
|
||||
|
@ -2509,7 +2557,7 @@ static int lookup_one_len_common(const char *name, struct dentry *base,
|
|||
return err;
|
||||
}
|
||||
|
||||
return inode_permission(base->d_inode, MAY_EXEC);
|
||||
return inode_permission(&init_user_ns, base->d_inode, MAY_EXEC);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2703,7 +2751,7 @@ static int may_delete(struct inode *dir, struct dentry *victim, bool isdir)
|
|||
|
||||
audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE);
|
||||
|
||||
error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
|
||||
error = inode_permission(&init_user_ns, dir, MAY_WRITE | MAY_EXEC);
|
||||
if (error)
|
||||
return error;
|
||||
if (IS_APPEND(dir))
|
||||
|
@ -2747,7 +2795,7 @@ static inline int may_create(struct inode *dir, struct dentry *child)
|
|||
if (!kuid_has_mapping(s_user_ns, current_fsuid()) ||
|
||||
!kgid_has_mapping(s_user_ns, current_fsgid()))
|
||||
return -EOVERFLOW;
|
||||
return inode_permission(dir, MAY_WRITE | MAY_EXEC);
|
||||
return inode_permission(&init_user_ns, dir, MAY_WRITE | MAY_EXEC);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2877,7 +2925,7 @@ static int may_open(const struct path *path, int acc_mode, int flag)
|
|||
break;
|
||||
}
|
||||
|
||||
error = inode_permission(inode, MAY_OPEN | acc_mode);
|
||||
error = inode_permission(&init_user_ns, inode, MAY_OPEN | acc_mode);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
|
@ -2939,7 +2987,8 @@ static int may_o_create(const struct path *dir, struct dentry *dentry, umode_t m
|
|||
!kgid_has_mapping(s_user_ns, current_fsgid()))
|
||||
return -EOVERFLOW;
|
||||
|
||||
error = inode_permission(dir->dentry->d_inode, MAY_WRITE | MAY_EXEC);
|
||||
error = inode_permission(&init_user_ns, dir->dentry->d_inode,
|
||||
MAY_WRITE | MAY_EXEC);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
|
@ -3276,7 +3325,7 @@ struct dentry *vfs_tmpfile(struct dentry *dentry, umode_t mode, int open_flag)
|
|||
int error;
|
||||
|
||||
/* we want directory to be writable */
|
||||
error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
|
||||
error = inode_permission(&init_user_ns, dir, MAY_WRITE | MAY_EXEC);
|
||||
if (error)
|
||||
goto out_err;
|
||||
error = -EOPNOTSUPP;
|
||||
|
@ -4267,12 +4316,14 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||
*/
|
||||
if (new_dir != old_dir) {
|
||||
if (is_dir) {
|
||||
error = inode_permission(source, MAY_WRITE);
|
||||
error = inode_permission(&init_user_ns, source,
|
||||
MAY_WRITE);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
if ((flags & RENAME_EXCHANGE) && new_is_dir) {
|
||||
error = inode_permission(target, MAY_WRITE);
|
||||
error = inode_permission(&init_user_ns, target,
|
||||
MAY_WRITE);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
|
|
@ -2987,7 +2987,7 @@ out_notsup:
|
|||
|
||||
res = nfs_revalidate_inode(NFS_SERVER(inode), inode);
|
||||
if (res == 0)
|
||||
res = generic_permission(inode, mask);
|
||||
res = generic_permission(&init_user_ns, inode, mask);
|
||||
goto out;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_permission);
|
||||
|
|
|
@ -40,7 +40,8 @@ static int nfsd_acceptable(void *expv, struct dentry *dentry)
|
|||
/* make sure parents give x permission to user */
|
||||
int err;
|
||||
parent = dget_parent(tdentry);
|
||||
err = inode_permission(d_inode(parent), MAY_EXEC);
|
||||
err = inode_permission(&init_user_ns,
|
||||
d_inode(parent), MAY_EXEC);
|
||||
if (err < 0) {
|
||||
dput(parent);
|
||||
break;
|
||||
|
|
|
@ -2391,13 +2391,14 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp,
|
|||
return 0;
|
||||
|
||||
/* This assumes NFSD_MAY_{READ,WRITE,EXEC} == MAY_{READ,WRITE,EXEC} */
|
||||
err = inode_permission(inode, acc & (MAY_READ|MAY_WRITE|MAY_EXEC));
|
||||
err = inode_permission(&init_user_ns, inode,
|
||||
acc & (MAY_READ | MAY_WRITE | MAY_EXEC));
|
||||
|
||||
/* Allow read access to binaries even when mode 111 */
|
||||
if (err == -EACCES && S_ISREG(inode->i_mode) &&
|
||||
(acc == (NFSD_MAY_READ | NFSD_MAY_OWNER_OVERRIDE) ||
|
||||
acc == (NFSD_MAY_READ | NFSD_MAY_READ_IF_EXEC)))
|
||||
err = inode_permission(inode, MAY_EXEC);
|
||||
err = inode_permission(&init_user_ns, inode, MAY_EXEC);
|
||||
|
||||
return err? nfserrno(err) : 0;
|
||||
}
|
||||
|
|
|
@ -851,7 +851,7 @@ int nilfs_permission(struct inode *inode, int mask)
|
|||
root->cno != NILFS_CPTREE_CURRENT_CNO)
|
||||
return -EROFS; /* snapshot is not writable */
|
||||
|
||||
return generic_permission(inode, mask);
|
||||
return generic_permission(&init_user_ns, inode, mask);
|
||||
}
|
||||
|
||||
int nilfs_load_inode_block(struct inode *inode, struct buffer_head **pbh)
|
||||
|
|
|
@ -1355,7 +1355,7 @@ int ocfs2_permission(struct inode *inode, int mask)
|
|||
dump_stack();
|
||||
}
|
||||
|
||||
ret = generic_permission(inode, mask);
|
||||
ret = generic_permission(&init_user_ns, inode, mask);
|
||||
|
||||
ocfs2_inode_unlock_tracker(inode, 0, &oh, had_lock);
|
||||
out:
|
||||
|
|
|
@ -4346,7 +4346,7 @@ static inline int ocfs2_may_create(struct inode *dir, struct dentry *child)
|
|||
return -EEXIST;
|
||||
if (IS_DEADDIR(dir))
|
||||
return -ENOENT;
|
||||
return inode_permission(dir, MAY_WRITE | MAY_EXEC);
|
||||
return inode_permission(&init_user_ns, dir, MAY_WRITE | MAY_EXEC);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -4400,7 +4400,7 @@ static int ocfs2_vfs_reflink(struct dentry *old_dentry, struct inode *dir,
|
|||
* file.
|
||||
*/
|
||||
if (!preserve) {
|
||||
error = inode_permission(inode, MAY_READ);
|
||||
error = inode_permission(&init_user_ns, inode, MAY_READ);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ long vfs_truncate(const struct path *path, loff_t length)
|
|||
if (error)
|
||||
goto out;
|
||||
|
||||
error = inode_permission(inode, MAY_WRITE);
|
||||
error = inode_permission(&init_user_ns, inode, MAY_WRITE);
|
||||
if (error)
|
||||
goto mnt_drop_write_and_out;
|
||||
|
||||
|
@ -436,7 +436,7 @@ retry:
|
|||
goto out_path_release;
|
||||
}
|
||||
|
||||
res = inode_permission(inode, mode | MAY_ACCESS);
|
||||
res = inode_permission(&init_user_ns, inode, mode | MAY_ACCESS);
|
||||
/* SuS v2 requires we report a read only fs too */
|
||||
if (res || !(mode & S_IWOTH) || special_file(inode->i_mode))
|
||||
goto out_path_release;
|
||||
|
|
|
@ -933,7 +933,7 @@ int orangefs_permission(struct inode *inode, int mask)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return generic_permission(inode, mask);
|
||||
return generic_permission(&init_user_ns, inode, mask);
|
||||
}
|
||||
|
||||
int orangefs_update_time(struct inode *inode, struct timespec64 *time, int flags)
|
||||
|
|
|
@ -50,7 +50,7 @@ static struct file *ovl_open_realfile(const struct file *file,
|
|||
acc_mode |= MAY_APPEND;
|
||||
|
||||
old_cred = ovl_override_creds(inode->i_sb);
|
||||
err = inode_permission(realinode, MAY_OPEN | acc_mode);
|
||||
err = inode_permission(&init_user_ns, realinode, MAY_OPEN | acc_mode);
|
||||
if (err) {
|
||||
realfile = ERR_PTR(err);
|
||||
} else {
|
||||
|
|
|
@ -294,7 +294,7 @@ int ovl_permission(struct inode *inode, int mask)
|
|||
* Check overlay inode with the creds of task and underlying inode
|
||||
* with creds of mounter
|
||||
*/
|
||||
err = generic_permission(inode, mask);
|
||||
err = generic_permission(&init_user_ns, inode, mask);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
@ -305,7 +305,7 @@ int ovl_permission(struct inode *inode, int mask)
|
|||
/* Make sure mounter can read file for copy up later */
|
||||
mask |= MAY_READ;
|
||||
}
|
||||
err = inode_permission(realinode, mask);
|
||||
err = inode_permission(&init_user_ns, realinode, mask);
|
||||
revert_creds(old_cred);
|
||||
|
||||
return err;
|
||||
|
|
|
@ -479,7 +479,7 @@ struct file *ovl_path_open(struct path *path, int flags)
|
|||
BUG();
|
||||
}
|
||||
|
||||
err = inode_permission(inode, acc_mode | MAY_OPEN);
|
||||
err = inode_permission(&init_user_ns, inode, acc_mode | MAY_OPEN);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
|
|
|
@ -345,10 +345,13 @@ EXPORT_SYMBOL(posix_acl_from_mode);
|
|||
* by the acl. Returns -E... otherwise.
|
||||
*/
|
||||
int
|
||||
posix_acl_permission(struct inode *inode, const struct posix_acl *acl, int want)
|
||||
posix_acl_permission(struct user_namespace *mnt_userns, struct inode *inode,
|
||||
const struct posix_acl *acl, int want)
|
||||
{
|
||||
const struct posix_acl_entry *pa, *pe, *mask_obj;
|
||||
int found = 0;
|
||||
kuid_t uid;
|
||||
kgid_t gid;
|
||||
|
||||
want &= MAY_READ | MAY_WRITE | MAY_EXEC;
|
||||
|
||||
|
@ -356,22 +359,26 @@ posix_acl_permission(struct inode *inode, const struct posix_acl *acl, int want)
|
|||
switch(pa->e_tag) {
|
||||
case ACL_USER_OBJ:
|
||||
/* (May have been checked already) */
|
||||
if (uid_eq(inode->i_uid, current_fsuid()))
|
||||
uid = i_uid_into_mnt(mnt_userns, inode);
|
||||
if (uid_eq(uid, current_fsuid()))
|
||||
goto check_perm;
|
||||
break;
|
||||
case ACL_USER:
|
||||
if (uid_eq(pa->e_uid, current_fsuid()))
|
||||
uid = kuid_into_mnt(mnt_userns, pa->e_uid);
|
||||
if (uid_eq(uid, current_fsuid()))
|
||||
goto mask;
|
||||
break;
|
||||
case ACL_GROUP_OBJ:
|
||||
if (in_group_p(inode->i_gid)) {
|
||||
gid = i_gid_into_mnt(mnt_userns, inode);
|
||||
if (in_group_p(gid)) {
|
||||
found = 1;
|
||||
if ((pa->e_perm & want) == want)
|
||||
goto mask;
|
||||
}
|
||||
break;
|
||||
case ACL_GROUP:
|
||||
if (in_group_p(pa->e_gid)) {
|
||||
gid = kgid_into_mnt(mnt_userns, pa->e_gid);
|
||||
if (in_group_p(gid)) {
|
||||
found = 1;
|
||||
if ((pa->e_perm & want) == want)
|
||||
goto mask;
|
||||
|
|
|
@ -751,7 +751,7 @@ static int proc_pid_permission(struct inode *inode, int mask)
|
|||
|
||||
return -EPERM;
|
||||
}
|
||||
return generic_permission(inode, mask);
|
||||
return generic_permission(&init_user_ns, inode, mask);
|
||||
}
|
||||
|
||||
|
||||
|
@ -3492,7 +3492,7 @@ static int proc_tid_comm_permission(struct inode *inode, int mask)
|
|||
return 0;
|
||||
}
|
||||
|
||||
return generic_permission(inode, mask);
|
||||
return generic_permission(&init_user_ns, inode, mask);
|
||||
}
|
||||
|
||||
static const struct inode_operations proc_tid_comm_inode_operations = {
|
||||
|
|
|
@ -281,7 +281,7 @@ int proc_fd_permission(struct inode *inode, int mask)
|
|||
struct task_struct *p;
|
||||
int rv;
|
||||
|
||||
rv = generic_permission(inode, mask);
|
||||
rv = generic_permission(&init_user_ns, inode, mask);
|
||||
if (rv == 0)
|
||||
return rv;
|
||||
|
||||
|
|
|
@ -957,7 +957,7 @@ int reiserfs_permission(struct inode *inode, int mask)
|
|||
if (IS_PRIVATE(inode))
|
||||
return 0;
|
||||
|
||||
return generic_permission(inode, mask);
|
||||
return generic_permission(&init_user_ns, inode, mask);
|
||||
}
|
||||
|
||||
static int xattr_hide_revalidate(struct dentry *dentry, unsigned int flags)
|
||||
|
|
|
@ -438,7 +438,7 @@ static bool allow_file_dedupe(struct file *file)
|
|||
return true;
|
||||
if (uid_eq(current_fsuid(), file_inode(file)->i_uid))
|
||||
return true;
|
||||
if (!inode_permission(file_inode(file), MAY_WRITE))
|
||||
if (!inode_permission(&init_user_ns, file_inode(file), MAY_WRITE))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -131,7 +131,7 @@ xattr_permission(struct inode *inode, const char *name, int mask)
|
|||
return -EPERM;
|
||||
}
|
||||
|
||||
return inode_permission(inode, mask);
|
||||
return inode_permission(&init_user_ns, inode, mask);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -2810,15 +2810,17 @@ static inline int bmap(struct inode *inode, sector_t *block)
|
|||
#endif
|
||||
|
||||
extern int notify_change(struct dentry *, struct iattr *, struct inode **);
|
||||
extern int inode_permission(struct inode *, int);
|
||||
extern int generic_permission(struct inode *, int);
|
||||
int inode_permission(struct user_namespace *, struct inode *, int);
|
||||
int generic_permission(struct user_namespace *, struct inode *, int);
|
||||
static inline int file_permission(struct file *file, int mask)
|
||||
{
|
||||
return inode_permission(file_inode(file), mask);
|
||||
return inode_permission(file_mnt_user_ns(file),
|
||||
file_inode(file), mask);
|
||||
}
|
||||
static inline int path_permission(const struct path *path, int mask)
|
||||
{
|
||||
return inode_permission(d_inode(path->dentry), mask);
|
||||
return inode_permission(mnt_user_ns(path->mnt),
|
||||
d_inode(path->dentry), mask);
|
||||
}
|
||||
extern int __check_sticky(struct inode *dir, struct inode *inode);
|
||||
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#include <linux/refcount.h>
|
||||
#include <uapi/linux/posix_acl.h>
|
||||
|
||||
struct user_namespace;
|
||||
|
||||
struct posix_acl_entry {
|
||||
short e_tag;
|
||||
unsigned short e_perm;
|
||||
|
@ -61,8 +63,6 @@ posix_acl_release(struct posix_acl *acl)
|
|||
|
||||
extern void posix_acl_init(struct posix_acl *, int);
|
||||
extern struct posix_acl *posix_acl_alloc(int, gfp_t);
|
||||
extern int posix_acl_valid(struct user_namespace *, const struct posix_acl *);
|
||||
extern int posix_acl_permission(struct inode *, const struct posix_acl *, int);
|
||||
extern struct posix_acl *posix_acl_from_mode(umode_t, gfp_t);
|
||||
extern int posix_acl_equiv_mode(const struct posix_acl *, umode_t *);
|
||||
extern int __posix_acl_create(struct posix_acl **, gfp_t, umode_t *);
|
||||
|
@ -85,6 +85,9 @@ struct posix_acl *get_cached_acl_rcu(struct inode *inode, int type);
|
|||
void set_cached_acl(struct inode *inode, int type, struct posix_acl *acl);
|
||||
void forget_cached_acl(struct inode *inode, int type);
|
||||
void forget_all_cached_acls(struct inode *inode);
|
||||
int posix_acl_valid(struct user_namespace *, const struct posix_acl *);
|
||||
int posix_acl_permission(struct user_namespace *, struct inode *,
|
||||
const struct posix_acl *, int);
|
||||
|
||||
static inline void cache_no_acl(struct inode *inode)
|
||||
{
|
||||
|
|
|
@ -873,7 +873,7 @@ static int prepare_open(struct dentry *dentry, int oflag, int ro,
|
|||
if ((oflag & O_ACCMODE) == (O_RDWR | O_WRONLY))
|
||||
return -EINVAL;
|
||||
acc = oflag2acc[oflag & O_ACCMODE];
|
||||
return inode_permission(d_inode(dentry), acc);
|
||||
return inode_permission(&init_user_ns, d_inode(dentry), acc);
|
||||
}
|
||||
|
||||
static int do_mq_open(const char __user *u_name, int oflag, umode_t mode,
|
||||
|
|
|
@ -558,7 +558,7 @@ int bpf_obj_get_user(const char __user *pathname, int flags)
|
|||
static struct bpf_prog *__get_prog_inode(struct inode *inode, enum bpf_prog_type type)
|
||||
{
|
||||
struct bpf_prog *prog;
|
||||
int ret = inode_permission(inode, MAY_READ);
|
||||
int ret = inode_permission(&init_user_ns, inode, MAY_READ);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
|
|
|
@ -4670,7 +4670,7 @@ static int cgroup_may_write(const struct cgroup *cgrp, struct super_block *sb)
|
|||
if (!inode)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = inode_permission(inode, MAY_WRITE);
|
||||
ret = inode_permission(&init_user_ns, inode, MAY_WRITE);
|
||||
iput(inode);
|
||||
return ret;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче