linux-mm.kvack.org archive mirror
 help / color / mirror / Atom feed
* [RFC v2 0/9] S.A.R.A. a new stacked LSM
@ 2017-06-15 16:42 Salvatore Mesoraca
  2017-06-15 16:42 ` [RFC v2 1/9] S.A.R.A. Documentation Salvatore Mesoraca
                   ` (8 more replies)
  0 siblings, 9 replies; 21+ messages in thread
From: Salvatore Mesoraca @ 2017-06-15 16:42 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-security-module, kernel-hardening, Salvatore Mesoraca,
	Brad Spengler, PaX Team, Casey Schaufler, Kees Cook,
	James Morris, Serge E. Hallyn, linux-mm, x86, Jann Horn,
	Christoph Hellwig, Thomas Gleixner

S.A.R.A. (S.A.R.A. is Another Recursive Acronym) is a stacked Linux
Security Module that aims to collect heterogeneous security measures,
providing a common interface to manage them.
It can be useful to allow minor security features to use advanced
management options, like user-space configuration files and tools, without
too much overhead.
Some submodules that use this framework are also introduced.
The code is quite long, I apologize for this. Thank you in advance to
anyone who will take the time to review this patchset.

S.A.R.A. is meant to be stacked but it needs cred blobs and the procattr
interface, so I temporarily implemented those parts in a way that won't
be acceptable for upstream, but it works for now. I know that there
is some ongoing work to make cred blobs and procattr stackable, as soon
as the new interfaces will be available I'll reimplement the involved
parts.
At the moment I've been able to test it only on x86.

The only submodule introduced in this patchset is WX Protection.

The kernel-space part is complemented by its user-space counterpart:
saractl [1].
A test suite for WX Protection, called sara-test [2], is also available.

WX Protection aims to improve user-space programs security by applying:
- W^X enforcement: program can't have a page of memory that is marked, at
		   the same time, writable and executable.
- W!->X restriction: any page that could have been marked as writable in
		     the past won't ever be allowed to be marked as
		     executable.
- Executable MMAP prevention: prevents the creation of new executable mmaps
			      after the dynamic libraries have been loaded.
All of the above features can be enabled or disabled both system wide
or on a per executable basis through the use of configuration files managed
by "saractl".
It is important to note that some programs may have issues working with
WX Protection. In particular:
- W^X enforcement will cause problems to any programs that needs
  memory pages mapped both as writable and executable at the same time e.g.
  programs with executable stack markings in the PT_GNU_STACK segment.
- W!->X restriction will cause problems to any program that
  needs to generate executable code at run time or to modify executable
  pages e.g. programs with a JIT compiler built-in or linked against a
  non-PIC library.
- Executable MMAP prevention can work only with programs that have at least
  partial RELRO support. It's disabled automatically for programs that
  lack this feature. It will cause problems to any program that uses dlopen
  or tries to do an executable mmap. Unfortunately this feature is the one
  that could create most problems and should be enabled only after careful
  evaluation.
To extend the scope of the above features, despite the issues that they may
cause, they are complemented by:
- procattr interface: can be used by a program to discover which WX
		      Protection features are enabled and/or to tighten
		      them.
- Trampoline emulation: emulates the execution of well-known "trampolines"
			even when they are placed in non-executable memory.
Parts of WX Protection are inspired by some of the features available in
PaX.

More information can be found in the documentation introduced in the first
patch and in the "commit message" of the following emails.

Changes in v2:
	- Removed USB filtering submodule and relative hook
	- s/saralib/libsara/ typo
	- STR macro renamed to avoid conflicts
	- check_vmflags hook now returns an error code instead of just 1
	  or 0. (suggested by Casey Schaufler)
	- pr_wxp macro rewritten as function for readability
	- Fixed i386 compilation warnings
	- Documentation now states clearly that changes done via procattr
	  interface only apply to current thread. (suggested by Jann Horn)

[1] https://github.com/smeso/saractl
[2] https://github.com/smeso/sara-test

Salvatore Mesoraca (9):
  S.A.R.A. Documentation
  S.A.R.A. framework creation
  Creation of "check_vmflags" LSM hook
  S.A.R.A. cred blob management
  S.A.R.A. WX Protection
  Creation of "pagefault_handler_x86" LSM hook
  Trampoline emulation
  Allowing for stacking procattr support in S.A.R.A.
  S.A.R.A. WX Protection procattr interface

 Documentation/admin-guide/kernel-parameters.txt |  23 +
 Documentation/security/00-INDEX                 |   2 +
 Documentation/security/SARA.rst                 | 170 +++++
 arch/x86/mm/fault.c                             |   6 +
 fs/proc/base.c                                  |  38 +
 include/linux/cred.h                            |   3 +
 include/linux/lsm_hooks.h                       |  21 +
 include/linux/security.h                        |  17 +
 mm/mmap.c                                       |  13 +
 security/Kconfig                                |   1 +
 security/Makefile                               |   2 +
 security/sara/Kconfig                           | 134 ++++
 security/sara/Makefile                          |   4 +
 security/sara/include/sara.h                    |  29 +
 security/sara/include/sara_data.h               |  47 ++
 security/sara/include/securityfs.h              |  59 ++
 security/sara/include/trampolines.h             | 171 +++++
 security/sara/include/utils.h                   |  69 ++
 security/sara/include/wxprot.h                  |  27 +
 security/sara/main.c                            | 107 +++
 security/sara/sara_data.c                       |  79 ++
 security/sara/securityfs.c                      | 560 +++++++++++++++
 security/sara/utils.c                           | 151 ++++
 security/sara/wxprot.c                          | 910 ++++++++++++++++++++++++
 security/security.c                             |  37 +-
 25 files changed, 2678 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/security/SARA.rst
 create mode 100644 security/sara/Kconfig
 create mode 100644 security/sara/Makefile
 create mode 100644 security/sara/include/sara.h
 create mode 100644 security/sara/include/sara_data.h
 create mode 100644 security/sara/include/securityfs.h
 create mode 100644 security/sara/include/trampolines.h
 create mode 100644 security/sara/include/utils.h
 create mode 100644 security/sara/include/wxprot.h
 create mode 100644 security/sara/main.c
 create mode 100644 security/sara/sara_data.c
 create mode 100644 security/sara/securityfs.c
 create mode 100644 security/sara/utils.c
 create mode 100644 security/sara/wxprot.c

-- 
1.9.1

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [RFC v2 1/9] S.A.R.A. Documentation
  2017-06-15 16:42 [RFC v2 0/9] S.A.R.A. a new stacked LSM Salvatore Mesoraca
@ 2017-06-15 16:42 ` Salvatore Mesoraca
  2017-06-15 16:42 ` [RFC v2 2/9] S.A.R.A. framework creation Salvatore Mesoraca
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 21+ messages in thread
From: Salvatore Mesoraca @ 2017-06-15 16:42 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-security-module, kernel-hardening, Salvatore Mesoraca,
	Brad Spengler, PaX Team, Casey Schaufler, Kees Cook,
	James Morris, Serge E. Hallyn, linux-mm, x86, Jann Horn,
	Christoph Hellwig, Thomas Gleixner

Adding documentation for S.A.R.A. LSM.

Signed-off-by: Salvatore Mesoraca <s.mesoraca16@gmail.com>
---
 Documentation/admin-guide/kernel-parameters.txt |  23 ++++
 Documentation/security/00-INDEX                 |   2 +
 Documentation/security/SARA.rst                 | 170 ++++++++++++++++++++++++
 3 files changed, 195 insertions(+)
 create mode 100644 Documentation/security/SARA.rst

diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 0f5c3b4..d8a8d57 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -3702,6 +3702,29 @@
 			1 -- enable.
 			Default value is set via kernel config option.
 
+	sara=		[SARA] Disable or enable S.A.R.A. at boot time.
+			If disabled this way S.A.R.A. can't be enabled
+			again.
+			Format: { "0" | "1" }
+			See security/sara/Kconfig help text
+			0 -- disable.
+			1 -- enable.
+			Default value is set via kernel config option.
+
+	sara_wxprot=	[SARA] Disable or enable S.A.R.A. WX Protection
+			at boot time.
+			Format: { "0" | "1" }
+			See security/sara/Kconfig help text
+			0 -- disable.
+			1 -- enable.
+			Default value is 1.
+
+	sara_wxprot_default_flags= [SARA]
+			Set S.A.R.A. WX Protection default flags.
+			Format: <integer>
+			See S.A.R.A. documentation.
+			Default value is set via kernel config option.
+
 	serialnumber	[BUGS=X86-32]
 
 	shapers=	[NET]
diff --git a/Documentation/security/00-INDEX b/Documentation/security/00-INDEX
index 45c82fd..fe3583c 100644
--- a/Documentation/security/00-INDEX
+++ b/Documentation/security/00-INDEX
@@ -10,6 +10,8 @@ Yama.txt
 	- documentation on the Yama Linux Security Module.
 apparmor.txt
 	- documentation on the AppArmor security extension.
+SARA.rst
+	- documentation on the S.A.R.A. Linux Security Module.
 credentials.txt
 	- documentation about credentials in Linux.
 keys-ecryptfs.txt
