Commit ba73d987 authored by Christian Brauner's avatar Christian Brauner

namei: handle idmapped mounts in may_*() helpers

The may_follow_link(), may_linkat(), may_lookup(), may_open(),
may_o_create(), may_create_in_sticky(), may_delete(), and may_create()
helpers determine whether the caller is privileged enough to perform the
associated operations. Let them handle idmapped mounts by mapping the
inode or fsids according to the mount's user namespace. Afterwards the
checks are identical to non-idmapped inodes. The patch takes care to
retrieve the mount's user namespace right before performing permission
checks and passing it down into the fileystem so the user namespace
can't change in between by someone idmapping a mount that is currently
not idmapped. 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-13-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: default avatarChristoph Hellwig <hch@lst.de>
Reviewed-by: default avatarJames Morris <jamorris@linux.microsoft.com>
Signed-off-by: default avatarChristian Brauner <christian.brauner@ubuntu.com>
parent 0d56a451
......@@ -927,8 +927,9 @@ static int btrfs_may_delete(struct inode *dir, struct dentry *victim, int isdir)
return error;
if (IS_APPEND(dir))
return -EPERM;
if (check_sticky(dir, d_inode(victim)) || IS_APPEND(d_inode(victim)) ||
IS_IMMUTABLE(d_inode(victim)) || IS_SWAPFILE(d_inode(victim)))
if (check_sticky(&init_user_ns, dir, d_inode(victim)) ||
IS_APPEND(d_inode(victim)) || IS_IMMUTABLE(d_inode(victim)) ||
IS_SWAPFILE(d_inode(victim)))
return -EPERM;
if (isdir) {
if (!d_is_dir(victim))
......
......@@ -181,7 +181,7 @@ int __init init_link(const char *oldname, const char *newname)
error = -EXDEV;
if (old_path.mnt != new_path.mnt)
goto out_dput;
error = may_linkat(&old_path);
error = may_linkat(&init_user_ns, &old_path);
if (unlikely(error))
goto out_dput;
error = security_path_link(old_path.dentry, &new_path, new_dentry);
......
......@@ -1796,7 +1796,7 @@ bool atime_needs_update(const struct path *path, struct inode *inode)
/* Atime updates will likely cause i_uid and i_gid to be written
* back improprely if their true value is unknown to the vfs.
*/
if (HAS_UNMAPPED_ID(inode))
if (HAS_UNMAPPED_ID(mnt_user_ns(mnt), inode))
return false;
if (IS_NOATIME(inode))
......
......@@ -73,7 +73,7 @@ extern int vfs_path_lookup(struct dentry *, struct vfsmount *,
const char *, unsigned int, struct path *);
long do_rmdir(int dfd, struct filename *name);
long do_unlinkat(int dfd, struct filename *name);
int may_linkat(struct path *link);
int may_linkat(struct user_namespace *mnt_userns, struct path *link);
int do_renameat2(int olddfd, struct filename *oldname, int newdfd,
struct filename *newname, unsigned int flags);
......
This diff is collapsed.
......@@ -98,7 +98,7 @@ xattr_permission(struct user_namespace *mnt_userns, struct inode *inode,
* to be writen back improperly if their true value is
* unknown to the vfs.
*/
if (HAS_UNMAPPED_ID(inode))
if (HAS_UNMAPPED_ID(mnt_userns, inode))
return -EPERM;
}
......
......@@ -2083,9 +2083,11 @@ static inline bool sb_rdonly(const struct super_block *sb) { return sb->s_flags
#define IS_WHITEOUT(inode) (S_ISCHR(inode->i_mode) && \
(inode)->i_rdev == WHITEOUT_DEV)
static inline bool HAS_UNMAPPED_ID(struct inode *inode)
static inline bool HAS_UNMAPPED_ID(struct user_namespace *mnt_userns,
struct inode *inode)
{
return !uid_valid(inode->i_uid) || !gid_valid(inode->i_gid);
return !uid_valid(i_uid_into_mnt(mnt_userns, inode)) ||
!gid_valid(i_gid_into_mnt(mnt_userns, inode));
}
static inline enum rw_hint file_write_hint(struct file *file)
......@@ -2823,7 +2825,8 @@ static inline int path_permission(const struct path *path, int mask)
return inode_permission(mnt_user_ns(path->mnt),
d_inode(path->dentry), mask);
}
extern int __check_sticky(struct inode *dir, struct inode *inode);
int __check_sticky(struct user_namespace *mnt_userns, struct inode *dir,
struct inode *inode);
static inline bool execute_ok(struct inode *inode)
{
......@@ -3442,12 +3445,13 @@ static inline bool is_sxid(umode_t mode)
return (mode & S_ISUID) || ((mode & S_ISGID) && (mode & S_IXGRP));
}
static inline int check_sticky(struct inode *dir, struct inode *inode)
static inline int check_sticky(struct user_namespace *mnt_userns,
struct inode *dir, struct inode *inode)
{
if (!(dir->i_mode & S_ISVTX))
return 0;
return __check_sticky(dir, inode);
return __check_sticky(mnt_userns, dir, inode);
}
static inline void inode_has_no_xattr(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