
From: Stephen Smalley <sds@tycho.nsa.gov>

This patch adds a reqprot parameter to the security_file_mmap and
security_file_mprotect hooks that is the original requested protection
value prior to any modification for read-implies-exec, and changes the
SELinux module to allow a mode of operation (controllable via a
checkreqprot setting) where it applies checks based on that protection
value rather than the protection that will be applied by the kernel,
effectively restoring SELinux's original behavior prior to the introduction
of the read-implies-exec logic in the mainline kernel.  The patch also
disables execmem and execmod checking entirely on PPC32, as the PPC32 ELF
ABI presently requires RWE segments per Ulrich Drepper.

At present, the read-implies-exec logic causes SELinux to see every
mmap/mprotect read request by legacy binaries or binaries marked with
PT_GNU_STACK RWE as a read|execute request, which tends to distort policy
even if it reflects what is ultimately possible.  The checkreqprot setting
allows one to set the desired behavior for SELinux, so either the current
behavior or the original behavior is possible.  The checkreqprot value has
a compile-time configurable default value and can also be set via boot
parameter or at runtime via /selinux/checkreqprot if allowed by policy. 
Thanks to Chris Wright, James Morris, and Colin Walters for comments on an
earlier version of the patch.

Signed-off-by: Stephen Smalley <sds@tycho.nsa.gov>
Signed-off-by: James Morris <jmorris@redhat.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/include/linux/security.h                     |   23 +++++--
 25-akpm/mm/mmap.c                                    |    4 -
 25-akpm/mm/mprotect.c                                |    6 +
 25-akpm/security/dummy.c                             |    7 +-
 25-akpm/security/selinux/Kconfig                     |   19 ++++++
 25-akpm/security/selinux/hooks.c                     |   18 ++++-
 25-akpm/security/selinux/include/av_perm_to_string.h |    1 
 25-akpm/security/selinux/include/av_permissions.h    |    1 
 25-akpm/security/selinux/include/objsec.h            |    2 
 25-akpm/security/selinux/selinuxfs.c                 |   60 +++++++++++++++++++
 10 files changed, 125 insertions(+), 16 deletions(-)

diff -puN include/linux/security.h~selinux-pass-requested-protection-to-security_file_mmap-mprotect-hooks include/linux/security.h
--- 25/include/linux/security.h~selinux-pass-requested-protection-to-security_file_mmap-mprotect-hooks	Mon Mar  7 16:10:38 2005
+++ 25-akpm/include/linux/security.h	Mon Mar  7 16:10:38 2005
@@ -458,13 +458,15 @@ struct swap_info_struct;
  *	Check permissions for a mmap operation.  The @file may be NULL, e.g.
  *	if mapping anonymous memory.
  *	@file contains the file structure for file to map (may be NULL).
- *	@prot contains the requested permissions.
+ *	@reqprot contains the protection requested by the application.
+ *	@prot contains the protection that will be applied by the kernel.
  *	@flags contains the operational flags.
  *	Return 0 if permission is granted.
  * @file_mprotect:
  *	Check permissions before changing memory access permissions.
  *	@vma contains the memory region to modify.
- *	@prot contains the requested permissions.
+ *	@reqprot contains the protection requested by the application.
+ *	@prot contains the protection that will be applied by the kernel.
  *	Return 0 if permission is granted.
  * @file_lock:
  *	Check permission before performing file locking operations.
@@ -1129,8 +1131,11 @@ struct security_operations {
 	int (*file_ioctl) (struct file * file, unsigned int cmd,
 			   unsigned long arg);
 	int (*file_mmap) (struct file * file,
+			  unsigned long reqprot,
 			  unsigned long prot, unsigned long flags);
-	int (*file_mprotect) (struct vm_area_struct * vma, unsigned long prot);
+	int (*file_mprotect) (struct vm_area_struct * vma,
+			      unsigned long reqprot,
+			      unsigned long prot);
 	int (*file_lock) (struct file * file, unsigned int cmd);
 	int (*file_fcntl) (struct file * file, unsigned int cmd,
 			   unsigned long arg);
@@ -1693,16 +1698,18 @@ static inline int security_file_ioctl (s
 	return security_ops->file_ioctl (file, cmd, arg);
 }
 
-static inline int security_file_mmap (struct file *file, unsigned long prot,
+static inline int security_file_mmap (struct file *file, unsigned long reqprot,
+				      unsigned long prot,
 				      unsigned long flags)
 {
-	return security_ops->file_mmap (file, prot, flags);
+	return security_ops->file_mmap (file, reqprot, prot, flags);
 }
 
 static inline int security_file_mprotect (struct vm_area_struct *vma,
+					  unsigned long reqprot,
 					  unsigned long prot)
 {
-	return security_ops->file_mprotect (vma, prot);
+	return security_ops->file_mprotect (vma, reqprot, prot);
 }
 
 static inline int security_file_lock (struct file *file, unsigned int cmd)
@@ -2342,13 +2349,15 @@ static inline int security_file_ioctl (s
 	return 0;
 }
 
-static inline int security_file_mmap (struct file *file, unsigned long prot,
+static inline int security_file_mmap (struct file *file, unsigned long reqprot,
+				      unsigned long prot,
 				      unsigned long flags)
 {
 	return 0;
 }
 
 static inline int security_file_mprotect (struct vm_area_struct *vma,
+					  unsigned long reqprot,
 					  unsigned long prot)
 {
 	return 0;
diff -puN mm/mmap.c~selinux-pass-requested-protection-to-security_file_mmap-mprotect-hooks mm/mmap.c
--- 25/mm/mmap.c~selinux-pass-requested-protection-to-security_file_mmap-mprotect-hooks	Mon Mar  7 16:10:38 2005
+++ 25-akpm/mm/mmap.c	Mon Mar  7 16:10:38 2005
@@ -872,7 +872,7 @@ unsigned long do_mmap_pgoff(struct file 
 	int error;
 	struct rb_node ** rb_link, * rb_parent;
 	int accountable = 1;
-	unsigned long charged = 0;
+	unsigned long charged = 0, reqprot = prot;
 
 	if (file) {
 		if (is_file_hugepages(file))
@@ -990,7 +990,7 @@ unsigned long do_mmap_pgoff(struct file 
 		}
 	}
 
-	error = security_file_mmap(file, prot, flags);
+	error = security_file_mmap(file, reqprot, prot, flags);
 	if (error)
 		return error;
 		
diff -puN mm/mprotect.c~selinux-pass-requested-protection-to-security_file_mmap-mprotect-hooks mm/mprotect.c
--- 25/mm/mprotect.c~selinux-pass-requested-protection-to-security_file_mmap-mprotect-hooks	Mon Mar  7 16:10:38 2005
+++ 25-akpm/mm/mprotect.c	Mon Mar  7 16:10:38 2005
@@ -222,7 +222,7 @@ fail:
 asmlinkage long
 sys_mprotect(unsigned long start, size_t len, unsigned long prot)
 {
-	unsigned long vm_flags, nstart, end, tmp;
+	unsigned long vm_flags, nstart, end, tmp, reqprot;
 	struct vm_area_struct *vma, *prev;
 	int error = -EINVAL;
 	const int grows = prot & (PROT_GROWSDOWN|PROT_GROWSUP);
@@ -240,6 +240,8 @@ sys_mprotect(unsigned long start, size_t
 		return -EINVAL;
 	if (end == start)
 		return 0;
+
+	reqprot = prot;
 	/*
 	 * Does the application expect PROT_READ to imply PROT_EXEC:
 	 */
@@ -293,7 +295,7 @@ sys_mprotect(unsigned long start, size_t
 			goto out;
 		}
 
-		error = security_file_mprotect(vma, prot);
+		error = security_file_mprotect(vma, reqprot, prot);
 		if (error)
 			goto out;
 
diff -puN security/dummy.c~selinux-pass-requested-protection-to-security_file_mmap-mprotect-hooks security/dummy.c
--- 25/security/dummy.c~selinux-pass-requested-protection-to-security_file_mmap-mprotect-hooks	Mon Mar  7 16:10:38 2005
+++ 25-akpm/security/dummy.c	Mon Mar  7 16:10:38 2005
@@ -446,13 +446,16 @@ static int dummy_file_ioctl (struct file
 	return 0;
 }
 
-static int dummy_file_mmap (struct file *file, unsigned long prot,
+static int dummy_file_mmap (struct file *file, unsigned long reqprot,
+			    unsigned long prot,
 			    unsigned long flags)
 {
 	return 0;
 }
 
-static int dummy_file_mprotect (struct vm_area_struct *vma, unsigned long prot)
+static int dummy_file_mprotect (struct vm_area_struct *vma,
+				unsigned long reqprot,
+				unsigned long prot)
 {
 	return 0;
 }
diff -puN security/selinux/hooks.c~selinux-pass-requested-protection-to-security_file_mmap-mprotect-hooks security/selinux/hooks.c
--- 25/security/selinux/hooks.c~selinux-pass-requested-protection-to-security_file_mmap-mprotect-hooks	Mon Mar  7 16:10:38 2005
+++ 25-akpm/security/selinux/hooks.c	Mon Mar  7 16:10:38 2005
@@ -2415,6 +2415,7 @@ static int selinux_file_ioctl(struct fil
 
 static int file_map_prot_check(struct file *file, unsigned long prot, int shared)
 {
+#ifndef CONFIG_PPC32
 	if ((prot & PROT_EXEC) && (!file || (!shared && (prot & PROT_WRITE)))) {
 		/*
 		 * We are making executable an anonymous mapping or a
@@ -2425,6 +2426,7 @@ static int file_map_prot_check(struct fi
 		if (rc)
 			return rc;
 	}
+#endif
 
 	if (file) {
 		/* read access is always possible with a mapping */
@@ -2442,27 +2444,36 @@ static int file_map_prot_check(struct fi
 	return 0;
 }
 
-static int selinux_file_mmap(struct file *file, unsigned long prot, unsigned long flags)
+static int selinux_file_mmap(struct file *file, unsigned long reqprot,
+			     unsigned long prot, unsigned long flags)
 {
 	int rc;
 
-	rc = secondary_ops->file_mmap(file, prot, flags);
+	rc = secondary_ops->file_mmap(file, reqprot, prot, flags);
 	if (rc)
 		return rc;
 
+	if (selinux_checkreqprot)
+		prot = reqprot;
+
 	return file_map_prot_check(file, prot,
 				   (flags & MAP_TYPE) == MAP_SHARED);
 }
 
 static int selinux_file_mprotect(struct vm_area_struct *vma,
+				 unsigned long reqprot,
 				 unsigned long prot)
 {
 	int rc;
 
-	rc = secondary_ops->file_mprotect(vma, prot);
+	rc = secondary_ops->file_mprotect(vma, reqprot, prot);
 	if (rc)
 		return rc;
 
+	if (selinux_checkreqprot)
+		prot = reqprot;
+
+#ifndef CONFIG_PPC32
 	if (vma->vm_file != NULL && vma->anon_vma != NULL && (prot & PROT_EXEC)) {
 		/*
 		 * We are making executable a file mapping that has
@@ -2474,6 +2485,7 @@ static int selinux_file_mprotect(struct 
 		if (rc)
 			return rc;
 	}
+#endif
 
 	return file_map_prot_check(vma->vm_file, prot, vma->vm_flags&VM_SHARED);
 }
diff -puN security/selinux/include/av_permissions.h~selinux-pass-requested-protection-to-security_file_mmap-mprotect-hooks security/selinux/include/av_permissions.h
--- 25/security/selinux/include/av_permissions.h~selinux-pass-requested-protection-to-security_file_mmap-mprotect-hooks	Mon Mar  7 16:10:38 2005
+++ 25-akpm/security/selinux/include/av_permissions.h	Mon Mar  7 16:10:38 2005
@@ -522,6 +522,7 @@
 #define SECURITY__SETENFORCE                      0x00000080UL
 #define SECURITY__SETBOOL                         0x00000100UL
 #define SECURITY__SETSECPARAM                     0x00000200UL
+#define SECURITY__SETCHECKREQPROT                 0x00000400UL
 
 #define SYSTEM__IPC_INFO                          0x00000001UL
 #define SYSTEM__SYSLOG_READ                       0x00000002UL
diff -puN security/selinux/include/av_perm_to_string.h~selinux-pass-requested-protection-to-security_file_mmap-mprotect-hooks security/selinux/include/av_perm_to_string.h
--- 25/security/selinux/include/av_perm_to_string.h~selinux-pass-requested-protection-to-security_file_mmap-mprotect-hooks	Mon Mar  7 16:10:38 2005
+++ 25-akpm/security/selinux/include/av_perm_to_string.h	Mon Mar  7 16:10:38 2005
@@ -83,6 +83,7 @@
    S_(SECCLASS_SECURITY, SECURITY__SETENFORCE, "setenforce")
    S_(SECCLASS_SECURITY, SECURITY__SETBOOL, "setbool")
    S_(SECCLASS_SECURITY, SECURITY__SETSECPARAM, "setsecparam")
+   S_(SECCLASS_SECURITY, SECURITY__SETCHECKREQPROT, "setcheckreqprot")
    S_(SECCLASS_SYSTEM, SYSTEM__IPC_INFO, "ipc_info")
    S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_READ, "syslog_read")
    S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_MOD, "syslog_mod")
diff -puN security/selinux/include/objsec.h~selinux-pass-requested-protection-to-security_file_mmap-mprotect-hooks security/selinux/include/objsec.h
--- 25/security/selinux/include/objsec.h~selinux-pass-requested-protection-to-security_file_mmap-mprotect-hooks	Mon Mar  7 16:10:38 2005
+++ 25-akpm/security/selinux/include/objsec.h	Mon Mar  7 16:10:38 2005
@@ -109,4 +109,6 @@ struct sk_security_struct {
 
 extern int inode_security_set_sid(struct inode *inode, u32 sid);
 
+extern unsigned int selinux_checkreqprot;
+
 #endif /* _SELINUX_OBJSEC_H_ */
diff -puN security/selinux/Kconfig~selinux-pass-requested-protection-to-security_file_mmap-mprotect-hooks security/selinux/Kconfig
--- 25/security/selinux/Kconfig~selinux-pass-requested-protection-to-security_file_mmap-mprotect-hooks	Mon Mar  7 16:10:38 2005
+++ 25-akpm/security/selinux/Kconfig	Mon Mar  7 16:10:38 2005
@@ -76,3 +76,22 @@ config SECURITY_SELINUX_AVC_STATS
 	  /selinux/avc/cache_stats, which may be monitored via
 	  tools such as avcstat.
 
+config SECURITY_SELINUX_CHECKREQPROT_VALUE
+	int "NSA SELinux checkreqprot default value"
+	depends on SECURITY_SELINUX
+	range 0 1
+	default 1
+	help
+	  This option sets the default value for the 'checkreqprot' flag
+	  that determines whether SELinux checks the protection requested
+	  by the application or the protection that will be applied by the
+	  kernel (including any implied execute for read-implies-exec) for
+	  mmap and mprotect calls.  If this option is set to 0 (zero),
+	  SELinux will default to checking the protection that will be applied
+	  by the kernel.  If this option is set to 1 (one), SELinux will
+	  default to checking the protection requested by the application.
+	  The checkreqprot flag may be changed from the default via the
+	  'checkreqprot=' boot parameter.  It may also be changed at runtime
+	  via /selinux/checkreqprot if authorized by policy.
+
+	  If you are unsure how to answer this question, answer 1.
diff -puN security/selinux/selinuxfs.c~selinux-pass-requested-protection-to-security_file_mmap-mprotect-hooks security/selinux/selinuxfs.c
--- 25/security/selinux/selinuxfs.c~selinux-pass-requested-protection-to-security_file_mmap-mprotect-hooks	Mon Mar  7 16:10:38 2005
+++ 25-akpm/security/selinux/selinuxfs.c	Mon Mar  7 16:10:38 2005
@@ -34,6 +34,16 @@
 #include "objsec.h"
 #include "conditional.h"
 
+unsigned int selinux_checkreqprot = CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE;
+
+static int __init checkreqprot_setup(char *str)
+{
+	selinux_checkreqprot = simple_strtoul(str,NULL,0) ? 1 : 0;
+	return 1;
+}
+__setup("checkreqprot=", checkreqprot_setup);
+
+
 static DECLARE_MUTEX(sel_sem);
 
 /* global data for booleans */
@@ -72,6 +82,7 @@ enum sel_inos {
 	SEL_DISABLE,	/* disable SELinux until next reboot */
 	SEL_AVC,	/* AVC management directory */
 	SEL_MEMBER,	/* compute polyinstantiation membership decision */
+	SEL_CHECKREQPROT, /* check requested protection, not kernel-applied one */
 };
 
 #define TMPBUFLEN	12
@@ -300,6 +311,54 @@ static struct file_operations sel_contex
 	.write		= sel_write_context,
 };
 
+static ssize_t sel_read_checkreqprot(struct file *filp, char __user *buf,
+				     size_t count, loff_t *ppos)
+{
+	char tmpbuf[TMPBUFLEN];
+	ssize_t length;
+
+	length = scnprintf(tmpbuf, TMPBUFLEN, "%u", selinux_checkreqprot);
+	return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
+}
+
+static ssize_t sel_write_checkreqprot(struct file * file, const char __user * buf,
+				      size_t count, loff_t *ppos)
+{
+	char *page;
+	ssize_t length;
+	unsigned int new_value;
+
+	length = task_has_security(current, SECURITY__SETCHECKREQPROT);
+	if (length)
+		return length;
+
+	if (count < 0 || count >= PAGE_SIZE)
+		return -ENOMEM;
+	if (*ppos != 0) {
+		/* No partial writes. */
+		return -EINVAL;
+	}
+	page = (char*)get_zeroed_page(GFP_KERNEL);
+	if (!page)
+		return -ENOMEM;
+	length = -EFAULT;
+	if (copy_from_user(page, buf, count))
+		goto out;
+
+	length = -EINVAL;
+	if (sscanf(page, "%u", &new_value) != 1)
+		goto out;
+
+	selinux_checkreqprot = new_value ? 1 : 0;
+	length = count;
+out:
+	free_page((unsigned long) page);
+	return length;
+}
+static struct file_operations sel_checkreqprot_ops = {
+	.read		= sel_read_checkreqprot,
+	.write		= sel_write_checkreqprot,
+};
 
 /*
  * Remaining nodes use transaction based IO methods like nfsd/nfsctl.c
@@ -1182,6 +1241,7 @@ static int sel_fill_super(struct super_b
 		[SEL_MLS] = {"mls", &sel_mls_ops, S_IRUGO},
 		[SEL_DISABLE] = {"disable", &sel_disable_ops, S_IWUSR},
 		[SEL_MEMBER] = {"member", &transaction_ops, S_IRUGO|S_IWUGO},
+		[SEL_CHECKREQPROT] = {"checkreqprot", &sel_checkreqprot_ops, S_IRUGO|S_IWUSR},
 		/* last one */ {""}
 	};
 	ret = simple_fill_super(sb, SELINUX_MAGIC, selinux_files);
_