diff --git a/Documentation/security/SARA.rst b/Documentation/security/SARA.rst
new file mode 100644
index 0000000..65651d8
--- /dev/null
+++ b/Documentation/security/SARA.rst
@@ -0,0 +1,170 @@
+========
+S.A.R.A.
+========
+
+S.A.R.A. (S.A.R.A. is Another Recursive Acronym) is a stacked Linux Security
+Module that aims to collect heterogeneous security measures, providing a common
+interface to manage them.
+As of today it consists of one submodule:
+
+- WX Protection
+
+
+The kernel-space part is complemented by its user-space counterpart: `saractl` [2]_.
+A test suite for WX Protection, called `sara-test` [4]_, is also available.
+More information about where to find these tools and the full S.A.R.A.
+documentation are in the `External Links and Documentation`_ section.
+
+-------------------------------------------------------------------------------
+
+S.A.R.A.'s Submodules
+=====================
+
+WX Protection
+-------------
+WX Protection aims to improve user-space programs security by applying:
+
+- `W^X enforcement`_
+- `W!->X (once writable never executable) mprotect restriction`_
+- `Executable MMAP prevention`_
+
+All of the above features can be enabled or disabled both system wide
+or on a per executable basis through the use of configuration files managed by
+`saractl` [2]_.
+
+It is important to note that some programs may have issues working with
+WX Protection. In particular:
+
+- **W^X enforcement** will cause problems to any programs that needs
+  memory pages mapped both as writable and executable at the same time e.g.
+  programs with executable stack markings in the *PT_GNU_STACK* segment.
+- **W!->X mprotect restriction** will cause problems to any program that
+  needs to generate executable code at run time or to modify executable
+  pages e.g. programs with a *JIT* compiler built-in or linked against a
+  *non-PIC* library.
+- **Executable MMAP prevention** can work only with programs that have at least
+  partial *RELRO* support. It's disabled automatically for programs that
+  lack this feature. It will cause problems to any program that uses *dlopen*
+  or tries to do an executable mmap. Unfortunately this feature is the one
+  that could create most problems and should be enabled only after careful
+  evaluation.
+
+To extend the scope of the above features, despite the issues that they may
+cause, they are complemented by **/proc/PID/attr/sara/wxprot** interface
+and **trampoline emulation**.
+
+At the moment, WX Protection (unless specified otherwise) runs on `x86_64` and
+`x86_32` (with PAE).
+
+Parts of WX Protection are inspired by some of the features available in PaX.
+
+For further information about configuration file format and user-space
+utilities please take a look at the full documentation [1]_.
+
+W^X enforcement
+----------------------
+W^X means that a program can't have a page of memory that is marked, at the
+same time, writable and executable. This also allow to detect many bad
+behaviours that make life much more easy for attackers. Programs running with
+this feature enabled will be more difficult to exploit in the case they are
+affected by some vulnerabilities, because the attacker will be forced
+to make more steps in order to exploit them.
+
+W!->X (once writable never executable) mprotect restriction
+-----------------------------------------------------------
+"Once writable never executable" means that any page that could have been
+marked as writable in the past won't ever be allowed to be marked (e.g. via
+an mprotect syscall) as executable.
+This goes on the same track as W^X, but is much stricter and prevents
+the runtime creation of new executable code in memory.
+Obviously, this feature does not prevent a program from creating a new file and
+*mmapping* it as executable, however, it will be way more difficult for attackers
+to exploit vulnerabilities if this feature is enabled.
+
+Executable MMAP prevention
+--------------------------
+This feature prevents the creation of new executable mmaps after the dynamic
+libraries have been loaded. When used in combination with **W!->X mprotect
+restriction** this feature will completely prevent the creation of new
+executable code in the current program.
+Obviously, this feature does not prevent cases in which an attacker uses an
+*execve* to start a completely new program. This kind of restriction, if
+needed, can be applied using one of the other LSM that focuses on MAC.
+Please be aware that this feature can break many programs and so it should be
+enabled after careful evaluation.
+
+/proc/PID/attr/sara/wxprot interface
+------------------------------------
+The `procattr` interface can be used by a thread to discover which
+WX Protection features are enabled and/or to tighten them: protection
+can't be softened via procattr.
+The interface is simple: it's a text file with an hexadecimal
+number in it representing enabled features (more information can be
+found in the `Flags values`_ section). Via this interface it is also
+possible to perform a complete memory scan to remove the write permission
+from pages that are both writable and executable.
+
+Protections that prevent the runtime creation of executable code
+can be troublesome for all those programs that actually need to do it
+e.g. programs shipping with a JIT compiler built-in.
+This feature can be use to run the JIT compiler with few restrictions
+while enforcing full WX Protection in the rest of the program.
+
+The preferred way to access this interface is via `libsara` [3]_.
+If you don't want it as a dependency, you can just statically link it
+in your project or copy/paste parts of it.
+To make things simpler `libsara` is the only part of S.A.R.A. released under
+*CC0 - No Rights Reserved* license.
+
+Trampoline emulation
+--------------------
+Some programs need to generate part of their code at runtime. Luckily enough,
+in some cases they only generate well-known code sequences (the
+*trampolines*) that can be easily recognized and emulated by the kernel.
+This way WX Protection can still be active, so a potential attacker won't be
+able to generate arbitrary sequences of code, but just those that are
+explicitly allowed. This is not ideal, but it's still better than having WX
+Protection completely disabled.
+
+In particular S.A.R.A. is able to recognize trampolines used by GCC for nested
+C functions and libffi's trampolines.
+This feature is available only on x86_32 and x86_64.
+
+Flags values
+------------
+Flags are represented as a 16 bit unsigned integer in which every bit indicates
+the status of a given feature:
+
++------------------------------+----------+
+|           Feature            |  Value   |
++==============================+==========+
+| W!->X Heap                   |  0x0001  |
++------------------------------+----------+
+| W!->X Stack                  |  0x0002  |
++------------------------------+----------+
+| W!->X Other memory           |  0x0004  |
++------------------------------+----------+
+| W^X                          |  0x0008  |
++------------------------------+----------+
+| Don't enforce, just complain |  0x0010  |
++------------------------------+----------+
+| Be Verbose                   |  0x0020  |
++------------------------------+----------+
+| Executable MMAP prevention   |  0x0040  |
++------------------------------+----------+
+| Force W^X on setprocattr     |  0x0080  |
++------------------------------+----------+
+| Trampoline emulation         |  0x0100  |
++------------------------------+----------+
+| Children will inherit flags  |  0x0200  |
++------------------------------+----------+
+
+-------------------------------------------------------------------------------
+
+External Links and Documentation
+================================
+
+.. [1] `Documentation	<https://github.com/smeso/sara-doc>`_
+.. [2] `saractl		<https://github.com/smeso/saractl>`_
+.. [3] `libsara		<https://github.com/smeso/libsara>`_
+.. [4] `sara-test	<https://github.com/smeso/sara-test>`_
-- 
1.9.1

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [RFC v2 2/9] S.A.R.A. framework creation
  2017-06-15 16:42 [RFC v2 0/9] S.A.R.A. a new stacked LSM Salvatore Mesoraca
  2017-06-15 16:42 ` [RFC v2 1/9] S.A.R.A. Documentation Salvatore Mesoraca
@ 2017-06-15 16:42 ` Salvatore Mesoraca
  2017-06-15 16:42 ` [RFC v2 3/9] Creation of "check_vmflags" LSM hook Salvatore Mesoraca
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 21+ messages in thread
From: Salvatore Mesoraca @ 2017-06-15 16:42 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-security-module, kernel-hardening, Salvatore Mesoraca,
	Brad Spengler, PaX Team, Casey Schaufler, Kees Cook,
	James Morris, Serge E. Hallyn, linux-mm, x86, Jann Horn,
	Christoph Hellwig, Thomas Gleixner

Initial S.A.R.A. framework setup.
Creation of a simplified interface to securityfs API to store and retrieve
configurations and flags from user-space.
Creation of some generic functions and macros to handle concurrent access
to configurations, memory allocation and path resolution.

Signed-off-by: Salvatore Mesoraca <s.mesoraca16@gmail.com>
---
 include/linux/lsm_hooks.h          |   5 +
 security/Kconfig                   |   1 +
 security/Makefile                  |   2 +
 security/sara/Kconfig              |  42 +++
 security/sara/Makefile             |   3 +
 security/sara/include/sara.h       |  29 ++
 security/sara/include/securityfs.h |  59 ++++
 security/sara/include/utils.h      |  69 +++++
 security/sara/main.c               |  95 +++++++
 security/sara/securityfs.c         | 560 +++++++++++++++++++++++++++++++++++++
 security/sara/utils.c              | 151 ++++++++++
 security/security.c                |   1 +
 12 files changed, 1017 insertions(+)
 create mode 100644 security/sara/Kconfig
 create mode 100644 security/sara/Makefile
 create mode 100644 security/sara/include/sara.h
 create mode 100644 security/sara/include/securityfs.h
 create mode 100644 security/sara/include/utils.h
 create mode 100644 security/sara/main.c
 create mode 100644 security/sara/securityfs.c
 create mode 100644 security/sara/utils.c

diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 080f34e..bd274db 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -1946,5 +1946,10 @@ static inline void __init yama_add_hooks(void) { }
 #else
 static inline void loadpin_add_hooks(void) { };
 #endif
+#ifdef CONFIG_SECURITY_SARA
+void __init sara_init(void);
+#else
+static inline void __init sara_init(void) { };
+#endif
 
 #endif /* ! __LINUX_LSM_HOOKS_H */
diff --git a/security/Kconfig b/security/Kconfig
index 93027fd..1c13545 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -195,6 +195,7 @@ source security/tomoyo/Kconfig
 source security/apparmor/Kconfig
 source security/loadpin/Kconfig
 source security/yama/Kconfig
+source security/sara/Kconfig
 
 source security/integrity/Kconfig
 
diff --git a/security/Makefile b/security/Makefile
index f2d71cd..5d3578e 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -9,6 +9,7 @@ subdir-$(CONFIG_SECURITY_TOMOYO)        += tomoyo
 subdir-$(CONFIG_SECURITY_APPARMOR)	+= apparmor
 subdir-$(CONFIG_SECURITY_YAMA)		+= yama
 subdir-$(CONFIG_SECURITY_LOADPIN)	+= loadpin
+subdir-$(CONFIG_SECURITY_SARA)		+= sara
 
 # always enable default capabilities
 obj-y					+= commoncap.o
@@ -24,6 +25,7 @@ obj-$(CONFIG_SECURITY_TOMOYO)		+= tomoyo/
 obj-$(CONFIG_SECURITY_APPARMOR)		+= apparmor/
 obj-$(CONFIG_SECURITY_YAMA)		+= yama/
 obj-$(CONFIG_SECURITY_LOADPIN)		+= loadpin/
+obj-$(CONFIG_SECURITY_SARA)		+= sara/
 obj-$(CONFIG_CGROUP_DEVICE)		+= device_cgroup.o
 
 # Object integrity file lists
diff --git a/security/sara/Kconfig b/security/sara/Kconfig
new file mode 100644
index 0000000..5b61020
--- /dev/null
+++ b/security/sara/Kconfig
@@ -0,0 +1,42 @@
+menu S.A.R.A.
+
+config SECURITY_SARA
+	bool "Enable S.A.R.A."
+	depends on SECURITY
+	select SECURITYFS
+	default n
+	help
+	  This selects S.A.R.A. LSM which aims to collect heterogeneous
+	  security measures providing a common interface to manage them.
+	  This LSM will always be stacked with the selected primary LSM and
+	  other stacked LSMs.
+	  Further information can be found in Documentation/security/SARA.rst.
+
+	  If unsure, answer N.
+
+config SECURITY_SARA_DEFAULT_DISABLED
+	bool "S.A.R.A. will be disabled at boot."
+	depends on SECURITY_SARA
+	default n
+	help
+	  If you say Y here, S.A.R.A. will not be enabled at startup. You can
+	  override this option at boot time via "sara=[1|0]" kernel parameter
+	  or via user-space utilities.
+	  This option is useful for distro kernels.
+
+	  If unsure, answer N.
+
+config SECURITY_SARA_NO_RUNTIME_ENABLE
+	bool "S.A.R.A. can be turn on only at boot time."
+	depends on SECURITY_SARA_DEFAULT_DISABLED
+	default y
+	help
+	  By enabling this option it won't be possible to turn on S.A.R.A.
+	  at runtime via user-space utilities. However it can still be
+	  turned on at boot time via the "sara=1" kernel parameter.
+	  This option is functionally equivalent to "sara=0" kernel parameter.
+	  This option is useful for distro kernels.
+
+	  If unsure, answer Y.
+
+endmenu
diff --git a/security/sara/Makefile b/security/sara/Makefile
new file mode 100644
index 0000000..8acd291
--- /dev/null
+++ b/security/sara/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_SECURITY_SARA) := sara.o
+
+sara-y := main.o securityfs.o utils.o
diff --git a/security/sara/include/sara.h b/security/sara/include/sara.h
new file mode 100644
index 0000000..1001a8a
--- /dev/null
+++ b/security/sara/include/sara.h
@@ -0,0 +1,29 @@
+/*
+ * S.A.R.A. Linux Security Module
+ *
+ * Copyright (C) 2017 Salvatore Mesoraca <s.mesoraca16@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef __SARA_H
+#define __SARA_H
+
+#include <linux/types.h>
+#include <uapi/linux/limits.h>
+
+#define SARA_VERSION 0
+#define SARA_PATH_MAX PATH_MAX
+
+#undef pr_fmt
+#define pr_fmt(fmt) "SARA: " fmt
+
+extern bool sara_config_locked __read_mostly;
+extern bool sara_enabled __read_mostly;
+
+void sara_init(void) __init;
+
+#endif /* __SARA_H */
diff --git a/security/sara/include/securityfs.h b/security/sara/include/securityfs.h
new file mode 100644
index 0000000..57e6306
--- /dev/null
+++ b/security/sara/include/securityfs.h
@@ -0,0 +1,59 @@
+/*
+ * S.A.R.A. Linux Security Module
+ *
+ * Copyright (C) 2017 Salvatore Mesoraca <s.mesoraca16@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef __SARA_SECURITYFS_H
+#define __SARA_SECURITYFS_H
+
+#include <linux/init.h>
+
+#define SARA_SUBTREE_NN_LEN 24
+#define SARA_CONFIG_HASH_LEN 20
+
+struct sara_secfs_node;
+
+int sara_secfs_init(void) __init;
+int sara_secfs_subtree_register(const char *subtree_name,
+				const struct sara_secfs_node *nodes,
+				size_t size) __init;
+
+enum sara_secfs_node_type {
+	SARA_SECFS_BOOL,
+	SARA_SECFS_READONLY_INT,
+	SARA_SECFS_CONFIG_LOAD,
+	SARA_SECFS_CONFIG_DUMP,
+	SARA_SECFS_CONFIG_HASH,
+};
+
+struct sara_secfs_node {
+	const enum sara_secfs_node_type type;
+	void *const data;
+	const size_t dir_contents_len;
+	const char name[SARA_SUBTREE_NN_LEN];
+};
+
+struct sara_secfs_fptrs {
+	int (*const load)(const char *, size_t);
+	ssize_t (*const dump)(char **);
+	int (*const hash)(char **);
+};
+
+struct sara_secfs_bool_flag {
+	const char notice_line[SARA_SUBTREE_NN_LEN];
+	bool *const flag;
+};
+
+#define DEFINE_SARA_SECFS_BOOL_FLAG(NAME, VAR)		\
+const struct sara_secfs_bool_flag NAME = {		\
+	.notice_line = #VAR,				\
+	.flag = &VAR,					\
+}
+
+#endif /* __SARA_SECURITYFS_H */
diff --git a/security/sara/include/utils.h b/security/sara/include/utils.h
new file mode 100644
index 0000000..166b9ed
--- /dev/null
+++ b/security/sara/include/utils.h
@@ -0,0 +1,69 @@
+/*
+ * S.A.R.A. Linux Security Module
+ *
+ * Copyright (C) 2017 Salvatore Mesoraca <s.mesoraca16@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef __SARA_UTILS_H
+#define __SARA_UTILS_H
+
+#include <linux/kref.h>
+#include <linux/rcupdate.h>
+#include <linux/spinlock.h>
+
+char *get_absolute_path(const struct path *spath, char **buf);
+char *get_current_path(char **buf);
+void *sara_kvmalloc(size_t size) __attribute__((malloc));
+void *sara_kvcalloc(size_t n, size_t size) __attribute__((malloc));
+
+static inline void release_entry(struct kref *ref)
+{
+	/* All work is done after the return from kref_put(). */
+}
+
+#define SARA_CONFIG_GET_RCU(DEST, CONFIG) do {	\
+	rcu_read_lock();			\
+	DEST = rcu_dereference(CONFIG);		\
+} while (0)
+
+#define SARA_CONFIG_PUT_RCU(DATA) do {		\
+	rcu_read_unlock();			\
+	DATA = NULL;				\
+} while (0)
+
+#define SARA_CONFIG_GET(DEST, CONFIG) do {				\
+	rcu_read_lock();						\
+	do {								\
+		DEST = rcu_dereference(CONFIG);				\
+	} while (DEST && !kref_get_unless_zero(&DEST->refcount));	\
+	rcu_read_unlock();						\
+} while (0)
+
+#define SARA_CONFIG_PUT(DATA, FREE) do {		\
+	if (kref_put(&DATA->refcount, release_entry)) {	\
+		synchronize_rcu();			\
+		FREE(DATA);				\
+	}						\
+	DATA = NULL;					\
+} while (0)
+
+#define SARA_CONFIG_REPLACE(CONFIG, NEW, FREE, LOCK) do {	\
+	typeof(NEW) tmp;					\
+	spin_lock(LOCK);					\
+	tmp = rcu_dereference_protected(CONFIG,			\
+					lockdep_is_held(LOCK));	\
+	rcu_assign_pointer(CONFIG, NEW);			\
+	if (kref_put(&tmp->refcount, release_entry)) {		\
+		spin_unlock(LOCK);				\
+		synchronize_rcu();				\
+		FREE(tmp);					\
+	} else							\
+		spin_unlock(LOCK);				\
+} while (0)
+
+#endif /* __SARA_UTILS_H */
diff --git a/security/sara/main.c b/security/sara/main.c
new file mode 100644
index 0000000..2007735
--- /dev/null
+++ b/security/sara/main.c
@@ -0,0 +1,95 @@
+/*
+ * S.A.R.A. Linux Security Module
+ *
+ * Copyright (C) 2017 Salvatore Mesoraca <s.mesoraca16@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/bug.h>
+#include <linux/kernel.h>
+#include <linux/printk.h>
+
+#include "include/sara.h"
+#include "include/securityfs.h"
+
+static const int sara_version = SARA_VERSION;
+
+#ifdef CONFIG_SECURITY_SARA_NO_RUNTIME_ENABLE
+bool sara_config_locked __read_mostly = true;
+#else
+bool sara_config_locked __read_mostly;
+#endif
+
+#ifdef CONFIG_SECURITY_SARA_DEFAULT_DISABLED
+bool sara_enabled __read_mostly;
+#else
+bool sara_enabled __read_mostly = true;
+#endif
+
+static DEFINE_SARA_SECFS_BOOL_FLAG(sara_enabled_data, sara_enabled);
+static DEFINE_SARA_SECFS_BOOL_FLAG(sara_config_locked_data, sara_config_locked);
+
+static int __init sara_enabled_setup(char *str)
+{
+	if (str[0] == '1' && str[1] == '\0') {
+		sara_enabled = true;
+		sara_config_locked = false;
+	} else {
+		sara_enabled = false;
+		sara_config_locked = true;
+	}
+	return 1;
+}
+__setup("sara=", sara_enabled_setup);
+
+static const struct sara_secfs_node main_fs[] __initconst = {
+	{
+		.name = "enabled",
+		.type = SARA_SECFS_BOOL,
+		.data = (void *) &sara_enabled_data,
+	},
+	{
+		.name = "locked",
+		.type = SARA_SECFS_BOOL,
+		.data = (void *) &sara_config_locked_data,
+	},
+	{
+		.name = "version",
+		.type = SARA_SECFS_READONLY_INT,
+		.data = (int *) &sara_version,
+	},
+};
+
+void __init sara_init(void)
+{
+	if (!sara_enabled && sara_config_locked) {
+		pr_notice("permanently disabled.\n");
+		return;
+	}
+
+	pr_debug("initializing...\n");
+
+	if (sara_secfs_subtree_register("main",
+					main_fs,
+					ARRAY_SIZE(main_fs))) {
+		pr_crit("impossible to register main fs.\n");
+		goto error;
+	}
+
+	pr_debug("initialized.\n");
+
+	if (sara_enabled)
+		pr_info("enabled\n");
+	else
+		pr_notice("disabled\n");
+	return;
+
+error:
+	sara_enabled = false;
+	sara_config_locked = true;
+	pr_crit("permanently disabled.\n");
+}
diff --git a/security/sara/securityfs.c b/security/sara/securityfs.c
new file mode 100644
index 0000000..d493245
--- /dev/null
+++ b/security/sara/securityfs.c
@@ -0,0 +1,560 @@
+/*
+ * S.A.R.A. Linux Security Module
+ *
+ * Copyright (C) 2017 Salvatore Mesoraca <s.mesoraca16@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/capability.h>
+#include <linux/ctype.h>
+#include <linux/mm.h>
+#include <linux/printk.h>
+#include <linux/security.h>
+#include <linux/seq_file.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+
+#include "include/sara.h"
+#include "include/utils.h"
+#include "include/securityfs.h"
+
+#define __SARA_STR_HELPER(x) #x
+#define SARA_STR(x) __SARA_STR_HELPER(x)
+
+static struct dentry *fs_root;
+
+static inline bool check_config_write_access(void)
+{
+	if (unlikely(sara_config_locked && !capable(CAP_MAC_ADMIN))) {
+		pr_warn("config write access blocked.\n");
+		return false;
+	}
+	return true;
+}
+
+static bool check_config_access(const struct file *file)
+{
+	if (file->f_flags & O_WRONLY || file->f_flags & O_RDWR)
+		if (unlikely(!check_config_write_access()))
+			return false;
+	return true;
+}
+
+static int file_flag_show(struct seq_file *seq, void *v)
+{
+	bool *flag = ((struct sara_secfs_bool_flag *)seq->private)->flag;
+
+	seq_printf(seq, "%d\n", *flag);
+	return 0;
+}
+
+static ssize_t file_flag_write(struct file *file,
+				const char __user *ubuf,
+				size_t buf_size,
+				loff_t *offset)
+{
+	struct sara_secfs_bool_flag *bool_flag =
+		((struct seq_file *) file->private_data)->private;
+	char kbuf[2] = {'A', '\n'};
+	bool nf;
+
+	if (unlikely(*offset != 0))
+		return -ESPIPE;
+
+	if (unlikely(buf_size != 1 && buf_size != 2))
+		return -EPERM;
+
+	if (unlikely(copy_from_user(kbuf, ubuf, buf_size)))
+		return -EFAULT;
+
+	if (unlikely(kbuf[1] != '\n'))
+		return -EPERM;
+
+	switch (kbuf[0]) {
+	case '0':
+		nf = false;
+		break;
+	case '1':
+		nf = true;
+		break;
+	default:
+		return -EPERM;
+	}
+
+	*bool_flag->flag = nf;
+
+	if (strlen(bool_flag->notice_line) > 0)
+		pr_notice("flag \"%s\" set to %d\n",
+			  bool_flag->notice_line,
+			  nf);
+
+	return buf_size;
+}
+
+static int file_flag_open(struct inode *inode, struct file *file)
+{
+	if (unlikely(!check_config_access(file)))
+		return -EACCES;
+	return single_open(file, file_flag_show, inode->i_private);
+}
+
+static const struct file_operations file_flag = {
+	.owner		= THIS_MODULE,
+	.open		= file_flag_open,
+	.write		= file_flag_write,
+	.read		= seq_read,
+	.release	= single_release,
+};
+
+static int file_readonly_int_show(struct seq_file *seq, void *v)
+{
+	int *flag = seq->private;
+
+	seq_printf(seq, "%d\n", *flag);
+	return 0;
+}
+
+static int file_readonly_int_open(struct inode *inode, struct file *file)
+{
+	if (unlikely(!check_config_access(file)))
+		return -EACCES;
+	return single_open(file, file_readonly_int_show, inode->i_private);
+}
+
+static const struct file_operations file_readonly_int = {
+	.owner		= THIS_MODULE,
+	.open		= file_readonly_int_open,
+	.read		= seq_read,
+	.release	= single_release,
+};
+
+static ssize_t file_config_loader_write(struct file *file,
+					const char __user *ubuf,
+					size_t buf_size,
+					loff_t *offset)
+{
+	const struct sara_secfs_fptrs *fptrs = file->private_data;
+	char *kbuf = NULL;
+	ssize_t ret;
+
+	ret = -ESPIPE;
+	if (unlikely(*offset != 0))
+		goto out;
+
+	ret = -ENOMEM;
+	kbuf = sara_kvmalloc(buf_size);
+	if (unlikely(kbuf == NULL))
+		goto out;
+
+	ret = -EFAULT;
+	if (unlikely(copy_from_user(kbuf, ubuf, buf_size)))
+		goto out;
+
+	ret = fptrs->load(kbuf, buf_size);
+
+	if (unlikely(ret))
+		goto out;
+
+	ret = buf_size;
+
+out:
+	kvfree(kbuf);
+	return ret;
+}
+
+static int file_config_loader_open(struct inode *inode, struct file *file)
+{
+	if (unlikely(!check_config_access(file)))
+		return -EACCES;
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static const struct file_operations file_config_loader = {
+	.owner		= THIS_MODULE,
+	.open		= file_config_loader_open,
+	.write		= file_config_loader_write,
+};
+
+static int file_config_show(struct seq_file *seq, void *v)
+{
+	const struct sara_secfs_fptrs *fptrs = seq->private;
+	char *buf = NULL;
+	ssize_t ret;
+
+	ret = fptrs->dump(&buf);
+	if (unlikely(ret <= 0))
+		goto out;
+	seq_write(seq, buf, ret);
+	kvfree(buf);
+	ret = 0;
+out:
+	return ret;
+}
+
+static int file_dumper_open(struct inode *inode, struct file *file)
+{
+	if (unlikely(!check_config_access(file)))
+		return -EACCES;
+	return single_open(file, file_config_show, inode->i_private);
+}
+
+static const struct file_operations file_config_dumper = {
+	.owner		= THIS_MODULE,
+	.open		= file_dumper_open,
+	.read		= seq_read,
+	.release	= single_release,
+};
+
+static int file_hash_show(struct seq_file *seq, void *v)
+{
+	const struct sara_secfs_fptrs *fptrs = seq->private;
+	char *buf = NULL;
+	int ret;
+
+	ret = fptrs->hash(&buf);
+	if (unlikely(ret))
+		goto out;
+	seq_printf(seq, "%" SARA_STR(SARA_CONFIG_HASH_LEN) "phN\n", buf);
+	kvfree(buf);
+	ret = 0;
+out:
+	return ret;
+}
+
+static int file_hash_open(struct inode *inode, struct file *file)
+{
+	if (unlikely(!check_config_access(file)))
+		return -EACCES;
+	return single_open(file, file_hash_show, inode->i_private);
+}
+
+static const struct file_operations file_hash = {
+	.owner		= THIS_MODULE,
+	.open		= file_hash_open,
+	.read		= seq_read,
+	.release	= single_release,
+};
+
+static int mk_dir(struct dentry *parent,
+		const char *dir_name,
+		struct dentry **dir_out)
+{
+	int ret = 0;
+
+	*dir_out = securityfs_create_dir(dir_name, parent);
+	if (IS_ERR(*dir_out)) {
+		ret = -PTR_ERR(*dir_out);
+		*dir_out = NULL;
+	}
+	return ret;
+}
+
+static int mk_bool_flag(struct dentry *parent,
+			const char *file_name,
+			struct dentry **dir_out,
+			void *flag)
+{
+	int ret = 0;
+
+	*dir_out = securityfs_create_file(file_name,
+					0600,
+					parent,
+					flag,
+					&file_flag);
+	if (IS_ERR(*dir_out)) {
+		ret = -PTR_ERR(*dir_out);
+		*dir_out = NULL;
+	}
+	return 0;
+}
+
+static int mk_readonly_int(struct dentry *parent,
+			const char *file_name,
+			struct dentry **dir_out,
+			void *readonly_int)
+{
+	int ret = 0;
+
+	*dir_out = securityfs_create_file(file_name,
+					0400,
+					parent,
+					readonly_int,
+					&file_readonly_int);
+	if (IS_ERR(*dir_out)) {
+		ret = -PTR_ERR(*dir_out);
+		*dir_out = NULL;
+	}
+	return 0;
+}
+
+static int mk_config_loader(struct dentry *parent,
+			const char *file_name,
+			struct dentry **dir_out,
+			void *fptrs)
+{
+	int ret = 0;
+
+	*dir_out = securityfs_create_file(file_name,
+					0200,
+					parent,
+					fptrs,
+					&file_config_loader);
+	if (IS_ERR(*dir_out)) {
+		ret = -PTR_ERR(*dir_out);
+		*dir_out = NULL;
+	}
+	return 0;
+}
+
+static int mk_config_dumper(struct dentry *parent,
+				const char *file_name,
+				struct dentry **dir_out,
+				void *fptrs)
+{
+	int ret = 0;
+
+	*dir_out = securityfs_create_file(file_name,
+					0400,
+					parent,
+					fptrs,
+					&file_config_dumper);
+	if (IS_ERR(*dir_out)) {
+		ret = -PTR_ERR(*dir_out);
+		*dir_out = NULL;
+	}
+	return 0;
+}
+
+static int mk_config_hash(struct dentry *parent,
+			const char *file_name,
+			struct dentry **dir_out,
+			void *fptrs)
+{
+	int ret = 0;
+
+	*dir_out = securityfs_create_file(file_name,
+					0400,
+					parent,
+					fptrs,
+					&file_hash);
+	if (IS_ERR(*dir_out)) {
+		ret = -PTR_ERR(*dir_out);
+		*dir_out = NULL;
+	}
+	return 0;
+}
+
+struct sara_secfs_subtree {
+	char name[SARA_SUBTREE_NN_LEN];
+	size_t size;
+	struct dentry **nodes;
+	const struct sara_secfs_node *nodes_description;
+	struct list_head subtree_list;
+};
+
+static LIST_HEAD(subtree_list);
+
+int __init sara_secfs_subtree_register(const char *subtree_name,
+				const struct sara_secfs_node *nodes,
+				size_t size)
+{
+	int ret;
+	struct sara_secfs_subtree *subtree = NULL;
+
+	ret = -EINVAL;
+	if (unlikely(size < 1))
+		goto error;
+	ret = -ENOMEM;
+	subtree = kmalloc(sizeof(*subtree), GFP_KERNEL);
+	if (unlikely(subtree == NULL))
+		goto error;
+	strncpy(subtree->name,
+		subtree_name,
+		sizeof(subtree->name));
+	subtree->size = size+1;
+	subtree->nodes = kcalloc(subtree->size,
+				sizeof(*subtree->nodes),
+				GFP_KERNEL);
+	if (unlikely(subtree->nodes == NULL))
+		goto error;
+	subtree->nodes_description = nodes;
+	INIT_LIST_HEAD(&subtree->subtree_list);
+	list_add(&subtree->subtree_list, &subtree_list);
+	return 0;
+
+error:
+	kfree(subtree);
+	pr_warn("SECFS: Impossible to register '%s' (%d).\n",
+		subtree_name, ret);
+	return ret;
+}
+
+static inline int __init create_node(enum sara_secfs_node_type type,
+					struct dentry *parent,
+					const char *name,
+					struct dentry **output,
+					void *data)
+{
+	switch (type) {
+	case SARA_SECFS_BOOL:
+		return mk_bool_flag(parent, name, output, data);
+	case SARA_SECFS_READONLY_INT:
+		return mk_readonly_int(parent, name, output, data);
+	case SARA_SECFS_CONFIG_LOAD:
+		return mk_config_loader(parent, name, output, data);
+	case SARA_SECFS_CONFIG_DUMP:
+		return mk_config_dumper(parent, name, output, data);
+	case SARA_SECFS_CONFIG_HASH:
+		return mk_config_hash(parent, name, output, data);
+	default:
+		return -EINVAL;
+	}
+}
+
+static void subtree_unplug(struct sara_secfs_subtree *subtree)
+{
+	int i;
+
+	for (i = 0; i < subtree->size; ++i) {
+		if (subtree->nodes[i] != NULL) {
+			securityfs_remove(subtree->nodes[i]);
+			subtree->nodes[i] = NULL;
+		}
+	}
+}
+
+static int __init subtree_plug(struct sara_secfs_subtree *subtree)
+{
+	int ret;
+	int i;
+	const struct sara_secfs_node *nodes = subtree->nodes_description;
+
+	ret = -EINVAL;
+	if (unlikely(fs_root == NULL))
+		goto out;
+	ret = mk_dir(fs_root,
+			subtree->name,
+			&subtree->nodes[subtree->size-1]);
+	if (unlikely(ret))
+		goto out_unplug;
+	for (i = 0; i < subtree->size-1; ++i) {
+		ret = create_node(nodes[i].type,
+				  subtree->nodes[subtree->size-1],
+				  nodes[i].name,
+				  &subtree->nodes[i],
+				  nodes[i].data);
+		if (unlikely(ret))
+			goto out_unplug;
+	}
+	return 0;
+
+out_unplug:
+	subtree_unplug(subtree);
+out:
+	pr_warn("SECFS: Impossible to plug '%s' (%d).\n", subtree->name, ret);
+	return ret;
+}
+
+static int __init subtree_plug_all(void)
+{
+	int ret;
+	struct list_head *position;
+	struct sara_secfs_subtree *subtree;
+
+	ret = -EINVAL;
+	if (unlikely(fs_root == NULL))
+		goto out;
+	ret = 0;
+	list_for_each(position, &subtree_list) {
+		subtree = list_entry(position,
+					struct sara_secfs_subtree,
+					subtree_list);
+		if (subtree->nodes[0] == NULL) {
+			ret = subtree_plug(subtree);
+			if (unlikely(ret))
+				goto out;
+		}
+	}
+out:
+	if (unlikely(ret))
+		pr_warn("SECFS: Impossible to plug subtrees (%d).\n", ret);
+	return ret;
+}
+
+static void __init subtree_free_all(bool unplug)
+{
+	struct list_head *position;
+	struct list_head *next;
+	struct sara_secfs_subtree *subtree;
+
+	list_for_each_safe(position, next, &subtree_list) {
+		subtree = list_entry(position,
+					struct sara_secfs_subtree,
+					subtree_list);
+		list_del(position);
+		if (unplug)
+			subtree_unplug(subtree);
+		kfree(subtree->nodes);
+		kfree(subtree);
+	}
+}
+
+static int mk_root(void)
+{
+	int ret = -1;
+
+	if (fs_root == NULL)
+		ret = mk_dir(NULL, "sara", &fs_root);
+	if (unlikely(ret || fs_root == NULL))
+		pr_warn("SECFS: Impossible to create root (%d).\n", ret);
+	return ret;
+}
+
+static inline void rm_root(void)
+{
+	if (likely(fs_root != NULL)) {
+		securityfs_remove(fs_root);
+		fs_root = NULL;
+	}
+}
+
+static inline void __init sara_secfs_destroy(void)
+{
+	subtree_free_all(true);
+	rm_root();
+}
+
+int __init sara_secfs_init(void)
+{
+	int ret;
+
+	if (!sara_enabled && sara_config_locked)
+		return 0;
+
+	fs_root = NULL;
+
+	ret = mk_root();
+	if (unlikely(ret))
+		goto error;
+
+	ret = subtree_plug_all();
+	if (unlikely(ret))
+		goto error;
+
+	subtree_free_all(false);
+
+	pr_debug("securityfs initilaized.\n");
+	return 0;
+
+error:
+	sara_secfs_destroy();
+	pr_crit("impossible to build securityfs.\n");
+	return ret;
+}
+
+fs_initcall(sara_secfs_init);
diff --git a/security/sara/utils.c b/security/sara/utils.c
new file mode 100644
index 0000000..9250b7b
--- /dev/null
+++ b/security/sara/utils.c
@@ -0,0 +1,151 @@
+/*
+ * S.A.R.A. Linux Security Module
+ *
+ * Copyright (C) 2017 Salvatore Mesoraca <s.mesoraca16@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/dcache.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#include "include/sara.h"
+#include "include/utils.h"
+
+/**
+ * get_absolute_path - return the absolute path for a struct path
+ * @spath: the struct path to report
+ * @buf: double pointer where the newly allocated buffer will be placed
+ *
+ * Returns a pointer into @buf or an error code.
+ *
+ * The caller MUST kvfree @buf when finished using it.
+ */
+char *get_absolute_path(const struct path *spath, char **buf)
+{
+	size_t size = 128;
+	char *work_buf = NULL;
+	char *path = NULL;
+
+	do {
+		kvfree(work_buf);
+		work_buf = NULL;
+		if (size > SARA_PATH_MAX) {
+			path = ERR_PTR(-ENAMETOOLONG);
+			goto error;
+		}
+		work_buf = sara_kvmalloc(size);
+		if (unlikely(work_buf == NULL)) {
+			path = ERR_PTR(-ENOMEM);
+			goto error;
+		}
+		path = d_absolute_path(spath, work_buf, size);
+		size *= 2;
+	} while (PTR_ERR(path) == -ENAMETOOLONG);
+	if (!IS_ERR(path))
+		goto out;
+
+error:
+	kvfree(work_buf);
+	work_buf = NULL;
+out:
+	*buf = work_buf;
+	return path;
+}
+
+/**
+ * get_current_path - return the absolute path for the exe_file
+ *		      in the current task_struct, falling back
+ *		      to the contents of the comm field.
+ * @buf: double pointer where the newly allocated buffer will be placed
+ *
+ * Returns a pointer into @buf or an error code.
+ *
+ * The caller MUST kvfree @buf when finished using it.
+ */
+char *get_current_path(char **buf)
+{
+	struct file *exe_file;
+	char *path = NULL;
+
+	exe_file = get_task_exe_file(current);
+	if (exe_file) {
+		path = get_absolute_path(&exe_file->f_path, buf);
+		fput(exe_file);
+	}
+	if (IS_ERR_OR_NULL(path)) {
+		*buf = kzalloc(sizeof(current->comm), GFP_KERNEL);
+		get_task_comm(*buf, current);
+		path = *buf;
+	}
+	return path;
+}
+
+/**
+ * sara_kvmalloc - do allocation preferring kmalloc but falling back to vmalloc
+ * @size: how many bytes of memory are required
+ *
+ * Return: allocated buffer or NULL if failed
+ *
+ * It is possible that ruleset being loaded from the user is larger than
+ * what can be allocated by kmalloc, in those cases fall back to vmalloc.
+ *
+ * Nearly identical copy of AppArmor's __aa_kvmalloc.
+ */
+void *__attribute__((malloc)) sara_kvmalloc(size_t size)
+{
+	void *buffer = NULL;
+
+	__might_sleep(__FILE__, __LINE__, 0);
+
+	if (size == 0)
+		return NULL;
+
+	/* do not attempt kmalloc if we need more than 16 pages at once */
+	if (size <= (16*PAGE_SIZE))
+		buffer = kmalloc(size, GFP_KERNEL | __GFP_NORETRY |
+				 __GFP_NOWARN);
+	if (!buffer)
+		buffer = vmalloc(size);
+
+	return buffer;
+}
+
+/**
+ * sara_kvcalloc - do allocation preferring kcalloc but falling back to vmalloc
+ * @size: number of object to allocate
+ * @size: how many bytes of memory are required per each object
+ *
+ * Return: allocated buffer or NULL if failed
+ *
+ * It is possible that ruleset being loaded from the user is larger than
+ * what can be allocated by kcalloc, in those cases fall back to vmalloc.
+ *
+ * Nearly identical copy of AppArmor's __aa_kvmalloc.
+ */
+void *__attribute__((malloc)) sara_kvcalloc(size_t n, size_t size)
+{
+	void *buffer = NULL;
+
+	__might_sleep(__FILE__, __LINE__, 0);
+
+	if (size == 0)
+		return NULL;
+
+	/* do not attempt kmalloc if we need more than 16 pages at once */
+	if (size <= (16*PAGE_SIZE))
+		buffer = kcalloc(n, size, GFP_KERNEL | __GFP_NORETRY |
+				 __GFP_NOWARN);
+	if (!buffer)
+		buffer = vzalloc(n*size);
+
+	return buffer;
+}
diff --git a/security/security.c b/security/security.c
index b9fea39..42c8028 100644
--- a/security/security.c
+++ b/security/security.c
@@ -69,6 +69,7 @@ int __init security_init(void)
 	capability_add_hooks();
 	yama_add_hooks();
 	loadpin_add_hooks();
+	sara_init();
 
 	/*
 	 * Load all the remaining security modules.
-- 
1.9.1

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [RFC v2 3/9] Creation of "check_vmflags" LSM hook
  2017-06-15 16:42 [RFC v2 0/9] S.A.R.A. a new stacked LSM Salvatore Mesoraca
  2017-06-15 16:42 ` [RFC v2 1/9] S.A.R.A. Documentation Salvatore Mesoraca
  2017-06-15 16:42 ` [RFC v2 2/9] S.A.R.A. framework creation Salvatore Mesoraca
@ 2017-06-15 16:42 ` Salvatore Mesoraca
  2017-06-27 23:05   ` Kees Cook
  2017-06-15 16:42 ` [RFC v2 4/9] S.A.R.A. cred blob management Salvatore Mesoraca
                   ` (5 subsequent siblings)
  8 siblings, 1 reply; 21+ messages in thread
From: Salvatore Mesoraca @ 2017-06-15 16:42 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-security-module, kernel-hardening, Salvatore Mesoraca,
	Brad Spengler, PaX Team, Casey Schaufler, Kees Cook,
	James Morris, Serge E. Hallyn, linux-mm, x86, Jann Horn,
	Christoph Hellwig, Thomas Gleixner

Creation of a new LSM hook to check if a given configuration of vmflags,
for a new memory allocation request, should be allowed or not.
It's placed in "do_mmap", "do_brk_flags" and "__install_special_mapping".

Signed-off-by: Salvatore Mesoraca <s.mesoraca16@gmail.com>
---
 include/linux/lsm_hooks.h |  7 +++++++
 include/linux/security.h  |  6 ++++++
 mm/mmap.c                 | 13 +++++++++++++
 security/security.c       |  5 +++++
 4 files changed, 31 insertions(+)

diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index bd274db..33dab16 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -483,6 +483,11 @@
  *	@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.
+ * @check_vmflags:
+ *	Check if the requested @vmflags are allowed.
+ *	@vmflags contains requested the vmflags.
+ *	Return 0 if the operation is allowed to continue otherwise return
+ *	the appropriate error code.
  * @file_lock:
  *	Check permission before performing file locking operations.
  *	Note: this hook mediates both flock and fcntl style locks.
@@ -1477,6 +1482,7 @@
 				unsigned long prot, unsigned long flags);
 	int (*file_mprotect)(struct vm_area_struct *vma, unsigned long reqprot,
 				unsigned long prot);
+	int (*check_vmflags)(vm_flags_t vmflags);
 	int (*file_lock)(struct file *file, unsigned int cmd);
 	int (*file_fcntl)(struct file *file, unsigned int cmd,
 				unsigned long arg);
@@ -1747,6 +1753,7 @@ struct security_hook_heads {
 	struct list_head mmap_addr;
 	struct list_head mmap_file;
 	struct list_head file_mprotect;
+	struct list_head check_vmflags;
 	struct list_head file_lock;
 	struct list_head file_fcntl;
 	struct list_head file_set_fowner;
diff --git a/include/linux/security.h b/include/linux/security.h
index af675b5..8701872 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -300,6 +300,7 @@ int security_mmap_file(struct file *file, unsigned long prot,
 int security_mmap_addr(unsigned long addr);
 int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
 			   unsigned long prot);
+int security_check_vmflags(vm_flags_t vmflags);
 int security_file_lock(struct file *file, unsigned int cmd);
 int security_file_fcntl(struct file *file, unsigned int cmd, unsigned long arg);
 void security_file_set_fowner(struct file *file);
@@ -823,6 +824,11 @@ static inline int security_file_mprotect(struct vm_area_struct *vma,
 	return 0;
 }
 
+static inline int security_check_vmflags(vm_flags_t vmflags)
+{
+	return 0;
+}
+
 static inline int security_file_lock(struct file *file, unsigned int cmd)
 {
 	return 0;
diff --git a/mm/mmap.c b/mm/mmap.c
index f82741e..132061b 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -1311,6 +1311,7 @@ unsigned long do_mmap(struct file *file, unsigned long addr,
 {
 	struct mm_struct *mm = current->mm;
 	int pkey = 0;
+	int error;
 
 	*populate = 0;
 
@@ -1363,6 +1364,10 @@ unsigned long do_mmap(struct file *file, unsigned long addr,
 	vm_flags |= calc_vm_prot_bits(prot, pkey) | calc_vm_flag_bits(flags) |
 			mm->def_flags | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;
 
+	error = security_check_vmflags(vm_flags);
+	if (error)
+		return error;
+
 	if (flags & MAP_LOCKED)
 		if (!can_do_mlock())
 			return -EPERM;
@@ -2833,6 +2838,10 @@ static int do_brk_flags(unsigned long addr, unsigned long request, unsigned long
 		return -EINVAL;
 	flags |= VM_DATA_DEFAULT_FLAGS | VM_ACCOUNT | mm->def_flags;
 
+	error = security_check_vmflags(flags);
+	if (error)
+		return error;
+
 	error = get_unmapped_area(NULL, addr, len, 0, MAP_FIXED);
 	if (offset_in_page(error))
 		return error;
@@ -3208,6 +3217,10 @@ static struct vm_area_struct *__install_special_mapping(
 	int ret;
 	struct vm_area_struct *vma;
 
+	ret = security_check_vmflags(vm_flags);
+	if (ret)
+		return ERR_PTR(ret);
+
 	vma = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL);
 	if (unlikely(vma == NULL))
 		return ERR_PTR(-ENOMEM);
diff --git a/security/security.c b/security/security.c
index 42c8028..7e45846 100644
--- a/security/security.c
+++ b/security/security.c
@@ -900,6 +900,11 @@ int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
 	return call_int_hook(file_mprotect, 0, vma, reqprot, prot);
 }
 
+int security_check_vmflags(vm_flags_t vmflags)
+{
+	return call_int_hook(check_vmflags, 0, vmflags);
+}
+
 int security_file_lock(struct file *file, unsigned int cmd)
 {
 	return call_int_hook(file_lock, 0, file, cmd);
-- 
1.9.1

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [RFC v2 4/9] S.A.R.A. cred blob management
  2017-06-15 16:42 [RFC v2 0/9] S.A.R.A. a new stacked LSM Salvatore Mesoraca
                   ` (2 preceding siblings ...)
  2017-06-15 16:42 ` [RFC v2 3/9] Creation of "check_vmflags" LSM hook Salvatore Mesoraca
@ 2017-06-15 16:42 ` Salvatore Mesoraca
  2017-06-15 16:42 ` [RFC v2 5/9] S.A.R.A. WX Protection Salvatore Mesoraca
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 21+ messages in thread
From: Salvatore Mesoraca @ 2017-06-15 16:42 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-security-module, kernel-hardening, Salvatore Mesoraca,
	Brad Spengler, PaX Team, Casey Schaufler, Kees Cook,
	James Morris, Serge E. Hallyn, linux-mm, x86, Jann Horn,
	Christoph Hellwig, Thomas Gleixner

Creation of the S.A.R.A. cred blob management "API".
In order to allow S.A.R.A. to be stackable with other LSMs, it doesn't use
the "security" field of struct cred, instead it uses an ad hoc field named
security_sara.
This solution is probably not acceptable for upstream, so this part will
be modified as soon as the LSM stackable cred blob management will be
available.

Signed-off-by: Salvatore Mesoraca <s.mesoraca16@gmail.com>
---
 include/linux/cred.h              |  3 ++
 security/sara/Makefile            |  2 +-
 security/sara/include/sara_data.h | 47 +++++++++++++++++++++++
 security/sara/main.c              |  6 +++
 security/sara/sara_data.c         | 79 +++++++++++++++++++++++++++++++++++++++
 5 files changed, 136 insertions(+), 1 deletion(-)
 create mode 100644 security/sara/include/sara_data.h
 create mode 100644 security/sara/sara_data.c

diff --git a/include/linux/cred.h b/include/linux/cred.h
index b03e7d0..007feb5 100644
--- a/include/linux/cred.h
+++ b/include/linux/cred.h
@@ -141,6 +141,9 @@ struct cred {
 #ifdef CONFIG_SECURITY
 	void		*security;	/* subjective LSM security */
 #endif
+#ifdef CONFIG_SECURITY_SARA
+	void		*security_sara;
+#endif
 	struct user_struct *user;	/* real user ID subscription */
 	struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */
 	struct group_info *group_info;	/* supplementary groups for euid/fsgid */
diff --git a/security/sara/Makefile b/security/sara/Makefile
index 8acd291..14bf7a8 100644
--- a/security/sara/Makefile
+++ b/security/sara/Makefile
@@ -1,3 +1,3 @@
 obj-$(CONFIG_SECURITY_SARA) := sara.o
 
-sara-y := main.o securityfs.o utils.o
+sara-y := main.o securityfs.o utils.o sara_data.o
diff --git a/security/sara/include/sara_data.h b/security/sara/include/sara_data.h
new file mode 100644
index 0000000..7ed04fd
--- /dev/null
+++ b/security/sara/include/sara_data.h
@@ -0,0 +1,47 @@
+/*
+ * S.A.R.A. Linux Security Module
+ *
+ * Copyright (C) 2017 Salvatore Mesoraca <s.mesoraca16@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef __SARA_DATA_H
+#define __SARA_DATA_H
+
+#include <linux/init.h>
+
+int sara_data_init(void) __init;
+
+#ifdef CONFIG_SECURITY_SARA_WXPROT
+
+struct sara_data {
+	unsigned long	relro_page;
+	u16		wxp_flags;
+	bool		relro_page_found;
+	bool		mmap_blocked;
+};
+
+#define get_sara_data_leftvalue(X) ((X)->security_sara)
+#define get_sara_data(X) ((struct sara_data *) (X)->security_sara)
+#define get_current_sara_data() get_sara_data(current_cred())
+
+#define get_sara_wxp_flags(X) (get_sara_data((X))->wxp_flags)
+#define get_current_sara_wxp_flags() get_sara_wxp_flags(current_cred())
+
+#define get_sara_relro_page(X) (get_sara_data((X))->relro_page)
+#define get_current_sara_relro_page() get_sara_relro_page(current_cred())
+
+#define get_sara_relro_page_found(X) (get_sara_data((X))->relro_page_found)
+#define get_current_sara_relro_page_found() \
+	get_sara_relro_page_found(current_cred())
+
+#define get_sara_mmap_blocked(X) (get_sara_data((X))->mmap_blocked)
+#define get_current_sara_mmap_blocked() get_sara_mmap_blocked(current_cred())
+
+#endif
+
+#endif /* __SARA_H */
diff --git a/security/sara/main.c b/security/sara/main.c
index 2007735..644ff6d 100644
--- a/security/sara/main.c
+++ b/security/sara/main.c
@@ -14,6 +14,7 @@
 #include <linux/printk.h>
 
 #include "include/sara.h"
+#include "include/sara_data.h"
 #include "include/securityfs.h"
 
 static const int sara_version = SARA_VERSION;
@@ -80,6 +81,11 @@ void __init sara_init(void)
 		goto error;
 	}
 
+	if (sara_data_init()) {
+		pr_crit("impossible to initialize creds.\n");
+		goto error;
+	}
+
 	pr_debug("initialized.\n");
 
 	if (sara_enabled)
diff --git a/security/sara/sara_data.c b/security/sara/sara_data.c
new file mode 100644
index 0000000..8f11cd1
--- /dev/null
+++ b/security/sara/sara_data.c
@@ -0,0 +1,79 @@
+/*
+ * S.A.R.A. Linux Security Module
+ *
+ * Copyright (C) 2017 Salvatore Mesoraca <s.mesoraca16@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include "include/sara_data.h"
+
+#ifdef CONFIG_SECURITY_SARA_WXPROT
+#include <linux/cred.h>
+#include <linux/lsm_hooks.h>
+#include <linux/mm.h>
+
+static int sara_cred_alloc_blank(struct cred *cred, gfp_t gfp)
+{
+	struct sara_data *d;
+
+	d = kzalloc(sizeof(*d), gfp);
+	if (d == NULL)
+		return -ENOMEM;
+	get_sara_data_leftvalue(cred) = d;
+	return 0;
+}
+
+static void sara_cred_free(struct cred *cred)
+{
+	struct sara_data *d;
+
+	d = get_sara_data(cred);
+	if (d != NULL) {
+		kfree(d);
+		get_sara_data_leftvalue(cred) = NULL;
+	}
+}
+
+static int sara_cred_prepare(struct cred *new, const struct cred *old,
+			     gfp_t gfp)
+{
+	struct sara_data *d;
+
+	d = kmemdup(get_sara_data(old), sizeof(*d), gfp);
+	if (d == NULL)
+		return -ENOMEM;
+	get_sara_data_leftvalue(new) = d;
+	return 0;
+}
+
+static void sara_cred_transfer(struct cred *new, const struct cred *old)
+{
+	*get_sara_data(new) = *get_sara_data(old);
+}
+
+static struct security_hook_list data_hooks[] __ro_after_init = {
+	LSM_HOOK_INIT(cred_alloc_blank, sara_cred_alloc_blank),
+	LSM_HOOK_INIT(cred_free, sara_cred_free),
+	LSM_HOOK_INIT(cred_prepare, sara_cred_prepare),
+	LSM_HOOK_INIT(cred_transfer, sara_cred_transfer),
+};
+
+int __init sara_data_init(void)
+{
+	security_add_hooks(data_hooks, ARRAY_SIZE(data_hooks), "sara");
+	return sara_cred_alloc_blank((struct cred *) current->real_cred,
+				     GFP_KERNEL);
+}
+
+#else /* CONFIG_SECURITY_SARA_WXPROT */
+
+int __init sara_data_init(void)
+{
+	return 0;
+}
+
+#endif
-- 
1.9.1

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [RFC v2 5/9] S.A.R.A. WX Protection
  2017-06-15 16:42 [RFC v2 0/9] S.A.R.A. a new stacked LSM Salvatore Mesoraca
                   ` (3 preceding siblings ...)
  2017-06-15 16:42 ` [RFC v2 4/9] S.A.R.A. cred blob management Salvatore Mesoraca
@ 2017-06-15 16:42 ` Salvatore Mesoraca
  2017-06-27 23:04   ` Kees Cook
  2017-06-15 16:42 ` [RFC v2 6/9] Creation of "pagefault_handler_x86" LSM hook Salvatore Mesoraca
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 21+ messages in thread
From: Salvatore Mesoraca @ 2017-06-15 16:42 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-security-module, kernel-hardening, Salvatore Mesoraca,
	Brad Spengler, PaX Team, Casey Schaufler, Kees Cook,
	James Morris, Serge E. Hallyn, linux-mm, x86, Jann Horn,
	Christoph Hellwig, Thomas Gleixner

