From b5e2c7126265ee60669186f36f4c6856a4dc574c Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Thu, 21 Jan 2016 15:37:53 -0600 Subject: [PATCH 6/6] overlayfs: Propogate nosuid from lower and upper mounts An overlayfs mount using an upper or lower directory from a nosuid filesystem bypasses this restriction. Change this so that if any lower or upper directory is nosuid at mount time the overlayfs superblock is marked nosuid. This requires some additions at the vfs level since nosuid currently only applies to mounts, so a SB_I_NOSUID flag is added along with a helper function to check a path for nosuid in both the mount and the superblock. Signed-off-by: Seth Forshee --- fs/exec.c | 9 ++++++++- fs/overlayfs/super.c | 6 ++++++ include/linux/fs.h | 3 +++ security/commoncap.c | 2 +- security/selinux/hooks.c | 2 +- 5 files changed, 19 insertions(+), 3 deletions(-) diff --git a/fs/exec.c b/fs/exec.c index e95d8c5..ca617d5 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -100,6 +100,13 @@ static inline void put_binfmt(struct linux_binfmt * fmt) module_put(fmt->module); } +bool path_nosuid(const struct path *path) +{ + return (path->mnt->mnt_flags & MNT_NOSUID) || + (path->mnt->mnt_sb->s_iflags & SB_I_NOSUID); +} +EXPORT_SYMBOL(path_nosuid); + #ifdef CONFIG_USELIB /* * Note that a shared library must be both readable and executable due to @@ -1293,7 +1300,7 @@ static void bprm_fill_uid(struct linux_binprm *bprm) bprm->cred->euid = current_euid(); bprm->cred->egid = current_egid(); - if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) + if (path_nosuid(&bprm->file->f_path)) return; if (task_no_new_privs(current)) diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index b19d889..0313470 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -1074,6 +1074,9 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) goto out_put_lowerpath; } + if (ufs->upper_mnt->mnt_flags & MNT_NOSUID) + sb->s_iflags |= SB_I_NOSUID; + ufs->workdir = ovl_workdir_create(ufs->upper_mnt, workpath.dentry); err = PTR_ERR(ufs->workdir); if (IS_ERR(ufs->workdir)) { @@ -1102,6 +1105,9 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) */ mnt->mnt_flags |= MNT_READONLY; + if (mnt->mnt_flags & MNT_NOSUID) + sb->s_iflags |= SB_I_NOSUID; + ufs->lower_mnt[ufs->numlower] = mnt; ufs->numlower++; } diff --git a/include/linux/fs.h b/include/linux/fs.h index 999456d..b0b0295 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1260,6 +1260,7 @@ struct mm_struct; /* sb->s_iflags */ #define SB_I_CGROUPWB 0x00000001 /* cgroup-aware writeback enabled */ +#define SB_I_NOSUID 0x00000004 /* Ignore nosuid on this fs */ /* Possible states of 'frozen' field */ enum { @@ -3051,4 +3052,6 @@ static inline bool dir_relax(struct inode *inode) return !IS_DEADDIR(inode); } +extern bool path_nosuid(const struct path *path); + #endif /* _LINUX_FS_H */ diff --git a/security/commoncap.c b/security/commoncap.c index 393654e..2887934 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -437,7 +437,7 @@ static int get_file_caps(struct linux_binprm *bprm, bool *effective, bool *has_c if (!file_caps_enabled) return 0; - if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) + if (path_nosuid(&bprm->file->f_path)) return 0; rc = get_vfs_caps_from_disk(bprm->file->f_path.dentry, &vcaps); diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index cdf4c58..4c6fca9 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2137,7 +2137,7 @@ static int check_nnp_nosuid(const struct linux_binprm *bprm, const struct task_security_struct *new_tsec) { int nnp = (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS); - int nosuid = (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID); + int nosuid = path_nosuid(&bprm->file->f_path); int rc; if (!nnp && !nosuid) -- 2.7.0.rc3