]> git.proxmox.com Git - mirror_ubuntu-focal-kernel.git/commitdiff
UBUNTU: SAUCE: shiftfs: lock down certain superblock flags
authorChristian Brauner <christian@brauner.io>
Wed, 8 May 2019 12:13:14 +0000 (14:13 +0200)
committerAndrea Righi <andrea.righi@canonical.com>
Mon, 25 Nov 2019 13:56:40 +0000 (14:56 +0100)
BugLink: https://bugs.launchpad.net/bugs/1827122
This locks down various superblock flags to prevent userns-root from
remounting a superblock with less restrictive options than the original
mark or underlay mount.

Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
Signed-off-by: Seth Forshee <seth.forshee@canonical.com>
fs/shiftfs.c

index 9771165d1ce079c6c7ac2227f4db863d3db0434e..a1dae7ea593bf37440fe71bb9bb954470c2f0e8c 100644 (file)
@@ -1808,6 +1808,33 @@ static inline bool passthrough_is_subset(int old_flags, int new_flags)
        return true;
 }
 
+static int shiftfs_super_check_flags(unsigned long old_flags,
+                                    unsigned long new_flags)
+{
+       if ((old_flags & SB_RDONLY) && !(new_flags & SB_RDONLY))
+               return -EPERM;
+
+       if ((old_flags & SB_NOSUID) && !(new_flags & SB_NOSUID))
+               return -EPERM;
+
+       if ((old_flags & SB_NODEV) && !(new_flags & SB_NODEV))
+               return -EPERM;
+
+       if ((old_flags & SB_NOEXEC) && !(new_flags & SB_NOEXEC))
+               return -EPERM;
+
+       if ((old_flags & SB_NOATIME) && !(new_flags & SB_NOATIME))
+               return -EPERM;
+
+       if ((old_flags & SB_NODIRATIME) && !(new_flags & SB_NODIRATIME))
+               return -EPERM;
+
+       if (!(old_flags & SB_POSIXACL) && (new_flags & SB_POSIXACL))
+               return -EPERM;
+
+       return 0;
+}
+
 static int shiftfs_remount(struct super_block *sb, int *flags, char *data)
 {
        int err;
@@ -1818,6 +1845,10 @@ static int shiftfs_remount(struct super_block *sb, int *flags, char *data)
        if (err)
                return err;
 
+       err = shiftfs_super_check_flags(sb->s_flags, *flags);
+       if (err)
+               return err;
+
        /* Mark mount option cannot be changed. */
        if (info->mark || (info->mark != new.mark))
                return -EPERM;
@@ -1847,6 +1878,16 @@ struct shiftfs_data {
        const char *path;
 };
 
+static void shiftfs_super_force_flags(struct super_block *sb,
+                                     unsigned long lower_flags)
+{
+       sb->s_flags |= lower_flags & (SB_RDONLY | SB_NOSUID | SB_NODEV |
+                                     SB_NOEXEC | SB_NOATIME | SB_NODIRATIME);
+
+       if (!(lower_flags & SB_POSIXACL))
+               sb->s_flags &= ~SB_POSIXACL;
+}
+
 static int shiftfs_fill_super(struct super_block *sb, void *raw_data,
                              int silent)
 {
@@ -1888,6 +1929,8 @@ static int shiftfs_fill_super(struct super_block *sb, void *raw_data,
                goto out_put_path;
        }
 
+       sb->s_flags |= SB_POSIXACL;
+
        if (sbinfo->mark) {
                struct super_block *lower_sb = path.mnt->mnt_sb;
 
@@ -1904,6 +1947,8 @@ static int shiftfs_fill_super(struct super_block *sb, void *raw_data,
                 */
                sb->s_iflags = SB_I_NOEXEC;
 
+               shiftfs_super_force_flags(sb, lower_sb->s_flags);
+
                /*
                 * Handle nesting of shiftfs mounts by referring this mark
                 * mount back to the original mark mount. This is more
@@ -1972,6 +2017,7 @@ static int shiftfs_fill_super(struct super_block *sb, void *raw_data,
                 * passthrough settings.
                 */
                sbinfo->passthrough_mark = sbinfo_mp->passthrough;
+               shiftfs_super_force_flags(sb, path.mnt->mnt_sb->s_flags);
        }
 
        sb->s_stack_depth = dentry->d_sb->s_stack_depth + 1;
@@ -1995,7 +2041,6 @@ static int shiftfs_fill_super(struct super_block *sb, void *raw_data,
        sb->s_op = &shiftfs_super_ops;
        sb->s_xattr = shiftfs_xattr_handlers;
        sb->s_d_op = &shiftfs_dentry_ops;
-       sb->s_flags |= SB_POSIXACL;
        sb->s_root = d_make_root(inode);
        if (!sb->s_root) {
                err = -ENOMEM;