Introduction of S.A.R.A. WX Protection.
It aims to improve user-space programs security by applying:
- W^X enforcement
- W!->X (once writable never executable) mprotect restriction
- Executable MMAP prevention

All of the above features can be enabled or disabled both system wide
or on a per executable basis through the use of configuration.
W^X enforcement works by blocking any memory allocation or mprotect
invocation with both the WRITE and the EXEC flags enabled.
W!->X restriction works by preventing any mprotect invocation that makes
executable any page that is flagged VM_MAYWRITE.
This feature can be configured separately for stack, heap and other
allocations.
Executable MMAP prevention works by preventing any new executable
allocation after the dynamic libraries have been loaded. It works under the
assumption that, when the dynamic libraries have been finished loading, the
RELRO section will be marked read only.

Parts of WX Protection are inspired by some of the features available in
PaX.

Signed-off-by: Salvatore Mesoraca <s.mesoraca16@gmail.com>
---
 security/sara/Kconfig          |  75 +++++
 security/sara/Makefile         |   1 +
 security/sara/include/wxprot.h |  27 ++
 security/sara/main.c           |   6 +
 security/sara/wxprot.c         | 646 +++++++++++++++++++++++++++++++++++++++++
 5 files changed, 755 insertions(+)
 create mode 100644 security/sara/include/wxprot.h
 create mode 100644 security/sara/wxprot.c

diff --git a/security/sara/Kconfig b/security/sara/Kconfig
index 5b61020..6c74069 100644
--- a/security/sara/Kconfig
+++ b/security/sara/Kconfig
@@ -39,4 +39,79 @@ config SECURITY_SARA_NO_RUNTIME_ENABLE
 
 	  If unsure, answer Y.
 
+config SECURITY_SARA_WXPROT
+	bool "WX Protection: W^X and W!->X protections"
+	depends on SECURITY_SARA
+	default y
+	help
+	  WX Protection aims to improve user-space programs security by applying:
+	    - W^X memory restriction
+	    - W!->X (once writable never executable) mprotect restriction
+	    - Executable MMAP prevention
+	  See Documentation/security/SARA.rst. for further information.
+
+	  If unsure, answer Y.
+
+choice
+	prompt "Default action for W^X and W!->X protections"
+	depends on SECURITY_SARA
+	depends on SECURITY_SARA_WXPROT
+	default SECURITY_SARA_WXPROT_DEFAULT_FLAGS_ALL_COMPLAIN_VERBOSE
+
+        help
+	  Choose the default behaviour of WX Protection when no config
+	  rule matches or no rule is loaded.
+	  For further information on available flags and their meaning
+	  see Documentation/security/SARA.rst.
+
+	config SECURITY_SARA_WXPROT_DEFAULT_FLAGS_ALL_COMPLAIN_VERBOSE
+		bool "Protections enabled but not enforced."
+		help
+		  All features enabled except "Executable MMAP prevention",
+		  verbose reporting, but no actual enforce: it just complains.
+		  Its numeric value is 0x3f, for more information see
+		  Documentation/security/SARA.rst.
+
+        config SECURITY_SARA_WXPROT_DEFAULT_FLAGS_ALL_ENFORCE_VERBOSE
+		bool "Full protection, verbose."
+		help
+		  All features enabled except "Executable MMAP prevention".
+		  The enabled features will be enforced with verbose reporting.
+		  Its numeric value is 0x2f, for more information see
+		  Documentation/security/SARA.rst.
+
+        config SECURITY_SARA_WXPROT_DEFAULT_FLAGS_ALL_ENFORCE
+		bool "Full protection, quiet."
+		help
+		  All features enabled except "Executable MMAP prevention".
+		  The enabled features will be enforced quietly.
+		  Its numeric value is 0xf, for more information see
+		  Documentation/security/SARA.rst.
+
+	config SECURITY_SARA_WXPROT_DEFAULT_FLAGS_NONE
+		bool "No protection at all."
+		help
+		  All features disabled.
+		  Its numeric value is 0, for more information see
+		  Documentation/security/SARA.rst.
+endchoice
+
+config SECURITY_SARA_WXPROT_DISABLED
+	bool "WX protection will be disabled at boot."
+	depends on SECURITY_SARA_WXPROT
+	default n
+	help
+	  If you say Y here WX protection won't be enabled at startup. You can
+	  override this option via user-space utilities or at boot time via
+	  "sara_wxprot=[0|1]" kernel parameter.
+
+	  If unsure, answer N.
+
+config SECURITY_SARA_WXPROT_DEFAULT_FLAGS
+	hex
+	default "0x3f" if SECURITY_SARA_WXPROT_DEFAULT_FLAGS_ALL_COMPLAIN_VERBOSE
+	default "0x2f" if SECURITY_SARA_WXPROT_DEFAULT_FLAGS_ALL_ENFORCE_VERBOSE
+	default "0xf" if SECURITY_SARA_WXPROT_DEFAULT_FLAGS_ALL_ENFORCE
+	default "0" if SECURITY_SARA_WXPROT_DEFAULT_FLAGS_NONE
+
 endmenu
diff --git a/security/sara/Makefile b/security/sara/Makefile
index 14bf7a8..74a3338 100644
--- a/security/sara/Makefile
+++ b/security/sara/Makefile
@@ -1,3 +1,4 @@
 obj-$(CONFIG_SECURITY_SARA) := sara.o
 
 sara-y := main.o securityfs.o utils.o sara_data.o
+sara-$(CONFIG_SECURITY_SARA_WXPROT) += wxprot.o
diff --git a/security/sara/include/wxprot.h b/security/sara/include/wxprot.h
new file mode 100644
index 0000000..23b8a7b
--- /dev/null
+++ b/security/sara/include/wxprot.h
@@ -0,0 +1,27 @@
+/*
+ * S.A.R.A. Linux Security Module
+ *
+ * Copyright (C) 2017 Salvatore Mesoraca <s.mesoraca16@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef __SARA_WXPROT_H
+#define __SARA_WXPROT_H
+
+#ifdef CONFIG_SECURITY_SARA_WXPROT
+
+#include <linux/init.h>
+int sara_wxprot_init(void) __init;
+
+#else /* CONFIG_SECURITY_SARA_WXPROT */
+inline int sara_wxprot_init(void)
+{
+	return 0;
+}
+#endif /* CONFIG_SECURITY_SARA_WXPROT */
+
+#endif /* __SARA_WXPROT_H */
diff --git a/security/sara/main.c b/security/sara/main.c
index 644ff6d..2512560 100644
--- a/security/sara/main.c
+++ b/security/sara/main.c
@@ -16,6 +16,7 @@
 #include "include/sara.h"
 #include "include/sara_data.h"
 #include "include/securityfs.h"
+#include "include/wxprot.h"
 
 static const int sara_version = SARA_VERSION;
 
@@ -86,6 +87,11 @@ void __init sara_init(void)
 		goto error;
 	}
 
+	if (sara_wxprot_init()) {
+		pr_crit("impossible to initialize WX protections.\n");
+		goto error;
+	}
+
 	pr_debug("initialized.\n");
 
 	if (sara_enabled)
