[PATCH] Set nosuid, noexec, nodev if any lowerdirs have it

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This closes the security issue of a execute
of a binary which is disallowed in the lower layers with
mount flags, but are able to execute with overlayfs. This
circumvention can be done by users using fusermounts
as well.

nosuid[1] and noexec[2] has been attempted earlier, but with no
review/response.

Perhaps a discussion would be helpful.

[1] http://www.spinics.net/lists/linux-unionfs/msg00379.html
[2] http://www.spinics.net/lists/linux-unionfs/msg00381.html

Signed-off-by: Goldwyn Rodrigues <rgoldwyn@xxxxxxxx>
---
 fs/block_dev.c           |    2 +-
 fs/exec.c                |   16 +++++++++++++++-
 fs/namei.c               |    2 +-
 fs/overlayfs/super.c     |   13 ++++++++++++-
 include/linux/fs.h       |    4 ++++
 security/commoncap.c     |    2 +-
 security/selinux/hooks.c |    2 +-
 7 files changed, 35 insertions(+), 6 deletions(-)

diff --git a/fs/block_dev.c b/fs/block_dev.c
index 826b164..85697d2 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -1841,7 +1841,7 @@ struct block_device *lookup_bdev(const char *pathname)
 	if (!S_ISBLK(inode->i_mode))
 		goto fail;
 	error = -EACCES;
-	if (path.mnt->mnt_flags & MNT_NODEV)
+	if (path_nodev(&path))
 		goto fail;
 	error = -ENOMEM;
 	bdev = bd_acquire(inode);
diff --git a/fs/exec.c b/fs/exec.c
index dcd4ac7..ff1b20b 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -104,6 +104,20 @@ bool path_noexec(const struct path *path)
 	       (path->mnt->mnt_sb->s_iflags & SB_I_NOEXEC);
 }
 
+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);
+
+bool path_nodev(const struct path *path)
+{
+	return (path->mnt->mnt_flags & MNT_NODEV) ||
+	       (path->mnt->mnt_sb->s_iflags & SB_I_NODEV);
+}
+EXPORT_SYMBOL(path_nodev);
+
 #ifdef CONFIG_USELIB
 /*
  * Note that a shared library must be both readable and executable due to
@@ -1295,7 +1309,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/namei.c b/fs/namei.c
index 9c590e0..6027fbe 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2752,7 +2752,7 @@ static int may_open(struct path *path, int acc_mode, int flag)
 		break;
 	case S_IFBLK:
 	case S_IFCHR:
-		if (path->mnt->mnt_flags & MNT_NODEV)
+		if (path_nodev(path))
 			return -EACCES;
 		/*FALLTHRU*/
 	case S_IFIFO:
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index 619ad4b..569a04a 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -1019,6 +1019,12 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
 			pr_err("overlayfs: failed to clone upperpath\n");
 			goto out_put_lowerpath;
 		}
+		if (ufs->upper_mnt->mnt_flags & MNT_NOSUID)
+			sb->s_iflags |= SB_I_NOSUID;
+		if (ufs->upper_mnt->mnt_flags & MNT_NOEXEC)
+			sb->s_iflags |= SB_I_NOEXEC;
+		if (ufs->upper_mnt->mnt_flags & MNT_NODEV)
+			sb->s_iflags |= SB_I_NODEV;
 
 		ufs->workdir = ovl_workdir_create(ufs->upper_mnt, workpath.dentry);
 		err = PTR_ERR(ufs->workdir);
@@ -1047,7 +1053,12 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
 		 * will fail instead of modifying lower fs.
 		 */
 		mnt->mnt_flags |= MNT_READONLY;
-
+		if (mnt->mnt_flags & MNT_NOSUID)
+			sb->s_iflags |= SB_I_NOSUID;
+		if (mnt->mnt_flags & MNT_NOEXEC)
+			sb->s_iflags |= SB_I_NOEXEC;
+		if (mnt->mnt_flags & MNT_NODEV)
+			sb->s_iflags |= SB_I_NODEV;
 		ufs->lower_mnt[ufs->numlower] = mnt;
 		ufs->numlower++;
 	}
diff --git a/include/linux/fs.h b/include/linux/fs.h
index ae68100..b38835b 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1281,6 +1281,8 @@ struct mm_struct;
 /* sb->s_iflags */
 #define SB_I_CGROUPWB	0x00000001	/* cgroup-aware writeback enabled */
 #define SB_I_NOEXEC	0x00000002	/* Ignore executables on this fs */
+#define SB_I_NODEV	0x00000004      /* Ignore devs on this fs */
+#define SB_I_NOSUID	0x00000008      /* Disallow suid on this fs */
 
 /* Possible states of 'frozen' field */
 enum {
@@ -3076,6 +3078,8 @@ static inline bool dir_relax(struct inode *inode)
 }
 
 extern bool path_noexec(const struct path *path);
+extern bool path_nosuid(const struct path *path);
+extern bool path_nodev(const struct path *path);
 extern void inode_nohighmem(struct inode *inode);
 
 #endif /* _LINUX_FS_H */
diff --git a/security/commoncap.c b/security/commoncap.c
index 48071ed..32f3140 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -453,7 +453,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 f1ab715..c4725cc 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2234,7 +2234,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)
--
To unsubscribe from this list: send the line "unsubscribe linux-unionfs" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Filesystems Devel]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux