]> Pileus Git - ~andy/linux/blobdiff - fs/namespace.c
vfs: Carefully propogate mounts across user namespaces
[~andy/linux] / fs / namespace.c
index edac42c6eff22b556c63bd17d386f6998d6f6141..968d4c5eae03aa18b1326e09371aa5ada795939e 100644 (file)
@@ -384,7 +384,7 @@ EXPORT_SYMBOL_GPL(mnt_clone_write);
  */
 int __mnt_want_write_file(struct file *file)
 {
-       struct inode *inode = file->f_dentry->d_inode;
+       struct inode *inode = file_inode(file);
 
        if (!(file->f_mode & FMODE_WRITE) || special_file(inode->i_mode))
                return __mnt_want_write(file->f_path.mnt);
@@ -798,6 +798,10 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root,
        }
 
        mnt->mnt.mnt_flags = old->mnt.mnt_flags & ~MNT_WRITE_HOLD;
+       /* Don't allow unprivileged users to change mount flags */
+       if ((flag & CL_UNPRIVILEGED) && (mnt->mnt.mnt_flags & MNT_READONLY))
+               mnt->mnt.mnt_flags |= MNT_LOCK_READONLY;
+
        atomic_inc(&sb->s_active);
        mnt->mnt.mnt_sb = sb;
        mnt->mnt.mnt_root = dget(root);
@@ -1300,24 +1304,6 @@ SYSCALL_DEFINE1(oldumount, char __user *, name)
 
 #endif
 
-static int mount_is_safe(struct path *path)
-{
-       if (may_mount())
-               return 0;
-       return -EPERM;
-#ifdef notyet
-       if (S_ISLNK(path->dentry->d_inode->i_mode))
-               return -EPERM;
-       if (path->dentry->d_inode->i_mode & S_ISVTX) {
-               if (current_uid() != path->dentry->d_inode->i_uid)
-                       return -EPERM;
-       }
-       if (inode_permission(path->dentry->d_inode, MAY_WRITE))
-               return -EPERM;
-       return 0;
-#endif
-}
-
 static bool mnt_ns_loop(struct path *path)
 {
        /* Could bind mounting the mount namespace inode cause a
@@ -1640,9 +1626,6 @@ static int do_change_type(struct path *path, int flag)
        int type;
        int err = 0;
 
-       if (!may_mount())
-               return -EPERM;
-
        if (path->dentry != path->mnt->mnt_root)
                return -EINVAL;
 
@@ -1676,9 +1659,7 @@ static int do_loopback(struct path *path, const char *old_name,
        LIST_HEAD(umount_list);
        struct path old_path;
        struct mount *mnt = NULL, *old;
-       int err = mount_is_safe(path);
-       if (err)
-               return err;
+       int err;
        if (!old_name || !*old_name)
                return -EINVAL;
        err = kern_path(old_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path);
@@ -1736,6 +1717,9 @@ static int change_mount_flags(struct vfsmount *mnt, int ms_flags)
        if (readonly_request == __mnt_is_readonly(mnt))
                return 0;
 
+       if (mnt->mnt_flags & MNT_LOCK_READONLY)
+               return -EPERM;
+
        if (readonly_request)
                error = mnt_make_readonly(real_mount(mnt));
        else
@@ -1755,9 +1739,6 @@ static int do_remount(struct path *path, int flags, int mnt_flags,
        struct super_block *sb = path->mnt->mnt_sb;
        struct mount *mnt = real_mount(path->mnt);
 
-       if (!capable(CAP_SYS_ADMIN))
-               return -EPERM;
-
        if (!check_mnt(mnt))
                return -EINVAL;
 
@@ -1771,6 +1752,8 @@ static int do_remount(struct path *path, int flags, int mnt_flags,
        down_write(&sb->s_umount);
        if (flags & MS_BIND)
                err = change_mount_flags(path->mnt, flags);
+       else if (!capable(CAP_SYS_ADMIN))
+               err = -EPERM;
        else
                err = do_remount_sb(sb, flags, data, 0);
        if (!err) {
@@ -1803,9 +1786,7 @@ static int do_move_mount(struct path *path, const char *old_name)
        struct path old_path, parent_path;
        struct mount *p;
        struct mount *old;
-       int err = 0;
-       if (!may_mount())
-               return -EPERM;
+       int err;
        if (!old_name || !*old_name)
                return -EINVAL;
        err = kern_path(old_name, LOOKUP_FOLLOW, &old_path);
@@ -1947,9 +1928,6 @@ static int do_new_mount(struct path *path, const char *fstype, int flags,
        if (!fstype)
                return -EINVAL;
 
-       if (!may_mount())
-               return -EPERM;
-
        type = get_fs_type(fstype);
        if (!type)
                return -ENODEV;
@@ -2263,6 +2241,9 @@ long do_mount(const char *dev_name, const char *dir_name,
        if (retval)
                goto dput_out;
 
+       if (!may_mount())
+               return -EPERM;
+
        /* Default to relatime unless overriden */
        if (!(flags & MS_NOATIME))
                mnt_flags |= MNT_RELATIME;
@@ -2365,7 +2346,7 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns,
        /* First pass: copy the tree topology */
        copy_flags = CL_COPY_ALL | CL_EXPIRE;
        if (user_ns != mnt_ns->user_ns)
-               copy_flags |= CL_SHARED_TO_SLAVE;
+               copy_flags |= CL_SHARED_TO_SLAVE | CL_UNPRIVILEGED;
        new = copy_tree(old, old->mnt.mnt_root, copy_flags);
        if (IS_ERR(new)) {
                up_write(&namespace_sem);
@@ -2758,6 +2739,30 @@ bool our_mnt(struct vfsmount *mnt)
        return check_mnt(real_mount(mnt));
 }
 
+bool current_chrooted(void)
+{
+       /* Does the current process have a non-standard root */
+       struct path ns_root;
+       struct path fs_root;
+       bool chrooted;
+
+       /* Find the namespace root */
+       ns_root.mnt = &current->nsproxy->mnt_ns->root->mnt;
+       ns_root.dentry = ns_root.mnt->mnt_root;
+       path_get(&ns_root);
+       while (d_mountpoint(ns_root.dentry) && follow_down_one(&ns_root))
+               ;
+
+       get_fs_root(current->fs, &fs_root);
+
+       chrooted = !path_equal(&fs_root, &ns_root);
+
+       path_put(&fs_root);
+       path_put(&ns_root);
+
+       return chrooted;
+}
+
 static void *mntns_get(struct task_struct *task)
 {
        struct mnt_namespace *ns = NULL;