diff --git a/security/sara/wxprot.c b/security/sara/wxprot.c
new file mode 100644
index 0000000..f9233a5
--- /dev/null
+++ b/security/sara/wxprot.c
@@ -0,0 +1,646 @@
+/*
+ * S.A.R.A. Linux Security Module
+ *
+ * Copyright (C) 2017 Salvatore Mesoraca <s.mesoraca16@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifdef CONFIG_SECURITY_SARA_WXPROT
+
+#include <linux/binfmts.h>
+#include <linux/cred.h>
+#include <linux/elf.h>
+#include <linux/kref.h>
+#include <linux/lsm_hooks.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/printk.h>
+#include <linux/ratelimit.h>
+#include <linux/spinlock.h>
+
+#include "include/sara.h"
+#include "include/sara_data.h"
+#include "include/utils.h"
+#include "include/securityfs.h"
+#include "include/wxprot.h"
+
+#define SARA_WXPROT_CONFIG_VERSION 0
+
+#define SARA_WXP_HEAP		0x0001
+#define SARA_WXP_STACK		0x0002
+#define SARA_WXP_OTHER		0x0004
+#define SARA_WXP_WXORX		0x0008
+#define SARA_WXP_COMPLAIN	0x0010
+#define SARA_WXP_VERBOSE	0x0020
+#define SARA_WXP_MMAP		0x0040
+#define SARA_WXP_TRANSFER	0x0200
+#define SARA_WXP_NONE		0x0000
+#define SARA_WXP_MPROTECT	(SARA_WXP_HEAP	| \
+				SARA_WXP_STACK	| \
+				SARA_WXP_OTHER)
+#define __SARA_WXP_ALL		(SARA_WXP_MPROTECT	| \
+				SARA_WXP_MMAP		| \
+				SARA_WXP_WXORX		| \
+				SARA_WXP_COMPLAIN	| \
+				SARA_WXP_VERBOSE)
+#define SARA_WXP_ALL		__SARA_WXP_ALL
+
+struct wxprot_rule {
+	char *path;
+	u16 flags;
+	bool exact;
+};
+
+struct wxprot_config_container {
+	u32 rules_size;
+	struct wxprot_rule *rules;
+	size_t buf_len;
+	struct kref refcount;
+	char hash[SARA_CONFIG_HASH_LEN];
+};
+
+static struct wxprot_config_container __rcu *wxprot_config;
+
+static const int wxprot_config_version = SARA_WXPROT_CONFIG_VERSION;
+static bool wxprot_enabled __read_mostly = true;
+static DEFINE_SPINLOCK(wxprot_config_lock);
+
+static u16 default_flags __ro_after_init =
+				CONFIG_SECURITY_SARA_WXPROT_DEFAULT_FLAGS;
+
+static const bool wxprot_emutramp;
+
+static void pr_wxp(char *msg)
+{
+	char *buf, *path;
+
+	path = get_current_path(&buf);
+	pr_notice_ratelimited("WXP: %s in '%s' (%d).\n",
+			      msg, path, current->pid);
+	kvfree(buf);
+}
+
+static bool are_flags_valid(u16 flags)
+{
+	flags &= ~SARA_WXP_TRANSFER;
+	if (unlikely((flags & SARA_WXP_ALL) != flags))
+		return false;
+	if (unlikely(flags & SARA_WXP_MPROTECT &&
+		     !(flags & SARA_WXP_WXORX)))
+		return false;
+	if (unlikely(flags & (SARA_WXP_COMPLAIN | SARA_WXP_VERBOSE) &&
+		     !(flags & (SARA_WXP_MPROTECT |
+				SARA_WXP_WXORX |
+				SARA_WXP_MMAP))))
+		return false;
+	return true;
+}
+
+static int __init sara_wxprot_enabled_setup(char *str)
+{
+	if (str[0] == '1' && str[1] == '\0')
+		wxprot_enabled = true;
+	else
+		wxprot_enabled = false;
+	return 1;
+}
+__setup("sara_wxprot=", sara_wxprot_enabled_setup);
+
+static int __init sara_wxprot_default_setup(char *str)
+{
+	u16 flags = default_flags;
+
+	if (kstrtou16(str, 0, &flags) != 0 || !are_flags_valid(flags))
+		return 1;
+	default_flags = flags;
+	return 1;
+}
+__setup("sara_wxprot_default_flags=", sara_wxprot_default_setup);
+
+
+/*
+ * MMAP exec restriction
+ */
+#define PT_GNU_RELRO (PT_LOOS + 0x474e552)
+
+union elfh {
+	struct elf32_hdr c32;
+	struct elf64_hdr c64;
+};
+
+union elfp {
+	struct elf32_phdr c32;
+	struct elf64_phdr c64;
+};
+
+#define find_relro_section(ELFH, ELFP, FILE, RELRO, FOUND) do {		\
+	unsigned long i;						\
+	int _tmp;							\
+	if (ELFH.e_type == ET_DYN || ELFH.e_type == ET_EXEC) {		\
+		for (i = 0; i < ELFH.e_phnum; ++i) {			\
+			_tmp = kernel_read(FILE,			\
+				ELFH.e_phoff + i*sizeof(ELFP),		\
+				(char *)&ELFP,				\
+				sizeof(ELFP));				\
+			if (_tmp != sizeof(ELFP))			\
+				break;					\
+			if (ELFP.p_type == PT_GNU_RELRO) {		\
+				RELRO = ELFP.p_offset >> PAGE_SHIFT;	\
+				FOUND = true;				\
+				break;					\
+			}						\
+		}							\
+	}								\
+} while (0)
+
+static int set_relro_page(struct linux_binprm *bprm)
+{
+	union elfh elf_h;
+	union elfp elf_p;
+	unsigned long relro_page = 0;
+	bool relro_page_found = false;
+	int ret;
+
+	ret = kernel_read(bprm->file, 0,
+			(char *)&elf_h,
+			sizeof(elf_h));
+	if (ret == sizeof(elf_h) &&
+	    strncmp(elf_h.c32.e_ident, ELFMAG, SELFMAG) == 0) {
+		if (elf_h.c32.e_ident[EI_CLASS] == ELFCLASS32) {
+			find_relro_section(elf_h.c32,
+					   elf_p.c32,
+					   bprm->file,
+					   relro_page,
+					   relro_page_found);
+		} else if (IS_ENABLED(CONFIG_X86_64) &&
+			   elf_h.c64.e_ident[EI_CLASS] == ELFCLASS64) {
+			find_relro_section(elf_h.c64,
+					   elf_p.c64,
+					   bprm->file,
+					   relro_page,
+					   relro_page_found);
+		}
+	}
+	get_sara_relro_page(bprm->cred) = relro_page;
+	get_sara_relro_page_found(bprm->cred) = relro_page_found;
+	if (relro_page_found)
+		return 0;
+	else
+		return 1;
+}
+
+static inline int is_relro_page(const struct vm_area_struct *vma)
+{
+	if (get_current_sara_relro_page_found() &&
+	    get_current_sara_relro_page() == vma->vm_pgoff)
+		return 1;
+	return 0;
+}
+
+/*
+ * LSM hooks
+ */
+static int sara_bprm_set_creds(struct linux_binprm *bprm)
+{
+	int i;
+	struct wxprot_config_container *c;
+	u16 sara_wxp_flags = default_flags;
+	char *buf = NULL;
+	char *path = NULL;
+
+	sara_wxp_flags = get_sara_wxp_flags(bprm->cred);
+	get_sara_mmap_blocked(bprm->cred) = false;
+	get_sara_relro_page_found(bprm->cred) = false;
+	get_sara_wxp_flags(bprm->cred) = SARA_WXP_NONE;
+
+	if (!sara_enabled || !wxprot_enabled)
+		return 0;
+
+	if (!(sara_wxp_flags & SARA_WXP_TRANSFER)) {
+		sara_wxp_flags = default_flags;
+		path = get_absolute_path(&bprm->file->f_path, &buf);
+		if (IS_ERR(path)) {
+			path = (char *) bprm->interp;
+			if (PTR_ERR(path) == -ENAMETOOLONG)
+				pr_warn_ratelimited("WXP: path too long for '%s'. Default flags will be used.\n",
+						path);
+			else
+				pr_warn_ratelimited("WXP: can't find path for '%s'. Default flags will be used.\n",
+						path);
+			goto skip_flags;
+		}
+		SARA_CONFIG_GET_RCU(c, wxprot_config);
+		for (i = 0; i < c->rules_size; ++i) {
+			if ((c->rules[i].exact &&
+			strcmp(c->rules[i].path, path) == 0) ||
+			(!c->rules[i].exact &&
+			strncmp(c->rules[i].path,
+				path,
+				strlen(c->rules[i].path)) == 0)) {
+				sara_wxp_flags = c->rules[i].flags;
+				/* most specific path always come first */
+				break;
+			}
+		}
+		SARA_CONFIG_PUT_RCU(c);
+	} else
+		path = (char *) bprm->interp;
+
+	if (sara_wxp_flags != default_flags &&
+	    sara_wxp_flags & SARA_WXP_VERBOSE)
+		pr_info_ratelimited("WXP: '%s' run with flags '0x%x'.\n",
+				    path, sara_wxp_flags);
+
+skip_flags:
+	if (set_relro_page(bprm)) {
+		if (sara_wxp_flags & SARA_WXP_VERBOSE &&
+		    sara_wxp_flags & SARA_WXP_MMAP)
+			pr_notice_ratelimited("WXP: failed to find RELRO section in '%s'.\n",
+					      path);
+		sara_wxp_flags &= ~SARA_WXP_MMAP;
+	}
+	kvfree(buf);
+	get_sara_wxp_flags(bprm->cred) = sara_wxp_flags;
+	return 0;
+}
+
+static int sara_check_vmflags(vm_flags_t vm_flags)
+{
+	u16 sara_wxp_flags = get_current_sara_wxp_flags();
+
+	if (sara_enabled && wxprot_enabled) {
+		if (sara_wxp_flags & SARA_WXP_WXORX &&
+		    vm_flags & VM_WRITE &&
+		    vm_flags & VM_EXEC) {
+			if ((sara_wxp_flags & SARA_WXP_VERBOSE))
+				pr_wxp("W^X");
+			if (!(sara_wxp_flags & SARA_WXP_COMPLAIN))
+				return -EPERM;
+		}
+		if (sara_wxp_flags & SARA_WXP_MMAP &&
+		    (vm_flags & VM_EXEC ||
+		     (!(vm_flags & VM_MAYWRITE) && (vm_flags & VM_MAYEXEC))) &&
+		    get_current_sara_mmap_blocked()) {
+			if ((sara_wxp_flags & SARA_WXP_VERBOSE))
+				pr_wxp("executable mmap");
+			if (!(sara_wxp_flags & SARA_WXP_COMPLAIN))
+				return -EPERM;
+		}
+	}
+
+	return 0;
+}
+
+static int sara_file_mprotect(struct vm_area_struct *vma,
+				unsigned long reqprot,
+				unsigned long prot)
+{
+	u16 sara_wxp_flags = get_current_sara_wxp_flags();
+
+	if (!sara_enabled || !wxprot_enabled)
+		return 0;
+
+	if (sara_wxp_flags & SARA_WXP_MPROTECT &&
+	    prot & PROT_EXEC &&
+	    !(vma->vm_flags & VM_EXEC) &&
+	    vma->vm_flags & VM_MAYWRITE) {
+		if ((sara_wxp_flags & SARA_WXP_MPROTECT) == SARA_WXP_MPROTECT &&
+		    !(sara_wxp_flags & SARA_WXP_COMPLAIN) &&
+		    !(sara_wxp_flags & SARA_WXP_VERBOSE))
+			return -EACCES;
+		else if (vma->vm_file) {
+			if (sara_wxp_flags & SARA_WXP_OTHER) {
+				if ((sara_wxp_flags & SARA_WXP_VERBOSE))
+					pr_wxp("mprotect on file mmap");
+				if (!(sara_wxp_flags & SARA_WXP_COMPLAIN))
+					return -EACCES;
+			}
+		} else if (vma->vm_start >= vma->vm_mm->start_brk &&
+			vma->vm_end <= vma->vm_mm->brk) {
+			if (sara_wxp_flags & SARA_WXP_HEAP) {
+				if ((sara_wxp_flags & SARA_WXP_VERBOSE))
+					pr_wxp("mprotect on heap");
+				if (!(sara_wxp_flags &
+					SARA_WXP_COMPLAIN))
+					return -EACCES;
+			}
+		} else if ((vma->vm_start <= vma->vm_mm->start_stack &&
+			    vma->vm_end >= vma->vm_mm->start_stack) ||
+			   vma_is_stack_for_current(vma)) {
+			if (sara_wxp_flags & SARA_WXP_STACK) {
+				if ((sara_wxp_flags &
+					SARA_WXP_VERBOSE))
+					pr_wxp("mprotect on stack");
+				if (!(sara_wxp_flags &
+					SARA_WXP_COMPLAIN))
+					return -EACCES;
+			}
+		} else if (sara_wxp_flags & SARA_WXP_OTHER) {
+			if ((sara_wxp_flags & SARA_WXP_VERBOSE))
+				pr_wxp("mprotect on anon mmap");
+			if (!(sara_wxp_flags & SARA_WXP_COMPLAIN))
+				return -EACCES;
+		}
+	}
+
+	if (sara_wxp_flags & SARA_WXP_WXORX &&
+	    prot & PROT_EXEC &&
+	    prot & PROT_WRITE &&
+	    (!(vma->vm_flags & VM_EXEC) ||
+	     !(vma->vm_flags & VM_WRITE))) {
+		if ((sara_wxp_flags & SARA_WXP_VERBOSE))
+			pr_wxp("W^X");
+		if (!(sara_wxp_flags & SARA_WXP_COMPLAIN))
+			return -EACCES;
+	}
+
+	if (vma->vm_flags & VM_WRITE &&
+	    !(prot & PROT_WRITE) &&
+	    is_relro_page(vma))
+		get_current_sara_mmap_blocked() = true;
+
+	return 0;
+}
+
+static struct security_hook_list wxprot_hooks[] __ro_after_init = {
+	LSM_HOOK_INIT(bprm_set_creds, sara_bprm_set_creds),
+	LSM_HOOK_INIT(check_vmflags, sara_check_vmflags),
+	LSM_HOOK_INIT(file_mprotect, sara_file_mprotect),
+};
+
+struct binary_config_header {
+	char magic[8];
+	__le32 version;
+	__le32 rules_size;
+	char hash[SARA_CONFIG_HASH_LEN];
+} __packed;
+
+struct binary_config_rule {
+	__le16 path_len;
+	__le16 flags;
+	u8 exact;
+} __packed;
+
+static void config_free(struct wxprot_config_container *data)
+{
+	int i;
+
+	for (i = 0; i < data->rules_size; ++i)
+		kfree(data->rules[i].path);
+	kvfree(data->rules);
+	kfree(data);
+}
+
+static int config_load(const char *buf, size_t buf_len)
+{
+	int ret;
+	int i;
+	int path_len;
+	size_t inc;
+	size_t last_path_len = SARA_PATH_MAX;
+	bool last_exact = true;
+	const char *pos;
+	struct wxprot_config_container *new;
+	struct binary_config_header *h;
+	struct binary_config_rule *r;
+
+	ret = -EINVAL;
+	if (unlikely(buf_len < sizeof(*h)))
+		goto out;
+
+	h = (struct binary_config_header *) buf;
+	pos = buf + sizeof(*h);
+
+	ret = -EINVAL;
+	if (unlikely(memcmp(h->magic, "SARAWXPR", 8) != 0))
+		goto out;
+	if (unlikely(le32_to_cpu(h->version) != wxprot_config_version))
+		goto out;
+
+	ret = -ENOMEM;
+	new = kmalloc(sizeof(*new), GFP_KERNEL);
+	if (unlikely(new == NULL))
+		goto out;
+	kref_init(&new->refcount);
+	new->rules_size = le32_to_cpu(h->rules_size);
+	BUILD_BUG_ON(sizeof(new->hash) != sizeof(h->hash));
+	memcpy(new->hash, h->hash, sizeof(new->hash));
+	if (unlikely(new->rules_size == 0)) {
+		new->rules = NULL;
+		goto replace;
+	}
+
+	ret = -ENOMEM;
+	new->rules = sara_kvcalloc(new->rules_size,
+				   sizeof(*new->rules));
+	if (unlikely(new->rules == NULL))
+		goto out_new;
+	for (i = 0; i < new->rules_size; ++i) {
+		r = (struct binary_config_rule *) pos;
+		pos += sizeof(*r);
+		inc = pos-buf;
+		path_len = le16_to_cpu(r->path_len);
+		new->rules[i].flags = le16_to_cpu(r->flags);
+		new->rules[i].exact = r->exact;
+
+		ret = -EINVAL;
+		if (unlikely(inc + path_len > buf_len))
+			goto out_rules;
+		if (unlikely(path_len > last_path_len))
+			goto out_rules;
+		if (unlikely((int) new->rules[i].exact != 0 &&
+			     (int) new->rules[i].exact != 1))
+			goto out_rules;
+		if (unlikely(path_len == last_path_len &&
+			     new->rules[i].exact &&
+			     !last_exact))
+			goto out_rules;
+		if (!are_flags_valid(new->rules[i].flags))
+			goto out_rules;
+
+		ret = -ENOMEM;
+		new->rules[i].path = kmalloc(path_len+1, GFP_KERNEL);
+		if (unlikely(new->rules[i].path == NULL))
+			goto out_rules;
+		memcpy(new->rules[i].path, pos, path_len);
+		new->rules[i].path[path_len] = '\0';
+		if (i > 0 &&
+		    unlikely(new->rules[i].exact == new->rules[i-1].exact &&
+			     strcmp(new->rules[i].path,
+				    new->rules[i-1].path) == 0))
+			goto out_rules;
+		pos += path_len;
+		last_path_len = path_len;
+		last_exact = new->rules[i].exact;
+	}
+	new->buf_len = (size_t) (pos-buf);
+
+replace:
+	SARA_CONFIG_REPLACE(wxprot_config,
+			    new,
+			    config_free,
+			    &wxprot_config_lock);
+	pr_notice("WXP: new rules loaded.\n");
+	return 0;
+
+out_rules:
+	for (i = 0; i < new->rules_size; ++i)
+		kfree(new->rules[i].path);
+	kvfree(new->rules);
+out_new:
+	kfree(new);
+out:
+	pr_notice("WXP: failed to load rules.\n");
+	return ret;
+}
+
+static ssize_t config_dump(char **buf)
+{
+	int i;
+	ssize_t ret;
+	size_t buf_len;
+	char *pos;
+	char *mybuf;
+	u16 path_len;
+	int rulen;
+	struct wxprot_config_container *c;
+	struct wxprot_rule *rc;
+	struct binary_config_header *h;
+	struct binary_config_rule *r;
+
+	ret = -ENOMEM;
+	SARA_CONFIG_GET(c, wxprot_config);
+	buf_len = c->buf_len;
+	mybuf = sara_kvmalloc(buf_len);
+	if (unlikely(mybuf == NULL))
+		goto out;
+	rulen = c->rules_size;
+	h = (struct binary_config_header *) mybuf;
+	memcpy(h->magic, "SARAWXPR", 8);
+	h->version = cpu_to_le32(SARA_WXPROT_CONFIG_VERSION);
+	h->rules_size = cpu_to_le32(rulen);
+	BUILD_BUG_ON(sizeof(c->hash) != sizeof(h->hash));
+	memcpy(h->hash, c->hash, sizeof(h->hash));
+	pos = mybuf + sizeof(*h);
+	for (i = 0; i < rulen; ++i) {
+		r = (struct binary_config_rule *) pos;
+		pos += sizeof(*r);
+		if (buf_len < (pos - mybuf))
+			goto out;
+		rc = &c->rules[i];
+		r->flags = cpu_to_le16(rc->flags);
+		r->exact = (u8) rc->exact;
+		path_len = strlen(rc->path);
+		r->path_len = cpu_to_le16(path_len);
+		if (buf_len < ((pos - mybuf) + path_len))
+			goto out;
+		memcpy(pos, rc->path, path_len);
+		pos += path_len;
+	}
+	ret = (ssize_t) (pos - mybuf);
+	*buf = mybuf;
+out:
+	SARA_CONFIG_PUT(c, config_free);
+	return ret;
+}
+
+static int config_hash(char **buf)
+{
+	int ret;
+	struct wxprot_config_container *config;
+
+	ret = -ENOMEM;
+	*buf = kzalloc(sizeof(config->hash), GFP_KERNEL);
+	if (unlikely(*buf == NULL))
+		goto out;
+
+	SARA_CONFIG_GET_RCU(config, wxprot_config);
+	memcpy(*buf, config->hash, sizeof(config->hash));
+	SARA_CONFIG_PUT_RCU(config);
+
+	ret = 0;
+out:
+	return ret;
+}
+
+static DEFINE_SARA_SECFS_BOOL_FLAG(wxprot_enabled_data,
+				   wxprot_enabled);
+
+static struct sara_secfs_fptrs fptrs __ro_after_init = {
+	.load = config_load,
+	.dump = config_dump,
+	.hash = config_hash,
+};
+
+static const struct sara_secfs_node wxprot_fs[] __initconst = {
+	{
+		.name = "enabled",
+		.type = SARA_SECFS_BOOL,
+		.data = (void *) &wxprot_enabled_data,
+	},
+	{
+		.name = "version",
+		.type = SARA_SECFS_READONLY_INT,
+		.data = (int *) &wxprot_config_version,
+	},
+	{
+		.name = "default_flags",
+		.type = SARA_SECFS_READONLY_INT,
+		.data = &default_flags,
+	},
+	{
+		.name = "emutramp_available",
+		.type = SARA_SECFS_READONLY_INT,
+		.data = (int *) &wxprot_emutramp,
+	},
+	{
+		.name = ".load",
+		.type = SARA_SECFS_CONFIG_LOAD,
+		.data = &fptrs,
+	},
+	{
+		.name = ".dump",
+		.type = SARA_SECFS_CONFIG_DUMP,
+		.data = &fptrs,
+	},
+	{
+		.name = "hash",
+		.type = SARA_SECFS_CONFIG_HASH,
+		.data = &fptrs,
+	},
+};
+
+
+int __init sara_wxprot_init(void)
+{
+	int ret;
+	struct wxprot_config_container *tmpc;
+
+	ret = -EINVAL;
+	if (!are_flags_valid(default_flags))
+		goto out_fail;
+	ret = -ENOMEM;
+	tmpc = kzalloc(sizeof(*tmpc), GFP_KERNEL);
+	if (unlikely(tmpc == NULL))
+		goto out_fail;
+	tmpc->buf_len = sizeof(struct binary_config_header);
+	kref_init(&tmpc->refcount);
+	wxprot_config = (struct wxprot_config_container __rcu *) tmpc;
+	ret = sara_secfs_subtree_register("wxprot",
+					  wxprot_fs,
+					  ARRAY_SIZE(wxprot_fs));
+	if (unlikely(ret))
+		goto out_fail;
+	security_add_hooks(wxprot_hooks, ARRAY_SIZE(wxprot_hooks), "sara");
+	return 0;
+
+out_fail:
+	kfree(tmpc);
+	return ret;
+}
+
+#endif /* CONFIG_SECURITY_SARA_WXPROT */
-- 
1.9.1

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [RFC v2 6/9] Creation of "pagefault_handler_x86" LSM hook
  2017-06-15 16:42 [RFC v2 0/9] S.A.R.A. a new stacked LSM Salvatore Mesoraca
                   ` (4 preceding siblings ...)
  2017-06-15 16:42 ` [RFC v2 5/9] S.A.R.A. WX Protection Salvatore Mesoraca
@ 2017-06-15 16:42 ` Salvatore Mesoraca
  2017-06-27 23:07   ` Kees Cook
  2017-06-15 16:42 ` [RFC v2 7/9] Trampoline emulation Salvatore Mesoraca
                   ` (2 subsequent siblings)
  8 siblings, 1 reply; 21+ messages in thread
From: Salvatore Mesoraca @ 2017-06-15 16:42 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-security-module, kernel-hardening, Salvatore Mesoraca,
	Brad Spengler, PaX Team, Casey Schaufler, Kees Cook,
	James Morris, Serge E. Hallyn, linux-mm, x86, Jann Horn,
	Christoph Hellwig, Thomas Gleixner

Creation of a new hook to let LSM modules handle user-space pagefaults on
x86.
It can be used to avoid segfaulting the originating process.
If it's the case it can modify process registers before returning.
This is not a security feature by itself, it's a way to soften some
unwanted side-effects of restrictive security features.
In particular this is used by S.A.R.A. can be used to implement what
PaX call "trampoline emulation" that, in practice, allow for some specific
code sequences to be executed even if they are in non executable memory.
This may look like a bad thing at first, but you have to consider
that:
- This allows for strict memory restrictions (e.g. W^X) to stay on even
  when they should be turned off. And, even if this emulation
  makes those features less effective, it's still better than having
  them turned off completely.
- The only code sequences emulated are trampolines used to make
  function calls. In many cases, when you have the chance to
  make arbitrary memory writes, you can already manipulate the
  control flow of the program by overwriting function pointers or
  return values. So, in many cases, the "trampoline emulation"
  doesn't introduce new exploit vectors.
- It's a feature that can be turned on only if needed, on a per
  executable file basis.

Signed-off-by: Salvatore Mesoraca <s.mesoraca16@gmail.com>
---
 arch/x86/mm/fault.c       |  6 ++++++
 include/linux/lsm_hooks.h |  9 +++++++++
 include/linux/security.h  | 11 +++++++++++
 security/security.c       | 11 +++++++++++
 4 files changed, 37 insertions(+)

diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
index 8ad91a0..b75b81a 100644
--- a/arch/x86/mm/fault.c
+++ b/arch/x86/mm/fault.c
@@ -15,6 +15,7 @@
 #include <linux/prefetch.h>		/* prefetchw			*/
 #include <linux/context_tracking.h>	/* exception_enter(), ...	*/
 #include <linux/uaccess.h>		/* faulthandler_disabled()	*/
+#include <linux/security.h>		/* security_pagefault_handler	*/
 
 #include <asm/cpufeature.h>		/* boot_cpu_has, ...		*/
 #include <asm/traps.h>			/* dotraplinkage, ...		*/
@@ -1358,6 +1359,11 @@ static inline bool smap_violation(int error_code, struct pt_regs *regs)
 			local_irq_enable();
 	}
 
+	if (unlikely(security_pagefault_handler_x86(regs,
+						    error_code,
+						    address)))
+		return;
+
 	perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address);
 
 	if (error_code & PF_WRITE)
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 33dab16..da487e5 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -488,6 +488,11 @@
  *	@vmflags contains requested the vmflags.
  *	Return 0 if the operation is allowed to continue otherwise return
  *	the appropriate error code.
