]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - fs/namespace.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm...
[karo-tx-linux.git] / fs / namespace.c
index 419f746d851d1c1745fc0edca4a37459a4b17f35..7bb2cda3bfef50b27f9bb8b3b478cc7aeb3d1049 100644 (file)
@@ -2186,13 +2186,7 @@ static int do_remount(struct path *path, int flags, int mnt_flags,
        }
        if ((mnt->mnt.mnt_flags & MNT_LOCK_NODEV) &&
            !(mnt_flags & MNT_NODEV)) {
-               /* Was the nodev implicitly added in mount? */
-               if ((mnt->mnt_ns->user_ns != &init_user_ns) &&
-                   !(sb->s_type->fs_flags & FS_USERNS_DEV_MOUNT)) {
-                       mnt_flags |= MNT_NODEV;
-               } else {
-                       return -EPERM;
-               }
+               return -EPERM;
        }
        if ((mnt->mnt.mnt_flags & MNT_LOCK_NOSUID) &&
            !(mnt_flags & MNT_NOSUID)) {
@@ -2376,7 +2370,7 @@ unlock:
        return err;
 }
 
-static bool fs_fully_visible(struct file_system_type *fs_type, int *new_mnt_flags);
+static bool mount_too_revealing(struct vfsmount *mnt, int *new_mnt_flags);
 
 /*
  * create a new mount for userspace and request it to be added into the
@@ -2386,7 +2380,6 @@ static int do_new_mount(struct path *path, const char *fstype, int flags,
                        int mnt_flags, const char *name, void *data)
 {
        struct file_system_type *type;
-       struct user_namespace *user_ns = current->nsproxy->mnt_ns->user_ns;
        struct vfsmount *mnt;
        int err;
 
@@ -2397,26 +2390,6 @@ static int do_new_mount(struct path *path, const char *fstype, int flags,
        if (!type)
                return -ENODEV;
 
-       if (user_ns != &init_user_ns) {
-               if (!(type->fs_flags & FS_USERNS_MOUNT)) {
-                       put_filesystem(type);
-                       return -EPERM;
-               }
-               /* Only in special cases allow devices from mounts
-                * created outside the initial user namespace.
-                */
-               if (!(type->fs_flags & FS_USERNS_DEV_MOUNT)) {
-                       flags |= MS_NODEV;
-                       mnt_flags |= MNT_NODEV | MNT_LOCK_NODEV;
-               }
-               if (type->fs_flags & FS_USERNS_VISIBLE) {
-                       if (!fs_fully_visible(type, &mnt_flags)) {
-                               put_filesystem(type);
-                               return -EPERM;
-                       }
-               }
-       }
-
        mnt = vfs_kern_mount(type, flags, name, data);
        if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE) &&
            !mnt->mnt_sb->s_subtype)
@@ -2426,6 +2399,11 @@ static int do_new_mount(struct path *path, const char *fstype, int flags,
        if (IS_ERR(mnt))
                return PTR_ERR(mnt);
 
+       if (mount_too_revealing(mnt, &mnt_flags)) {
+               mntput(mnt);
+               return -EPERM;
+       }
+
        err = do_add_mount(real_mount(mnt), path, mnt_flags);
        if (err)
                mntput(mnt);
@@ -3217,22 +3195,19 @@ bool current_chrooted(void)
        return chrooted;
 }
 
