Skip to content

Commit 5ff9d8a

Browse files
committed
vfs: Lock in place mounts from more privileged users
When creating a less privileged mount namespace or propogating mounts from a more privileged to a less privileged mount namespace lock the submounts so they may not be unmounted individually in the child mount namespace revealing what is under them. This enforces the reasonable expectation that it is not possible to see under a mount point. Most of the time mounts are on empty directories and revealing that does not matter, however I have seen an occassionaly sloppy configuration where there were interesting things concealed under a mount point that probably should not be revealed. Expirable submounts are not locked because they will eventually unmount automatically so whatever is under them already needs to be safe for unprivileged users to access. From a practical standpoint these restrictions do not appear to be significant for unprivileged users of the mount namespace. Recursive bind mounts and pivot_root continues to work, and mounts that are created in a mount namespace may be unmounted there. All of which means that the common idiom of keeping a directory of interesting files and using pivot_root to throw everything else away continues to work just fine. Acked-by: Serge Hallyn <serge.hallyn@canonical.com> Acked-by: Andy Lutomirski <luto@amacapital.net> Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
1 parent ad81f05 commit 5ff9d8a

File tree

2 files changed

+35
-0
lines changed

2 files changed

+35
-0
lines changed

fs/namespace.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -831,6 +831,10 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root,
831831
if ((flag & CL_UNPRIVILEGED) && (mnt->mnt.mnt_flags & MNT_READONLY))
832832
mnt->mnt.mnt_flags |= MNT_LOCK_READONLY;
833833

834+
/* Don't allow unprivileged users to reveal what is under a mount */
835+
if ((flag & CL_UNPRIVILEGED) && list_empty(&old->mnt_expire))
836+
mnt->mnt.mnt_flags |= MNT_LOCKED;
837+
834838
atomic_inc(&sb->s_active);
835839
mnt->mnt.mnt_sb = sb;
836840
mnt->mnt.mnt_root = dget(root);
@@ -1327,6 +1331,8 @@ SYSCALL_DEFINE2(umount, char __user *, name, int, flags)
13271331
goto dput_and_out;
13281332
if (!check_mnt(mnt))
13291333
goto dput_and_out;
1334+
if (mnt->mnt.mnt_flags & MNT_LOCKED)
1335+
goto dput_and_out;
13301336

13311337
retval = do_umount(mnt, flags);
13321338
dput_and_out:
@@ -1381,6 +1387,7 @@ struct mount *copy_tree(struct mount *mnt, struct dentry *dentry,
13811387
if (IS_ERR(q))
13821388
return q;
13831389

1390+
q->mnt.mnt_flags &= ~MNT_LOCKED;
13841391
q->mnt_mountpoint = mnt->mnt_mountpoint;
13851392

13861393
p = mnt;
@@ -1696,6 +1703,19 @@ static int do_change_type(struct path *path, int flag)
16961703
return err;
16971704
}
16981705

1706+
static bool has_locked_children(struct mount *mnt, struct dentry *dentry)
1707+
{
1708+
struct mount *child;
1709+
list_for_each_entry(child, &mnt->mnt_mounts, mnt_child) {
1710+
if (!is_subdir(child->mnt_mountpoint, dentry))
1711+
continue;
1712+
1713+
if (child->mnt.mnt_flags & MNT_LOCKED)
1714+
return true;
1715+
}
1716+
return false;
1717+
}
1718+
16991719
/*
17001720
* do loopback mount.
17011721
*/
@@ -1731,6 +1751,9 @@ static int do_loopback(struct path *path, const char *old_name,
17311751
if (!check_mnt(parent) || !check_mnt(old))
17321752
goto out2;
17331753

1754+
if (!recurse && has_locked_children(old, old_path.dentry))
1755+
goto out2;
1756+
17341757
if (recurse)
17351758
mnt = copy_tree(old, old_path.dentry, 0);
17361759
else
@@ -1741,6 +1764,8 @@ static int do_loopback(struct path *path, const char *old_name,
17411764
goto out2;
17421765
}
17431766

1767+
mnt->mnt.mnt_flags &= ~MNT_LOCKED;
1768+
17441769
err = graft_tree(mnt, parent, mp);
17451770
if (err) {
17461771
br_write_lock(&vfsmount_lock);
@@ -1853,6 +1878,9 @@ static int do_move_mount(struct path *path, const char *old_name)
18531878
if (!check_mnt(p) || !check_mnt(old))
18541879
goto out1;
18551880

1881+
if (old->mnt.mnt_flags & MNT_LOCKED)
1882+
goto out1;
1883+
18561884
err = -EINVAL;
18571885
if (old_path.dentry != old_path.mnt->mnt_root)
18581886
goto out1;
@@ -2630,6 +2658,8 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
26302658
goto out4;
26312659
if (!check_mnt(root_mnt) || !check_mnt(new_mnt))
26322660
goto out4;
2661+
if (new_mnt->mnt.mnt_flags & MNT_LOCKED)
2662+
goto out4;
26332663
error = -ENOENT;
26342664
if (d_unlinked(new.dentry))
26352665
goto out4;
@@ -2653,6 +2683,10 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
26532683
br_write_lock(&vfsmount_lock);
26542684
detach_mnt(new_mnt, &parent_path);
26552685
detach_mnt(root_mnt, &root_parent);
2686+
if (root_mnt->mnt.mnt_flags & MNT_LOCKED) {
2687+
new_mnt->mnt.mnt_flags |= MNT_LOCKED;
2688+
root_mnt->mnt.mnt_flags &= ~MNT_LOCKED;
2689+
}
26562690
/* mount old root on put_old */
26572691
attach_mnt(root_mnt, old_mnt, old_mp);
26582692
/* mount new_root on / */

include/linux/mount.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ struct mnt_namespace;
4848
#define MNT_INTERNAL 0x4000
4949

5050
#define MNT_LOCK_READONLY 0x400000
51+
#define MNT_LOCKED 0x800000
5152

5253
struct vfsmount {
5354
struct dentry *mnt_root; /* root of the mounted tree */

0 commit comments

Comments
 (0)