+ * @pagefault_handler_x86:
+ *	Handle pagefaults on x86.
+ *	@regs contains process' registers.
+ *	@error_code contains error code for the pagefault.
+ *	@address contains the address that caused the pagefault.
  * @file_lock:
  *	Check permission before performing file locking operations.
  *	Note: this hook mediates both flock and fcntl style locks.
@@ -1483,6 +1488,9 @@
 	int (*file_mprotect)(struct vm_area_struct *vma, unsigned long reqprot,
 				unsigned long prot);
 	int (*check_vmflags)(vm_flags_t vmflags);
+	int (*pagefault_handler_x86)(struct pt_regs *regs,
+				     unsigned long error_code,
+				     unsigned long address);
 	int (*file_lock)(struct file *file, unsigned int cmd);
 	int (*file_fcntl)(struct file *file, unsigned int cmd,
 				unsigned long arg);
@@ -1754,6 +1762,7 @@ struct security_hook_heads {
 	struct list_head mmap_file;
 	struct list_head file_mprotect;
 	struct list_head check_vmflags;
+	struct list_head pagefault_handler_x86;
 	struct list_head file_lock;
 	struct list_head file_fcntl;
 	struct list_head file_set_fowner;
diff --git a/include/linux/security.h b/include/linux/security.h
index 8701872..3b91999 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -301,6 +301,9 @@ int security_mmap_file(struct file *file, unsigned long prot,
 int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
 			   unsigned long prot);
 int security_check_vmflags(vm_flags_t vmflags);
+int __maybe_unused security_pagefault_handler_x86(struct pt_regs *regs,
+						  unsigned long error_code,
+						  unsigned long address);
 int security_file_lock(struct file *file, unsigned int cmd);
 int security_file_fcntl(struct file *file, unsigned int cmd, unsigned long arg);
 void security_file_set_fowner(struct file *file);
@@ -829,6 +832,14 @@ static inline int security_check_vmflags(vm_flags_t vmflags)
 	return 0;
 }
 
+static inline int __maybe_unused security_pagefault_handler_x86(
+						struct pt_regs *regs,
+						unsigned long error_code,
+						unsigned long address)
+{
+	return 0;
+}
+
 static inline int security_file_lock(struct file *file, unsigned int cmd)
 {
 	return 0;
diff --git a/security/security.c b/security/security.c
index 7e45846..f7df697 100644
--- a/security/security.c
+++ b/security/security.c
@@ -905,6 +905,17 @@ int security_check_vmflags(vm_flags_t vmflags)
 	return call_int_hook(check_vmflags, 0, vmflags);
 }
 
