diff options
author | David Howells <dhowells@redhat.com> | 2018-11-04 07:43:08 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2019-01-30 17:44:25 -0500 |
commit | 132e460848f4261b8a6b9c28fae52bf9e02b52fd (patch) | |
tree | 6efa9049652905a5931b564a64d0aa609979d91b | |
parent | a0c9a8b8fd9fd572b0d60276beb2142c8f59f9b8 (diff) |
new helper: do_new_mount_fc()
Create an fs_context-aware version of do_new_mount(). This takes an
fs_context with a superblock already attached to it.
Make do_new_mount() use do_new_mount_fc() rather than do_new_mount(); this
allows the consolidation of the mount creation, check and add steps.
To make this work, mount_too_revealing() is changed to take a superblock
rather than a mount (which the fs_context doesn't have available), allowing
this check to be done before the mount object is created.
Signed-off-by: David Howells <dhowells@redhat.com>
Co-developed-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r-- | fs/namespace.c | 65 |
1 files changed, 39 insertions, 26 deletions
diff --git a/fs/namespace.c b/fs/namespace.c index 0354cb6ac2d3..f629e1c7f3cc 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
@@ -2523,7 +2523,37 @@ unlock: | |||
2523 | return err; | 2523 | return err; |
2524 | } | 2524 | } |
2525 | 2525 | ||
2526 | static bool mount_too_revealing(struct vfsmount *mnt, int *new_mnt_flags); | 2526 | static bool mount_too_revealing(const struct super_block *sb, int *new_mnt_flags); |
2527 | |||
2528 | /* | ||
2529 | * Create a new mount using a superblock configuration and request it | ||
2530 | * be added to the namespace tree. | ||
2531 | */ | ||
2532 | static int do_new_mount_fc(struct fs_context *fc, struct path *mountpoint, | ||
2533 | unsigned int mnt_flags) | ||
2534 | { | ||
2535 | struct vfsmount *mnt; | ||
2536 | struct super_block *sb = fc->root->d_sb; | ||
2537 | int error; | ||
2538 | |||
2539 | if (mount_too_revealing(sb, &mnt_flags)) { | ||
2540 | dput(fc->root); | ||
2541 | fc->root = NULL; | ||
2542 | deactivate_locked_super(sb); | ||
2543 | return -EPERM; | ||
2544 | } | ||
2545 | |||
2546 | up_write(&sb->s_umount); | ||
2547 | |||
2548 | mnt = vfs_create_mount(fc); | ||
2549 | if (IS_ERR(mnt)) | ||
2550 | return PTR_ERR(mnt); | ||
2551 | |||
2552 | error = do_add_mount(real_mount(mnt), mountpoint, mnt_flags); | ||
2553 | if (error < 0) | ||
2554 | mntput(mnt); | ||
2555 | return error; | ||
2556 | } | ||
2527 | 2557 | ||
2528 | /* | 2558 | /* |
2529 | * create a new mount for userspace and request it to be added into the | 2559 | * create a new mount for userspace and request it to be added into the |
@@ -2533,7 +2563,6 @@ static int do_new_mount(struct path *path, const char *fstype, int sb_flags, | |||
2533 | int mnt_flags, const char *name, void *data) | 2563 | int mnt_flags, const char *name, void *data) |
2534 | { | 2564 | { |
2535 | struct file_system_type *type; | 2565 | struct file_system_type *type; |
2536 | struct vfsmount *mnt; | ||
2537 | struct fs_context *fc; | 2566 | struct fs_context *fc; |
2538 | const char *subtype = NULL; | 2567 | const char *subtype = NULL; |
2539 | int err = 0; | 2568 | int err = 0; |
@@ -2577,26 +2606,9 @@ static int do_new_mount(struct path *path, const char *fstype, int sb_flags, | |||
2577 | err = parse_monolithic_mount_data(fc, data); | 2606 | err = parse_monolithic_mount_data(fc, data); |
2578 | if (!err) | 2607 | if (!err) |
2579 | err = vfs_get_tree(fc); | 2608 | err = vfs_get_tree(fc); |
2580 | if (err) | 2609 | if (!err) |
2581 | goto out; | 2610 | err = do_new_mount_fc(fc, path, mnt_flags); |
2582 | |||
2583 | up_write(&fc->root->d_sb->s_umount); | ||
2584 | mnt = vfs_create_mount(fc); | ||
2585 | if (IS_ERR(mnt)) { | ||
2586 | err = PTR_ERR(mnt); | ||
2587 | goto out; | ||
2588 | } | ||
2589 | |||
2590 | if (mount_too_revealing(mnt, &mnt_flags)) { | ||
2591 | mntput(mnt); | ||
2592 | err = -EPERM; | ||
2593 | goto out; | ||
2594 | } | ||
2595 | 2611 | ||
2596 | err = do_add_mount(real_mount(mnt), path, mnt_flags); | ||
2597 | if (err) | ||
2598 | mntput(mnt); | ||
2599 | out: | ||
2600 | put_fs_context(fc); | 2612 | put_fs_context(fc); |
2601 | return err; | 2613 | return err; |
2602 | } | 2614 | } |
@@ -3421,7 +3433,8 @@ bool current_chrooted(void) | |||
3421 | return chrooted; | 3433 | return chrooted; |
3422 | } | 3434 | } |
3423 | 3435 | ||
3424 | static bool mnt_already_visible(struct mnt_namespace *ns, struct vfsmount *new, | 3436 | static bool mnt_already_visible(struct mnt_namespace *ns, |
3437 | const struct super_block *sb, | ||
3425 | int *new_mnt_flags) | 3438 | int *new_mnt_flags) |
3426 | { | 3439 | { |
3427 | int new_flags = *new_mnt_flags; | 3440 | int new_flags = *new_mnt_flags; |
@@ -3433,7 +3446,7 @@ static bool mnt_already_visible(struct mnt_namespace *ns, struct vfsmount *new, | |||
3433 | struct mount *child; | 3446 | struct mount *child; |
3434 | int mnt_flags; | 3447 | int mnt_flags; |
3435 | 3448 | ||
3436 | if (mnt->mnt.mnt_sb->s_type != new->mnt_sb->s_type) | 3449 | if (mnt->mnt.mnt_sb->s_type != sb->s_type) |
3437 | continue; | 3450 | continue; |
3438 | 3451 | ||
3439 | /* This mount is not fully visible if it's root directory | 3452 | /* This mount is not fully visible if it's root directory |
@@ -3484,7 +3497,7 @@ found: | |||
3484 | return visible; | 3497 | return visible; |
3485 | } | 3498 | } |
3486 | 3499 | ||
3487 | static bool mount_too_revealing(struct vfsmount *mnt, int *new_mnt_flags) | 3500 | static bool mount_too_revealing(const struct super_block *sb, int *new_mnt_flags) |
3488 | { | 3501 | { |
3489 | const unsigned long required_iflags = SB_I_NOEXEC | SB_I_NODEV; | 3502 | const unsigned long required_iflags = SB_I_NOEXEC | SB_I_NODEV; |
3490 | struct mnt_namespace *ns = current->nsproxy->mnt_ns; | 3503 | struct mnt_namespace *ns = current->nsproxy->mnt_ns; |
@@ -3494,7 +3507,7 @@ static bool mount_too_revealing(struct vfsmount *mnt, int *new_mnt_flags) | |||
3494 | return false; | 3507 | return false; |
3495 | 3508 | ||
3496 | /* Can this filesystem be too revealing? */ | 3509 | /* Can this filesystem be too revealing? */ |
3497 | s_iflags = mnt->mnt_sb->s_iflags; | 3510 | s_iflags = sb->s_iflags; |
3498 | if (!(s_iflags & SB_I_USERNS_VISIBLE)) | 3511 | if (!(s_iflags & SB_I_USERNS_VISIBLE)) |
3499 | return false; | 3512 | return false; |
3500 | 3513 | ||
@@ -3504,7 +3517,7 @@ static bool mount_too_revealing(struct vfsmount *mnt, int *new_mnt_flags) | |||
3504 | return true; | 3517 | return true; |
3505 | } | 3518 | } |
3506 | 3519 | ||
3507 | return !mnt_already_visible(ns, mnt, new_mnt_flags); | 3520 | return !mnt_already_visible(ns, sb, new_mnt_flags); |
3508 | } | 3521 | } |
3509 | 3522 | ||
3510 | bool mnt_may_suid(struct vfsmount *mnt) | 3523 | bool mnt_may_suid(struct vfsmount *mnt) |