]> Pileus Git - ~andy/linux/blobdiff - fs/namespace.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm...
[~andy/linux] / fs / namespace.c
index a45ba4f267fe6e834909f122dd166cdd608dcce5..ef69fa5d2e5bd4806eda5d82f3c30e47ebf19c35 100644 (file)
@@ -831,6 +831,10 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root,
        if ((flag & CL_UNPRIVILEGED) && (mnt->mnt.mnt_flags & MNT_READONLY))
                mnt->mnt.mnt_flags |= MNT_LOCK_READONLY;
 
+       /* Don't allow unprivileged users to reveal what is under a mount */
+       if ((flag & CL_UNPRIVILEGED) && list_empty(&old->mnt_expire))
+               mnt->mnt.mnt_flags |= MNT_LOCKED;
+
        atomic_inc(&sb->s_active);
        mnt->mnt.mnt_sb = sb;
        mnt->mnt.mnt_root = dget(root);
@@ -1318,7 +1322,7 @@ SYSCALL_DEFINE2(umount, char __user *, name, int, flags)
        if (!(flags & UMOUNT_NOFOLLOW))
                lookup_flags |= LOOKUP_FOLLOW;
 
-       retval = user_path_at(AT_FDCWD, name, lookup_flags, &path);
+       retval = user_path_umountat(AT_FDCWD, name, lookup_flags, &path);
        if (retval)
                goto out;
        mnt = real_mount(path.mnt);
@@ -1327,6 +1331,8 @@ SYSCALL_DEFINE2(umount, char __user *, name, int, flags)
                goto dput_and_out;
        if (!check_mnt(mnt))
                goto dput_and_out;
+       if (mnt->mnt.mnt_flags & MNT_LOCKED)
+               goto dput_and_out;
 
        retval = do_umount(mnt, flags);
 dput_and_out:
@@ -1349,14 +1355,11 @@ SYSCALL_DEFINE1(oldumount, char __user *, name)
 
 #endif
 
-static bool mnt_ns_loop(struct path *path)
+static bool is_mnt_ns_file(struct dentry *dentry)
 {
-       /* Could bind mounting the mount namespace inode cause a
-        * mount namespace loop?
-        */
-       struct inode *inode = path->dentry->d_inode;
+       /* Is this a proxy for a mount namespace? */
+       struct inode *inode = dentry->d_inode;
        struct proc_ns *ei;
-       struct mnt_namespace *mnt_ns;
 
        if (!proc_ns_inode(inode))
                return false;
@@ -1365,7 +1368,19 @@ static bool mnt_ns_loop(struct path *path)
        if (ei->ns_ops != &mntns_operations)
                return false;
 
-       mnt_ns = ei->ns;
+       return true;
+}
+
+static bool mnt_ns_loop(struct dentry *dentry)
+{
+       /* Could bind mounting the mount namespace inode cause a
+        * mount namespace loop?
+        */
+       struct mnt_namespace *mnt_ns;
+       if (!is_mnt_ns_file(dentry))
+               return false;
+
+       mnt_ns = get_proc_ns(dentry->d_inode)->ns;
        return current->nsproxy->mnt_ns->seq >= mnt_ns->seq;
 }
 