+int __maybe_unused security_pagefault_handler_x86(struct pt_regs *regs,
+						  unsigned long error_code,
+						  unsigned long address)
+{
+	return call_int_hook(pagefault_handler_x86,
+			     0,
+			     regs,
+			     error_code,
+			     address);
+}
+
 int security_file_lock(struct file *file, unsigned int cmd)
 {
 	return call_int_hook(file_lock, 0, file, cmd);
-- 
1.9.1

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [RFC v2 7/9] Trampoline emulation
  2017-06-15 16:42 [RFC v2 0/9] S.A.R.A. a new stacked LSM Salvatore Mesoraca
                   ` (5 preceding siblings ...)
  2017-06-15 16:42 ` [RFC v2 6/9] Creation of "pagefault_handler_x86" LSM hook Salvatore Mesoraca
@ 2017-06-15 16:42 ` Salvatore Mesoraca
  2017-06-15 16:47   ` [kernel-hardening] " aconcernedfossdev
  2017-06-27 23:13   ` Kees Cook
  2017-06-15 16:42 ` [RFC v2 8/9] Allowing for stacking procattr support in S.A.R.A Salvatore Mesoraca
  2017-06-15 16:42 ` [RFC v2 9/9] S.A.R.A. WX Protection procattr interface Salvatore Mesoraca
  8 siblings, 2 replies; 21+ messages in thread
From: Salvatore Mesoraca @ 2017-06-15 16:42 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-security-module, kernel-hardening, Salvatore Mesoraca,
	Brad Spengler, PaX Team, Casey Schaufler, Kees Cook,
	James Morris, Serge E. Hallyn, linux-mm, x86, Jann Horn,
	Christoph Hellwig, Thomas Gleixner

Some programs need to generate part of their code at runtime. Luckily
enough, in some cases they only generate well-known code sequences (the
"trampolines") that can be easily recognized and emulated by the kernel.
This way WX Protection can still be active, so a potential attacker won't
be able to generate arbitrary sequences of code, but just those that are
explicitly allowed. This is not ideal, but it's still better than having WX
Protection completely disabled.
In particular S.A.R.A. is able to recognize trampolines used by GCC for
nested C functions and libffi's trampolines.
This feature is implemented only on x86_32 and x86_64.
The assembly sequences used here were originally obtained from PaX source
code.

Signed-off-by: Salvatore Mesoraca <s.mesoraca16@gmail.com>
---
 security/sara/Kconfig               |  17 ++++
 security/sara/include/trampolines.h | 171 ++++++++++++++++++++++++++++++++++++
 security/sara/wxprot.c              | 140 +++++++++++++++++++++++++++++
 3 files changed, 328 insertions(+)
 create mode 100644 security/sara/include/trampolines.h

diff --git a/security/sara/Kconfig b/security/sara/Kconfig
index 6c74069..f406805 100644
--- a/security/sara/Kconfig
+++ b/security/sara/Kconfig
@@ -96,6 +96,23 @@ choice
 		  Documentation/security/SARA.rst.
 endchoice
 
+config SECURITY_SARA_WXPROT_EMUTRAMP
+	bool "Enable emulation for some types of trampolines"
+	depends on SECURITY_SARA_WXPROT
+	depends on X86
+	default y
+	help
+	  Some programs and libraries need to execute special small code
+	  snippets from non-executable memory pages.
+	  Most notable examples are the GCC and libffi trampolines.
+	  This features make it possible to execute those trampolines even
+	  if they reside in non-executable memory pages.
+	  This features need to be enabled on a per-executable basis
+	  via user-space utilities.
+	  See Documentation/security/SARA.rst. for further information.
+
+	  If unsure, answer y.
+
 config SECURITY_SARA_WXPROT_DISABLED
 	bool "WX protection will be disabled at boot."
 	depends on SECURITY_SARA_WXPROT
diff --git a/security/sara/include/trampolines.h b/security/sara/include/trampolines.h
new file mode 100644
index 0000000..eab0a85
--- /dev/null
+++ b/security/sara/include/trampolines.h
@@ -0,0 +1,171 @@
+/*
+ * S.A.R.A. Linux Security Module
+ *
+ * Copyright (C) 2017 Salvatore Mesoraca <s.mesoraca16@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ * Assembly sequences used here were copied from
+ * PaX patch by PaX Team <pageexec@freemail.hu>
+ *
+ */
+
+#ifndef __SARA_TRAMPOLINES_H
+#define __SARA_TRAMPOLINES_H
+#ifdef CONFIG_SECURITY_SARA_WXPROT_EMUTRAMP
+
+
+/* x86_32 */
+
+
+struct libffi_trampoline_x86_32 {
+	unsigned char mov;
+	unsigned int addr1;
+	unsigned char jmp;
+	unsigned int addr2;
+} __packed;
+
+struct gcc_trampoline_x86_32_type1 {
+	unsigned char mov1;
+	unsigned int addr1;
+	unsigned char mov2;
+	unsigned int addr2;
+	unsigned short jmp;
+} __packed;
+
+struct gcc_trampoline_x86_32_type2 {
+	unsigned char mov;
+	unsigned int addr1;
+	unsigned char jmp;
+	unsigned int addr2;
+} __packed;
+
+union trampolines_x86_32 {
+	struct libffi_trampoline_x86_32 lf;
+	struct gcc_trampoline_x86_32_type1 g1;
+	struct gcc_trampoline_x86_32_type2 g2;
+};
+
+#define is_valid_libffi_trampoline_x86_32(UNION)	\
+	(UNION.lf.mov == 0xB8 &&			\
+	UNION.lf.jmp == 0xE9)
+
+#define emulate_libffi_trampoline_x86_32(UNION, REGS) do {	\
+	(REGS)->ax = UNION.lf.addr1;				\
+	(REGS)->ip = (unsigned int) ((REGS)->ip +		\
+				     UNION.lf.addr2 +		\
+				     sizeof(UNION.lf));		\
+} while (0)
+
+#define is_valid_gcc_trampoline_x86_32_type1(UNION, REGS)	\
+	(UNION.g1.mov1 == 0xB9 &&				\
+	UNION.g1.mov2 == 0xB8 &&				\
+	UNION.g1.jmp == 0xE0FF &&				\
+	REGS->ip > REGS->sp)
+
+#define emulate_gcc_trampoline_x86_32_type1(UNION, REGS) do {	\
+	(REGS)->cx = UNION.g1.addr1;				\
+	(REGS)->ax = UNION.g1.addr2;				\
+	(REGS)->ip = UNION.g1.addr2;				\
+} while (0)
+
+#define is_valid_gcc_trampoline_x86_32_type2(UNION, REGS)	\
+	(UNION.g2.mov == 0xB9 &&				\
+	UNION.g2.jmp == 0xE9 &&					\
+	REGS->ip > REGS->sp)
+
+#define emulate_gcc_trampoline_x86_32_type2(UNION, REGS) do {	\
+	(REGS)->cx = UNION.g2.addr1;				\
+	(REGS)->ip = (unsigned int) ((REGS)->ip +		\
+				     UNION.g2.addr2 +		\
+				     sizeof(UNION.g2));		\
+} while (0)
+
+
+
+#ifdef CONFIG_X86_64
+
+struct libffi_trampoline_x86_64 {
+	unsigned short mov1;
+	unsigned long addr1;
+	unsigned short mov2;
+	unsigned long addr2;
+	unsigned char stcclc;
+	unsigned short jmp1;
+	unsigned char jmp2;
+} __packed;
+
+struct gcc_trampoline_x86_64_type1 {
+	unsigned short mov1;
+	unsigned long addr1;
+	unsigned short mov2;
+	unsigned long addr2;
+	unsigned short jmp1;
+	unsigned char jmp2;
+} __packed;
+
+struct gcc_trampoline_x86_64_type2 {
+	unsigned short mov1;
+	unsigned int addr1;
+	unsigned short mov2;
+	unsigned long addr2;
+	unsigned short jmp1;
+	unsigned char jmp2;
+} __packed;
+
+union trampolines_x86_64 {
+	struct libffi_trampoline_x86_64 lf;
+	struct gcc_trampoline_x86_64_type1 g1;
+	struct gcc_trampoline_x86_64_type2 g2;
+};
+
+#define is_valid_libffi_trampoline_x86_64(UNION)	\
+	(UNION.lf.mov1 == 0xBB49 &&			\
+	UNION.lf.mov2 == 0xBA49 &&			\
+	(UNION.lf.stcclc == 0xF8 ||			\
+	 UNION.lf.stcclc == 0xF9) &&			\
+	UNION.lf.jmp1 == 0xFF49 &&			\
+	UNION.lf.jmp2 == 0xE3)
+
+#define emulate_libffi_trampoline_x86_64(UNION, REGS) do {	\
+	(REGS)->r11 = UNION.lf.addr1;				\
+	(REGS)->r10 = UNION.lf.addr2;				\
+	(REGS)->ip = UNION.lf.addr1;				\
+	if (UNION.lf.stcclc == 0xF8)				\
+		(REGS)->flags &= ~X86_EFLAGS_CF;		\
+	else							\
+		(REGS)->flags |= X86_EFLAGS_CF;			\
+} while (0)
+
+#define is_valid_gcc_trampoline_x86_64_type1(UNION, REGS)	\
+	(UNION.g1.mov1 == 0xBB49 &&				\
+	UNION.g1.mov2 == 0xBA49 &&				\
+	UNION.g1.jmp1 == 0xFF49 &&				\
+	UNION.g1.jmp2 == 0xE3 &&				\
+	REGS->ip > REGS->sp)
+
+#define emulate_gcc_trampoline_x86_64_type1(UNION, REGS) do {	\
+	(REGS)->r11 = UNION.g1.addr1;				\
+	(REGS)->r10 = UNION.g1.addr2;				\
+	(REGS)->ip = UNION.g1.addr1;				\
+} while (0)
+
+#define is_valid_gcc_trampoline_x86_64_type2(UNION, REGS)	\
+	(UNION.g2.mov1 == 0xBB41 &&				\
+	UNION.g2.mov2 == 0xBA49 &&				\
+	UNION.g2.jmp1 == 0xFF49 &&				\
+	UNION.g2.jmp2 == 0xE3 &&				\
+	REGS->ip > REGS->sp)
+
+#define emulate_gcc_trampoline_x86_64_type2(UNION, REGS) do {	\
+	(REGS)->r11 = UNION.g2.addr1;				\
+	(REGS)->r10 = UNION.g2.addr2;				\
+	(REGS)->ip = UNION.g2.addr1;				\
+} while (0)
+
+#endif /* CONFIG_X86_64 */
+
+#endif /* CONFIG_SECURITY_SARA_WXPROT_EMUTRAMP */
+#endif /* __SARA_TRAMPOLINES_H */
diff --git a/security/sara/wxprot.c b/security/sara/wxprot.c
index f9233a5..38c86be 100644
--- a/security/sara/wxprot.c
+++ b/security/sara/wxprot.c
@@ -22,6 +22,11 @@
 #include <linux/ratelimit.h>
 #include <linux/spinlock.h>
 
+#ifdef CONFIG_SECURITY_SARA_WXPROT_EMUTRAMP
+#include <linux/uaccess.h>
+#include "include/trampolines.h"
+#endif
+
 #include "include/sara.h"
 #include "include/sara_data.h"
 #include "include/utils.h"
@@ -37,6 +42,7 @@
 #define SARA_WXP_COMPLAIN	0x0010
 #define SARA_WXP_VERBOSE	0x0020
 #define SARA_WXP_MMAP		0x0040
+#define SARA_WXP_EMUTRAMP	0x0100
 #define SARA_WXP_TRANSFER	0x0200
 #define SARA_WXP_NONE		0x0000
 #define SARA_WXP_MPROTECT	(SARA_WXP_HEAP	| \
@@ -47,7 +53,12 @@
 				SARA_WXP_WXORX		| \
 				SARA_WXP_COMPLAIN	| \
 				SARA_WXP_VERBOSE)
+#ifdef CONFIG_SECURITY_SARA_WXPROT_EMUTRAMP
+#define SARA_WXP_ALL		(__SARA_WXP_ALL		| \
+				SARA_WXP_EMUTRAMP)
+#else /* CONFIG_SECURITY_SARA_WXPROT_EMUTRAMP */
 #define SARA_WXP_ALL		__SARA_WXP_ALL
+#endif /* CONFIG_SECURITY_SARA_WXPROT_EMUTRAMP */
 
 struct wxprot_rule {
 	char *path;
@@ -72,7 +83,11 @@ struct wxprot_config_container {
 static u16 default_flags __ro_after_init =
 				CONFIG_SECURITY_SARA_WXPROT_DEFAULT_FLAGS;
 
+#ifdef CONFIG_SECURITY_SARA_WXPROT_EMUTRAMP
+static const bool wxprot_emutramp = true;
+#else
 static const bool wxprot_emutramp;
+#endif
 
 static void pr_wxp(char *msg)
 {
@@ -97,6 +112,9 @@ static bool are_flags_valid(u16 flags)
 				SARA_WXP_WXORX |
 				SARA_WXP_MMAP))))
 		return false;
+	if (unlikely(flags & SARA_WXP_EMUTRAMP &&
+		     ((flags & SARA_WXP_MPROTECT) != SARA_WXP_MPROTECT)))
+		return false;
 	return true;
 }
 
@@ -366,10 +384,132 @@ static int sara_file_mprotect(struct vm_area_struct *vma,
 	return 0;
 }
 
+#ifdef CONFIG_SECURITY_SARA_WXPROT_EMUTRAMP
+#define PF_PROT		(1 << 0)
+#define PF_USER		(1 << 2)
+#define PF_INSTR	(1 << 4)
+static int sara_pagefault_handler_x86_32(struct pt_regs *regs);
+static int sara_pagefault_handler_x86_64(struct pt_regs *regs);
+static int sara_pagefault_handler_x86(struct pt_regs *regs,
+					unsigned long error_code,
+					unsigned long address)
+{
+	int ret = 0;
+
+	if (!sara_enabled || !wxprot_enabled ||
+	    !(error_code & PF_USER) ||
+	    !(error_code & PF_INSTR) ||
+	    !(error_code & PF_PROT) ||
+	    !(get_current_sara_wxp_flags() & SARA_WXP_EMUTRAMP))
+		return 0;
+
+	local_irq_enable();
+	might_sleep();
+	might_fault();
+
+#ifdef	CONFIG_X86_32
+	ret = sara_pagefault_handler_x86_32(regs);
+#else
+	if (regs->cs == __USER32_CS ||
+	    regs->cs & (1<<2)) {
+		if (!(address >> 32))	/* K8 erratum #100 */
+			ret = sara_pagefault_handler_x86_32(regs);
+	} else
+		ret = sara_pagefault_handler_x86_64(regs);
+#endif
+
+	return ret;
+}
+
+static int sara_pagefault_handler_x86_32(struct pt_regs *regs)
+{
+	int ret;
+	void __user *ip = (void __user *) regs->ip;
+	union trampolines_x86_32 t;
+
+	BUILD_BUG_ON(sizeof(t.lf) > sizeof(t.g1));
+	BUILD_BUG_ON(sizeof(t.g2) > sizeof(t.lf));
+
+	ret = copy_from_user(&t, ip, sizeof(t.g1));
+	if (ret)
+		ret = copy_from_user(&t, ip, sizeof(t.lf));
+	if (ret)
+		ret = copy_from_user(&t, ip, sizeof(t.g2));
+	if (ret)
+		return 0;
+
+	if (is_valid_gcc_trampoline_x86_32_type1(t, regs)) {
+		pr_debug("Trampoline: gcc1 x86_32.\n");
+		emulate_gcc_trampoline_x86_32_type1(t, regs);
+		return 1;
+	} else if (is_valid_libffi_trampoline_x86_32(t)) {
+		pr_debug("Trampoline: libffi x86_32.\n");
+		emulate_libffi_trampoline_x86_32(t, regs);
+		return 1;
+	} else if (is_valid_gcc_trampoline_x86_32_type2(t, regs)) {
+		pr_debug("Trampoline: gcc2 x86_32.\n");
+		emulate_gcc_trampoline_x86_32_type2(t, regs);
+		return 1;
+	}
+
+	pr_debug("Not a trampoline (x86_32).\n");
+
+	return 0;
+}
+
+#ifdef CONFIG_X86_64
+static int sara_pagefault_handler_x86_64(struct pt_regs *regs)
+{
+	int ret;
+	void __user *ip = (void __user *) regs->ip;
+	union trampolines_x86_64 t;
+
+	BUILD_BUG_ON(sizeof(t.g1) > sizeof(t.lf));
+	BUILD_BUG_ON(sizeof(t.g2) > sizeof(t.g1));
+
+	ret = copy_from_user(&t, ip, sizeof(t.lf));
+	if (ret)
+		ret = copy_from_user(&t, ip, sizeof(t.g1));
+	if (ret)
+		ret = copy_from_user(&t, ip, sizeof(t.g2));
+	if (ret)
+		return 0;
+
+	if (is_valid_libffi_trampoline_x86_64(t)) {
+		pr_debug("Trampoline: libffi x86_64.\n");
+		emulate_libffi_trampoline_x86_64(t, regs);
+		return 1;
+	} else if (is_valid_gcc_trampoline_x86_64_type1(t, regs)) {
+		pr_debug("Trampoline: gcc1 x86_64.\n");
+		emulate_gcc_trampoline_x86_64_type1(t, regs);
+		return 1;
+	} else if (is_valid_gcc_trampoline_x86_64_type2(t, regs)) {
+		pr_debug("Trampoline: gcc2 x86_64.\n");
+		emulate_gcc_trampoline_x86_64_type2(t, regs);
+		return 1;
+	}
+
+	pr_debug("Not a trampoline (x86_64).\n");
+
+	return 0;
+
+}
+#else /* CONFIG_X86_64 */
+static inline int sara_pagefault_handler_x86_64(struct pt_regs *regs)
+{
+	return 0;
+}
+#endif /* CONFIG_X86_64 */
+
+#endif /* CONFIG_SECURITY_SARA_WXPROT_EMUTRAMP */
+
 static struct security_hook_list wxprot_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(bprm_set_creds, sara_bprm_set_creds),
 	LSM_HOOK_INIT(check_vmflags, sara_check_vmflags),
 	LSM_HOOK_INIT(file_mprotect, sara_file_mprotect),
+#ifdef CONFIG_SECURITY_SARA_WXPROT_EMUTRAMP
+	LSM_HOOK_INIT(pagefault_handler_x86, sara_pagefault_handler_x86),
+#endif
 };
 
 struct binary_config_header {
-- 
1.9.1

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [RFC v2 8/9] Allowing for stacking procattr support in S.A.R.A.
  2017-06-15 16:42 [RFC v2 0/9] S.A.R.A. a new stacked LSM Salvatore Mesoraca
                   ` (6 preceding siblings ...)
  2017-06-15 16:42 ` [RFC v2 7/9] Trampoline emulation Salvatore Mesoraca
@ 2017-06-15 16:42 ` Salvatore Mesoraca
  2017-06-15 16:42 ` [RFC v2 9/9] S.A.R.A. WX Protection procattr interface Salvatore Mesoraca
  8 siblings, 0 replies; 21+ messages in thread
From: Salvatore Mesoraca @ 2017-06-15 16:42 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-security-module, kernel-hardening, Salvatore Mesoraca,
	Brad Spengler, PaX Team, Casey Schaufler, Kees Cook,
	James Morris, Serge E. Hallyn, linux-mm, x86, Jann Horn,
	Christoph Hellwig, Thomas Gleixner

This allow S.A.R.A. to use the procattr interface without interfering
with other LSMs.
This part should be reimplemented as soon as upstream procattr stacking
support is available.

Signed-off-by: Salvatore Mesoraca <s.mesoraca16@gmail.com>
---
 fs/proc/base.c      | 38 ++++++++++++++++++++++++++++++++++++++
 security/security.c | 20 ++++++++++++++++++--
 2 files changed, 56 insertions(+), 2 deletions(-)

diff --git a/fs/proc/base.c b/fs/proc/base.c
index f1e1927..6d0fd1c 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -2515,6 +2515,40 @@ static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf,
 	.llseek		= generic_file_llseek,
 };
 
+#ifdef CONFIG_SECURITY_SARA
+static const struct pid_entry sara_attr_dir_stuff[] = {
+	REG("wxprot", 0666, proc_pid_attr_operations),
+};
+
+static int proc_sara_attr_dir_readdir(struct file *file,
+				      struct dir_context *ctx)
+{
+	return proc_pident_readdir(file, ctx,
+				   sara_attr_dir_stuff,
+				   ARRAY_SIZE(sara_attr_dir_stuff));
+}
+
+static const struct file_operations proc_sara_attr_dir_ops = {
+	.read		= generic_read_dir,
+	.iterate_shared	= proc_sara_attr_dir_readdir,
+	.llseek		= generic_file_llseek,
+};
+
+static struct dentry *proc_sara_attr_dir_lookup(struct inode *dir,
+				struct dentry *dentry, unsigned int flags)
+{
+	return proc_pident_lookup(dir, dentry,
+				  sara_attr_dir_stuff,
+				  ARRAY_SIZE(sara_attr_dir_stuff));
+};
+
+static const struct inode_operations proc_sara_attr_dir_inode_ops = {
+	.lookup		= proc_sara_attr_dir_lookup,
+	.getattr	= pid_getattr,
+	.setattr	= proc_setattr,
+};
+#endif /* CONFIG_SECURITY_SARA */
+
 static const struct pid_entry attr_dir_stuff[] = {
 	REG("current",    S_IRUGO|S_IWUGO, proc_pid_attr_operations),
 	REG("prev",       S_IRUGO,	   proc_pid_attr_operations),
@@ -2522,6 +2556,10 @@ static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf,
 	REG("fscreate",   S_IRUGO|S_IWUGO, proc_pid_attr_operations),
 	REG("keycreate",  S_IRUGO|S_IWUGO, proc_pid_attr_operations),
 	REG("sockcreate", S_IRUGO|S_IWUGO, proc_pid_attr_operations),
+#ifdef CONFIG_SECURITY_SARA
+	DIR("sara", 0555, proc_sara_attr_dir_inode_ops,
+				proc_sara_attr_dir_ops),
+#endif
 };
 
 static int proc_attr_dir_readdir(struct file *file, struct dir_context *ctx)
diff --git a/security/security.c b/security/security.c
index f7df697..c6c78dd 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1239,12 +1239,28 @@ void security_d_instantiate(struct dentry *dentry, struct inode *inode)
 
 int security_getprocattr(struct task_struct *p, char *name, char **value)
 {
-	return call_int_hook(getprocattr, -EINVAL, p, name, value);
+	struct security_hook_list *hp;
+	int rc;
+
+	list_for_each_entry(hp, &security_hook_heads.getprocattr, list) {
+		rc = hp->hook.getprocattr(p, name, value);
+		if (rc != -EINVAL)
+			return rc;
+	}
+	return -EINVAL;
 }
 
 int security_setprocattr(const char *name, void *value, size_t size)
 {
-	return call_int_hook(setprocattr, -EINVAL, name, value, size);
+	struct security_hook_list *hp;
+	int rc;
+
+	list_for_each_entry(hp, &security_hook_heads.setprocattr, list) {
+		rc = hp->hook.setprocattr(name, value, size);
+		if (rc != -EINVAL)
+			return rc;
+	}
+	return -EINVAL;
 }
 
 int security_netlink_send(struct sock *sk, struct sk_buff *skb)
-- 
1.9.1

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [RFC v2 9/9] S.A.R.A. WX Protection procattr interface
  2017-06-15 16:42 [RFC v2 0/9] S.A.R.A. a new stacked LSM Salvatore Mesoraca
                   ` (7 preceding siblings ...)
  2017-06-15 16:42 ` [RFC v2 8/9] Allowing for stacking procattr support in S.A.R.A Salvatore Mesoraca
@ 2017-06-15 16:42 ` Salvatore Mesoraca
  8 siblings, 0 replies; 21+ messages in thread
From: Salvatore Mesoraca @ 2017-06-15 16:42 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-security-module, kernel-hardening, Salvatore Mesoraca,
	Brad Spengler, PaX Team, Casey Schaufler, Kees Cook,
	James Morris, Serge E. Hallyn, linux-mm, x86, Jann Horn,
	Christoph Hellwig, Thomas Gleixner

This allow threads to get current WX Protection flags for themselves or
for other threads (if they have CAP_MAC_ADMIN).
It also allow a thread to set itself flags to a stricter set of rules than
the current one.
Via a new wxprot flag (SARA_WXP_FORCE_WXORX) is it possible to ask the
kernel to rescan the memory and remove the VM_WRITE flag from any area
that is marked both writable and executable.
Protections that prevent the runtime creation of executable code
can be troublesome for all those programs that actually need to do it
e.g. programs shipping with a JIT compiler built-in.
This feature can be use to run the JIT compiler with few restrictions while
enforcing full WX Protection in the rest of the program.
To simplify access to this interface a CC0 licensed library is available
here: https://github.com/smeso/libsara

Signed-off-by: Salvatore Mesoraca <s.mesoraca16@gmail.com>
---
 security/sara/wxprot.c | 124 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 124 insertions(+)

diff --git a/security/sara/wxprot.c b/security/sara/wxprot.c
index 38c86be..0939591 100644
--- a/security/sara/wxprot.c
+++ b/security/sara/wxprot.c
@@ -12,6 +12,7 @@
 #ifdef CONFIG_SECURITY_SARA_WXPROT
 
 #include <linux/binfmts.h>
+#include <linux/capability.h>
 #include <linux/cred.h>
 #include <linux/elf.h>
 #include <linux/kref.h>
@@ -42,6 +43,7 @@
 #define SARA_WXP_COMPLAIN	0x0010
 #define SARA_WXP_VERBOSE	0x0020
 #define SARA_WXP_MMAP		0x0040
+#define SARA_WXP_FORCE_WXORX	0x0080
 #define SARA_WXP_EMUTRAMP	0x0100
 #define SARA_WXP_TRANSFER	0x0200
 #define SARA_WXP_NONE		0x0000
@@ -503,6 +505,126 @@ static inline int sara_pagefault_handler_x86_64(struct pt_regs *regs)
 
 #endif /* CONFIG_SECURITY_SARA_WXPROT_EMUTRAMP */
 
+static int sara_getprocattr(struct task_struct *p, char *name, char **value)
+{
+	int ret;
+	u16 flags;
+	char *buf;
+
+	ret = -EINVAL;
+	if (strcmp(name, "wxprot") != 0)
+		goto out;
+
+	ret = -EACCES;
+	if (unlikely(current != p &&
+		     !capable(CAP_MAC_ADMIN)))
+		goto out;
+
+	ret = -ENOMEM;
+	buf = kzalloc(8, GFP_KERNEL);
+	if (unlikely(buf == NULL))
+		goto out;
+
+	if (!sara_enabled || !wxprot_enabled) {
+		flags = 0x0;
+	} else {
+		rcu_read_lock();
+		flags = get_sara_wxp_flags(__task_cred(p));
+		rcu_read_unlock();
+	}
+
+	snprintf(buf, 8, "0x%04x\n", flags);
+	ret = strlen(buf);
+	*value = buf;
+
+out:
+	return ret;
+}
+
+static int sara_setprocattr(const char *name, void *value, size_t size)
+{
+	int ret;
+	struct vm_area_struct *vma;
+	struct cred *new = prepare_creds();
+	u16 cur_flags;
+	u16 req_flags;
+	char *buf = NULL;
+
+	ret = -EINVAL;
+	if (!sara_enabled || !wxprot_enabled)
+		goto error;
+	if (unlikely(new == NULL))
+		return -ENOMEM;
+	if (strcmp(name, "wxprot") != 0)
+		goto error;
+	if (unlikely(value == NULL || size == 0 || size > 7))
+		goto error;
+	ret = -ENOMEM;
+	buf = kmalloc(size+1, GFP_KERNEL);
+	if (unlikely(buf == NULL))
+		goto error;
+	buf[size] = '\0';
+	memcpy(buf, value, size);
+	ret = -EINVAL;
+	if (unlikely(strlen(buf) != size))
+		goto error;
+	if (unlikely(kstrtou16(buf, 16, &req_flags) != 0))
+		goto error;
+	if (unlikely(!are_flags_valid(req_flags & ~SARA_WXP_FORCE_WXORX)))
+		goto error;
+	if (unlikely(req_flags & SARA_WXP_FORCE_WXORX &&
+		     !(req_flags & SARA_WXP_WXORX)))
+		goto error;
+	if (unlikely(!get_current_sara_relro_page_found() &&
+		     req_flags & SARA_WXP_MMAP))
+		goto error;
+	cur_flags = get_current_sara_wxp_flags();
+	if (unlikely((req_flags & SARA_WXP_COMPLAIN) &&
+		     !(cur_flags & SARA_WXP_COMPLAIN)))
+		goto error;
+	if (unlikely((req_flags & SARA_WXP_EMUTRAMP) &&
+		     !(cur_flags & SARA_WXP_EMUTRAMP) &&
+		     (cur_flags & (SARA_WXP_MPROTECT |
+				   SARA_WXP_WXORX))))
+		goto error;
+	if (cur_flags & SARA_WXP_VERBOSE)
+		req_flags |= SARA_WXP_VERBOSE;
+	else
+		req_flags &= ~SARA_WXP_VERBOSE;
+	if (unlikely(cur_flags & (req_flags ^ cur_flags) &
+		     ~(SARA_WXP_COMPLAIN|SARA_WXP_EMUTRAMP)))
+		goto error;
+	ret = -EINTR;
+	if (req_flags & SARA_WXP_FORCE_WXORX) {
+		if (down_write_killable(&current->mm->mmap_sem))
+			goto error;
+		for (vma = current->mm->mmap; vma; vma = vma->vm_next) {
+			if (vma->vm_flags & VM_EXEC &&
+			    vma->vm_flags & VM_WRITE) {
+				vma->vm_flags &= ~VM_WRITE;
+				vma_set_page_prot(vma);
+				change_protection(vma,
+						  vma->vm_start,
+						  vma->vm_end,
+						  vma->vm_page_prot,
+						  0,
+						  0);
+			}
+		}
+		up_write(&current->mm->mmap_sem);
+	}
+	get_sara_wxp_flags(new) = req_flags & ~SARA_WXP_FORCE_WXORX;
+	commit_creds(new);
+	ret = size;
+	goto out;
+
+error:
+	abort_creds(new);
+out:
+	kfree(buf);
+	return ret;
+}
+
 static struct security_hook_list wxprot_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(bprm_set_creds, sara_bprm_set_creds),
 	LSM_HOOK_INIT(check_vmflags, sara_check_vmflags),
@@ -510,6 +632,8 @@ static inline int sara_pagefault_handler_x86_64(struct pt_regs *regs)
 #ifdef CONFIG_SECURITY_SARA_WXPROT_EMUTRAMP
 	LSM_HOOK_INIT(pagefault_handler_x86, sara_pagefault_handler_x86),
 #endif
+	LSM_HOOK_INIT(getprocattr, sara_getprocattr),
+	LSM_HOOK_INIT(setprocattr, sara_setprocattr),
 };
 
 struct binary_config_header {
-- 
1.9.1

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [kernel-hardening] [RFC v2 7/9] Trampoline emulation
  2017-06-15 16:42 ` [RFC v2 7/9] Trampoline emulation Salvatore Mesoraca
@ 2017-06-15 16:47   ` aconcernedfossdev
  2017-06-15 17:19     ` Salvatore Mesoraca
  2017-06-27 23:13   ` Kees Cook
  1 sibling, 1 reply; 21+ messages in thread
From: aconcernedfossdev @ 2017-06-15 16:47 UTC (permalink / raw)
  To: Salvatore Mesoraca
  Cc: linux-kernel, linux-security-module, kernel-hardening,
	Brad Spengler, PaX Team, Casey Schaufler, Kees Cook,
	James Morris, Serge E. Hallyn, linux-mm, x86, Jann Horn,
	Christoph Hellwig, Thomas Gleixner

Thanks for doing this porting work. Look forward to using GRSecurity/PAX 
features on ARM eventually. ARM's taking over as we know. x86 is almost 
done.

On 2017-06-15 16:42, Salvatore Mesoraca wrote:
> Some programs need to generate part of their code at runtime. Luckily
> enough, in some cases they only generate well-known code sequences (the
> "trampolines") that can be easily recognized and emulated by the 
> kernel.
> This way WX Protection can still be active, so a potential attacker 
> won't
> be able to generate arbitrary sequences of code, but just those that 
> are
> explicitly allowed. This is not ideal, but it's still better than 
> having WX
> Protection completely disabled.
> In particular S.A.R.A. is able to recognize trampolines used by GCC for
> nested C functions and libffi's trampolines.
> This feature is implemented only on x86_32 and x86_64.
> The assembly sequences used here were originally obtained from PaX 
> source
> code.
> 
> Signed-off-by: Salvatore Mesoraca <s.mesoraca16@gmail.com>
> ---
>  security/sara/Kconfig               |  17 ++++
>  security/sara/include/trampolines.h | 171 
> ++++++++++++++++++++++++++++++++++++
>  security/sara/wxprot.c              | 140 
> +++++++++++++++++++++++++++++
>  3 files changed, 328 insertions(+)
>  create mode 100644 security/sara/include/trampolines.h
> 
> diff --git a/security/sara/Kconfig b/security/sara/Kconfig
> index 6c74069..f406805 100644
> --- a/security/sara/Kconfig
> +++ b/security/sara/Kconfig
> @@ -96,6 +96,23 @@ choice
>  		  Documentation/security/SARA.rst.
>  endchoice
> 
> +config SECURITY_SARA_WXPROT_EMUTRAMP
> +	bool "Enable emulation for some types of trampolines"
> +	depends on SECURITY_SARA_WXPROT
> +	depends on X86
> +	default y
> +	help
> +	  Some programs and libraries need to execute special small code
> +	  snippets from non-executable memory pages.
> +	  Most notable examples are the GCC and libffi trampolines.
> +	  This features make it possible to execute those trampolines even
> +	  if they reside in non-executable memory pages.
> +	  This features need to be enabled on a per-executable basis
> +	  via user-space utilities.
> +	  See Documentation/security/SARA.rst. for further information.
> +
> +	  If unsure, answer y.
> +
>  config SECURITY_SARA_WXPROT_DISABLED
>  	bool "WX protection will be disabled at boot."
>  	depends on SECURITY_SARA_WXPROT
> diff --git a/security/sara/include/trampolines.h
> b/security/sara/include/trampolines.h
> new file mode 100644
> index 0000000..eab0a85
> --- /dev/null
> +++ b/security/sara/include/trampolines.h
> @@ -0,0 +1,171 @@
> +/*
> + * S.A.R.A. Linux Security Module
> + *
> + * Copyright (C) 2017 Salvatore Mesoraca <s.mesoraca16@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or 
> modify
> + * it under the terms of the GNU General Public License version 2, as
> + * published by the Free Software Foundation.
> + *
> + * Assembly sequences used here were copied from
> + * PaX patch by PaX Team <pageexec@freemail.hu>
> + *
> + */
> +
> +#ifndef __SARA_TRAMPOLINES_H
> +#define __SARA_TRAMPOLINES_H
> +#ifdef CONFIG_SECURITY_SARA_WXPROT_EMUTRAMP
> +
> +
> +/* x86_32 */
> +
> +
> +struct libffi_trampoline_x86_32 {
> +	unsigned char mov;
> +	unsigned int addr1;
> +	unsigned char jmp;
> +	unsigned int addr2;
> +} __packed;
> +
> +struct gcc_trampoline_x86_32_type1 {
> +	unsigned char mov1;
> +	unsigned int addr1;
> +	unsigned char mov2;
> +	unsigned int addr2;
> +	unsigned short jmp;
> +} __packed;
> +
> +struct gcc_trampoline_x86_32_type2 {
> +	unsigned char mov;
> +	unsigned int addr1;
> +	unsigned char jmp;
> +	unsigned int addr2;
> +} __packed;
> +
> +union trampolines_x86_32 {
> +	struct libffi_trampoline_x86_32 lf;
> +	struct gcc_trampoline_x86_32_type1 g1;
> +	struct gcc_trampoline_x86_32_type2 g2;
> +};
> +
> +#define is_valid_libffi_trampoline_x86_32(UNION)	\
> +	(UNION.lf.mov == 0xB8 &&			\
> +	UNION.lf.jmp == 0xE9)
> +
> +#define emulate_libffi_trampoline_x86_32(UNION, REGS) do {	\
> +	(REGS)->ax = UNION.lf.addr1;				\
> +	(REGS)->ip = (unsigned int) ((REGS)->ip +		\
> +				     UNION.lf.addr2 +		\
> +				     sizeof(UNION.lf));		\
> +} while (0)
> +
> +#define is_valid_gcc_trampoline_x86_32_type1(UNION, REGS)	\
> +	(UNION.g1.mov1 == 0xB9 &&				\
> +	UNION.g1.mov2 == 0xB8 &&				\
> +	UNION.g1.jmp == 0xE0FF &&				\
> +	REGS->ip > REGS->sp)
> +
> +#define emulate_gcc_trampoline_x86_32_type1(UNION, REGS) do {	\
> +	(REGS)->cx = UNION.g1.addr1;				\
> +	(REGS)->ax = UNION.g1.addr2;				\
> +	(REGS)->ip = UNION.g1.addr2;				\
> +} while (0)
> +
> +#define is_valid_gcc_trampoline_x86_32_type2(UNION, REGS)	\
> +	(UNION.g2.mov == 0xB9 &&				\
> +	UNION.g2.jmp == 0xE9 &&					\
> +	REGS->ip > REGS->sp)
> +
> +#define emulate_gcc_trampoline_x86_32_type2(UNION, REGS) do {	\
> +	(REGS)->cx = UNION.g2.addr1;				\
> +	(REGS)->ip = (unsigned int) ((REGS)->ip +		\
> +				     UNION.g2.addr2 +		\
> +				     sizeof(UNION.g2));		\
> +} while (0)
> +
> +
> +
> +#ifdef CONFIG_X86_64
> +
> +struct libffi_trampoline_x86_64 {
> +	unsigned short mov1;
> +	unsigned long addr1;
> +	unsigned short mov2;
> +	unsigned long addr2;
> +	unsigned char stcclc;
> +	unsigned short jmp1;
> +	unsigned char jmp2;
> +} __packed;
> +
> +struct gcc_trampoline_x86_64_type1 {
> +	unsigned short mov1;
> +	unsigned long addr1;
> +	unsigned short mov2;
> +	unsigned long addr2;
> +	unsigned short jmp1;
> +	unsigned char jmp2;
> +} __packed;
> +
> +struct gcc_trampoline_x86_64_type2 {
> +	unsigned short mov1;
> +	unsigned int addr1;
> +	unsigned short mov2;
> +	unsigned long addr2;
> +	unsigned short jmp1;
> +	unsigned char jmp2;
> +} __packed;
> +
> +union trampolines_x86_64 {
> +	struct libffi_trampoline_x86_64 lf;
> +	struct gcc_trampoline_x86_64_type1 g1;
> +	struct gcc_trampoline_x86_64_type2 g2;
> +};
> +
> +#define is_valid_libffi_trampoline_x86_64(UNION)	\
> +	(UNION.lf.mov1 == 0xBB49 &&			\
> +	UNION.lf.mov2 == 0xBA49 &&			\
> +	(UNION.lf.stcclc == 0xF8 ||			\
> +	 UNION.lf.stcclc == 0xF9) &&			\
> +	UNION.lf.jmp1 == 0xFF49 &&			\
> +	UNION.lf.jmp2 == 0xE3)
> +
> +#define emulate_libffi_trampoline_x86_64(UNION, REGS) do {	\
> +	(REGS)->r11 = UNION.lf.addr1;				\
> +	(REGS)->r10 = UNION.lf.addr2;				\
> +	(REGS)->ip = UNION.lf.addr1;				\
> +	if (UNION.lf.stcclc == 0xF8)				\
> +		(REGS)->flags &= ~X86_EFLAGS_CF;		\
> +	else							\
> +		(REGS)->flags |= X86_EFLAGS_CF;			\
> +} while (0)
> +
> +#define is_valid_gcc_trampoline_x86_64_type1(UNION, REGS)	\
> +	(UNION.g1.mov1 == 0xBB49 &&				\
> +	UNION.g1.mov2 == 0xBA49 &&				\
> +	UNION.g1.jmp1 == 0xFF49 &&				\
> +	UNION.g1.jmp2 == 0xE3 &&				\
> +	REGS->ip > REGS->sp)
> +
> +#define emulate_gcc_trampoline_x86_64_type1(UNION, REGS) do {	\
> +	(REGS)->r11 = UNION.g1.addr1;				\
> +	(REGS)->r10 = UNION.g1.addr2;				\
> +	(REGS)->ip = UNION.g1.addr1;				\
> +} while (0)
> +
> +#define is_valid_gcc_trampoline_x86_64_type2(UNION, REGS)	\
> +	(UNION.g2.mov1 == 0xBB41 &&				\
> +	UNION.g2.mov2 == 0xBA49 &&				\
> +	UNION.g2.jmp1 == 0xFF49 &&				\
> +	UNION.g2.jmp2 == 0xE3 &&				\
> +	REGS->ip > REGS->sp)
> +
> +#define emulate_gcc_trampoline_x86_64_type2(UNION, REGS) do {	\
> +	(REGS)->r11 = UNION.g2.addr1;				\
> +	(REGS)->r10 = UNION.g2.addr2;				\
> +	(REGS)->ip = UNION.g2.addr1;				\
> +} while (0)
> +
> +#endif /* CONFIG_X86_64 */
> +
> +#endif /* CONFIG_SECURITY_SARA_WXPROT_EMUTRAMP */
> +#endif /* __SARA_TRAMPOLINES_H */
> diff --git a/security/sara/wxprot.c b/security/sara/wxprot.c
> index f9233a5..38c86be 100644
> --- a/security/sara/wxprot.c
> +++ b/security/sara/wxprot.c
> @@ -22,6 +22,11 @@
>  #include <linux/ratelimit.h>
>  #include <linux/spinlock.h>
> 
> +#ifdef CONFIG_SECURITY_SARA_WXPROT_EMUTRAMP
> +#include <linux/uaccess.h>
> +#include "include/trampolines.h"
> +#endif
> +
>  #include "include/sara.h"
>  #include "include/sara_data.h"
>  #include "include/utils.h"
> @@ -37,6 +42,7 @@
>  #define SARA_WXP_COMPLAIN	0x0010
>  #define SARA_WXP_VERBOSE	0x0020
>  #define SARA_WXP_MMAP		0x0040
> +#define SARA_WXP_EMUTRAMP	0x0100
>  #define SARA_WXP_TRANSFER	0x0200
>  #define SARA_WXP_NONE		0x0000
>  #define SARA_WXP_MPROTECT	(SARA_WXP_HEAP	| \
> @@ -47,7 +53,12 @@
>  				SARA_WXP_WXORX		| \
>  				SARA_WXP_COMPLAIN	| \
>  				SARA_WXP_VERBOSE)
> +#ifdef CONFIG_SECURITY_SARA_WXPROT_EMUTRAMP
> +#define SARA_WXP_ALL		(__SARA_WXP_ALL		| \
> +				SARA_WXP_EMUTRAMP)
> +#else /* CONFIG_SECURITY_SARA_WXPROT_EMUTRAMP */
>  #define SARA_WXP_ALL		__SARA_WXP_ALL
> +#endif /* CONFIG_SECURITY_SARA_WXPROT_EMUTRAMP */
> 
>  struct wxprot_rule {
>  	char *path;
> @@ -72,7 +83,11 @@ struct wxprot_config_container {
>  static u16 default_flags __ro_after_init =
>  				CONFIG_SECURITY_SARA_WXPROT_DEFAULT_FLAGS;
> 
> +#ifdef CONFIG_SECURITY_SARA_WXPROT_EMUTRAMP
> +static const bool wxprot_emutramp = true;
> +#else
>  static const bool wxprot_emutramp;
> +#endif
> 
>  static void pr_wxp(char *msg)
>  {
> @@ -97,6 +112,9 @@ static bool are_flags_valid(u16 flags)
>  				SARA_WXP_WXORX |
>  				SARA_WXP_MMAP))))
>  		return false;
> +	if (unlikely(flags & SARA_WXP_EMUTRAMP &&
> +		     ((flags & SARA_WXP_MPROTECT) != SARA_WXP_MPROTECT)))
> +		return false;
>  	return true;
>  }
> 
> @@ -366,10 +384,132 @@ static int sara_file_mprotect(struct 
> vm_area_struct *vma,
>  	return 0;
>  }
> 
> +#ifdef CONFIG_SECURITY_SARA_WXPROT_EMUTRAMP
> +#define PF_PROT		(1 << 0)
> +#define PF_USER		(1 << 2)
> +#define PF_INSTR	(1 << 4)
> +static int sara_pagefault_handler_x86_32(struct pt_regs *regs);
> +static int sara_pagefault_handler_x86_64(struct pt_regs *regs);
> +static int sara_pagefault_handler_x86(struct pt_regs *regs,
> +					unsigned long error_code,
> +					unsigned long address)
> +{
> +	int ret = 0;
> +
> +	if (!sara_enabled || !wxprot_enabled ||
> +	    !(error_code & PF_USER) ||
> +	    !(error_code & PF_INSTR) ||
> +	    !(error_code & PF_PROT) ||
> +	    !(get_current_sara_wxp_flags() & SARA_WXP_EMUTRAMP))
> +		return 0;
> +
> +	local_irq_enable();
> +	might_sleep();
> +	might_fault();
> +
> +#ifdef	CONFIG_X86_32
> +	ret = sara_pagefault_handler_x86_32(regs);
> +#else
> +	if (regs->cs == __USER32_CS ||
> +	    regs->cs & (1<<2)) {
> +		if (!(address >> 32))	/* K8 erratum #100 */
> +			ret = sara_pagefault_handler_x86_32(regs);
> +	} else
> +		ret = sara_pagefault_handler_x86_64(regs);
> +#endif
> +
> +	return ret;
> +}
> +
> +static int sara_pagefault_handler_x86_32(struct pt_regs *regs)
> +{
> +	int ret;
> +	void __user *ip = (void __user *) regs->ip;
> +	union trampolines_x86_32 t;
> +
> +	BUILD_BUG_ON(sizeof(t.lf) > sizeof(t.g1));
> +	BUILD_BUG_ON(sizeof(t.g2) > sizeof(t.lf));
> +
> +	ret = copy_from_user(&t, ip, sizeof(t.g1));
> +	if (ret)
> +		ret = copy_from_user(&t, ip, sizeof(t.lf));
> +	if (ret)
> +		ret = copy_from_user(&t, ip, sizeof(t.g2));
> +	if (ret)
> +		return 0;
> +
> +	if (is_valid_gcc_trampoline_x86_32_type1(t, regs)) {
> +		pr_debug("Trampoline: gcc1 x86_32.\n");
> +		emulate_gcc_trampoline_x86_32_type1(t, regs);
> +		return 1;
> +	} else if (is_valid_libffi_trampoline_x86_32(t)) {
> +		pr_debug("Trampoline: libffi x86_32.\n");
> +		emulate_libffi_trampoline_x86_32(t, regs);
> +		return 1;
> +	} else if (is_valid_gcc_trampoline_x86_32_type2(t, regs)) {
> +		pr_debug("Trampoline: gcc2 x86_32.\n");
> +		emulate_gcc_trampoline_x86_32_type2(t, regs);
> +		return 1;
> +	}
> +
> +	pr_debug("Not a trampoline (x86_32).\n");
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_X86_64
> +static int sara_pagefault_handler_x86_64(struct pt_regs *regs)
> +{
> +	int ret;
> +	void __user *ip = (void __user *) regs->ip;
> +	union trampolines_x86_64 t;
> +
> +	BUILD_BUG_ON(sizeof(t.g1) > sizeof(t.lf));
> +	BUILD_BUG_ON(sizeof(t.g2) > sizeof(t.g1));
> +
> +	ret = copy_from_user(&t, ip, sizeof(t.lf));
> +	if (ret)
> +		ret = copy_from_user(&t, ip, sizeof(t.g1));
> +	if (ret)
> +		ret = copy_from_user(&t, ip, sizeof(t.g2));
> +	if (ret)
> +		return 0;
> +
> +	if (is_valid_libffi_trampoline_x86_64(t)) {
> +		pr_debug("Trampoline: libffi x86_64.\n");
> +		emulate_libffi_trampoline_x86_64(t, regs);
> +		return 1;
> +	} else if (is_valid_gcc_trampoline_x86_64_type1(t, regs)) {
> +		pr_debug("Trampoline: gcc1 x86_64.\n");
> +		emulate_gcc_trampoline_x86_64_type1(t, regs);
> +		return 1;
> +	} else if (is_valid_gcc_trampoline_x86_64_type2(t, regs)) {
> +		pr_debug("Trampoline: gcc2 x86_64.\n");
> +		emulate_gcc_trampoline_x86_64_type2(t, regs);
> +		return 1;
> +	}
> +
> +	pr_debug("Not a trampoline (x86_64).\n");
> +
> +	return 0;
> +
> +}
> +#else /* CONFIG_X86_64 */
> +static inline int sara_pagefault_handler_x86_64(struct pt_regs *regs)
> +{
> +	return 0;
> +}
> +#endif /* CONFIG_X86_64 */
> +
> +#endif /* CONFIG_SECURITY_SARA_WXPROT_EMUTRAMP */
> +
>  static struct security_hook_list wxprot_hooks[] __ro_after_init = {
>  	LSM_HOOK_INIT(bprm_set_creds, sara_bprm_set_creds),
>  	LSM_HOOK_INIT(check_vmflags, sara_check_vmflags),
>  	LSM_HOOK_INIT(file_mprotect, sara_file_mprotect),
> +#ifdef CONFIG_SECURITY_SARA_WXPROT_EMUTRAMP
> +	LSM_HOOK_INIT(pagefault_handler_x86, sara_pagefault_handler_x86),
> +#endif
>  };
> 
>  struct binary_config_header {

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [kernel-hardening] [RFC v2 7/9] Trampoline emulation
  2017-06-15 16:47   ` [kernel-hardening] " aconcernedfossdev
@ 2017-06-15 17:19     ` Salvatore Mesoraca
  0 siblings, 0 replies; 21+ messages in thread
From: Salvatore Mesoraca @ 2017-06-15 17:19 UTC (permalink / raw)
  To: aconcernedfossdev
  Cc: kernel list, linux-security-module, Kernel Hardening,
	Brad Spengler, PaX Team, Casey Schaufler, Kees Cook,
	James Morris, Serge E. Hallyn, linux-mm, x86, Jann Horn,
	Christoph Hellwig, Thomas Gleixner

2017-06-15 18:47 GMT+02:00  <aconcernedfossdev@airmail.cc>:
> Thanks for doing this porting work. Look forward to using GRSecurity/PAX
> features on ARM eventually. ARM's taking over as we know. x86 is almost
> done.

Do you have any suggestion about potential use of trampoline emulation on ARM?
Thank you for your comment.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC v2 5/9] S.A.R.A. WX Protection
  2017-06-15 16:42 ` [RFC v2 5/9] S.A.R.A. WX Protection Salvatore Mesoraca
@ 2017-06-27 23:04   ` Kees Cook
  2017-06-29 19:39     ` Salvatore Mesoraca
  0 siblings, 1 reply; 21+ messages in thread
From: Kees Cook @ 2017-06-27 23:04 UTC (permalink / raw)
  To: Salvatore Mesoraca
  Cc: LKML, linux-security-module, kernel-hardening, Brad Spengler,
	PaX Team, Casey Schaufler, James Morris, Serge E. Hallyn,
	Linux-MM, x86, Jann Horn, Christoph Hellwig, Thomas Gleixner

On Thu, Jun 15, 2017 at 9:42 AM, Salvatore Mesoraca
<s.mesoraca16@gmail.com> wrote:
> +static int sara_check_vmflags(vm_flags_t vm_flags)
> +{
> +       u16 sara_wxp_flags = get_current_sara_wxp_flags();
> +
> +       if (sara_enabled && wxprot_enabled) {
> +               if (sara_wxp_flags & SARA_WXP_WXORX &&
> +                   vm_flags & VM_WRITE &&
> +                   vm_flags & VM_EXEC) {
> +                       if ((sara_wxp_flags & SARA_WXP_VERBOSE))
> +                               pr_wxp("W^X");
> +                       if (!(sara_wxp_flags & SARA_WXP_COMPLAIN))
> +                               return -EPERM;
> +               }
> +               if (sara_wxp_flags & SARA_WXP_MMAP &&
> +                   (vm_flags & VM_EXEC ||
> +                    (!(vm_flags & VM_MAYWRITE) && (vm_flags & VM_MAYEXEC))) &&
> +                   get_current_sara_mmap_blocked()) {
> +                       if ((sara_wxp_flags & SARA_WXP_VERBOSE))
> +                               pr_wxp("executable mmap");
> +                       if (!(sara_wxp_flags & SARA_WXP_COMPLAIN))
> +                               return -EPERM;
> +               }
> +       }

Given the subtle differences between these various if blocks (here and
in the other hook), I think it would be nice to have some beefy
comments here to describe specifically what's being checked (and why).
It'll help others review this code, and help validate code against
intent.

I would also try to minimize the written code by creating a macro for
a repeated pattern here:

> +                               if ((sara_wxp_flags & SARA_WXP_VERBOSE))
> +                                       pr_wxp("mprotect on file mmap");
> +                               if (!(sara_wxp_flags & SARA_WXP_COMPLAIN))
> +                                       return -EACCES;

These four lines are repeated several times with only the const char *
and return value changing. Perhaps something like:

#define sara_return(err, msg) do { \
                               if ((sara_wxp_flags & SARA_WXP_VERBOSE)) \
                                       pr_wxp(err); \
                               if (!(sara_wxp_flags & SARA_WXP_COMPLAIN)) \
                                       return -err; \
} while (0)

Then each if block turns into something quite easier to parse:

               if (sara_wxp_flags & SARA_WXP_WXORX &&
                   vm_flags & VM_WRITE &&
                   vm_flags & VM_EXEC)
                       sara_return(EPERM, "W^X");


-Kees

-- 
Kees Cook
Pixel Security

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC v2 3/9] Creation of "check_vmflags" LSM hook
  2017-06-15 16:42 ` [RFC v2 3/9] Creation of "check_vmflags" LSM hook Salvatore Mesoraca
@ 2017-06-27 23:05   ` Kees Cook
  2017-06-29 19:28     ` Salvatore Mesoraca
  0 siblings, 1 reply; 21+ messages in thread
From: Kees Cook @ 2017-06-27 23:05 UTC (permalink / raw)
  To: Salvatore Mesoraca
  Cc: LKML, linux-security-module, kernel-hardening, Brad Spengler,
	PaX Team, Casey Schaufler, James Morris, Serge E. Hallyn,
	Linux-MM, x86, Jann Horn, Christoph Hellwig, Thomas Gleixner,
	Stephen Smalley, John Johansen

On Thu, Jun 15, 2017 at 9:42 AM, Salvatore Mesoraca
<s.mesoraca16@gmail.com> wrote:
> Creation of a new LSM hook to check if a given configuration of vmflags,
> for a new memory allocation request, should be allowed or not.
> It's placed in "do_mmap", "do_brk_flags" and "__install_special_mapping".

I like this. I think this is something the other LSMs should be
checking too. (Though I wonder if it would be helpful to include the
VMA in the hook, though it does exist yet, so... hmm.)

-Kees

>
> Signed-off-by: Salvatore Mesoraca <s.mesoraca16@gmail.com>
> ---
>  include/linux/lsm_hooks.h |  7 +++++++
>  include/linux/security.h  |  6 ++++++
>  mm/mmap.c                 | 13 +++++++++++++
>  security/security.c       |  5 +++++
>  4 files changed, 31 insertions(+)
>
> diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
> index bd274db..33dab16 100644
> --- a/include/linux/lsm_hooks.h
> +++ b/include/linux/lsm_hooks.h
> @@ -483,6 +483,11 @@
>   *     @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.
> + * @check_vmflags:
> + *     Check if the requested @vmflags are allowed.
> + *     @vmflags contains requested the vmflags.
> + *     Return 0 if the operation is allowed to continue otherwise return
> + *     the appropriate error code.
>   * @file_lock:
>   *     Check permission before performing file locking operations.
>   *     Note: this hook mediates both flock and fcntl style locks.
> @@ -1477,6 +1482,7 @@
>                                 unsigned long prot, unsigned long flags);
>         int (*file_mprotect)(struct vm_area_struct *vma, unsigned long reqprot,
>                                 unsigned long prot);
> +       int (*check_vmflags)(vm_flags_t vmflags);
>         int (*file_lock)(struct file *file, unsigned int cmd);
>         int (*file_fcntl)(struct file *file, unsigned int cmd,
>                                 unsigned long arg);
> @@ -1747,6 +1753,7 @@ struct security_hook_heads {
>         struct list_head mmap_addr;
>         struct list_head mmap_file;
>         struct list_head file_mprotect;
> +       struct list_head check_vmflags;
>         struct list_head file_lock;
>         struct list_head file_fcntl;
>         struct list_head file_set_fowner;
> diff --git a/include/linux/security.h b/include/linux/security.h
> index af675b5..8701872 100644
> --- a/include/linux/security.h
> +++ b/include/linux/security.h
> @@ -300,6 +300,7 @@ int security_mmap_file(struct file *file, unsigned long prot,
>  int security_mmap_addr(unsigned long addr);
>  int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
>                            unsigned long prot);
> +int security_check_vmflags(vm_flags_t vmflags);
>  int security_file_lock(struct file *file, unsigned int cmd);
>  int security_file_fcntl(struct file *file, unsigned int cmd, unsigned long arg);
>  void security_file_set_fowner(struct file *file);
> @@ -823,6 +824,11 @@ static inline int security_file_mprotect(struct vm_area_struct *vma,
>         return 0;
>  }
>
> +static inline int security_check_vmflags(vm_flags_t vmflags)
> +{
> +       return 0;
> +}
> +
>  static inline int security_file_lock(struct file *file, unsigned int cmd)
>  {
>         return 0;
> diff --git a/mm/mmap.c b/mm/mmap.c
> index f82741e..132061b 100644
> --- a/mm/mmap.c
> +++ b/mm/mmap.c
> @@ -1311,6 +1311,7 @@ unsigned long do_mmap(struct file *file, unsigned long addr,
>  {
>         struct mm_struct *mm = current->mm;
>         int pkey = 0;
> +       int error;
>
>         *populate = 0;
>
> @@ -1363,6 +1364,10 @@ unsigned long do_mmap(struct file *file, unsigned long addr,
>         vm_flags |= calc_vm_prot_bits(prot, pkey) | calc_vm_flag_bits(flags) |
>                         mm->def_flags | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;
>
> +       error = security_check_vmflags(vm_flags);
> +       if (error)
> +               return error;
> +
>         if (flags & MAP_LOCKED)
>                 if (!can_do_mlock())
>                         return -EPERM;
> @@ -2833,6 +2838,10 @@ static int do_brk_flags(unsigned long addr, unsigned long request, unsigned long
>                 return -EINVAL;
>         flags |= VM_DATA_DEFAULT_FLAGS | VM_ACCOUNT | mm->def_flags;
>
> +       error = security_check_vmflags(flags);
> +       if (error)
> +               return error;
> +
>         error = get_unmapped_area(NULL, addr, len, 0, MAP_FIXED);
>         if (offset_in_page(error))
>                 return error;
> @@ -3208,6 +3217,10 @@ static struct vm_area_struct *__install_special_mapping(
>         int ret;
>         struct vm_area_struct *vma;
>
> +       ret = security_check_vmflags(vm_flags);
> +       if (ret)
> +               return ERR_PTR(ret);
> +
>         vma = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL);
>         if (unlikely(vma == NULL))
>                 return ERR_PTR(-ENOMEM);
> diff --git a/security/security.c b/security/security.c
> index 42c8028..7e45846 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -900,6 +900,11 @@ int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
>         return call_int_hook(file_mprotect, 0, vma, reqprot, prot);
>  }
>
> +int security_check_vmflags(vm_flags_t vmflags)
> +{
> +       return call_int_hook(check_vmflags, 0, vmflags);
> +}
> +
>  int security_file_lock(struct file *file, unsigned int cmd)
>  {
>         return call_int_hook(file_lock, 0, file, cmd);
> --
> 1.9.1
>



-- 
Kees Cook
Pixel Security

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC v2 6/9] Creation of "pagefault_handler_x86" LSM hook
  2017-06-15 16:42 ` [RFC v2 6/9] Creation of "pagefault_handler_x86" LSM hook Salvatore Mesoraca
@ 2017-06-27 23:07   ` Kees Cook
  2017-06-29 19:30     ` Salvatore Mesoraca
  0 siblings, 1 reply; 21+ messages in thread
From: Kees Cook @ 2017-06-27 23:07 UTC (permalink / raw)
  To: Salvatore Mesoraca
  Cc: LKML, linux-security-module, kernel-hardening, Brad Spengler,
	PaX Team, Casey Schaufler, James Morris, Serge E. Hallyn,
	Linux-MM, x86, Jann Horn, Christoph Hellwig, Thomas Gleixner

On Thu, Jun 15, 2017 at 9:42 AM, Salvatore Mesoraca
<s.mesoraca16@gmail.com> wrote:
> Creation of a new hook to let LSM modules handle user-space pagefaults on
> x86.
> It can be used to avoid segfaulting the originating process.
> If it's the case it can modify process registers before returning.
> This is not a security feature by itself, it's a way to soften some
> unwanted side-effects of restrictive security features.
> In particular this is used by S.A.R.A. can be used to implement what
> PaX call "trampoline emulation" that, in practice, allow for some specific
> code sequences to be executed even if they are in non executable memory.
> This may look like a bad thing at first, but you have to consider
> that:
> - This allows for strict memory restrictions (e.g. W^X) to stay on even
>   when they should be turned off. And, even if this emulation
>   makes those features less effective, it's still better than having
>   them turned off completely.
> - The only code sequences emulated are trampolines used to make
>   function calls. In many cases, when you have the chance to
>   make arbitrary memory writes, you can already manipulate the
>   control flow of the program by overwriting function pointers or
>   return values. So, in many cases, the "trampoline emulation"
>   doesn't introduce new exploit vectors.
> - It's a feature that can be turned on only if needed, on a per
>   executable file basis.

Can this be made arch-agnostic? It seems a per-arch register-handling
routine would be needed, though. :(

-Kees

>
> Signed-off-by: Salvatore Mesoraca <s.mesoraca16@gmail.com>
> ---
>  arch/x86/mm/fault.c       |  6 ++++++
>  include/linux/lsm_hooks.h |  9 +++++++++
>  include/linux/security.h  | 11 +++++++++++
>  security/security.c       | 11 +++++++++++
>  4 files changed, 37 insertions(+)
>
> diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
> index 8ad91a0..b75b81a 100644
> --- a/arch/x86/mm/fault.c
> +++ b/arch/x86/mm/fault.c
> @@ -15,6 +15,7 @@
>  #include <linux/prefetch.h>            /* prefetchw                    */
>  #include <linux/context_tracking.h>    /* exception_enter(), ...       */
>  #include <linux/uaccess.h>             /* faulthandler_disabled()      */
> +#include <linux/security.h>            /* security_pagefault_handler   */
>
>  #include <asm/cpufeature.h>            /* boot_cpu_has, ...            */
>  #include <asm/traps.h>                 /* dotraplinkage, ...           */
> @@ -1358,6 +1359,11 @@ static inline bool smap_violation(int error_code, struct pt_regs *regs)
>                         local_irq_enable();
>         }
>
> +       if (unlikely(security_pagefault_handler_x86(regs,
> +                                                   error_code,
> +                                                   address)))
> +               return;
> +
>         perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address);
>
>         if (error_code & PF_WRITE)
> diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
> index 33dab16..da487e5 100644
> --- a/include/linux/lsm_hooks.h
> +++ b/include/linux/lsm_hooks.h
> @@ -488,6 +488,11 @@
>   *     @vmflags contains requested the vmflags.
>   *     Return 0 if the operation is allowed to continue otherwise return
>   *     the appropriate error code.
> + * @pagefault_handler_x86:
> + *     Handle pagefaults on x86.
> + *     @regs contains process' registers.
> + *     @error_code contains error code for the pagefault.
> + *     @address contains the address that caused the pagefault.
>   * @file_lock:
>   *     Check permission before performing file locking operations.
>   *     Note: this hook mediates both flock and fcntl style locks.
> @@ -1483,6 +1488,9 @@
>         int (*file_mprotect)(struct vm_area_struct *vma, unsigned long reqprot,
>                                 unsigned long prot);
>         int (*check_vmflags)(vm_flags_t vmflags);
> +       int (*pagefault_handler_x86)(struct pt_regs *regs,
> +                                    unsigned long error_code,
> +                                    unsigned long address);
>         int (*file_lock)(struct file *file, unsigned int cmd);
>         int (*file_fcntl)(struct file *file, unsigned int cmd,
>                                 unsigned long arg);
> @@ -1754,6 +1762,7 @@ struct security_hook_heads {
>         struct list_head mmap_file;
>         struct list_head file_mprotect;
>         struct list_head check_vmflags;
> +       struct list_head pagefault_handler_x86;
>         struct list_head file_lock;
>         struct list_head file_fcntl;
>         struct list_head file_set_fowner;
> diff --git a/include/linux/security.h b/include/linux/security.h
> index 8701872..3b91999 100644
> --- a/include/linux/security.h
> +++ b/include/linux/security.h
> @@ -301,6 +301,9 @@ int security_mmap_file(struct file *file, unsigned long prot,
>  int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
>                            unsigned long prot);
>  int security_check_vmflags(vm_flags_t vmflags);
> +int __maybe_unused security_pagefault_handler_x86(struct pt_regs *regs,
> +                                                 unsigned long error_code,
> +                                                 unsigned long address);
>  int security_file_lock(struct file *file, unsigned int cmd);
>  int security_file_fcntl(struct file *file, unsigned int cmd, unsigned long arg);
>  void security_file_set_fowner(struct file *file);
> @@ -829,6 +832,14 @@ static inline int security_check_vmflags(vm_flags_t vmflags)
>         return 0;
>  }
>
> +static inline int __maybe_unused security_pagefault_handler_x86(
> +                                               struct pt_regs *regs,
> +                                               unsigned long error_code,
> +                                               unsigned long address)
> +{
> +       return 0;
> +}
> +
>  static inline int security_file_lock(struct file *file, unsigned int cmd)
>  {
>         return 0;
> diff --git a/security/security.c b/security/security.c
> index 7e45846..f7df697 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -905,6 +905,17 @@ int security_check_vmflags(vm_flags_t vmflags)
>         return call_int_hook(check_vmflags, 0, vmflags);
>  }
>
> +int __maybe_unused security_pagefault_handler_x86(struct pt_regs *regs,
> +                                                 unsigned long error_code,
> +                                                 unsigned long address)
> +{
> +       return call_int_hook(pagefault_handler_x86,
> +                            0,
> +                            regs,
> +                            error_code,
> +                            address);
> +}
> +
>  int security_file_lock(struct file *file, unsigned int cmd)
>  {
>         return call_int_hook(file_lock, 0, file, cmd);
> --
> 1.9.1
>



-- 
Kees Cook
Pixel Security

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC v2 7/9] Trampoline emulation
  2017-06-15 16:42 ` [RFC v2 7/9] Trampoline emulation Salvatore Mesoraca
  2017-06-15 16:47   ` [kernel-hardening] " aconcernedfossdev
@ 2017-06-27 23:13   ` Kees Cook
  2017-06-29 19:35     ` Salvatore Mesoraca
  1 sibling, 1 reply; 21+ messages in thread
From: Kees Cook @ 2017-06-27 23:13 UTC (permalink / raw)
  To: Salvatore Mesoraca
  Cc: LKML, linux-security-module, kernel-hardening, Brad Spengler,
	PaX Team, Casey Schaufler, James Morris, Serge E. Hallyn,
	Linux-MM, x86, Jann Horn, Christoph Hellwig, Thomas Gleixner

On Thu, Jun 15, 2017 at 9:42 AM, Salvatore Mesoraca
<s.mesoraca16@gmail.com> wrote:
> Some programs need to generate part of their code at runtime. Luckily
> enough, in some cases they only generate well-known code sequences (the
> "trampolines") that can be easily recognized and emulated by the kernel.
> This way WX Protection can still be active, so a potential attacker won't
> be able to generate arbitrary sequences of code, but just those that are
> explicitly allowed. This is not ideal, but it's still better than having WX
> Protection completely disabled.
> In particular S.A.R.A. is able to recognize trampolines used by GCC for
> nested C functions and libffi's trampolines.
> This feature is implemented only on x86_32 and x86_64.
> The assembly sequences used here were originally obtained from PaX source
> code.

See below about the language grsecurity has asked people to use in commit logs.

>
> Signed-off-by: Salvatore Mesoraca <s.mesoraca16@gmail.com>
> ---
>  security/sara/Kconfig               |  17 ++++
>  security/sara/include/trampolines.h | 171 ++++++++++++++++++++++++++++++++++++
>  security/sara/wxprot.c              | 140 +++++++++++++++++++++++++++++
>  3 files changed, 328 insertions(+)
>  create mode 100644 security/sara/include/trampolines.h
>
> diff --git a/security/sara/Kconfig b/security/sara/Kconfig
> index 6c74069..f406805 100644
> --- a/security/sara/Kconfig
> +++ b/security/sara/Kconfig
> @@ -96,6 +96,23 @@ choice
>                   Documentation/security/SARA.rst.
>  endchoice
>
> +config SECURITY_SARA_WXPROT_EMUTRAMP
> +       bool "Enable emulation for some types of trampolines"
> +       depends on SECURITY_SARA_WXPROT
> +       depends on X86
> +       default y
> +       help
> +         Some programs and libraries need to execute special small code
> +         snippets from non-executable memory pages.
> +         Most notable examples are the GCC and libffi trampolines.
> +         This features make it possible to execute those trampolines even
> +         if they reside in non-executable memory pages.
> +         This features need to be enabled on a per-executable basis
> +         via user-space utilities.
> +         See Documentation/security/SARA.rst. for further information.
> +
> +         If unsure, answer y.
> +
>  config SECURITY_SARA_WXPROT_DISABLED
>         bool "WX protection will be disabled at boot."
>         depends on SECURITY_SARA_WXPROT
> diff --git a/security/sara/include/trampolines.h b/security/sara/include/trampolines.h
> new file mode 100644
> index 0000000..eab0a85
> --- /dev/null
> +++ b/security/sara/include/trampolines.h
> @@ -0,0 +1,171 @@
> +/*
> + * S.A.R.A. Linux Security Module
> + *
> + * Copyright (C) 2017 Salvatore Mesoraca <s.mesoraca16@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2, as
> + * published by the Free Software Foundation.
> + *
> + * Assembly sequences used here were copied from
> + * PaX patch by PaX Team <pageexec@freemail.hu>

Given this copying, please include the grsecurity/PaX copyright notice
too. Please see the recommendations here:
http://kernsec.org/wiki/index.php/Kernel_Self_Protection_Project/Get_Involved

> + *
> + */
> +
> +#ifndef __SARA_TRAMPOLINES_H
> +#define __SARA_TRAMPOLINES_H
> +#ifdef CONFIG_SECURITY_SARA_WXPROT_EMUTRAMP
> +
> +
> +/* x86_32 */
> +
> +
> +struct libffi_trampoline_x86_32 {
> +       unsigned char mov;
> +       unsigned int addr1;
> +       unsigned char jmp;
> +       unsigned int addr2;
> +} __packed;
> +
> +struct gcc_trampoline_x86_32_type1 {
> +       unsigned char mov1;
> +       unsigned int addr1;
> +       unsigned char mov2;
> +       unsigned int addr2;
> +       unsigned short jmp;
> +} __packed;
> +
> +struct gcc_trampoline_x86_32_type2 {
> +       unsigned char mov;
> +       unsigned int addr1;
> +       unsigned char jmp;
> +       unsigned int addr2;
> +} __packed;
> +
> +union trampolines_x86_32 {
> +       struct libffi_trampoline_x86_32 lf;
> +       struct gcc_trampoline_x86_32_type1 g1;
> +       struct gcc_trampoline_x86_32_type2 g2;
> +};
> +
> +#define is_valid_libffi_trampoline_x86_32(UNION)       \
> +       (UNION.lf.mov == 0xB8 &&                        \
> +       UNION.lf.jmp == 0xE9)
> +
> +#define emulate_libffi_trampoline_x86_32(UNION, REGS) do {     \
> +       (REGS)->ax = UNION.lf.addr1;                            \
> +       (REGS)->ip = (unsigned int) ((REGS)->ip +               \
> +                                    UNION.lf.addr2 +           \
> +                                    sizeof(UNION.lf));         \
> +} while (0)
> +
> +#define is_valid_gcc_trampoline_x86_32_type1(UNION, REGS)      \
> +       (UNION.g1.mov1 == 0xB9 &&                               \
> +       UNION.g1.mov2 == 0xB8 &&                                \
> +       UNION.g1.jmp == 0xE0FF &&                               \
> +       REGS->ip > REGS->sp)
> +
> +#define emulate_gcc_trampoline_x86_32_type1(UNION, REGS) do {  \
> +       (REGS)->cx = UNION.g1.addr1;                            \
> +       (REGS)->ax = UNION.g1.addr2;                            \
> +       (REGS)->ip = UNION.g1.addr2;                            \
> +} while (0)
> +
> +#define is_valid_gcc_trampoline_x86_32_type2(UNION, REGS)      \
> +       (UNION.g2.mov == 0xB9 &&                                \
> +       UNION.g2.jmp == 0xE9 &&                                 \
> +       REGS->ip > REGS->sp)
> +
> +#define emulate_gcc_trampoline_x86_32_type2(UNION, REGS) do {  \
> +       (REGS)->cx = UNION.g2.addr1;                            \
> +       (REGS)->ip = (unsigned int) ((REGS)->ip +               \
> +                                    UNION.g2.addr2 +           \
> +                                    sizeof(UNION.g2));         \
> +} while (0)

These all seem like they need to live in arch/x86/... somewhere rather
than in the LSM, but maybe this isn't needed on other architectures?
This seems to be very arch and compiler specific...

-Kees

-- 
Kees Cook
Pixel Security

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC v2 3/9] Creation of "check_vmflags" LSM hook
  2017-06-27 23:05   ` Kees Cook
@ 2017-06-29 19:28     ` Salvatore Mesoraca
  0 siblings, 0 replies; 21+ messages in thread
From: Salvatore Mesoraca @ 2017-06-29 19:28 UTC (permalink / raw)
  To: Kees Cook
  Cc: LKML, linux-security-module, kernel-hardening, Brad Spengler,
	PaX Team, Casey Schaufler, James Morris, Serge E. Hallyn,
	Linux-MM, x86, Jann Horn, Christoph Hellwig, Thomas Gleixner,
	Stephen Smalley, John Johansen

2017-06-28 1:05 GMT+02:00 Kees Cook <keescook@chromium.org>:
> On Thu, Jun 15, 2017 at 9:42 AM, Salvatore Mesoraca
> <s.mesoraca16@gmail.com> wrote:
>> Creation of a new LSM hook to check if a given configuration of vmflags,
>> for a new memory allocation request, should be allowed or not.
>> It's placed in "do_mmap", "do_brk_flags" and "__install_special_mapping".
>
> I like this. I think this is something the other LSMs should be
> checking too. (Though I wonder if it would be helpful to include the
> VMA in the hook, though it does exist yet, so... hmm.)

For the particular case of my LSM and the type of check it does, the VMA
isn't needed, of course.
Maybe some other LSM can benefit from it, but it depends on what they
want to do with this hook.
Looking forward to feedback from potential future users.
Thank you for your interest.

Salvatore

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC v2 6/9] Creation of "pagefault_handler_x86" LSM hook
  2017-06-27 23:07   ` Kees Cook
@ 2017-06-29 19:30     ` Salvatore Mesoraca
  2017-06-29 20:20       ` Kees Cook
  0 siblings, 1 reply; 21+ messages in thread
From: Salvatore Mesoraca @ 2017-06-29 19:30 UTC (permalink / raw)
  To: Kees Cook
  Cc: LKML, linux-security-module, kernel-hardening, Brad Spengler,
	PaX Team, Casey Schaufler, James Morris, Serge E. Hallyn,
	Linux-MM, x86, Jann Horn, Christoph Hellwig, Thomas Gleixner

2017-06-28 1:07 GMT+02:00 Kees Cook <keescook@chromium.org>:
> On Thu, Jun 15, 2017 at 9:42 AM, Salvatore Mesoraca
> <s.mesoraca16@gmail.com> wrote:
>> Creation of a new hook to let LSM modules handle user-space pagefaults on
>> x86.
>> It can be used to avoid segfaulting the originating process.
>> If it's the case it can modify process registers before returning.
>> This is not a security feature by itself, it's a way to soften some
>> unwanted side-effects of restrictive security features.
>> In particular this is used by S.A.R.A. can be used to implement what
>> PaX call "trampoline emulation" that, in practice, allow for some specific
>> code sequences to be executed even if they are in non executable memory.
>> This may look like a bad thing at first, but you have to consider
>> that:
>> - This allows for strict memory restrictions (e.g. W^X) to stay on even
>>   when they should be turned off. And, even if this emulation
>>   makes those features less effective, it's still better than having
>>   them turned off completely.
>> - The only code sequences emulated are trampolines used to make
>>   function calls. In many cases, when you have the chance to
>>   make arbitrary memory writes, you can already manipulate the
>>   control flow of the program by overwriting function pointers or
>>   return values. So, in many cases, the "trampoline emulation"
>>   doesn't introduce new exploit vectors.
>> - It's a feature that can be turned on only if needed, on a per
>>   executable file basis.
>
> Can this be made arch-agnostic? It seems a per-arch register-handling
> routine would be needed, though. :(

S.A.R.A.'s "pagefault_handler_x86" implementation is fully arch specific
so it won't benefit too much from this change.
Anyway having a single hook for all archs is probably a cleaner solution,
I'll change it in the v3.
Would it be OK if I make it arch-agnostic while I actually keep it only
in arch/x86/mm/fault.c?
Thank you for your help.

Salvatore

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC v2 7/9] Trampoline emulation
  2017-06-27 23:13   ` Kees Cook
@ 2017-06-29 19:35     ` Salvatore Mesoraca
  0 siblings, 0 replies; 21+ messages in thread
From: Salvatore Mesoraca @ 2017-06-29 19:35 UTC (permalink / raw)
  To: Kees Cook
  Cc: LKML, linux-security-module, kernel-hardening, Brad Spengler,
	PaX Team, Casey Schaufler, James Morris, Serge E. Hallyn,
	Linux-MM, x86, Jann Horn, Christoph Hellwig, Thomas Gleixner

2017-06-28 1:13 GMT+02:00 Kees Cook <keescook@chromium.org>:
> On Thu, Jun 15, 2017 at 9:42 AM, Salvatore Mesoraca
> <s.mesoraca16@gmail.com> wrote:
>> Some programs need to generate part of their code at runtime. Luckily
>> enough, in some cases they only generate well-known code sequences (the
>> "trampolines") that can be easily recognized and emulated by the kernel.
>> This way WX Protection can still be active, so a potential attacker won't
>> be able to generate arbitrary sequences of code, but just those that are
>> explicitly allowed. This is not ideal, but it's still better than having WX
>> Protection completely disabled.
>> In particular S.A.R.A. is able to recognize trampolines used by GCC for
>> nested C functions and libffi's trampolines.
>> This feature is implemented only on x86_32 and x86_64.
>> The assembly sequences used here were originally obtained from PaX source
>> code.
>
> See below about the language grsecurity has asked people to use in commit logs.

OK, I'll change the commit message in v3.

>>
>> Signed-off-by: Salvatore Mesoraca <s.mesoraca16@gmail.com>
>> ---
>>  security/sara/Kconfig               |  17 ++++
>>  security/sara/include/trampolines.h | 171 ++++++++++++++++++++++++++++++++++++
>>  security/sara/wxprot.c              | 140 +++++++++++++++++++++++++++++
>>  3 files changed, 328 insertions(+)
>>  create mode 100644 security/sara/include/trampolines.h
>>
>> diff --git a/security/sara/Kconfig b/security/sara/Kconfig
>> index 6c74069..f406805 100644
>> --- a/security/sara/Kconfig
>> +++ b/security/sara/Kconfig
>> @@ -96,6 +96,23 @@ choice
>>                   Documentation/security/SARA.rst.
>>  endchoice
>>
>> +config SECURITY_SARA_WXPROT_EMUTRAMP
>> +       bool "Enable emulation for some types of trampolines"
>> +       depends on SECURITY_SARA_WXPROT
>> +       depends on X86
>> +       default y
>> +       help
>> +         Some programs and libraries need to execute special small code
>> +         snippets from non-executable memory pages.
>> +         Most notable examples are the GCC and libffi trampolines.
>> +         This features make it possible to execute those trampolines even
>> +         if they reside in non-executable memory pages.
>> +         This features need to be enabled on a per-executable basis
>> +         via user-space utilities.
>> +         See Documentation/security/SARA.rst. for further information.
>> +
>> +         If unsure, answer y.
>> +
>>  config SECURITY_SARA_WXPROT_DISABLED
>>         bool "WX protection will be disabled at boot."
>>         depends on SECURITY_SARA_WXPROT
>> diff --git a/security/sara/include/trampolines.h b/security/sara/include/trampolines.h
>> new file mode 100644
>> index 0000000..eab0a85
>> --- /dev/null
>> +++ b/security/sara/include/trampolines.h
>> @@ -0,0 +1,171 @@
>> +/*
>> + * S.A.R.A. Linux Security Module
>> + *
>> + * Copyright (C) 2017 Salvatore Mesoraca <s.mesoraca16@gmail.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2, as
>> + * published by the Free Software Foundation.
>> + *
>> + * Assembly sequences used here were copied from
>> + * PaX patch by PaX Team <pageexec@freemail.hu>
>
> Given this copying, please include the grsecurity/PaX copyright notice
> too. Please see the recommendations here:
> http://kernsec.org/wiki/index.php/Kernel_Self_Protection_Project/Get_Involved

I understand your concern, but I don't think that the formula
"Copyright (C) 2001-2017 PaX Team, Bradley Spengler, Open Source Security Inc"
is appropriate in this context.
I don't think that any of the code written here is covered by Grsecurity's
copyright. That line I wrote about "Assembly sequences" was just a courtesy
note because I didn't gdb-ed my way through all of them, but if someone owns
copyright on those "Assembly sequences" it's probably GCC and libffi authors.
I have no problem in giving the most appropriate credits to the relevant
people. And, for sure, I'm not going to get rich because of the attribution
of this code.
I wrote this code without copying any actual line and I prefer to not give
away my copyright without a good reason (let's call it a matter of principle).
I don't want to start the umpteenth flame war about grsecurity and copyright.
I'm acting in good faith and I hope this discussion can continue with
constructive input from the interested parties.

>> + *
>> + */
>> +
>> +#ifndef __SARA_TRAMPOLINES_H
>> +#define __SARA_TRAMPOLINES_H
>> +#ifdef CONFIG_SECURITY_SARA_WXPROT_EMUTRAMP
>> +
>> +
>> +/* x86_32 */
>> +
>> +
>> +struct libffi_trampoline_x86_32 {
>> +       unsigned char mov;
>> +       unsigned int addr1;
>> +       unsigned char jmp;
>> +       unsigned int addr2;
>> +} __packed;
>> +
>> +struct gcc_trampoline_x86_32_type1 {
>> +       unsigned char mov1;
>> +       unsigned int addr1;
>> +       unsigned char mov2;
>> +       unsigned int addr2;
>> +       unsigned short jmp;
>> +} __packed;
>> +
>> +struct gcc_trampoline_x86_32_type2 {
>> +       unsigned char mov;
>> +       unsigned int addr1;
>> +       unsigned char jmp;
>> +       unsigned int addr2;
>> +} __packed;
>> +
>> +union trampolines_x86_32 {
>> +       struct libffi_trampoline_x86_32 lf;
>> +       struct gcc_trampoline_x86_32_type1 g1;
>> +       struct gcc_trampoline_x86_32_type2 g2;
>> +};
>> +
>> +#define is_valid_libffi_trampoline_x86_32(UNION)       \
>> +       (UNION.lf.mov == 0xB8 &&                        \
>> +       UNION.lf.jmp == 0xE9)
>> +
>> +#define emulate_libffi_trampoline_x86_32(UNION, REGS) do {     \
>> +       (REGS)->ax = UNION.lf.addr1;                            \
>> +       (REGS)->ip = (unsigned int) ((REGS)->ip +               \
>> +                                    UNION.lf.addr2 +           \
>> +                                    sizeof(UNION.lf));         \
>> +} while (0)
>> +
>> +#define is_valid_gcc_trampoline_x86_32_type1(UNION, REGS)      \
>> +       (UNION.g1.mov1 == 0xB9 &&                               \
>> +       UNION.g1.mov2 == 0xB8 &&                                \
>> +       UNION.g1.jmp == 0xE0FF &&                               \
>> +       REGS->ip > REGS->sp)
>> +
>> +#define emulate_gcc_trampoline_x86_32_type1(UNION, REGS) do {  \
>> +       (REGS)->cx = UNION.g1.addr1;                            \
>> +       (REGS)->ax = UNION.g1.addr2;                            \
>> +       (REGS)->ip = UNION.g1.addr2;                            \
>> +} while (0)
>> +
>> +#define is_valid_gcc_trampoline_x86_32_type2(UNION, REGS)      \
>> +       (UNION.g2.mov == 0xB9 &&                                \
>> +       UNION.g2.jmp == 0xE9 &&                                 \
>> +       REGS->ip > REGS->sp)
>> +
>> +#define emulate_gcc_trampoline_x86_32_type2(UNION, REGS) do {  \
>> +       (REGS)->cx = UNION.g2.addr1;                            \
>> +       (REGS)->ip = (unsigned int) ((REGS)->ip +               \
>> +                                    UNION.g2.addr2 +           \
>> +                                    sizeof(UNION.g2));         \
>> +} while (0)
>
> These all seem like they need to live in arch/x86/... somewhere rather
> than in the LSM, but maybe this isn't needed on other architectures?
> This seems to be very arch and compiler specific...

It is very arch and compiler specific. At some extent it can even be
considered program specific, given that some of the trampolines are
used only by libffi (AFAIK).
I put it in S.A.R.A.'s directory because I didn't want to pollute
arch/x86 with code needed only by a single LSM.
In theory this feature could be useful on other archs too, but I'm not
sure if there is actual demand for it.
Do you have any suggestion about the best place in "arch/x86/..."
where it should be moved to?
Thank you very much for your time.

Salvatore

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC v2 5/9] S.A.R.A. WX Protection
  2017-06-27 23:04   ` Kees Cook
@ 2017-06-29 19:39     ` Salvatore Mesoraca
  0 siblings, 0 replies; 21+ messages in thread
From: Salvatore Mesoraca @ 2017-06-29 19:39 UTC (permalink / raw)
  To: Kees Cook
  Cc: LKML, linux-security-module, kernel-hardening, Brad Spengler,
	PaX Team, Casey Schaufler, James Morris, Serge E. Hallyn,
	Linux-MM, x86, Jann Horn, Christoph Hellwig, Thomas Gleixner

2017-06-28 1:04 GMT+02:00 Kees Cook <keescook@chromium.org>:
> On Thu, Jun 15, 2017 at 9:42 AM, Salvatore Mesoraca
> <s.mesoraca16@gmail.com> wrote:
>> +static int sara_check_vmflags(vm_flags_t vm_flags)
>> +{
>> +       u16 sara_wxp_flags = get_current_sara_wxp_flags();
>> +
>> +       if (sara_enabled && wxprot_enabled) {
>> +               if (sara_wxp_flags & SARA_WXP_WXORX &&
>> +                   vm_flags & VM_WRITE &&
>> +                   vm_flags & VM_EXEC) {
>> +                       if ((sara_wxp_flags & SARA_WXP_VERBOSE))
>> +                               pr_wxp("W^X");
>> +                       if (!(sara_wxp_flags & SARA_WXP_COMPLAIN))
>> +                               return -EPERM;
>> +               }
>> +               if (sara_wxp_flags & SARA_WXP_MMAP &&
>> +                   (vm_flags & VM_EXEC ||
>> +                    (!(vm_flags & VM_MAYWRITE) && (vm_flags & VM_MAYEXEC))) &&
>> +                   get_current_sara_mmap_blocked()) {
>> +                       if ((sara_wxp_flags & SARA_WXP_VERBOSE))
>> +                               pr_wxp("executable mmap");
>> +                       if (!(sara_wxp_flags & SARA_WXP_COMPLAIN))
>> +                               return -EPERM;
>> +               }
>> +       }
>
> Given the subtle differences between these various if blocks (here and
> in the other hook), I think it would be nice to have some beefy
> comments here to describe specifically what's being checked (and why).
> It'll help others review this code, and help validate code against
> intent.
>
> I would also try to minimize the written code by creating a macro for
> a repeated pattern here:
>
>> +                               if ((sara_wxp_flags & SARA_WXP_VERBOSE))
>> +                                       pr_wxp("mprotect on file mmap");
>> +                               if (!(sara_wxp_flags & SARA_WXP_COMPLAIN))
>> +                                       return -EACCES;
>
> These four lines are repeated several times with only the const char *
> and return value changing. Perhaps something like:
>
> #define sara_return(err, msg) do { \
>                                if ((sara_wxp_flags & SARA_WXP_VERBOSE)) \
>                                        pr_wxp(err); \
>                                if (!(sara_wxp_flags & SARA_WXP_COMPLAIN)) \
>                                        return -err; \
> } while (0)
>
> Then each if block turns into something quite easier to parse:
>
>                if (sara_wxp_flags & SARA_WXP_WXORX &&
>                    vm_flags & VM_WRITE &&
>                    vm_flags & VM_EXEC)
>                        sara_return(EPERM, "W^X");

I absolutely agree with all of the above. These issues will be addressed in v3.
Thank you for your contribution.

Salvatore

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC v2 6/9] Creation of "pagefault_handler_x86" LSM hook
  2017-06-29 19:30     ` Salvatore Mesoraca
@ 2017-06-29 20:20       ` Kees Cook
  0 siblings, 0 replies; 21+ messages in thread
