diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-03-28 16:43:46 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-03-28 16:43:46 -0400 |
commit | 2c3de1c2d7d68c6ba4c1ecd82c68285f34d9609e (patch) | |
tree | 6a09ce761173a966718f9009514dcc90bd9947b7 /fs | |
parent | 9064171268d838b8f283fe111ef086b9479d059a (diff) | |
parent | 87a8ebd637dafc255070f503909a053cf0d98d3f (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace
Pull userns fixes from Eric W Biederman:
"The bulk of the changes are fixing the worst consequences of the user
namespace design oversight in not considering what happens when one
namespace starts off as a clone of another namespace, as happens with
the mount namespace.
The rest of the changes are just plain bug fixes.
Many thanks to Andy Lutomirski for pointing out many of these issues."
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace:
userns: Restrict when proc and sysfs can be mounted
ipc: Restrict mounting the mqueue filesystem
vfs: Carefully propogate mounts across user namespaces
vfs: Add a mount flag to lock read only bind mounts
userns: Don't allow creation if the user is chrooted
yama: Better permission check for ptraceme
pid: Handle the exit of a multi-threaded init.
scm: Require CAP_SYS_ADMIN over the current pidns to spoof pids.
Diffstat (limited to 'fs')
-rw-r--r-- | fs/namespace.c | 54 | ||||
-rw-r--r-- | fs/pnode.c | 6 | ||||
-rw-r--r-- | fs/pnode.h | 1 | ||||
-rw-r--r-- | fs/proc/root.c | 4 | ||||
-rw-r--r-- | fs/sysfs/mount.c | 4 |
5 files changed, 68 insertions, 1 deletions
diff --git a/fs/namespace.c b/fs/namespace.c index 50ca17d3cb45..d581e45c0a9f 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
@@ -798,6 +798,10 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root, | |||
798 | } | 798 | } |
799 | 799 | ||
800 | mnt->mnt.mnt_flags = old->mnt.mnt_flags & ~MNT_WRITE_HOLD; | 800 | mnt->mnt.mnt_flags = old->mnt.mnt_flags & ~MNT_WRITE_HOLD; |
801 | /* Don't allow unprivileged users to change mount flags */ | ||
802 | if ((flag & CL_UNPRIVILEGED) && (mnt->mnt.mnt_flags & MNT_READONLY)) | ||
803 | mnt->mnt.mnt_flags |= MNT_LOCK_READONLY; | ||
804 | |||
801 | atomic_inc(&sb->s_active); | 805 | atomic_inc(&sb->s_active); |
802 | mnt->mnt.mnt_sb = sb; | 806 | mnt->mnt.mnt_sb = sb; |
803 | mnt->mnt.mnt_root = dget(root); | 807 | mnt->mnt.mnt_root = dget(root); |
@@ -1713,6 +1717,9 @@ static int change_mount_flags(struct vfsmount *mnt, int ms_flags) | |||
1713 | if (readonly_request == __mnt_is_readonly(mnt)) | 1717 | if (readonly_request == __mnt_is_readonly(mnt)) |
1714 | return 0; | 1718 | return 0; |
1715 | 1719 | ||
1720 | if (mnt->mnt_flags & MNT_LOCK_READONLY) | ||
1721 | return -EPERM; | ||
1722 | |||
1716 | if (readonly_request) | 1723 | if (readonly_request) |
1717 | error = mnt_make_readonly(real_mount(mnt)); | 1724 | error = mnt_make_readonly(real_mount(mnt)); |
1718 | else | 1725 | else |
@@ -2339,7 +2346,7 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns, | |||
2339 | /* First pass: copy the tree topology */ | 2346 | /* First pass: copy the tree topology */ |
2340 | copy_flags = CL_COPY_ALL | CL_EXPIRE; | 2347 | copy_flags = CL_COPY_ALL | CL_EXPIRE; |
2341 | if (user_ns != mnt_ns->user_ns) | 2348 | if (user_ns != mnt_ns->user_ns) |
2342 | copy_flags |= CL_SHARED_TO_SLAVE; | 2349 | copy_flags |= CL_SHARED_TO_SLAVE | CL_UNPRIVILEGED; |
2343 | new = copy_tree(old, old->mnt.mnt_root, copy_flags); | 2350 | new = copy_tree(old, old->mnt.mnt_root, copy_flags); |
2344 | if (IS_ERR(new)) { | 2351 | if (IS_ERR(new)) { |
2345 | up_write(&namespace_sem); | 2352 | up_write(&namespace_sem); |
@@ -2732,6 +2739,51 @@ bool our_mnt(struct vfsmount *mnt) | |||
2732 | return check_mnt(real_mount(mnt)); | 2739 | return check_mnt(real_mount(mnt)); |
2733 | } | 2740 | } |
2734 | 2741 | ||
2742 | bool current_chrooted(void) | ||
2743 | { | ||
2744 | /* Does the current process have a non-standard root */ | ||
2745 | struct path ns_root; | ||
2746 | struct path fs_root; | ||
2747 | bool chrooted; | ||
2748 | |||
2749 | /* Find the namespace root */ | ||
2750 | ns_root.mnt = ¤t->nsproxy->mnt_ns->root->mnt; | ||
2751 | ns_root.dentry = ns_root.mnt->mnt_root; | ||
2752 | path_get(&ns_root); | ||
2753 | while (d_mountpoint(ns_root.dentry) && follow_down_one(&ns_root)) | ||
2754 | ; | ||
2755 | |||
2756 | get_fs_root(current->fs, &fs_root); | ||
2757 | |||
2758 | chrooted = !path_equal(&fs_root, &ns_root); | ||
2759 | |||
2760 | path_put(&fs_root); | ||
2761 | path_put(&ns_root); | ||
2762 | |||
2763 | return chrooted; | ||
2764 | } | ||
2765 | |||
2766 | void update_mnt_policy(struct user_namespace *userns) | ||
2767 | { | ||
2768 | struct mnt_namespace *ns = current->nsproxy->mnt_ns; | ||
2769 | struct mount *mnt; | ||
2770 | |||
2771 | down_read(&namespace_sem); | ||
2772 | list_for_each_entry(mnt, &ns->list, mnt_list) { | ||
2773 | switch (mnt->mnt.mnt_sb->s_magic) { | ||
2774 | case SYSFS_MAGIC: | ||
2775 | userns->may_mount_sysfs = true; | ||
2776 | break; | ||
2777 | case PROC_SUPER_MAGIC: | ||
2778 | userns->may_mount_proc = true; | ||
2779 | break; | ||
2780 | } | ||
2781 | if (userns->may_mount_sysfs && userns->may_mount_proc) | ||
2782 | break; | ||
2783 | } | ||
2784 | up_read(&namespace_sem); | ||
2785 | } | ||
2786 | |||
2735 | static void *mntns_get(struct task_struct *task) | 2787 | static void *mntns_get(struct task_struct *task) |
2736 | { | 2788 | { |
2737 | struct mnt_namespace *ns = NULL; | 2789 | struct mnt_namespace *ns = NULL; |
diff --git a/fs/pnode.c b/fs/pnode.c index 3e000a51ac0d..8b29d2164da6 100644 --- a/fs/pnode.c +++ b/fs/pnode.c | |||
@@ -9,6 +9,7 @@ | |||
9 | #include <linux/mnt_namespace.h> | 9 | #include <linux/mnt_namespace.h> |
10 | #include <linux/mount.h> | 10 | #include <linux/mount.h> |
11 | #include <linux/fs.h> | 11 | #include <linux/fs.h> |
12 | #include <linux/nsproxy.h> | ||
12 | #include "internal.h" | 13 | #include "internal.h" |
13 | #include "pnode.h" | 14 | #include "pnode.h" |
14 | 15 | ||
@@ -220,6 +221,7 @@ static struct mount *get_source(struct mount *dest, | |||
220 | int propagate_mnt(struct mount *dest_mnt, struct dentry *dest_dentry, | 221 | int propagate_mnt(struct mount *dest_mnt, struct dentry *dest_dentry, |
221 | struct mount *source_mnt, struct list_head *tree_list) | 222 | struct mount *source_mnt, struct list_head *tree_list) |
222 | { | 223 | { |
224 | struct user_namespace *user_ns = current->nsproxy->mnt_ns->user_ns; | ||
223 | struct mount *m, *child; | 225 | struct mount *m, *child; |
224 | int ret = 0; | 226 | int ret = 0; |
225 | struct mount *prev_dest_mnt = dest_mnt; | 227 | struct mount *prev_dest_mnt = dest_mnt; |
@@ -237,6 +239,10 @@ int propagate_mnt(struct mount *dest_mnt, struct dentry *dest_dentry, | |||
237 | 239 | ||
238 | source = get_source(m, prev_dest_mnt, prev_src_mnt, &type); | 240 | source = get_source(m, prev_dest_mnt, prev_src_mnt, &type); |
239 | 241 | ||
242 | /* Notice when we are propagating across user namespaces */ | ||
243 | if (m->mnt_ns->user_ns != user_ns) | ||
244 | type |= CL_UNPRIVILEGED; | ||
245 | |||
240 | child = copy_tree(source, source->mnt.mnt_root, type); | 246 | child = copy_tree(source, source->mnt.mnt_root, type); |
241 | if (IS_ERR(child)) { | 247 | if (IS_ERR(child)) { |
242 | ret = PTR_ERR(child); | 248 | ret = PTR_ERR(child); |
diff --git a/fs/pnode.h b/fs/pnode.h index 19b853a3445c..a0493d5ebfbf 100644 --- a/fs/pnode.h +++ b/fs/pnode.h | |||
@@ -23,6 +23,7 @@ | |||
23 | #define CL_MAKE_SHARED 0x08 | 23 | #define CL_MAKE_SHARED 0x08 |
24 | #define CL_PRIVATE 0x10 | 24 | #define CL_PRIVATE 0x10 |
25 | #define CL_SHARED_TO_SLAVE 0x20 | 25 | #define CL_SHARED_TO_SLAVE 0x20 |
26 | #define CL_UNPRIVILEGED 0x40 | ||
26 | 27 | ||
27 | static inline void set_mnt_shared(struct mount *mnt) | 28 | static inline void set_mnt_shared(struct mount *mnt) |
28 | { | 29 | { |
diff --git a/fs/proc/root.c b/fs/proc/root.c index c6e9fac26bac..9c7fab1d23f0 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <linux/sched.h> | 16 | #include <linux/sched.h> |
17 | #include <linux/module.h> | 17 | #include <linux/module.h> |
18 | #include <linux/bitops.h> | 18 | #include <linux/bitops.h> |
19 | #include <linux/user_namespace.h> | ||
19 | #include <linux/mount.h> | 20 | #include <linux/mount.h> |
20 | #include <linux/pid_namespace.h> | 21 | #include <linux/pid_namespace.h> |
21 | #include <linux/parser.h> | 22 | #include <linux/parser.h> |
@@ -108,6 +109,9 @@ static struct dentry *proc_mount(struct file_system_type *fs_type, | |||
108 | } else { | 109 | } else { |
109 | ns = task_active_pid_ns(current); | 110 | ns = task_active_pid_ns(current); |
110 | options = data; | 111 | options = data; |
112 | |||
113 | if (!current_user_ns()->may_mount_proc) | ||
114 | return ERR_PTR(-EPERM); | ||
111 | } | 115 | } |
112 | 116 | ||
113 | sb = sget(fs_type, proc_test_super, proc_set_super, flags, ns); | 117 | sb = sget(fs_type, proc_test_super, proc_set_super, flags, ns); |
diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index 8d924b5ec733..afd83273e6ce 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <linux/module.h> | 19 | #include <linux/module.h> |
20 | #include <linux/magic.h> | 20 | #include <linux/magic.h> |
21 | #include <linux/slab.h> | 21 | #include <linux/slab.h> |
22 | #include <linux/user_namespace.h> | ||
22 | 23 | ||
23 | #include "sysfs.h" | 24 | #include "sysfs.h" |
24 | 25 | ||
@@ -111,6 +112,9 @@ static struct dentry *sysfs_mount(struct file_system_type *fs_type, | |||
111 | struct super_block *sb; | 112 | struct super_block *sb; |
112 | int error; | 113 | int error; |
113 | 114 | ||
115 | if (!(flags & MS_KERNMOUNT) && !current_user_ns()->may_mount_sysfs) | ||
116 | return ERR_PTR(-EPERM); | ||
117 | |||
114 | info = kzalloc(sizeof(*info), GFP_KERNEL); | 118 | info = kzalloc(sizeof(*info), GFP_KERNEL); |
115 | if (!info) | 119 | if (!info) |
116 | return ERR_PTR(-ENOMEM); | 120 | return ERR_PTR(-ENOMEM); |