-static bool fs_fully_visible(struct file_system_type *type, int *new_mnt_flags)
+static bool mnt_already_visible(struct mnt_namespace *ns, struct vfsmount *new,
+                               int *new_mnt_flags)
 {
-       struct mnt_namespace *ns = current->nsproxy->mnt_ns;
        int new_flags = *new_mnt_flags;
        struct mount *mnt;
        bool visible = false;
 
-       if (unlikely(!ns))
-               return false;
-
        down_read(&namespace_sem);
        list_for_each_entry(mnt, &ns->list, mnt_list) {
                struct mount *child;
                int mnt_flags;
 
-               if (mnt->mnt.mnt_sb->s_type != type)
+               if (mnt->mnt.mnt_sb->s_type != new->mnt_sb->s_type)
                        continue;
 
                /* This mount is not fully visible if it's root directory
@@ -3241,12 +3216,8 @@ static bool fs_fully_visible(struct file_system_type *type, int *new_mnt_flags)
                if (mnt->mnt.mnt_root != mnt->mnt.mnt_sb->s_root)
                        continue;
 
-               /* Read the mount flags and filter out flags that
-                * may safely be ignored.
-                */
+               /* A local view of the mount flags */
                mnt_flags = mnt->mnt.mnt_flags;
-               if (mnt->mnt.mnt_sb->s_iflags & SB_I_NOEXEC)
-                       mnt_flags &= ~(MNT_LOCK_NOSUID | MNT_LOCK_NOEXEC);
 
                /* Don't miss readonly hidden in the superblock flags */
                if (mnt->mnt.mnt_sb->s_flags & MS_RDONLY)
@@ -3258,15 +3229,6 @@ static bool fs_fully_visible(struct file_system_type *type, int *new_mnt_flags)
                if ((mnt_flags & MNT_LOCK_READONLY) &&
                    !(new_flags & MNT_READONLY))
                        continue;
-               if ((mnt_flags & MNT_LOCK_NODEV) &&
-                   !(new_flags & MNT_NODEV))
-                       continue;
-               if ((mnt_flags & MNT_LOCK_NOSUID) &&
-                   !(new_flags & MNT_NOSUID))
-                       continue;
-               if ((mnt_flags & MNT_LOCK_NOEXEC) &&
-                   !(new_flags & MNT_NOEXEC))
-                       continue;
                if ((mnt_flags & MNT_LOCK_ATIME) &&
                    ((mnt_flags & MNT_ATIME_MASK) != (new_flags & MNT_ATIME_MASK)))
                        continue;
@@ -3286,9 +3248,6 @@ static bool fs_fully_visible(struct file_system_type *type, int *new_mnt_flags)
                }
                /* Preserve the locked attributes */
                *new_mnt_flags |= mnt_flags & (MNT_LOCK_READONLY | \
-                                              MNT_LOCK_NODEV    | \
-                                              MNT_LOCK_NOSUID   | \
-                                              MNT_LOCK_NOEXEC   | \
                                               MNT_LOCK_ATIME);
                visible = true;
                goto found;
@@ -3299,6 +3258,42 @@ found:
        return visible;
 }
 
+static bool mount_too_revealing(struct vfsmount *mnt, int *new_mnt_flags)
+{
+       const unsigned long required_iflags = SB_I_NOEXEC | SB_I_NODEV;
+       struct mnt_namespace *ns = current->nsproxy->mnt_ns;
+       unsigned long s_iflags;
+
+       if (ns->user_ns == &init_user_ns)
+               return false;
+
+       /* Can this filesystem be too revealing? */
+       s_iflags = mnt->mnt_sb->s_iflags;
+       if (!(s_iflags & SB_I_USERNS_VISIBLE))
+               return false;
+
+       if ((s_iflags & required_iflags) != required_iflags) {
+               WARN_ONCE(1, "Expected s_iflags to contain 0x%lx\n",
+                         required_iflags);
+               return true;
+       }
+
+       return !mnt_already_visible(ns, mnt, new_mnt_flags);
+}
+
+bool mnt_may_suid(struct vfsmount *mnt)
+{
+       /*
+        * Foreign mounts (accessed via fchdir or through /proc
+        * symlinks) are always treated as if they are nosuid.  This
+        * prevents namespaces from trusting potentially unsafe
+        * suid/sgid bits, file caps, or security labels that originate
+        * in other namespaces.
+        */
+       return !(mnt->mnt_flags & MNT_NOSUID) && check_mnt(real_mount(mnt)) &&
+              current_in_userns(mnt->mnt_sb->s_user_ns);
+}
+
 static struct ns_common *mntns_get(struct task_struct *task)
 {
        struct ns_common *ns = NULL;