From: Kees Cook @ 2017-06-29 20:20 UTC (permalink / raw)
  To: Salvatore Mesoraca
  Cc: LKML, linux-security-module, kernel-hardening, Brad Spengler,
	PaX Team, Casey Schaufler, James Morris, Serge E. Hallyn,
	Linux-MM, x86, Jann Horn, Christoph Hellwig, Thomas Gleixner

On Thu, Jun 29, 2017 at 12:30 PM, Salvatore Mesoraca
<s.mesoraca16@gmail.com> wrote:
> 2017-06-28 1:07 GMT+02:00 Kees Cook <keescook@chromium.org>:
>> On Thu, Jun 15, 2017 at 9:42 AM, Salvatore Mesoraca
>> <s.mesoraca16@gmail.com> wrote:
>>> Creation of a new hook to let LSM modules handle user-space pagefaults on
>>> x86.
>>> It can be used to avoid segfaulting the originating process.
>>> If it's the case it can modify process registers before returning.
>>> This is not a security feature by itself, it's a way to soften some
>>> unwanted side-effects of restrictive security features.
>>> In particular this is used by S.A.R.A. can be used to implement what
>>> PaX call "trampoline emulation" that, in practice, allow for some specific
>>> code sequences to be executed even if they are in non executable memory.
>>> This may look like a bad thing at first, but you have to consider
>>> that:
>>> - This allows for strict memory restrictions (e.g. W^X) to stay on even
>>>   when they should be turned off. And, even if this emulation
>>>   makes those features less effective, it's still better than having
>>>   them turned off completely.
>>> - The only code sequences emulated are trampolines used to make
>>>   function calls. In many cases, when you have the chance to
>>>   make arbitrary memory writes, you can already manipulate the
>>>   control flow of the program by overwriting function pointers or
>>>   return values. So, in many cases, the "trampoline emulation"
>>>   doesn't introduce new exploit vectors.
>>> - It's a feature that can be turned on only if needed, on a per
>>>   executable file basis.
>>
>> Can this be made arch-agnostic? It seems a per-arch register-handling
>> routine would be needed, though. :(
>
> S.A.R.A.'s "pagefault_handler_x86" implementation is fully arch specific
> so it won't benefit too much from this change.
> Anyway having a single hook for all archs is probably a cleaner solution,
> I'll change it in the v3.
> Would it be OK if I make it arch-agnostic while I actually keep it only
> in arch/x86/mm/fault.c?
> Thank you for your help.

It'd be nicer to wire it up unconditionally to all architectures, but
I'm not entirely sure if that's feasible. Perhaps SARA (or this LSM
hook) would be hidden behind some CONFIG_ARCH_HAS_LSM_PAGEFAULT or
something that each architecture could "select" in its Kconfig.

Perhaps some other LSM folks have some better ideas?

-Kees

-- 
Kees Cook
Pixel Security

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2017-06-29 20:20 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-06-15 16:42 [RFC v2 0/9] S.A.R.A. a new stacked LSM Salvatore Mesoraca
2017-06-15 16:42 ` [RFC v2 1/9] S.A.R.A. Documentation Salvatore Mesoraca
2017-06-15 16:42 ` [RFC v2 2/9] S.A.R.A. framework creation Salvatore Mesoraca
2017-06-15 16:42 ` [RFC v2 3/9] Creation of "check_vmflags" LSM hook Salvatore Mesoraca
2017-06-27 23:05   ` Kees Cook
2017-06-29 19:28     ` Salvatore Mesoraca
2017-06-15 16:42 ` [RFC v2 4/9] S.A.R.A. cred blob management Salvatore Mesoraca
2017-06-15 16:42 ` [RFC v2 5/9] S.A.R.A. WX Protection Salvatore Mesoraca
2017-06-27 23:04   ` Kees Cook
2017-06-29 19:39     ` Salvatore Mesoraca
2017-06-15 16:42 ` [RFC v2 6/9] Creation of "pagefault_handler_x86" LSM hook Salvatore Mesoraca
2017-06-27 23:07   ` Kees Cook
2017-06-29 19:30     ` Salvatore Mesoraca
2017-06-29 20:20       ` Kees Cook
2017-06-15 16:42 ` [RFC v2 7/9] Trampoline emulation Salvatore Mesoraca
2017-06-15 16:47   ` [kernel-hardening] " aconcernedfossdev
2017-06-15 17:19     ` Salvatore Mesoraca
2017-06-27 23:13   ` Kees Cook
2017-06-29 19:35     ` Salvatore Mesoraca
2017-06-15 16:42 ` [RFC v2 8/9] Allowing for stacking procattr support in S.A.R.A Salvatore Mesoraca
2017-06-15 16:42 ` [RFC v2 9/9] S.A.R.A. WX Protection procattr interface Salvatore Mesoraca

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).