diff options
author | Eric W. Biederman <ebiederm@xmission.com> | 2016-06-09 17:06:06 -0400 |
---|---|---|
committer | Eric W. Biederman <ebiederm@xmission.com> | 2016-06-23 16:41:46 -0400 |
commit | 8654df4e2ac9704905198d63845554c2ddf6a93f (patch) | |
tree | 80c18b034fecf8e5754945f9e31ad2a807386907 /fs | |
parent | 695e9df010e40f407f4830dc11d53dce957710ba (diff) |
mnt: Refactor fs_fully_visible into mount_too_revealing
Replace the call of fs_fully_visible in do_new_mount from before the
new superblock is allocated with a call of mount_too_revealing after
the superblock is allocated. This winds up being a much better location
for maintainability of the code.
The first change this enables is the replacement of FS_USERNS_VISIBLE
with SB_I_USERNS_VISIBLE. Moving the flag from struct filesystem_type
to sb_iflags on the superblock.
Unfortunately mount_too_revealing fundamentally needs to touch
mnt_flags adding several MNT_LOCKED_XXX flags at the appropriate
times. If the mnt_flags did not need to be touched the code
could be easily moved into the filesystem specific mount code.
Acked-by: Seth Forshee <seth.forshee@canonical.com>
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/namespace.c | 38 | ||||
-rw-r--r-- | fs/proc/inode.c | 1 | ||||
-rw-r--r-- | fs/proc/root.c | 2 | ||||
-rw-r--r-- | fs/sysfs/mount.c | 4 |
4 files changed, 29 insertions, 16 deletions
diff --git a/fs/namespace.c b/fs/namespace.c index 783004af5707..1a69aa786975 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
@@ -2375,7 +2375,7 @@ unlock: | |||
2375 | return err; | 2375 | return err; |
2376 | } | 2376 | } |
2377 | 2377 | ||
2378 | static bool fs_fully_visible(struct file_system_type *fs_type, int *new_mnt_flags); | 2378 | static bool mount_too_revealing(struct vfsmount *mnt, int *new_mnt_flags); |
2379 | 2379 | ||
2380 | /* | 2380 | /* |
2381 | * create a new mount for userspace and request it to be added into the | 2381 | * create a new mount for userspace and request it to be added into the |
@@ -2408,12 +2408,6 @@ static int do_new_mount(struct path *path, const char *fstype, int flags, | |||
2408 | flags |= MS_NODEV; | 2408 | flags |= MS_NODEV; |
2409 | mnt_flags |= MNT_NODEV | MNT_LOCK_NODEV; | 2409 | mnt_flags |= MNT_NODEV | MNT_LOCK_NODEV; |
2410 | } | 2410 | } |
2411 | if (type->fs_flags & FS_USERNS_VISIBLE) { | ||
2412 | if (!fs_fully_visible(type, &mnt_flags)) { | ||
2413 | put_filesystem(type); | ||
2414 | return -EPERM; | ||
2415 | } | ||
2416 | } | ||
2417 | } | 2411 | } |
2418 | 2412 | ||
2419 | mnt = vfs_kern_mount(type, flags, name, data); | 2413 | mnt = vfs_kern_mount(type, flags, name, data); |
@@ -2425,6 +2419,11 @@ static int do_new_mount(struct path *path, const char *fstype, int flags, | |||
2425 | if (IS_ERR(mnt)) | 2419 | if (IS_ERR(mnt)) |
2426 | return PTR_ERR(mnt); | 2420 | return PTR_ERR(mnt); |
2427 | 2421 | ||
2422 | if (mount_too_revealing(mnt, &mnt_flags)) { | ||
2423 | mntput(mnt); | ||
2424 | return -EPERM; | ||
2425 | } | ||
2426 | |||
2428 | err = do_add_mount(real_mount(mnt), path, mnt_flags); | 2427 | err = do_add_mount(real_mount(mnt), path, mnt_flags); |
2429 | if (err) | 2428 | if (err) |
2430 | mntput(mnt); | 2429 | mntput(mnt); |
@@ -3216,22 +3215,19 @@ bool current_chrooted(void) | |||
3216 | return chrooted; | 3215 | return chrooted; |
3217 | } | 3216 | } |
3218 | 3217 | ||
3219 | static bool fs_fully_visible(struct file_system_type *type, int *new_mnt_flags) | 3218 | static bool mnt_already_visible(struct mnt_namespace *ns, struct vfsmount *new, |
3219 | int *new_mnt_flags) | ||
3220 | { | 3220 | { |
3221 | struct mnt_namespace *ns = current->nsproxy->mnt_ns; | ||
3222 | int new_flags = *new_mnt_flags; | 3221 | int new_flags = *new_mnt_flags; |
3223 | struct mount *mnt; | 3222 | struct mount *mnt; |
3224 | bool visible = false; | 3223 | bool visible = false; |
3225 | 3224 | ||
3226 | if (unlikely(!ns)) | ||
3227 | return false; | ||
3228 | |||
3229 | down_read(&namespace_sem); | 3225 | down_read(&namespace_sem); |
3230 | list_for_each_entry(mnt, &ns->list, mnt_list) { | 3226 | list_for_each_entry(mnt, &ns->list, mnt_list) { |
3231 | struct mount *child; | 3227 | struct mount *child; |
3232 | int mnt_flags; | 3228 | int mnt_flags; |
3233 | 3229 | ||
3234 | if (mnt->mnt.mnt_sb->s_type != type) | 3230 | if (mnt->mnt.mnt_sb->s_type != new->mnt_sb->s_type) |
3235 | continue; | 3231 | continue; |
3236 | 3232 | ||
3237 | /* This mount is not fully visible if it's root directory | 3233 | /* This mount is not fully visible if it's root directory |
@@ -3298,6 +3294,22 @@ found: | |||
3298 | return visible; | 3294 | return visible; |
3299 | } | 3295 | } |
3300 | 3296 | ||
3297 | static bool mount_too_revealing(struct vfsmount *mnt, int *new_mnt_flags) | ||
3298 | { | ||
3299 | struct mnt_namespace *ns = current->nsproxy->mnt_ns; | ||
3300 | unsigned long s_iflags; | ||
3301 | |||
3302 | if (ns->user_ns == &init_user_ns) | ||
3303 | return false; | ||
3304 | |||
3305 | /* Can this filesystem be too revealing? */ | ||
3306 | s_iflags = mnt->mnt_sb->s_iflags; | ||
3307 | if (!(s_iflags & SB_I_USERNS_VISIBLE)) | ||
3308 | return false; | ||
3309 | |||
3310 | return !mnt_already_visible(ns, mnt, new_mnt_flags); | ||
3311 | } | ||
3312 | |||
3301 | static struct ns_common *mntns_get(struct task_struct *task) | 3313 | static struct ns_common *mntns_get(struct task_struct *task) |
3302 | { | 3314 | { |
3303 | struct ns_common *ns = NULL; | 3315 | struct ns_common *ns = NULL; |
diff --git a/fs/proc/inode.c b/fs/proc/inode.c index 42305ddcbaa0..78fa452d65ed 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c | |||
@@ -462,6 +462,7 @@ int proc_fill_super(struct super_block *s) | |||
462 | struct inode *root_inode; | 462 | struct inode *root_inode; |
463 | int ret; | 463 | int ret; |
464 | 464 | ||
465 | s->s_iflags |= SB_I_USERNS_VISIBLE; | ||
465 | s->s_flags |= MS_NODIRATIME | MS_NOSUID | MS_NOEXEC; | 466 | s->s_flags |= MS_NODIRATIME | MS_NOSUID | MS_NOEXEC; |
466 | s->s_blocksize = 1024; | 467 | s->s_blocksize = 1024; |
467 | s->s_blocksize_bits = 10; | 468 | s->s_blocksize_bits = 10; |
diff --git a/fs/proc/root.c b/fs/proc/root.c index 55bc7d6c8aac..a1b2860fec62 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c | |||
@@ -158,7 +158,7 @@ static struct file_system_type proc_fs_type = { | |||
158 | .name = "proc", | 158 | .name = "proc", |
159 | .mount = proc_mount, | 159 | .mount = proc_mount, |
160 | .kill_sb = proc_kill_sb, | 160 | .kill_sb = proc_kill_sb, |
161 | .fs_flags = FS_USERNS_VISIBLE | FS_USERNS_MOUNT, | 161 | .fs_flags = FS_USERNS_MOUNT, |
162 | }; | 162 | }; |
163 | 163 | ||
164 | void __init proc_root_init(void) | 164 | void __init proc_root_init(void) |
diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index f3db82071cfb..f31e36994dfb 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c | |||
@@ -42,7 +42,7 @@ static struct dentry *sysfs_mount(struct file_system_type *fs_type, | |||
42 | kobj_ns_drop(KOBJ_NS_TYPE_NET, ns); | 42 | kobj_ns_drop(KOBJ_NS_TYPE_NET, ns); |
43 | else if (new_sb) | 43 | else if (new_sb) |
44 | /* Userspace would break if executables appear on sysfs */ | 44 | /* Userspace would break if executables appear on sysfs */ |
45 | root->d_sb->s_iflags |= SB_I_NOEXEC; | 45 | root->d_sb->s_iflags |= SB_I_USERNS_VISIBLE | SB_I_NOEXEC; |
46 | 46 | ||
47 | return root; | 47 | return root; |
48 | } | 48 | } |
@@ -59,7 +59,7 @@ static struct file_system_type sysfs_fs_type = { | |||
59 | .name = "sysfs", | 59 | .name = "sysfs", |
60 | .mount = sysfs_mount, | 60 | .mount = sysfs_mount, |
61 | .kill_sb = sysfs_kill_sb, | 61 | .kill_sb = sysfs_kill_sb, |
62 | .fs_flags = FS_USERNS_VISIBLE | FS_USERNS_MOUNT, | 62 | .fs_flags = FS_USERNS_MOUNT, |
63 | }; | 63 | }; |
64 | 64 | ||
65 | int __init sysfs_init(void) | 65 | int __init sysfs_init(void) |