aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric W. Biederman <ebiederm@xmission.com>2013-03-30 22:57:41 -0400
committerEric W. Biederman <ebiederm@xmission.com>2013-08-26 22:17:03 -0400
commite51db73532955dc5eaba4235e62b74b460709d5b (patch)
treeef2b73dd5e04d5b97a0bb10e8a163811ce9a3845
parent4ce5d2b1a8fde84c0eebe70652cf28b9beda6b4e (diff)
userns: Better restrictions on when proc and sysfs can be mounted
Rely on the fact that another flavor of the filesystem is already mounted and do not rely on state in the user namespace. Verify that the mounted filesystem is not covered in any significant way. I would love to verify that the previously mounted filesystem has no mounts on top but there are at least the directories /proc/sys/fs/binfmt_misc and /sys/fs/cgroup/ that exist explicitly for other filesystems to mount on top of. Refactor the test into a function named fs_fully_visible and call that function from the mount routines of proc and sysfs. This makes this test local to the filesystems involved and the results current of when the mounts take place, removing a weird threading of the user namespace, the mount namespace and the filesystems themselves. Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
-rw-r--r--fs/namespace.c37
-rw-r--r--fs/proc/root.c7
-rw-r--r--fs/sysfs/mount.c3
-rw-r--r--include/linux/fs.h1
-rw-r--r--include/linux/user_namespace.h4
-rw-r--r--kernel/user.c2
-rw-r--r--kernel/user_namespace.c2
7 files changed, 33 insertions, 23 deletions
diff --git a/fs/namespace.c b/fs/namespace.c
index 64627f883bf2..877e4277f496 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -2867,25 +2867,38 @@ bool current_chrooted(void)
2867 return chrooted; 2867 return chrooted;
2868} 2868}
2869 2869
2870void update_mnt_policy(struct user_namespace *userns) 2870bool fs_fully_visible(struct file_system_type *type)
2871{ 2871{
2872 struct mnt_namespace *ns = current->nsproxy->mnt_ns; 2872 struct mnt_namespace *ns = current->nsproxy->mnt_ns;
2873 struct mount *mnt; 2873 struct mount *mnt;
2874 bool visible = false;
2874 2875
2875 down_read(&namespace_sem); 2876 if (unlikely(!ns))
2877 return false;
2878
2879 namespace_lock();
2876 list_for_each_entry(mnt, &ns->list, mnt_list) { 2880 list_for_each_entry(mnt, &ns->list, mnt_list) {
2877 switch (mnt->mnt.mnt_sb->s_magic) { 2881 struct mount *child;
2878 case SYSFS_MAGIC: 2882 if (mnt->mnt.mnt_sb->s_type != type)
2879 userns->may_mount_sysfs = true; 2883 continue;
2880 break; 2884
2881 case PROC_SUPER_MAGIC: 2885 /* This mount is not fully visible if there are any child mounts
2882 userns->may_mount_proc = true; 2886 * that cover anything except for empty directories.
2883 break; 2887 */
2888 list_for_each_entry(child, &mnt->mnt_mounts, mnt_child) {
2889 struct inode *inode = child->mnt_mountpoint->d_inode;
2890 if (!S_ISDIR(inode->i_mode))
2891 goto next;
2892 if (inode->i_nlink != 2)
2893 goto next;
2884 } 2894 }
2885 if (userns->may_mount_sysfs && userns->may_mount_proc) 2895 visible = true;
2886 break; 2896 goto found;
2897 next: ;
2887 } 2898 }
2888 up_read(&namespace_sem); 2899found:
2900 namespace_unlock();
2901 return visible;
2889} 2902}
2890 2903
2891static void *mntns_get(struct task_struct *task) 2904static void *mntns_get(struct task_struct *task)
diff --git a/fs/proc/root.c b/fs/proc/root.c
index 38bd5d423fcd..45e5fb7da09b 100644
--- a/fs/proc/root.c
+++ b/fs/proc/root.c
@@ -110,8 +110,11 @@ static struct dentry *proc_mount(struct file_system_type *fs_type,
110 ns = task_active_pid_ns(current); 110 ns = task_active_pid_ns(current);
111 options = data; 111 options = data;
112 112
113 if (!current_user_ns()->may_mount_proc || 113 if (!capable(CAP_SYS_ADMIN) && !fs_fully_visible(fs_type))
114 !ns_capable(ns->user_ns, CAP_SYS_ADMIN)) 114 return ERR_PTR(-EPERM);
115
116 /* Does the mounter have privilege over the pid namespace? */
117 if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN))
115 return ERR_PTR(-EPERM); 118 return ERR_PTR(-EPERM);
116 } 119 }
117 120
diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c
index afd83273e6ce..4a2da3a4b1b1 100644
--- a/fs/sysfs/mount.c
+++ b/fs/sysfs/mount.c
@@ -112,7 +112,8 @@ static struct dentry *sysfs_mount(struct file_system_type *fs_type,
112 struct super_block *sb; 112 struct super_block *sb;
113 int error; 113 int error;
114 114
115 if (!(flags & MS_KERNMOUNT) && !current_user_ns()->may_mount_sysfs) 115 if (!(flags & MS_KERNMOUNT) && !capable(CAP_SYS_ADMIN) &&
116 !fs_fully_visible(fs_type))
116 return ERR_PTR(-EPERM); 117 return ERR_PTR(-EPERM);
117 118
118 info = kzalloc(sizeof(*info), GFP_KERNEL); 119 info = kzalloc(sizeof(*info), GFP_KERNEL);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 981874773e85..3050c620f062 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1897,6 +1897,7 @@ extern int vfs_ustat(dev_t, struct kstatfs *);
1897extern int freeze_super(struct super_block *super); 1897extern int freeze_super(struct super_block *super);
1898extern int thaw_super(struct super_block *super); 1898extern int thaw_super(struct super_block *super);
1899extern bool our_mnt(struct vfsmount *mnt); 1899extern bool our_mnt(struct vfsmount *mnt);
1900extern bool fs_fully_visible(struct file_system_type *);
1900 1901
1901extern int current_umask(void); 1902extern int current_umask(void);
1902 1903
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index b6b215f13b45..4ce009324933 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -26,8 +26,6 @@ struct user_namespace {
26 kuid_t owner; 26 kuid_t owner;
27 kgid_t group; 27 kgid_t group;
28 unsigned int proc_inum; 28 unsigned int proc_inum;
29 bool may_mount_sysfs;
30 bool may_mount_proc;
31}; 29};
32 30
33extern struct user_namespace init_user_ns; 31extern struct user_namespace init_user_ns;
@@ -84,6 +82,4 @@ static inline void put_user_ns(struct user_namespace *ns)
84 82
85#endif 83#endif
86 84
87void update_mnt_policy(struct user_namespace *userns);
88
89#endif /* _LINUX_USER_H */ 85#endif /* _LINUX_USER_H */
diff --git a/kernel/user.c b/kernel/user.c
index 69b4c3d48cde..5bbb91988e69 100644
--- a/kernel/user.c
+++ b/kernel/user.c
@@ -51,8 +51,6 @@ struct user_namespace init_user_ns = {
51 .owner = GLOBAL_ROOT_UID, 51 .owner = GLOBAL_ROOT_UID,
52 .group = GLOBAL_ROOT_GID, 52 .group = GLOBAL_ROOT_GID,
53 .proc_inum = PROC_USER_INIT_INO, 53 .proc_inum = PROC_USER_INIT_INO,
54 .may_mount_sysfs = true,
55 .may_mount_proc = true,
56}; 54};
57EXPORT_SYMBOL_GPL(init_user_ns); 55EXPORT_SYMBOL_GPL(init_user_ns);
58 56
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index d8c30db06c5b..d58ad1e7a794 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -97,8 +97,6 @@ int create_user_ns(struct cred *new)
97 97
98 set_cred_user_ns(new, ns); 98 set_cred_user_ns(new, ns);
99 99
100 update_mnt_policy(ns);
101
102 return 0; 100 return 0;
103} 101}
104 102