Commit 73b6fa8e authored by Linus Torvalds's avatar Linus Torvalds

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

Pull user namespace updates from Eric Biederman:
 "This finishes up the changes to ensure proc and sysfs do not start
  implementing executable files, as the there are application today that
  are only secure because such files do not exist.

  It akso fixes a long standing misfeature of /proc/<pid>/mountinfo that
  did not show the proper source for files bind mounted from
  /proc/<pid>/ns/*.

  It also straightens out the handling of clone flags related to user
  namespaces, fixing an unnecessary failure of unshare(CLONE_NEWUSER)
  when files such as /proc/<pid>/environ are read while <pid> is calling
  unshare.  This winds up fixing a minor bug in unshare flag handling
  that dates back to the first version of unshare in the kernel.

  Finally, this fixes a minor regression caused by the introduction of
  sysfs_create_mount_point, which broke someone's in house application,
  by restoring the size of /sys/fs/cgroup to 0 bytes.  Apparently that
  application uses the directory size to determine if a tmpfs is mounted
  on /sys/fs/cgroup.

  The bind mount escape fixes are present in Al Viros for-next branch.
  and I expect them to come from there.  The bind mount escape is the
  last of the user namespace related security bugs that I am aware of"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace:
  fs: Set the size of empty dirs to 0.
  userns,pidns: Force thread group sharing, not signal handler sharing.
  unshare: Unsharing a thread does not require unsharing a vm
  nsfs: Add a show_path method to fix mountinfo
  mnt: fs_fully_visible enforce noexec and nosuid  if !SB_I_NOEXEC
  vfs: Commit to never having exectuables on proc and sysfs.
parents e713c80a 4b75de86
...@@ -98,6 +98,12 @@ static inline void put_binfmt(struct linux_binfmt * fmt) ...@@ -98,6 +98,12 @@ static inline void put_binfmt(struct linux_binfmt * fmt)
module_put(fmt->module); module_put(fmt->module);
} }
bool path_noexec(const struct path *path)
{
return (path->mnt->mnt_flags & MNT_NOEXEC) ||
(path->mnt->mnt_sb->s_iflags & SB_I_NOEXEC);
}
#ifdef CONFIG_USELIB #ifdef CONFIG_USELIB
/* /*
* Note that a shared library must be both readable and executable due to * Note that a shared library must be both readable and executable due to
...@@ -132,7 +138,7 @@ SYSCALL_DEFINE1(uselib, const char __user *, library) ...@@ -132,7 +138,7 @@ SYSCALL_DEFINE1(uselib, const char __user *, library)
goto exit; goto exit;
error = -EACCES; error = -EACCES;
if (file->f_path.mnt->mnt_flags & MNT_NOEXEC) if (path_noexec(&file->f_path))
goto exit; goto exit;
fsnotify_open(file); fsnotify_open(file);
...@@ -777,7 +783,7 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags) ...@@ -777,7 +783,7 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags)
if (!S_ISREG(file_inode(file)->i_mode)) if (!S_ISREG(file_inode(file)->i_mode))
goto exit; goto exit;
if (file->f_path.mnt->mnt_flags & MNT_NOEXEC) if (path_noexec(&file->f_path))
goto exit; goto exit;
err = deny_write_access(file); err = deny_write_access(file);
......
...@@ -1185,7 +1185,7 @@ void make_empty_dir_inode(struct inode *inode) ...@@ -1185,7 +1185,7 @@ void make_empty_dir_inode(struct inode *inode)
inode->i_uid = GLOBAL_ROOT_UID; inode->i_uid = GLOBAL_ROOT_UID;
inode->i_gid = GLOBAL_ROOT_GID; inode->i_gid = GLOBAL_ROOT_GID;
inode->i_rdev = 0; inode->i_rdev = 0;
inode->i_size = 2; inode->i_size = 0;
inode->i_blkbits = PAGE_SHIFT; inode->i_blkbits = PAGE_SHIFT;
inode->i_blocks = 0; inode->i_blocks = 0;
......
...@@ -3218,6 +3218,8 @@ static bool fs_fully_visible(struct file_system_type *type, int *new_mnt_flags) ...@@ -3218,6 +3218,8 @@ static bool fs_fully_visible(struct file_system_type *type, int *new_mnt_flags)
down_read(&namespace_sem); down_read(&namespace_sem);
list_for_each_entry(mnt, &ns->list, mnt_list) { list_for_each_entry(mnt, &ns->list, mnt_list) {
struct mount *child; struct mount *child;
int mnt_flags;
if (mnt->mnt.mnt_sb->s_type != type) if (mnt->mnt.mnt_sb->s_type != type)
continue; continue;
...@@ -3227,17 +3229,30 @@ static bool fs_fully_visible(struct file_system_type *type, int *new_mnt_flags) ...@@ -3227,17 +3229,30 @@ static bool fs_fully_visible(struct file_system_type *type, int *new_mnt_flags)
if (mnt->mnt.mnt_root != mnt->mnt.mnt_sb->s_root) if (mnt->mnt.mnt_root != mnt->mnt.mnt_sb->s_root)
continue; continue;
/* Read the mount flags and filter out flags that
* may safely be ignored.
*/
mnt_flags = mnt->mnt.mnt_flags;
if (mnt->mnt.mnt_sb->s_iflags & SB_I_NOEXEC)
mnt_flags &= ~(MNT_LOCK_NOSUID | MNT_LOCK_NOEXEC);
/* Verify the mount flags are equal to or more permissive /* Verify the mount flags are equal to or more permissive
* than the proposed new mount. * than the proposed new mount.
*/ */
if ((mnt->mnt.mnt_flags & MNT_LOCK_READONLY) && if ((mnt_flags & MNT_LOCK_READONLY) &&
!(new_flags & MNT_READONLY)) !(new_flags & MNT_READONLY))
continue; continue;
if ((mnt->mnt.mnt_flags & MNT_LOCK_NODEV) && if ((mnt_flags & MNT_LOCK_NODEV) &&
!(new_flags & MNT_NODEV)) !(new_flags & MNT_NODEV))
continue; continue;
if ((mnt->mnt.mnt_flags & MNT_LOCK_ATIME) && if ((mnt_flags & MNT_LOCK_NOSUID) &&
((mnt->mnt.mnt_flags & MNT_ATIME_MASK) != (new_flags & MNT_ATIME_MASK))) !(new_flags & MNT_NOSUID))
continue;
if ((mnt_flags & MNT_LOCK_NOEXEC) &&
!(new_flags & MNT_NOEXEC))
continue;
if ((mnt_flags & MNT_LOCK_ATIME) &&
((mnt_flags & MNT_ATIME_MASK) != (new_flags & MNT_ATIME_MASK)))
continue; continue;
/* This mount is not fully visible if there are any /* This mount is not fully visible if there are any
...@@ -3247,15 +3262,17 @@ static bool fs_fully_visible(struct file_system_type *type, int *new_mnt_flags) ...@@ -3247,15 +3262,17 @@ static bool fs_fully_visible(struct file_system_type *type, int *new_mnt_flags)
list_for_each_entry(child, &mnt->mnt_mounts, mnt_child) { list_for_each_entry(child, &mnt->mnt_mounts, mnt_child) {
struct inode *inode = child->mnt_mountpoint->d_inode; struct inode *inode = child->mnt_mountpoint->d_inode;
/* Only worry about locked mounts */ /* Only worry about locked mounts */
if (!(mnt->mnt.mnt_flags & MNT_LOCKED)) if (!(mnt_flags & MNT_LOCKED))
continue; continue;
/* Is the directory permanetly empty? */ /* Is the directory permanetly empty? */
if (!is_empty_dir_inode(inode)) if (!is_empty_dir_inode(inode))
goto next; goto next;
} }
/* Preserve the locked attributes */ /* Preserve the locked attributes */
*new_mnt_flags |= mnt->mnt.mnt_flags & (MNT_LOCK_READONLY | \ *new_mnt_flags |= mnt_flags & (MNT_LOCK_READONLY | \
MNT_LOCK_NODEV | \ MNT_LOCK_NODEV | \
MNT_LOCK_NOSUID | \
MNT_LOCK_NOEXEC | \
MNT_LOCK_ATIME); MNT_LOCK_ATIME);
visible = true; visible = true;
goto found; goto found;
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include <linux/proc_ns.h> #include <linux/proc_ns.h>
#include <linux/magic.h> #include <linux/magic.h>
#include <linux/ktime.h> #include <linux/ktime.h>
#include <linux/seq_file.h>
static struct vfsmount *nsfs_mnt; static struct vfsmount *nsfs_mnt;
...@@ -136,9 +137,18 @@ struct file *proc_ns_fget(int fd) ...@@ -136,9 +137,18 @@ struct file *proc_ns_fget(int fd)
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
static int nsfs_show_path(struct seq_file *seq, struct dentry *dentry)
{
struct inode *inode = d_inode(dentry);
const struct proc_ns_operations *ns_ops = dentry->d_fsdata;
return seq_printf(seq, "%s:[%lu]", ns_ops->name, inode->i_ino);
}
static const struct super_operations nsfs_ops = { static const struct super_operations nsfs_ops = {
.statfs = simple_statfs, .statfs = simple_statfs,
.evict_inode = nsfs_evict, .evict_inode = nsfs_evict,
.show_path = nsfs_show_path,
}; };
static struct dentry *nsfs_mount(struct file_system_type *fs_type, static struct dentry *nsfs_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data) int flags, const char *dev_name, void *data)
......
...@@ -377,7 +377,7 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode) ...@@ -377,7 +377,7 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
* with the "noexec" flag. * with the "noexec" flag.
*/ */
res = -EACCES; res = -EACCES;
if (path.mnt->mnt_flags & MNT_NOEXEC) if (path_noexec(&path))
goto out_path_release; goto out_path_release;
} }
......
...@@ -134,6 +134,8 @@ static struct dentry *proc_mount(struct file_system_type *fs_type, ...@@ -134,6 +134,8 @@ static struct dentry *proc_mount(struct file_system_type *fs_type,
} }
sb->s_flags |= MS_ACTIVE; sb->s_flags |= MS_ACTIVE;
/* User space would break if executables appear on proc */
sb->s_iflags |= SB_I_NOEXEC;
} }
return dget(sb->s_root); return dget(sb->s_root);
......
...@@ -40,6 +40,10 @@ static struct dentry *sysfs_mount(struct file_system_type *fs_type, ...@@ -40,6 +40,10 @@ static struct dentry *sysfs_mount(struct file_system_type *fs_type,
SYSFS_MAGIC, &new_sb, ns); SYSFS_MAGIC, &new_sb, ns);
if (IS_ERR(root) || !new_sb) if (IS_ERR(root) || !new_sb)
kobj_ns_drop(KOBJ_NS_TYPE_NET, ns); kobj_ns_drop(KOBJ_NS_TYPE_NET, ns);
else if (new_sb)
/* Userspace would break if executables appear on sysfs */
root->d_sb->s_iflags |= SB_I_NOEXEC;
return root; return root;
} }
......
...@@ -1260,6 +1260,7 @@ struct mm_struct; ...@@ -1260,6 +1260,7 @@ struct mm_struct;
/* sb->s_iflags */ /* sb->s_iflags */
#define SB_I_CGROUPWB 0x00000001 /* cgroup-aware writeback enabled */ #define SB_I_CGROUPWB 0x00000001 /* cgroup-aware writeback enabled */
#define SB_I_NOEXEC 0x00000002 /* Ignore executables on this fs */
/* Possible states of 'frozen' field */ /* Possible states of 'frozen' field */
enum { enum {
...@@ -3041,4 +3042,6 @@ static inline bool dir_relax(struct inode *inode) ...@@ -3041,4 +3042,6 @@ static inline bool dir_relax(struct inode *inode)
return !IS_DEADDIR(inode); return !IS_DEADDIR(inode);
} }
extern bool path_noexec(const struct path *path);
#endif /* _LINUX_FS_H */ #endif /* _LINUX_FS_H */
...@@ -1280,10 +1280,9 @@ static struct task_struct *copy_process(unsigned long clone_flags, ...@@ -1280,10 +1280,9 @@ static struct task_struct *copy_process(unsigned long clone_flags,
/* /*
* If the new process will be in a different pid or user namespace * If the new process will be in a different pid or user namespace
* do not allow it to share a thread group or signal handlers or * do not allow it to share a thread group with the forking task.
* parent with the forking task.
*/ */
if (clone_flags & CLONE_SIGHAND) { if (clone_flags & CLONE_THREAD) {
if ((clone_flags & (CLONE_NEWUSER | CLONE_NEWPID)) || if ((clone_flags & (CLONE_NEWUSER | CLONE_NEWPID)) ||
(task_active_pid_ns(current) != (task_active_pid_ns(current) !=
current->nsproxy->pid_ns_for_children)) current->nsproxy->pid_ns_for_children))
...@@ -1872,13 +1871,21 @@ static int check_unshare_flags(unsigned long unshare_flags) ...@@ -1872,13 +1871,21 @@ static int check_unshare_flags(unsigned long unshare_flags)
CLONE_NEWUSER|CLONE_NEWPID)) CLONE_NEWUSER|CLONE_NEWPID))
return -EINVAL; return -EINVAL;
/* /*
* Not implemented, but pretend it works if there is nothing to * Not implemented, but pretend it works if there is nothing
* unshare. Note that unsharing CLONE_THREAD or CLONE_SIGHAND * to unshare. Note that unsharing the address space or the
* needs to unshare vm. * signal handlers also need to unshare the signal queues (aka
* CLONE_THREAD).
*/ */
if (unshare_flags & (CLONE_THREAD | CLONE_SIGHAND | CLONE_VM)) { if (unshare_flags & (CLONE_THREAD | CLONE_SIGHAND | CLONE_VM)) {
/* FIXME: get_task_mm() increments ->mm_users */ if (!thread_group_empty(current))
if (atomic_read(&current->mm->mm_users) > 1) return -EINVAL;
}
if (unshare_flags & (CLONE_SIGHAND | CLONE_VM)) {
if (atomic_read(&current->sighand->count) > 1)
return -EINVAL;
}
if (unshare_flags & CLONE_VM) {
if (!current_is_single_threaded())
return -EINVAL; return -EINVAL;
} }
...@@ -1942,20 +1949,21 @@ SYSCALL_DEFINE1(unshare, unsigned long, unshare_flags) ...@@ -1942,20 +1949,21 @@ SYSCALL_DEFINE1(unshare, unsigned long, unshare_flags)
int err; int err;
/* /*
* If unsharing a user namespace must also unshare the thread. * If unsharing a user namespace must also unshare the thread group
* and unshare the filesystem root and working directories.
*/ */
if (unshare_flags & CLONE_NEWUSER) if (unshare_flags & CLONE_NEWUSER)
unshare_flags |= CLONE_THREAD | CLONE_FS; unshare_flags |= CLONE_THREAD | CLONE_FS;
/*
* If unsharing a thread from a thread group, must also unshare vm.
*/
if (unshare_flags & CLONE_THREAD)
unshare_flags |= CLONE_VM;
/* /*
* If unsharing vm, must also unshare signal handlers. * If unsharing vm, must also unshare signal handlers.
*/ */
if (unshare_flags & CLONE_VM) if (unshare_flags & CLONE_VM)
unshare_flags |= CLONE_SIGHAND; unshare_flags |= CLONE_SIGHAND;
/*
* If unsharing a signal handlers, must also unshare the signal queues.
*/
if (unshare_flags & CLONE_SIGHAND)
unshare_flags |= CLONE_THREAD;
/* /*
* If unsharing namespace, must also unshare filesystem information. * If unsharing namespace, must also unshare filesystem information.
*/ */
......
...@@ -1668,8 +1668,7 @@ static int prctl_set_mm_exe_file(struct mm_struct *mm, unsigned int fd) ...@@ -1668,8 +1668,7 @@ static int prctl_set_mm_exe_file(struct mm_struct *mm, unsigned int fd)
* overall picture. * overall picture.
*/ */
err = -EACCES; err = -EACCES;
if (!S_ISREG(inode->i_mode) || if (!S_ISREG(inode->i_mode) || path_noexec(&exe.file->f_path))
exe.file->f_path.mnt->mnt_flags & MNT_NOEXEC)
goto exit; goto exit;
err = inode_permission(inode, MAY_EXEC); err = inode_permission(inode, MAY_EXEC);
......
...@@ -976,8 +976,8 @@ static int userns_install(struct nsproxy *nsproxy, struct ns_common *ns) ...@@ -976,8 +976,8 @@ static int userns_install(struct nsproxy *nsproxy, struct ns_common *ns)
if (user_ns == current_user_ns()) if (user_ns == current_user_ns())
return -EINVAL; return -EINVAL;
/* Threaded processes may not enter a different user namespace */ /* Tasks that share a thread group must share a user namespace */
if (atomic_read(&current->mm->mm_users) > 1) if (!thread_group_empty(current))
return -EINVAL; return -EINVAL;
if (current->fs->users != 1) if (current->fs->users != 1)
......
...@@ -1268,7 +1268,7 @@ unsigned long do_mmap_pgoff(struct file *file, unsigned long addr, ...@@ -1268,7 +1268,7 @@ unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
* mounted, in which case we dont add PROT_EXEC.) * mounted, in which case we dont add PROT_EXEC.)
*/ */
if ((prot & PROT_READ) && (current->personality & READ_IMPLIES_EXEC)) if ((prot & PROT_READ) && (current->personality & READ_IMPLIES_EXEC))
if (!(file && (file->f_path.mnt->mnt_flags & MNT_NOEXEC))) if (!(file && path_noexec(&file->f_path)))
prot |= PROT_EXEC; prot |= PROT_EXEC;
if (!(flags & MAP_FIXED)) if (!(flags & MAP_FIXED))
...@@ -1337,7 +1337,7 @@ unsigned long do_mmap_pgoff(struct file *file, unsigned long addr, ...@@ -1337,7 +1337,7 @@ unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
case MAP_PRIVATE: case MAP_PRIVATE:
if (!(file->f_mode & FMODE_READ)) if (!(file->f_mode & FMODE_READ))
return -EACCES; return -EACCES;
if (file->f_path.mnt->mnt_flags & MNT_NOEXEC) { if (path_noexec(&file->f_path)) {
if (vm_flags & VM_EXEC) if (vm_flags & VM_EXEC)
return -EPERM; return -EPERM;
vm_flags &= ~VM_MAYEXEC; vm_flags &= ~VM_MAYEXEC;
......
...@@ -1035,7 +1035,7 @@ static int validate_mmap_request(struct file *file, ...@@ -1035,7 +1035,7 @@ static int validate_mmap_request(struct file *file,
/* handle executable mappings and implied executable /* handle executable mappings and implied executable
* mappings */ * mappings */
if (file->f_path.mnt->mnt_flags & MNT_NOEXEC) { if (path_noexec(&file->f_path)) {
if (prot & PROT_EXEC) if (prot & PROT_EXEC)
return -EPERM; return -EPERM;
} else if ((prot & PROT_READ) && !(prot & PROT_EXEC)) { } else if ((prot & PROT_READ) && !(prot & PROT_EXEC)) {
......
...@@ -776,7 +776,7 @@ static inline unsigned long mmap_prot(struct file *file, unsigned long prot) ...@@ -776,7 +776,7 @@ static inline unsigned long mmap_prot(struct file *file, unsigned long prot)
* ditto if it's not on noexec mount, except that on !MMU we need * ditto if it's not on noexec mount, except that on !MMU we need
* NOMMU_MAP_EXEC (== VM_MAYEXEC) in this case * NOMMU_MAP_EXEC (== VM_MAYEXEC) in this case
*/ */
if (!(file->f_path.mnt->mnt_flags & MNT_NOEXEC)) { if (!path_noexec(&file->f_path)) {
#ifndef CONFIG_MMU #ifndef CONFIG_MMU
if (file->f_op->mmap_capabilities) { if (file->f_op->mmap_capabilities) {
unsigned caps = file->f_op->mmap_capabilities(file); unsigned caps = file->f_op->mmap_capabilities(file);
......
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