Merge branch 'userns-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace

Pull userns updates from Eric Biederman:
 "This is the last couple of vfs bits to enable root in a user namespace
  to mount and manipulate a filesystem with backing store (AKA not a
  virtual filesystem like proc, but a filesystem where the unprivileged
  user controls the content). The target filesystem for this work is
  fuse, and Miklos should be sending you the pull request for the fuse
  bits this merge window.

  The two key patches are "evm: Don't update hmacs in user ns mounts"
  and "vfs: Don't allow changing the link count of an inode with an
  invalid uid or gid". Those close small gaps in the vfs that would be a
  problem if an unprivileged fuse filesystem is mounted.

  The rest of the changes are things that are now safe to allow a root
  user in a user namespace to do with a filesystem they have mounted.
  The most interesting development is that remount is now safe"

* 'userns-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace:
  fs: Allow CAP_SYS_ADMIN in s_user_ns to freeze and thaw filesystems
  capabilities: Allow privileged user in s_user_ns to set security.* xattrs
  fs: Allow superblock owner to access do_remount_sb()
  fs: Allow superblock owner to replace invalid owners of inodes
  vfs: Allow userns root to call mknod on owned filesystems.
  vfs: Don't allow changing the link count of an inode with an invalid uid or gid
  evm: Don't update hmacs in user ns mounts
This commit is contained in:
Linus Torvalds 2018-06-04 15:21:19 -07:00
commit d8aed8415b
6 changed files with 52 additions and 19 deletions

View File

@ -18,6 +18,32 @@
#include <linux/evm.h>
#include <linux/ima.h>
static bool chown_ok(const struct inode *inode, kuid_t uid)
{
if (uid_eq(current_fsuid(), inode->i_uid) &&
uid_eq(uid, inode->i_uid))
return true;
if (capable_wrt_inode_uidgid(inode, CAP_CHOWN))
return true;
if (uid_eq(inode->i_uid, INVALID_UID) &&
ns_capable(inode->i_sb->s_user_ns, CAP_CHOWN))
return true;
return false;
}
static bool chgrp_ok(const struct inode *inode, kgid_t gid)
{
if (uid_eq(current_fsuid(), inode->i_uid) &&
(in_group_p(gid) || gid_eq(gid, inode->i_gid)))
return true;
if (capable_wrt_inode_uidgid(inode, CAP_CHOWN))
return true;
if (gid_eq(inode->i_gid, INVALID_GID) &&
ns_capable(inode->i_sb->s_user_ns, CAP_CHOWN))
return true;
return false;
}
/**
* setattr_prepare - check if attribute changes to a dentry are allowed
* @dentry: dentry to check
@ -52,17 +78,11 @@ int setattr_prepare(struct dentry *dentry, struct iattr *attr)
goto kill_priv;
/* Make sure a caller can chown. */
if ((ia_valid & ATTR_UID) &&
(!uid_eq(current_fsuid(), inode->i_uid) ||
!uid_eq(attr->ia_uid, inode->i_uid)) &&
!capable_wrt_inode_uidgid(inode, CAP_CHOWN))
if ((ia_valid & ATTR_UID) && !chown_ok(inode, attr->ia_uid))
return -EPERM;
/* Make sure caller can chgrp. */
if ((ia_valid & ATTR_GID) &&
(!uid_eq(current_fsuid(), inode->i_uid) ||
(!in_group_p(attr->ia_gid) && !gid_eq(attr->ia_gid, inode->i_gid))) &&
!capable_wrt_inode_uidgid(inode, CAP_CHOWN))
if ((ia_valid & ATTR_GID) && !chgrp_ok(inode, attr->ia_gid))
return -EPERM;
/* Make sure a caller can chmod. */

View File