@@ -1374,13 +1389,17 @@ struct mount *copy_tree(struct mount *mnt, struct dentry *dentry,
 {
        struct mount *res, *p, *q, *r, *parent;
 
-       if (!(flag & CL_COPY_ALL) && IS_MNT_UNBINDABLE(mnt))
+       if (!(flag & CL_COPY_UNBINDABLE) && IS_MNT_UNBINDABLE(mnt))
+               return ERR_PTR(-EINVAL);
+
+       if (!(flag & CL_COPY_MNT_NS_FILE) && is_mnt_ns_file(dentry))
                return ERR_PTR(-EINVAL);
 
        res = q = clone_mnt(mnt, dentry, flag);
        if (IS_ERR(q))
                return q;
 
+       q->mnt.mnt_flags &= ~MNT_LOCKED;
        q->mnt_mountpoint = mnt->mnt_mountpoint;
 
        p = mnt;
@@ -1390,7 +1409,13 @@ struct mount *copy_tree(struct mount *mnt, struct dentry *dentry,
                        continue;
 
                for (s = r; s; s = next_mnt(s, r)) {
-                       if (!(flag & CL_COPY_ALL) && IS_MNT_UNBINDABLE(s)) {
+                       if (!(flag & CL_COPY_UNBINDABLE) &&
+                           IS_MNT_UNBINDABLE(s)) {
+                               s = skip_mnt_tree(s);
+                               continue;
+                       }
+                       if (!(flag & CL_COPY_MNT_NS_FILE) &&
+                           is_mnt_ns_file(s->mnt.mnt_root)) {
                                s = skip_mnt_tree(s);
                                continue;
                        }
@@ -1696,6 +1721,19 @@ static int do_change_type(struct path *path, int flag)
        return err;
 }
 
+static bool has_locked_children(struct mount *mnt, struct dentry *dentry)
+{
+       struct mount *child;
+       list_for_each_entry(child, &mnt->mnt_mounts, mnt_child) {
+               if (!is_subdir(child->mnt_mountpoint, dentry))
+                       continue;
+
+               if (child->mnt.mnt_flags & MNT_LOCKED)
+                       return true;
+       }
+       return false;
+}
+
 /*
  * do loopback mount.
  */
@@ -1713,7 +1751,7 @@ static int do_loopback(struct path *path, const char *old_name,
                return err;
 
        err = -EINVAL;
-       if (mnt_ns_loop(&old_path))
+       if (mnt_ns_loop(old_path.dentry))
                goto out; 
 
        mp = lock_mount(path);
@@ -1731,8 +1769,11 @@ static int do_loopback(struct path *path, const char *old_name,
        if (!check_mnt(parent) || !check_mnt(old))
                goto out2;
 
+       if (!recurse && has_locked_children(old, old_path.dentry))
+               goto out2;
+
        if (recurse)
-               mnt = copy_tree(old, old_path.dentry, 0);
+               mnt = copy_tree(old, old_path.dentry, CL_COPY_MNT_NS_FILE);
        else
                mnt = clone_mnt(old, old_path.dentry, 0);
 
@@ -1741,6 +1782,8 @@ static int do_loopback(struct path *path, const char *old_name,
                goto out2;
        }
 
+       mnt->mnt.mnt_flags &= ~MNT_LOCKED;
+
        err = graft_tree(mnt, parent, mp);
        if (err) {
                br_write_lock(&vfsmount_lock);
@@ -1853,6 +1896,9 @@ static int do_move_mount(struct path *path, const char *old_name)
        if (!check_mnt(p) || !check_mnt(old))
                goto out1;
 
+       if (old->mnt.mnt_flags & MNT_LOCKED)
+               goto out1;
+
        err = -EINVAL;
        if (old_path.dentry != old_path.mnt->mnt_root)
                goto out1;
@@ -2389,7 +2435,7 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns,
 
        namespace_lock();
        /* First pass: copy the tree topology */
-       copy_flags = CL_COPY_ALL | CL_EXPIRE;
+       copy_flags = CL_COPY_UNBINDABLE | CL_EXPIRE;
        if (user_ns != mnt_ns->user_ns)
                copy_flags |= CL_SHARED_TO_SLAVE | CL_UNPRIVILEGED;
        new = copy_tree(old, old->mnt.mnt_root, copy_flags);
@@ -2424,6 +2470,10 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns,
                }
                p = next_mnt(p, old);
                q = next_mnt(q, new);
+               if (!q)
+                       break;
+               while (p->mnt.mnt_root != q->mnt.mnt_root)
+                       p = next_mnt(p, old);
        }
        namespace_unlock();
 
@@ -2630,6 +2680,8 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
                goto out4;
        if (!check_mnt(root_mnt) || !check_mnt(new_mnt))
                goto out4;
+       if (new_mnt->mnt.mnt_flags & MNT_LOCKED)
+               goto out4;
        error = -ENOENT;
        if (d_unlinked(new.dentry))
                goto out4;
@@ -2653,6 +2705,10 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
        br_write_lock(&vfsmount_lock);
        detach_mnt(new_mnt, &parent_path);
        detach_mnt(root_mnt, &root_parent);
+       if (root_mnt->mnt.mnt_flags & MNT_LOCKED) {
+               new_mnt->mnt.mnt_flags |= MNT_LOCKED;
+               root_mnt->mnt.mnt_flags &= ~MNT_LOCKED;
+       }
        /* mount old root on put_old */
        attach_mnt(root_mnt, old_mnt, old_mp);
        /* mount new_root on / */
@@ -2811,25 +2867,38 @@ bool current_chrooted(void)
        return chrooted;
 }
 
-void update_mnt_policy(struct user_namespace *userns)
+bool fs_fully_visible(struct file_system_type *type)
 {
        struct mnt_namespace *ns = current->nsproxy->mnt_ns;
        struct mount *mnt;
+       bool visible = false;
 
-       down_read(&namespace_sem);
+       if (unlikely(!ns))
+               return false;
+
+       namespace_lock();
        list_for_each_entry(mnt, &ns->list, mnt_list) {
-               switch (mnt->mnt.mnt_sb->s_magic) {
-               case SYSFS_MAGIC:
-                       userns->may_mount_sysfs = true;
-                       break;
-               case PROC_SUPER_MAGIC:
-                       userns->may_mount_proc = true;
-                       break;
+               struct mount *child;
+               if (mnt->mnt.mnt_sb->s_type != type)
+                       continue;
+
+               /* This mount is not fully visible if there are any child mounts
+                * that cover anything except for empty directories.
+                */
+               list_for_each_entry(child, &mnt->mnt_mounts, mnt_child) {
+                       struct inode *inode = child->mnt_mountpoint->d_inode;
+                       if (!S_ISDIR(inode->i_mode))
+                               goto next;
+                       if (inode->i_nlink != 2)
+                               goto next;
                }
-               if (userns->may_mount_sysfs && userns->may_mount_proc)
-                       break;
+               visible = true;
+               goto found;
+       next:   ;
        }
-       up_read(&namespace_sem);
+found:
+       namespace_unlock();
+       return visible;
 }
 
 static void *mntns_get(struct task_struct *task)
@@ -2860,8 +2929,8 @@ static int mntns_install(struct nsproxy *nsproxy, void *ns)
        struct path root;
 
        if (!ns_capable(mnt_ns->user_ns, CAP_SYS_ADMIN) ||
-           !nsown_capable(CAP_SYS_CHROOT) ||
-           !nsown_capable(CAP_SYS_ADMIN))
+           !ns_capable(current_user_ns(), CAP_SYS_CHROOT) ||
+           !ns_capable(current_user_ns(), CAP_SYS_ADMIN))
                return -EPERM;
 
        if (fs->users != 1)