@ -549,7 +549,7 @@ static int ioctl_fsfreeze(struct file *filp)
{
struct super_block *sb = file_inode(filp)->i_sb;
if (!capable(CAP_SYS_ADMIN))
if (!ns_capable(sb->s_user_ns, CAP_SYS_ADMIN))
return -EPERM;
/* If filesystem doesn't support freeze feature, return. */
@ -566,7 +566,7 @@ static int ioctl_fsthaw(struct file *filp)
{
struct super_block *sb = file_inode(filp)->i_sb;
if (!capable(CAP_SYS_ADMIN))
if (!ns_capable(sb->s_user_ns, CAP_SYS_ADMIN))
return -EPERM;
/* Thaw */

View File

@ -984,13 +984,15 @@ static bool safe_hardlink_source(struct inode *inode)
*/
static int may_linkat(struct path *link)
{
struct inode *inode;
struct inode *inode = link->dentry->d_inode;
/* Inode writeback is not safe when the uid or gid are invalid. */
if (!uid_valid(inode->i_uid) || !gid_valid(inode->i_gid))
return -EOVERFLOW;
if (!sysctl_protected_hardlinks)
return 0;
inode = link->dentry->d_inode;
/* Source inode owner (or CAP_FOWNER) can hardlink all they like,
* otherwise, it must be a safe source.
*/
@ -2747,6 +2749,11 @@ static int may_delete(struct inode *dir, struct dentry *victim, bool isdir)
BUG_ON(!inode);
BUG_ON(victim->d_parent->d_inode != dir);
/* Inode writeback is not safe when the uid or gid are invalid. */
if (!uid_valid(inode->i_uid) || !gid_valid(inode->i_gid))
return -EOVERFLOW;
audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE);
error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
@ -3675,7 +3682,8 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
if (error)
return error;
if ((S_ISCHR(mode) || S_ISBLK(mode)) && !capable(CAP_MKNOD))
if ((S_ISCHR(mode) || S_ISBLK(mode)) &&
!ns_capable(dentry->d_sb->s_user_ns, CAP_MKNOD))
return -EPERM;
if (!dir->i_op->mknod)

View File

@ -1590,7 +1590,7 @@ static int do_umount(struct mount *mnt, int flags)
* Special case for "unmounting" root ...
* we just try to remount it readonly.
*/
if (!capable(CAP_SYS_ADMIN))
if (!ns_capable(sb->s_user_ns, CAP_SYS_ADMIN))
return -EPERM;
down_write(&sb->s_umount);
if (!sb_rdonly(sb))
@ -2333,7 +2333,7 @@ static int do_remount(struct path *path, int ms_flags, int sb_flags,
down_write(&sb->s_umount);
if (ms_flags & MS_BIND)
err = change_mount_flags(path->mnt, ms_flags);
else if (!capable(CAP_SYS_ADMIN))
else if (!ns_capable(sb->s_user_ns, CAP_SYS_ADMIN))
err = -EPERM;
else
err = do_remount_sb(sb, sb_flags, data, 0);

View File

@ -919,6 +919,8 @@ int cap_bprm_set_creds(struct linux_binprm *bprm)
int cap_inode_setxattr(struct dentry *dentry, const char *name,
const void *value, size_t size, int flags)
{
struct user_namespace *user_ns = dentry->d_sb->s_user_ns;
/* Ignore non-security xattrs */
if (strncmp(name, XATTR_SECURITY_PREFIX,
sizeof(XATTR_SECURITY_PREFIX) - 1) != 0)
@ -931,7 +933,7 @@ int cap_inode_setxattr(struct dentry *dentry, const char *name,
if (strcmp(name, XATTR_NAME_CAPS) == 0)
return 0;
if (!capable(CAP_SYS_ADMIN))
if (!ns_capable(user_ns, CAP_SYS_ADMIN))
return -EPERM;
return 0;
}
@ -949,6 +951,8 @@ int cap_inode_setxattr(struct dentry *dentry, const char *name,
*/
int cap_inode_removexattr(struct dentry *dentry, const char *name)
{
struct user_namespace *user_ns = dentry->d_sb->s_user_ns;
/* Ignore non-security xattrs */
if (strncmp(name, XATTR_SECURITY_PREFIX,
sizeof(XATTR_SECURITY_PREFIX) - 1) != 0)
@ -964,7 +968,7 @@ int cap_inode_removexattr(struct dentry *dentry, const char *name)
return 0;
}
if (!capable(CAP_SYS_ADMIN))
if (!ns_capable(user_ns, CAP_SYS_ADMIN))
return -EPERM;
return 0;
}

View File

@ -200,7 +200,8 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
int size;
bool ima_present = false;
if (!(inode->i_opflags & IOP_XATTR))
if (!(inode->i_opflags & IOP_XATTR) ||
inode->i_sb->s_user_ns != &init_user_ns)
return -EOPNOTSUPP;
desc = init_desc(type);