2017-06-12 16:57:34

by Salvatore Mesoraca

[permalink] [raw]
Subject: [PATCH 00/11] S.A.R.A. a new stacked LSM

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.

S.A.R.A. submodules introduced in this patchset are: USB Filtering and
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.

USB Filtering aims to provide a mechanism to decide which USB devices
should be authorized to connect to the system and which shouldn't. The main
goal is to narrow the attack surface for custom USB devices designed to
exploit vulnerabilities found in some USB device drivers.
Via configuration it's possible to allow or to deny authorization, based
on one or more of: Vendor ID, Product ID, bus name and port number. There
is also limited support for wildcards.
Depending on the configuration, it can work both as a white list or as a
black list.
With the help of "saractl" it's also possible to completely disable new
USB devices when the screen is "locked".
The original idea is inspired by the Grsecurity "Deny USB" feature.

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.

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

Salvatore Mesoraca (11):
S.A.R.A. Documentation
S.A.R.A. framework creation
Creation of "usb_device_auth" LSM hook
S.A.R.A. USB Filtering
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 | 40 ++
Documentation/security/00-INDEX | 2 +
Documentation/security/SARA.rst | 192 +++++
arch/x86/mm/fault.c | 6 +
drivers/usb/core/hub.c | 4 +
drivers/usb/core/sysfs.c | 6 +-
fs/proc/base.c | 38 +
include/linux/cred.h | 3 +
include/linux/lsm_hooks.h | 26 +
include/linux/security.h | 24 +
mm/mmap.c | 9 +
security/Kconfig | 1 +
security/Makefile | 2 +
security/sara/Kconfig | 175 +++++
security/sara/Makefile | 5 +
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/usb_filtering.h | 27 +
security/sara/include/utils.h | 69 ++
security/sara/include/wxprot.h | 27 +
security/sara/main.c | 113 +++
security/sara/sara_data.c | 79 +++
security/sara/securityfs.c | 558 +++++++++++++++
security/sara/usb_filtering.c | 410 +++++++++++
security/sara/utils.c | 151 ++++
security/sara/wxprot.c | 902 ++++++++++++++++++++++++
security/security.c | 42 +-
29 files changed, 3214 insertions(+), 3 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/usb_filtering.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/usb_filtering.c
create mode 100644 security/sara/utils.c
create mode 100644 security/sara/wxprot.c

--
1.9.1


2017-06-12 16:57:51

by Salvatore Mesoraca

[permalink] [raw]
Subject: [PATCH 01/11] S.A.R.A. Documentation

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

Signed-off-by: Salvatore Mesoraca <[email protected]>
---
Documentation/admin-guide/kernel-parameters.txt | 40 +++++
Documentation/security/00-INDEX | 2 +
Documentation/security/SARA.rst | 192 ++++++++++++++++++++++++
3 files changed, 234 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..f3ee12d 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -3702,6 +3702,46 @@
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_usb_filtering= [SARA]
+ Disable or enable S.A.R.A. USB Filtering at boot
+ time.
+ Format: { "0" | "1" }
+ See security/sara/Kconfig help text
+ 0 -- disable.
+ 1 -- enable.
+ Default value is 1.
+
+ sara_usb_filtering_default= [SARA]
+ Set S.A.R.A. USB Filtering default action.
+ Format: { "a" | "d" }
+ See security/sara/Kconfig help text
+ a -- allow.
+ d -- deny.
+ 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..a1523033
--- /dev/null
+++ b/Documentation/security/SARA.rst
@@ -0,0 +1,192 @@
+========
+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 two main submodules:
+
+- USB Filtering
+- 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
+=====================
+
+USB Filtering
+-------------
+USB Filtering aims to provide a mechanism to decide which USB devices should
+be authorized to connect to the system and which shouldn't. The main goal
+is to narrow the attack surface for custom USB devices designed to exploit
+vulnerabilities found in some USB device drivers.
+
+Via configuration it's possible to allow or to deny authorization, based
+on one or more of: Vendor ID, Product ID, bus name and port number. There
+is also limited support for wildcards.
+Depending on the configuration, it can work both as a white list or as a black
+list.
+With the help of `saractl` [2]_ it's also possible to completely disable new
+USB devices when the screen is "locked".
+The original idea is inspired by the Grsecurity "Deny USB" feature.
+For further information on configuration file format and user-space utilities
+please look at the full documentation [1]_.
+
+
+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 program 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.
+Given that it's possible to segregate the part that runs untrusted
+code from the rest through a fork, 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 `saralib` [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 `saralib` 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] `saralib <https://github.com/smeso/saralib>`_
+.. [4] `sara-test <https://github.com/smeso/sara-test>`_
--
1.9.1

2017-06-12 16:58:03

by Salvatore Mesoraca

[permalink] [raw]
Subject: [PATCH 02/11] S.A.R.A. framework creation

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 <[email protected]>
---
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 | 558 +++++++++++++++++++++++++++++++++++++
security/sara/utils.c | 151 ++++++++++
security/security.c | 1 +
12 files changed, 1015 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 <[email protected]>
+ *
+ * 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 <[email protected]>
+ *
+ * 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 <[email protected]>
+ *
+ * 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 <[email protected]>
+ *
+ * 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..4dca504
--- /dev/null
+++ b/security/sara/securityfs.c
@@ -0,0 +1,558 @@
+/*
+ * S.A.R.A. Linux Security Module
+ *
+ * Copyright (C) 2017 Salvatore Mesoraca <[email protected]>
+ *
+ * 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/ctype.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <linux/mm.h>
+#include <linux/spinlock.h>
+#include <linux/security.h>
+
+#include "include/sara.h"
+#include "include/utils.h"
+#include "include/securityfs.h"
+
+#define STR_HELPER(x) #x
+#define STR(x) 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, "%" 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 <[email protected]>
+ *
+ * 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

2017-06-12 16:58:14

by Salvatore Mesoraca

[permalink] [raw]
Subject: [PATCH 04/11] S.A.R.A. USB Filtering

Introduction of S.A.R.A. USB Filtering.
It uses the "usb_device_auth" LSM hook to provide a mechanism to decide
which USB devices should be authorized to connect to the system and
which shouldn't.
The main goal is to narrow the attack surface for custom USB devices
designed to exploit vulnerabilities found in some USB device drivers.
Via configuration it's possible to allow or to deny authorization, based
on one or more of: Vendor ID, Product ID, bus name and port number. There
is also support for "trailing wildcards".
Depending on the configuration, it can work both as a white list or as a
black list.
The original idea is inspired by the Grsecurity "Deny USB" feature.

Signed-off-by: Salvatore Mesoraca <[email protected]>
---
security/sara/Kconfig | 41 ++++
security/sara/Makefile | 1 +
security/sara/include/usb_filtering.h | 27 +++
security/sara/main.c | 6 +
security/sara/usb_filtering.c | 410 ++++++++++++++++++++++++++++++++++
5 files changed, 485 insertions(+)
create mode 100644 security/sara/include/usb_filtering.h
create mode 100644 security/sara/usb_filtering.c

diff --git a/security/sara/Kconfig b/security/sara/Kconfig
index 5b61020..01ff246 100644
--- a/security/sara/Kconfig
+++ b/security/sara/Kconfig
@@ -39,4 +39,45 @@ config SECURITY_SARA_NO_RUNTIME_ENABLE

If unsure, answer Y.

+config SECURITY_SARA_USB_FILTERING
+ bool "USB Filtering"
+ depends on SECURITY_SARA && USB_SUPPORT
+ default y
+ help
+ Enable S.A.R.A. USB filtering capability. To prevent the connection
+ of unwanted USB devices, e.g. custom USB devices designed to
+ exploit vulnerabilities found in device drivers.
+ User-space utilities can be used to manage the configuration file.
+ Further information can be found in Documentation/security/SARA.rst.
+
+ If unsure, answer Y.
+
+config SECURITY_SARA_USB_FILTERING_DENY
+ bool "Default action for new USB devices is DENY"
+ depends on SECURITY_SARA_USB_FILTERING
+ default n
+ help
+ If you say Y here any device that is not explicitly allowed via the
+ configuration will be blocked. If you don't enable this option
+ any USB device connected at boot will be allowed, regardless of
+ your settings. However, if you enable this and you don't
+ set your user-space utilities adequately your machine could become
+ unusable (e.g. your USB keyboard will be disabled).
+ This option can be overridden at boot time via
+ "sara_usb_filtering_default=[d|a]" kernel parameter.
+
+ If unsure, answer N.
+
+config SECURITY_SARA_USB_FILTERING_DISABLED
+ bool "USB filtering will be disabled at boot."
+ depends on SECURITY_SARA_USB_FILTERING
+ default n
+ help
+ If you say Y here USB filtering won't be enabled at startup. You can
+ override this option via user-space utilities or at boot time via
+ "sara_usb_filtering=[0|1]" kernel parameter.
+ This option is useful for distro kernels.
+
+ If unsure, answer N.
+
endmenu
diff --git a/security/sara/Makefile b/security/sara/Makefile
index 8acd291..8acf8a9 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-$(CONFIG_SECURITY_SARA_USB_FILTERING) += usb_filtering.o
diff --git a/security/sara/include/usb_filtering.h b/security/sara/include/usb_filtering.h
new file mode 100644
index 0000000..5645e0f
--- /dev/null
+++ b/security/sara/include/usb_filtering.h
@@ -0,0 +1,27 @@
+/*
+ * S.A.R.A. Linux Security Module
+ *
+ * Copyright (C) 2017 Salvatore Mesoraca <[email protected]>
+ *
+ * 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_USB_FILTERING_H
+#define __SARA_USB_FILTERING_H
+
+#ifdef CONFIG_SECURITY_SARA_USB_FILTERING
+
+#include <linux/init.h>
+int sara_usb_filtering_init(void) __init;
+
+#else /* CONFIG_SECURITY_SARA_USB_FILTERING */
+inline int sara_usb_filtering_init(void)
+{
+ return 0;
+}
+#endif /* CONFIG_SECURITY_SARA_USB_FILTERING */
+
+#endif /* __SARA_USB_FILTERING_H */
diff --git a/security/sara/main.c b/security/sara/main.c
index 2007735..8783c3c 100644
--- a/security/sara/main.c
+++ b/security/sara/main.c
@@ -15,6 +15,7 @@

#include "include/sara.h"
#include "include/securityfs.h"
+#include "include/usb_filtering.h"

static const int sara_version = SARA_VERSION;

@@ -80,6 +81,11 @@ void __init sara_init(void)
goto error;
}

+ if (sara_usb_filtering_init()) {
+ pr_crit("impossible to initialize usb filtering.\n");
+ goto error;
+ }
+
pr_debug("initialized.\n");

if (sara_enabled)
diff --git a/security/sara/usb_filtering.c b/security/sara/usb_filtering.c
new file mode 100644
index 0000000..270f110
--- /dev/null
+++ b/security/sara/usb_filtering.c
@@ -0,0 +1,410 @@
+/*
+ * S.A.R.A. Linux Security Module
+ *
+ * Copyright (C) 2017 Salvatore Mesoraca <[email protected]>
+ *
+ * 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_USB_FILTERING
+
+#include <linux/kref.h>
+#include <linux/lsm_hooks.h>
+#include <linux/mm.h>
+#include <linux/spinlock.h>
+
+#include "include/sara.h"
+#include "include/utils.h"
+#include "include/securityfs.h"
+#include "include/usb_filtering.h"
+
+#define SARA_USB_FILTERING_CONFIG_VERSION 0
+
+#define SARA_USB_FILTERING_ALLOW 0
+#define SARA_USB_FILTERING_DENY 1
+
+struct usb_filtering_rule {
+ u16 product_id;
+ u16 vendor_id;
+ u8 product_id_shift;
+ u8 vendor_id_shift;
+ char *bus_name;
+ bool exact_bus_name;
+ u8 port;
+ u8 action;
+};
+
+struct usb_config_container {
+ u32 rules_size;
+ struct usb_filtering_rule *rules;
+ size_t buf_len;
+ struct kref refcount;
+ char hash[SARA_CONFIG_HASH_LEN];
+};
+
+static struct usb_config_container __rcu *usb_filtering_config;
+
+static const int usb_filtering_version =
+ SARA_USB_FILTERING_CONFIG_VERSION;
+static bool usb_filtering_enabled __read_mostly = true;
+static DEFINE_SPINLOCK(usb_config_lock);
+
+#ifdef CONFIG_SECURITY_SARA_USB_FILTERING_DENY
+static int default_action __ro_after_init = SARA_USB_FILTERING_DENY;
+#else
+static int default_action __ro_after_init = SARA_USB_FILTERING_ALLOW;
+#endif
+
+static int __init sara_usb_filtering_enabled_setup(char *str)
+{
+ if (str[0] == '1' && str[1] == '\0')
+ usb_filtering_enabled = true;
+ else
+ usb_filtering_enabled = false;
+ return 1;
+}
+__setup("sara_usb_filtering=", sara_usb_filtering_enabled_setup);
+
+static int __init sara_usb_filtering_default_setup(char *str)
+{
+ if (str[0] == 'd' && str[1] == '\0')
+ default_action = SARA_USB_FILTERING_DENY;
+ else
+ default_action = SARA_USB_FILTERING_ALLOW;
+ return 1;
+}
+__setup("sara_usb_filtering_default=", sara_usb_filtering_default_setup);
+
+static int sara_usb_device_auth(const struct usb_device *udev)
+{
+ int i;
+ int ret;
+ u16 vid = le16_to_cpu(udev->descriptor.idVendor);
+ u16 pid = le16_to_cpu(udev->descriptor.idProduct);
+ const char *bus_name = udev->bus->bus_name;
+ struct usb_config_container *c;
+
+ if (!sara_enabled || !usb_filtering_enabled)
+ return SARA_USB_FILTERING_ALLOW;
+
+ pr_debug("USB filtering: new usb device found \"%04x:%04x\" on \"%s\" port \"%d\".\n",
+ vid, pid, bus_name, udev->portnum);
+
+ SARA_CONFIG_GET_RCU(c, usb_filtering_config);
+ for (i = 0; i < c->rules_size; ++i) {
+ if ((vid >> c->rules[i].vendor_id_shift) ==
+ c->rules[i].vendor_id &&
+ (pid >> c->rules[i].product_id_shift) ==
+ c->rules[i].product_id) {
+ if (!c->rules[i].port ||
+ c->rules[i].port == udev->portnum) {
+ if (c->rules[i].exact_bus_name) {
+ if (strcmp(bus_name,
+ c->rules[i].bus_name) == 0)
+ goto match;
+ } else if (strncmp(bus_name,
+ c->rules[i].bus_name,
+ strlen(c->rules[i].bus_name))
+ == 0)
+ goto match;
+ }
+ }
+ }
+ SARA_CONFIG_PUT_RCU(c);
+
+ ret = default_action;
+ if (ret == SARA_USB_FILTERING_ALLOW)
+ pr_info("USB filtering: no match found for \"%04x:%04x\" on \"%s\" port \"%d\". Default action is ALLOW.\n",
+ vid, pid, bus_name, udev->portnum);
+ else
+ pr_info("USB filtering: no match found for \"%04x:%04x\" on \"%s\" port \"%d\". Default action is DENY.\n",
+ vid, pid, bus_name, udev->portnum);
+ goto out;
+
+match:
+ ret = c->rules[i].action;
+ SARA_CONFIG_PUT_RCU(c);
+ if (ret == SARA_USB_FILTERING_ALLOW)
+ pr_info("USB filtering: match found for \"%04x:%04x\" on \"%s\" port \"%d\". Action is ALLOW.\n",
+ vid, pid, bus_name, udev->portnum);
+ else
+ pr_notice("USB filtering: match found for \"%04x:%04x\" on \"%s\" port \"%d\". Action is DENY.\n",
+ vid, pid, bus_name, udev->portnum);
+out:
+ return ret;
+}
+
+static struct security_hook_list usb_hooks[] __ro_after_init = {
+ LSM_HOOK_INIT(usb_device_auth, sara_usb_device_auth),
+};
+
+struct binary_config_header {
+ char magic[8];
+ __le32 version;
+ __le32 rules_size;
+ char hash[SARA_CONFIG_HASH_LEN];
+} __packed;
+
+struct binary_config_rule {
+ __le16 product_id;
+ __le16 vendor_id;
+ u8 product_id_shift;
+ u8 vendor_id_shift;
+ u8 exact_bus_name;
+ u8 action;
+ u8 port;
+ u8 bus_name_len;
+} __packed;
+
+static void config_free(struct usb_config_container *data)
+{
+ int i;
+
+ for (i = 0; i < data->rules_size; ++i)
+ kfree(data->rules[i].bus_name);
+ kvfree(data->rules);
+ kfree(data);
+}
+
+static int config_load(const char *buf, size_t buf_len)
+{
+ int ret;
+ int i;
+ size_t inc;
+ const char *pos;
+ struct usb_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, "SARAUSBF", 8) != 0))
+ goto out;
+ if (unlikely(le32_to_cpu(h->version) != usb_filtering_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;
+
+ ret = -EINVAL;
+ if (unlikely(inc + r->bus_name_len > buf_len))
+ goto out_rules;
+
+ new->rules[i].product_id = le16_to_cpu(r->product_id);
+ new->rules[i].vendor_id = le16_to_cpu(r->vendor_id);
+ new->rules[i].product_id_shift = r->product_id_shift;
+ new->rules[i].vendor_id_shift = r->vendor_id_shift;
+ new->rules[i].exact_bus_name = r->exact_bus_name;
+ new->rules[i].action = r->action;
+ new->rules[i].port = r->port;
+
+ if (unlikely(new->rules[i].product_id_shift > 16))
+ goto out_rules;
+ if (unlikely(new->rules[i].vendor_id_shift > 16))
+ goto out_rules;
+ if (unlikely((int) new->rules[i].exact_bus_name != 0 &&
+ (int) new->rules[i].exact_bus_name != 1))
+ goto out_rules;
+ if (unlikely(new->rules[i].action != 0 &&
+ new->rules[i].action != 1))
+ goto out_rules;
+
+ ret = -ENOMEM;
+ new->rules[i].bus_name = kmalloc(r->bus_name_len+1, GFP_KERNEL);
+ if (unlikely(new->rules[i].bus_name == NULL))
+ goto out_rules;
+
+ memcpy(new->rules[i].bus_name, pos, r->bus_name_len);
+ new->rules[i].bus_name[r->bus_name_len] = '\0';
+ pos += r->bus_name_len;
+ }
+ new->buf_len = (size_t) (pos-buf);
+
+replace:
+ SARA_CONFIG_REPLACE(usb_filtering_config,
+ new,
+ config_free,
+ &usb_config_lock);
+ pr_notice("USB filtering: new rules loaded.\n");
+ return 0;
+
+out_rules:
+ for (i = 0; i < new->rules_size; ++i)
+ kfree(new->rules[i].bus_name);
+ kvfree(new->rules);
+out_new:
+ kfree(new);
+out:
+ pr_warn("USB filtering: 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;
+ int rulen;
+ struct usb_config_container *c;
+ struct usb_filtering_rule *rc;
+ struct binary_config_header *h;
+ struct binary_config_rule *r;
+
+ ret = -ENOMEM;
+ SARA_CONFIG_GET(c, usb_filtering_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, "SARAUSBF", 8);
+ h->version = cpu_to_le32(SARA_USB_FILTERING_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->product_id = cpu_to_le16(rc->product_id);
+ r->vendor_id = cpu_to_le16(rc->vendor_id);
+ r->product_id_shift = rc->product_id_shift;
+ r->vendor_id_shift = rc->vendor_id_shift;
+ r->exact_bus_name = (u8) rc->exact_bus_name;
+ r->action = rc->action;
+ r->port = rc->port;
+ r->bus_name_len = strlen(rc->bus_name);
+ if (buf_len < ((pos - mybuf) + r->bus_name_len))
+ goto out;
+ memcpy(pos, rc->bus_name, r->bus_name_len);
+ pos += r->bus_name_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 usb_config_container *config;
+
+ ret = -ENOMEM;
+ *buf = kzalloc(sizeof(config->hash), GFP_KERNEL);
+ if (unlikely(*buf == NULL))
+ goto out;
+
+ SARA_CONFIG_GET_RCU(config, usb_filtering_config);
+ memcpy(*buf, config->hash, sizeof(config->hash));
+ SARA_CONFIG_PUT_RCU(config);
+
+ ret = 0;
+out:
+ return ret;
+}
+
+static DEFINE_SARA_SECFS_BOOL_FLAG(usb_filtering_enabled_data,
+ usb_filtering_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 usb_filtering_fs[] __initconst = {
+ {
+ .name = "enabled",
+ .type = SARA_SECFS_BOOL,
+ .data = (void *) &usb_filtering_enabled_data,
+ },
+ {
+ .name = "version",
+ .type = SARA_SECFS_READONLY_INT,
+ .data = (int *) &usb_filtering_version,
+ },
+ {
+ .name = "default_action",
+ .type = SARA_SECFS_READONLY_INT,
+ .data = &default_action,
+ },
+ {
+ .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_usb_filtering_init(void)
+{
+ int ret;
+ struct usb_config_container *tmpc;
+
+ 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);
+ usb_filtering_config = (struct usb_config_container __rcu *) tmpc;
+ ret = sara_secfs_subtree_register("usb_filtering",
+ usb_filtering_fs,
+ ARRAY_SIZE(usb_filtering_fs));
+ if (unlikely(ret))
+ goto out_fail;
+ security_add_hooks(usb_hooks, ARRAY_SIZE(usb_hooks), "sara");
+ return 0;
+
+out_fail:
+ kfree(tmpc);
+ return ret;
+}
+
+#endif /* CONFIG_SECURITY_SARA_USB_FILTERING */
--
1.9.1

2017-06-12 16:58:06

by Salvatore Mesoraca

[permalink] [raw]
Subject: [PATCH 03/11] Creation of "usb_device_auth" LSM hook

Creation of a new LSM hook that can be used to authorize or deauthorize
new USB devices via the usb authorization interface.
The same hook can also prevent the authorization of a USB device via
"/sys/bus/usb/devices/DEVICE/authorized".
Using this hook an LSM could provide an higher level of granularity
than the current authorization interface.

Signed-off-by: Salvatore Mesoraca <[email protected]>
Cc: [email protected]
Cc: Greg Kroah-Hartman <[email protected]>
---
drivers/usb/core/hub.c | 4 ++++
drivers/usb/core/sysfs.c | 6 +++++-
include/linux/lsm_hooks.h | 6 ++++++
include/linux/security.h | 7 +++++++
security/security.c | 5 +++++
5 files changed, 27 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index b8bb20d..58be4f0 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -28,6 +28,7 @@
#include <linux/mutex.h>
#include <linux/random.h>
#include <linux/pm_qos.h>
+#include <linux/security.h>

#include <linux/uaccess.h>
#include <asm/byteorder.h>
@@ -4831,6 +4832,9 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
if (udev->quirks & USB_QUIRK_DELAY_INIT)
msleep(1000);

+ if (security_usb_device_auth(udev))
+ usb_deauthorize_device(udev);
+
/* consecutive bus-powered hubs aren't reliable; they can
* violate the voltage drop budget. if the new child has
* a "powered" LED, users should notice we didn't enable it
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index dfc68ed..fce9d39 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -17,6 +17,7 @@
#include <linux/usb.h>
#include <linux/usb/quirks.h>
#include <linux/of.h>
+#include <linux/security.h>
#include "usb.h"

/* Active configuration fields */
@@ -742,8 +743,11 @@ static ssize_t authorized_store(struct device *dev,
result = -EINVAL;
else if (val == 0)
result = usb_deauthorize_device(usb_dev);
- else
+ else {
+ if (security_usb_device_auth(usb_dev))
+ return -EPERM;
result = usb_authorize_device(usb_dev);
+ }
return result < 0 ? result : size;
}
static DEVICE_ATTR_IGNORE_LOCKDEP(authorized, S_IRUGO | S_IWUSR,
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index bd274db..cc0937e 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -1189,6 +1189,10 @@
* to the @parent process for tracing.
* @parent contains the task_struct structure for debugger process.
* Return 0 if permission is granted.
+ * @usb_device_auth:
+ * Check if @udev device should be authorized or not.
+ * @udev contains the usb_device structure for the USB device.
+ * Return 0 if the device is allowed.
* @capget:
* Get the @effective, @inheritable, and @permitted capability sets for
* the @target process. The hook may also perform permission checking to
@@ -1352,6 +1356,7 @@
int (*ptrace_access_check)(struct task_struct *child,
unsigned int mode);
int (*ptrace_traceme)(struct task_struct *parent);
+ int (*usb_device_auth)(const struct usb_device *udev);
int (*capget)(struct task_struct *target, kernel_cap_t *effective,
kernel_cap_t *inheritable, kernel_cap_t *permitted);
int (*capset)(struct cred *new, const struct cred *old,
@@ -1670,6 +1675,7 @@ struct security_hook_heads {
struct list_head binder_transfer_file;
struct list_head ptrace_access_check;
struct list_head ptrace_traceme;
+ struct list_head usb_device_auth;
struct list_head capget;
struct list_head capset;
struct list_head capable;
diff --git a/include/linux/security.h b/include/linux/security.h
index af675b5..19bc364 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -30,6 +30,7 @@
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/fs.h>
+#include <linux/usb.h>

struct linux_binprm;
struct cred;
@@ -196,6 +197,7 @@ int security_binder_transfer_file(struct task_struct *from,
struct task_struct *to, struct file *file);
int security_ptrace_access_check(struct task_struct *child, unsigned int mode);
int security_ptrace_traceme(struct task_struct *parent);
+int security_usb_device_auth(const struct usb_device *udev);
int security_capget(struct task_struct *target,
kernel_cap_t *effective,
kernel_cap_t *inheritable,
@@ -434,6 +436,11 @@ static inline int security_ptrace_traceme(struct task_struct *parent)
return cap_ptrace_traceme(parent);
}

+static inline int security_usb_device_auth(const struct usb_device *udev)
+{
+ return 0;
+}
+
static inline int security_capget(struct task_struct *target,
kernel_cap_t *effective,
kernel_cap_t *inheritable,
diff --git a/security/security.c b/security/security.c
index 42c8028..e390f99 100644
--- a/security/security.c
+++ b/security/security.c
@@ -214,6 +214,11 @@ int security_ptrace_traceme(struct task_struct *parent)
return call_int_hook(ptrace_traceme, 0, parent);
}

+int security_usb_device_auth(const struct usb_device *udev)
+{
+ return call_int_hook(usb_device_auth, 0, udev);
+}
+
int security_capget(struct task_struct *target,
kernel_cap_t *effective,
kernel_cap_t *inheritable,
--
1.9.1

2017-06-12 16:58:21

by Salvatore Mesoraca

[permalink] [raw]
Subject: [PATCH 05/11] Creation of "check_vmflags" LSM hook

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 <[email protected]>
Cc: [email protected]
---
include/linux/lsm_hooks.h | 6 ++++++
include/linux/security.h | 6 ++++++
mm/mmap.c | 9 +++++++++
security/security.c | 5 +++++
4 files changed, 26 insertions(+)

diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index cc0937e..6934cc5 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -483,6 +483,10 @@
* @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.
* @file_lock:
* Check permission before performing file locking operations.
* Note: this hook mediates both flock and fcntl style locks.
@@ -1482,6 +1486,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);
@@ -1753,6 +1758,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 19bc364..67e33b6 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -302,6 +302,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);
@@ -830,6 +831,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..e19f04e 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -1363,6 +1363,9 @@ 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;

+ if (security_check_vmflags(vm_flags))
+ return -EPERM;
+
if (flags & MAP_LOCKED)
if (!can_do_mlock())
return -EPERM;
@@ -2833,6 +2836,9 @@ 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;

+ if (security_check_vmflags(flags))
+ return -EPERM;
+
error = get_unmapped_area(NULL, addr, len, 0, MAP_FIXED);
if (offset_in_page(error))
return error;
@@ -3208,6 +3214,9 @@ static struct vm_area_struct *__install_special_mapping(
int ret;
struct vm_area_struct *vma;

+ if (security_check_vmflags(vm_flags))
+ return ERR_PTR(-EPERM);
+
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 e390f99..25d58f0 100644
--- a/security/security.c
+++ b/security/security.c
@@ -905,6 +905,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

2017-06-12 16:58:36

by Salvatore Mesoraca

[permalink] [raw]
Subject: [PATCH 08/11] Creation of "pagefault_handler_x86" LSM hook

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.

Signed-off-by: Salvatore Mesoraca <[email protected]>
Cc: [email protected]
Cc: Ingo Molnar <[email protected]>
---
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 6934cc5..a42c2f8 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -487,6 +487,11 @@
* Check if the requested @vmflags are allowed.
* @vmflags contains requested the vmflags.
* Return 0 if the operation is allowed to continue.
+ * @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.
@@ -1487,6 +1492,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);
@@ -1759,6 +1767,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 67e33b6..bc38c83 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -303,6 +303,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);
@@ -836,6 +839,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 25d58f0..cf15686 100644
--- a/security/security.c
+++ b/security/security.c
@@ -910,6 +910,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

2017-06-12 16:58:28

by Salvatore Mesoraca

[permalink] [raw]
Subject: [PATCH 07/11] S.A.R.A. WX Protection

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 <[email protected]>
---
security/sara/Kconfig | 75 +++++
security/sara/Makefile | 1 +
security/sara/include/wxprot.h | 27 ++
security/sara/main.c | 6 +
security/sara/wxprot.c | 642 +++++++++++++++++++++++++++++++++++++++++
5 files changed, 751 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 01ff246..cb49f20 100644
--- a/security/sara/Kconfig
+++ b/security/sara/Kconfig
@@ -80,4 +80,79 @@ config SECURITY_SARA_USB_FILTERING_DISABLED

If unsure, answer N.

+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 0543390..531b38b 100644
--- a/security/sara/Makefile
+++ b/security/sara/Makefile
@@ -2,3 +2,4 @@ obj-$(CONFIG_SECURITY_SARA) := sara.o

sara-y := main.o securityfs.o utils.o sara_data.o
sara-$(CONFIG_SECURITY_SARA_USB_FILTERING) += usb_filtering.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 <[email protected]>
+ *
+ * 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 e870c68..7b17167 100644
--- a/security/sara/main.c
+++ b/security/sara/main.c
@@ -17,6 +17,7 @@
#include "include/sara_data.h"
#include "include/securityfs.h"
#include "include/usb_filtering.h"
+#include "include/wxprot.h"

static const int sara_version = SARA_VERSION;

@@ -92,6 +93,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..cbeedd3
--- /dev/null
+++ b/security/sara/wxprot.c
@@ -0,0 +1,642 @@
+/*
+ * S.A.R.A. Linux Security Module
+ *
+ * Copyright (C) 2017 Salvatore Mesoraca <[email protected]>
+ *
+ * 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/kref.h>
+#include <linux/lsm_hooks.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/cred.h>
+#include <linux/binfmts.h>
+#include <linux/elf.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;
+
+#define pr_wxp(MESSAGE) do { \
+ char *BUF, *PATH; \
+ PATH = get_current_path(&BUF); \
+ pr_notice_ratelimited("WXP: %s in '%s' (%d).\n", \
+ MESSAGE, PATH, current->pid); \
+ kvfree(BUF); \
+} while (0)
+
+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 1;
+ }
+ 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 1;
+ }
+ }
+
+ 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

2017-06-12 16:58:43

by Salvatore Mesoraca

[permalink] [raw]
Subject: [PATCH 09/11] 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 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 <[email protected]>
---
security/sara/Kconfig | 17 ++++
security/sara/include/trampolines.h | 171 ++++++++++++++++++++++++++++++++++++
security/sara/wxprot.c | 137 +++++++++++++++++++++++++++++
3 files changed, 325 insertions(+)
create mode 100644 security/sara/include/trampolines.h

diff --git a/security/sara/Kconfig b/security/sara/Kconfig
index cb49f20..e91c147 100644
--- a/security/sara/Kconfig
+++ b/security/sara/Kconfig
@@ -137,6 +137,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 <[email protected]>
+ *
+ * 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 <[email protected]>
+ *
+ */
+
+#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 cbeedd3..44e42be 100644
--- a/security/sara/wxprot.c
+++ b/security/sara/wxprot.c
@@ -20,6 +20,11 @@
#include <linux/elf.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"
@@ -35,6 +40,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 | \
@@ -45,7 +51,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;
@@ -70,7 +81,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

#define pr_wxp(MESSAGE) do { \
char *BUF, *PATH; \
@@ -93,6 +108,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;
}

@@ -362,10 +380,129 @@ 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();
+
+ if (IS_ENABLED(CONFIG_X86_32) ||
+ 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);
+
+ 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

2017-06-12 16:59:06

by Salvatore Mesoraca

[permalink] [raw]
Subject: [PATCH 11/11] S.A.R.A. WX Protection procattr interface

This allow processes to get current WX Protection flags for themselves or
for other processes of the same user.
It also allow a process 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 process 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.
Given that it's possible to segregate the part that runs untrusted
code from the rest through a fork, 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/saralib

Signed-off-by: Salvatore Mesoraca <[email protected]>
---
security/sara/wxprot.c | 123 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 123 insertions(+)

diff --git a/security/sara/wxprot.c b/security/sara/wxprot.c
index 44e42be..00cd22c 100644
--- a/security/sara/wxprot.c
+++ b/security/sara/wxprot.c
@@ -40,6 +40,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
@@ -496,6 +497,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 &&
+ current_euid().val))
+ 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),
@@ -503,6 +624,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

2017-06-12 16:59:33

by Salvatore Mesoraca

[permalink] [raw]
Subject: [PATCH 10/11] Allowing for stacking procattr support in S.A.R.A.

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 <[email protected]>
---
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 cf15686..6ca93c6 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1244,12 +1244,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

2017-06-12 17:00:00

by Salvatore Mesoraca

[permalink] [raw]
Subject: [PATCH 06/11] S.A.R.A. cred blob management

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 <[email protected]>
---
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 8acf8a9..0543390 100644
--- a/security/sara/Makefile
+++ b/security/sara/Makefile
@@ -1,4 +1,4 @@
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
sara-$(CONFIG_SECURITY_SARA_USB_FILTERING) += usb_filtering.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 <[email protected]>
+ *
+ * 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 8783c3c..e870c68 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"
#include "include/usb_filtering.h"

@@ -81,6 +82,11 @@ void __init sara_init(void)
goto error;
}

+ if (sara_data_init()) {
+ pr_crit("impossible to initialize creds.\n");
+ goto error;
+ }
+
if (sara_usb_filtering_init()) {
pr_crit("impossible to initialize usb filtering.\n");
goto error;
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 <[email protected]>
+ *
+ * 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

2017-06-12 17:32:38

by Thomas Gleixner

[permalink] [raw]
Subject: Re: [PATCH 08/11] Creation of "pagefault_handler_x86" LSM hook

On Mon, 12 Jun 2017, Salvatore Mesoraca 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.

That explains, what you could do with it, but it completely lacks any
rationale WHY this is desired and good behaviour and how that is a security
feature.

Thanks,

tglx

2017-06-12 17:35:24

by Krzysztof Opasiak

[permalink] [raw]
Subject: Re: [PATCH 03/11] Creation of "usb_device_auth" LSM hook

Hi,

On 06/12/2017 06:56 PM, Salvatore Mesoraca wrote:
> Creation of a new LSM hook that can be used to authorize or deauthorize
> new USB devices via the usb authorization interface.
> The same hook can also prevent the authorization of a USB device via
> "/sys/bus/usb/devices/DEVICE/authorized".
> Using this hook an LSM could provide an higher level of granularity
> than the current authorization interface.
>

Could you please explain me why we need LSM for this?

There are tools like usbguard[1] and as far as I can tell it looks like
they can do everything for you...
Without kernel modification...
without matching and storing rules inside kernel..
just pure userspace which uses device/interface authorization

Footnote:
1 - https://dkopecek.github.io/usbguard/

Best regards,
--
Krzysztof Opasiak
Samsung R&D Institute Poland
Samsung Electronics

2017-06-12 17:49:36

by Jann Horn

[permalink] [raw]
Subject: Re: [kernel-hardening] [PATCH 01/11] S.A.R.A. Documentation

On Mon, Jun 12, 2017 at 6:56 PM, Salvatore Mesoraca
<[email protected]> wrote:
> Adding documentation for S.A.R.A. LSM.
>
> Signed-off-by: Salvatore Mesoraca <[email protected]>
[...]
> +/proc/PID/attr/sara/wxprot interface
> +------------------------------------
> +The `procattr` interface can be used by a program 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.
> +Given that it's possible to segregate the part that runs untrusted
> +code from the rest through a fork, this feature can be use to run the JIT
> +compiler with few restrictions while enforcing full WX Protection in the
> +rest of the program.

As far as I can tell, the wxprot interface in procfs, when used as
/proc/PID/attr/sara/wxprot, actually only sets restrictions on one of the
threads.
The documentation doesn't seem to mention this.


> +.. [3] `saralib <https://github.com/smeso/saralib>`_

This link is broken.

2017-06-12 19:38:38

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH 03/11] Creation of "usb_device_auth" LSM hook

On Mon, Jun 12, 2017 at 06:56:52PM +0200, Salvatore Mesoraca wrote:
> Creation of a new LSM hook that can be used to authorize or deauthorize
> new USB devices via the usb authorization interface.
> The same hook can also prevent the authorization of a USB device via
> "/sys/bus/usb/devices/DEVICE/authorized".
> Using this hook an LSM could provide an higher level of granularity
> than the current authorization interface.
>
> Signed-off-by: Salvatore Mesoraca <[email protected]>
> Cc: [email protected]
> Cc: Greg Kroah-Hartman <[email protected]>

No, like Krzysztof said, you can already do this today, just fine, from
userspace. I think that support has been there for over a decade now,
why are you not taking advantage of this already?

No need to add extra stuff to the kernel at all to do this, sorry you
implemented all of this for no reason :(

greg k-h

2017-06-12 21:31:43

by Casey Schaufler

[permalink] [raw]
Subject: Re: [PATCH 03/11] Creation of "usb_device_auth" LSM hook

On 6/12/2017 9:56 AM, Salvatore Mesoraca wrote:
> Creation of a new LSM hook that can be used to authorize or deauthorize
> new USB devices via the usb authorization interface.
> The same hook can also prevent the authorization of a USB device via
> "/sys/bus/usb/devices/DEVICE/authorized".
> Using this hook an LSM could provide an higher level of granularity
> than the current authorization interface.
>
> Signed-off-by: Salvatore Mesoraca <[email protected]>
> Cc: [email protected]
> Cc: Greg Kroah-Hartman <[email protected]>
> ---
> drivers/usb/core/hub.c | 4 ++++
> drivers/usb/core/sysfs.c | 6 +++++-
> include/linux/lsm_hooks.h | 6 ++++++
> include/linux/security.h | 7 +++++++
> security/security.c | 5 +++++
> 5 files changed, 27 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
> index b8bb20d..58be4f0 100644
> --- a/drivers/usb/core/hub.c
> +++ b/drivers/usb/core/hub.c
> @@ -28,6 +28,7 @@
> #include <linux/mutex.h>
> #include <linux/random.h>
> #include <linux/pm_qos.h>
> +#include <linux/security.h>
>
> #include <linux/uaccess.h>
> #include <asm/byteorder.h>
> @@ -4831,6 +4832,9 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
> if (udev->quirks & USB_QUIRK_DELAY_INIT)
> msleep(1000);
>
> + if (security_usb_device_auth(udev))
> + usb_deauthorize_device(udev);
> +
> /* consecutive bus-powered hubs aren't reliable; they can
> * violate the voltage drop budget. if the new child has
> * a "powered" LED, users should notice we didn't enable it
> diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
> index dfc68ed..fce9d39 100644
> --- a/drivers/usb/core/sysfs.c
> +++ b/drivers/usb/core/sysfs.c
> @@ -17,6 +17,7 @@
> #include <linux/usb.h>
> #include <linux/usb/quirks.h>
> #include <linux/of.h>
> +#include <linux/security.h>
> #include "usb.h"
>
> /* Active configuration fields */
> @@ -742,8 +743,11 @@ static ssize_t authorized_store(struct device *dev,
> result = -EINVAL;
> else if (val == 0)
> result = usb_deauthorize_device(usb_dev);
> - else
> + else {
> + if (security_usb_device_auth(usb_dev))
> + return -EPERM;

Return the error reported by the hook rather than -EPERM.

> result = usb_authorize_device(usb_dev);
> + }
> return result < 0 ? result : size;
> }
> static DEVICE_ATTR_IGNORE_LOCKDEP(authorized, S_IRUGO | S_IWUSR,
> diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
> index bd274db..cc0937e 100644
> --- a/include/linux/lsm_hooks.h
> +++ b/include/linux/lsm_hooks.h
> @@ -1189,6 +1189,10 @@
> * to the @parent process for tracing.
> * @parent contains the task_struct structure for debugger process.
> * Return 0 if permission is granted.
> + * @usb_device_auth:
> + * Check if @udev device should be authorized or not.
> + * @udev contains the usb_device structure for the USB device.
> + * Return 0 if the device is allowed.
> * @capget:
> * Get the @effective, @inheritable, and @permitted capability sets for
> * the @target process. The hook may also perform permission checking to
> @@ -1352,6 +1356,7 @@
> int (*ptrace_access_check)(struct task_struct *child,
> unsigned int mode);
> int (*ptrace_traceme)(struct task_struct *parent);
> + int (*usb_device_auth)(const struct usb_device *udev);
> int (*capget)(struct task_struct *target, kernel_cap_t *effective,
> kernel_cap_t *inheritable, kernel_cap_t *permitted);
> int (*capset)(struct cred *new, const struct cred *old,
> @@ -1670,6 +1675,7 @@ struct security_hook_heads {
> struct list_head binder_transfer_file;
> struct list_head ptrace_access_check;
> struct list_head ptrace_traceme;
> + struct list_head usb_device_auth;
> struct list_head capget;
> struct list_head capset;
> struct list_head capable;
> diff --git a/include/linux/security.h b/include/linux/security.h
> index af675b5..19bc364 100644
> --- a/include/linux/security.h
> +++ b/include/linux/security.h
> @@ -30,6 +30,7 @@
> #include <linux/string.h>
> #include <linux/mm.h>
> #include <linux/fs.h>
> +#include <linux/usb.h>
>
> struct linux_binprm;
> struct cred;
> @@ -196,6 +197,7 @@ int security_binder_transfer_file(struct task_struct *from,
> struct task_struct *to, struct file *file);
> int security_ptrace_access_check(struct task_struct *child, unsigned int mode);
> int security_ptrace_traceme(struct task_struct *parent);
> +int security_usb_device_auth(const struct usb_device *udev);
> int security_capget(struct task_struct *target,
> kernel_cap_t *effective,
> kernel_cap_t *inheritable,
> @@ -434,6 +436,11 @@ static inline int security_ptrace_traceme(struct task_struct *parent)
> return cap_ptrace_traceme(parent);
> }
>
> +static inline int security_usb_device_auth(const struct usb_device *udev)
> +{
> + return 0;
> +}
> +
> static inline int security_capget(struct task_struct *target,
> kernel_cap_t *effective,
> kernel_cap_t *inheritable,
> diff --git a/security/security.c b/security/security.c
> index 42c8028..e390f99 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -214,6 +214,11 @@ int security_ptrace_traceme(struct task_struct *parent)
> return call_int_hook(ptrace_traceme, 0, parent);
> }
>
> +int security_usb_device_auth(const struct usb_device *udev)
> +{
> + return call_int_hook(usb_device_auth, 0, udev);
> +}
> +
> int security_capget(struct task_struct *target,
> kernel_cap_t *effective,
> kernel_cap_t *inheritable,

2017-06-12 21:31:53

by Casey Schaufler

[permalink] [raw]
Subject: Re: [PATCH 05/11] Creation of "check_vmflags" LSM hook

On 6/12/2017 9:56 AM, Salvatore Mesoraca 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".
>
> Signed-off-by: Salvatore Mesoraca <[email protected]>
> Cc: [email protected]
> ---
> include/linux/lsm_hooks.h | 6 ++++++
> include/linux/security.h | 6 ++++++
> mm/mmap.c | 9 +++++++++
> security/security.c | 5 +++++
> 4 files changed, 26 insertions(+)
>
> diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
> index cc0937e..6934cc5 100644
> --- a/include/linux/lsm_hooks.h
> +++ b/include/linux/lsm_hooks.h
> @@ -483,6 +483,10 @@
> * @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.
> * @file_lock:
> * Check permission before performing file locking operations.
> * Note: this hook mediates both flock and fcntl style locks.
> @@ -1482,6 +1486,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);
> @@ -1753,6 +1758,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 19bc364..67e33b6 100644
> --- a/include/linux/security.h
> +++ b/include/linux/security.h
> @@ -302,6 +302,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);
> @@ -830,6 +831,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..e19f04e 100644
> --- a/mm/mmap.c
> +++ b/mm/mmap.c
> @@ -1363,6 +1363,9 @@ 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;
>
> + if (security_check_vmflags(vm_flags))
> + return -EPERM;
> +

Have the hook return a value and return that rather
than -EPERM. That way a security module can choose an
error that it determines is appropriate. It is possible
that a module might want to deny the access for a reason
other than lack of privilege.

> if (flags & MAP_LOCKED)
> if (!can_do_mlock())
> return -EPERM;
> @@ -2833,6 +2836,9 @@ 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;
>
> + if (security_check_vmflags(flags))
> + return -EPERM;
> +

Same here

> error = get_unmapped_area(NULL, addr, len, 0, MAP_FIXED);
> if (offset_in_page(error))
> return error;
> @@ -3208,6 +3214,9 @@ static struct vm_area_struct *__install_special_mapping(
> int ret;
> struct vm_area_struct *vma;
>
> + if (security_check_vmflags(vm_flags))
> + return ERR_PTR(-EPERM);
> +

And here.

> 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 e390f99..25d58f0 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -905,6 +905,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);

2017-06-13 00:03:08

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH 09/11] Trampoline emulation

Hi Salvatore,

[auto build test ERROR on security/next]
[also build test ERROR on v4.12-rc5]
[cannot apply to next-20170609]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url: https://github.com/0day-ci/linux/commits/Salvatore-Mesoraca/S-A-R-A-Documentation/20170613-050631
base: https://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security.git next
config: i386-allmodconfig (attached as .config)
compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
reproduce:
# save the attached .config to linux build tree
make ARCH=i386

All error/warnings (new ones prefixed by >>):

security/sara/wxprot.c: In function 'sara_pagefault_handler_x86':
>> security/sara/wxprot.c:407:18: error: '__USER32_CS' undeclared (first use in this function)
regs->cs == __USER32_CS ||
^~~~~~~~~~~
security/sara/wxprot.c:407:18: note: each undeclared identifier is reported only once for each function it appears in
>> security/sara/wxprot.c:409:17: warning: right shift count >= width of type [-Wshift-count-overflow]
if (!(address >> 32)) /* K8 erratum #100 */
^~

vim +/__USER32_CS +407 security/sara/wxprot.c

401
402 local_irq_enable();
403 might_sleep();
404 might_fault();
405
406 if (IS_ENABLED(CONFIG_X86_32) ||
> 407 regs->cs == __USER32_CS ||
408 (regs->cs & (1<<2))) {
> 409 if (!(address >> 32)) /* K8 erratum #100 */
410 ret = sara_pagefault_handler_x86_32(regs);
411 } else
412 ret = sara_pagefault_handler_x86_64(regs);

---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation


Attachments:
(No filename) (1.73 kB)
.config.gz (58.81 kB)
Download all attachments

2017-06-13 01:16:15

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH 03/11] Creation of "usb_device_auth" LSM hook

Hi Salvatore,

[auto build test WARNING on security/next]
[also build test WARNING on v4.12-rc5]
[cannot apply to next-20170609]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url: https://github.com/0day-ci/linux/commits/Salvatore-Mesoraca/S-A-R-A-Documentation/20170613-050631
base: https://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security.git next
config: cris-allyesconfig (attached as .config)
compiler: cris-linux-gcc (GCC) 6.2.0
reproduce:
wget https://raw.githubusercontent.com/01org/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
make.cross ARCH=cris

All warnings (new ones prefixed by >>):

>> security/sara/securityfs.c:24:0: warning: "STR" redefined
#define STR(x) STR_HELPER(x)

In file included from arch/cris/include/asm/irq.h:4:0,
from include/linux/irq.h:26,
from include/asm-generic/hardirq.h:12,
from ./arch/cris/include/generated/asm/hardirq.h:1,
from include/linux/hardirq.h:8,
from include/linux/interrupt.h:12,
from include/linux/usb.h:15,
from include/linux/security.h:33,
from security/sara/securityfs.c:17:
arch/cris/include/arch-v10/arch/irq.h:81:0: note: this is the location of the previous definition
#define STR(x) __STR(x)


vim +/STR +24 security/sara/securityfs.c

4c272b9a Salvatore Mesoraca 2017-06-12 8 * published by the Free Software Foundation.
4c272b9a Salvatore Mesoraca 2017-06-12 9 *
4c272b9a Salvatore Mesoraca 2017-06-12 10 */
4c272b9a Salvatore Mesoraca 2017-06-12 11
4c272b9a Salvatore Mesoraca 2017-06-12 12 #include <linux/ctype.h>
4c272b9a Salvatore Mesoraca 2017-06-12 13 #include <linux/seq_file.h>
4c272b9a Salvatore Mesoraca 2017-06-12 14 #include <linux/uaccess.h>
4c272b9a Salvatore Mesoraca 2017-06-12 15 #include <linux/mm.h>
4c272b9a Salvatore Mesoraca 2017-06-12 16 #include <linux/spinlock.h>
4c272b9a Salvatore Mesoraca 2017-06-12 17 #include <linux/security.h>
4c272b9a Salvatore Mesoraca 2017-06-12 18
4c272b9a Salvatore Mesoraca 2017-06-12 19 #include "include/sara.h"
4c272b9a Salvatore Mesoraca 2017-06-12 20 #include "include/utils.h"
4c272b9a Salvatore Mesoraca 2017-06-12 21 #include "include/securityfs.h"
4c272b9a Salvatore Mesoraca 2017-06-12 22
4c272b9a Salvatore Mesoraca 2017-06-12 23 #define STR_HELPER(x) #x
4c272b9a Salvatore Mesoraca 2017-06-12 @24 #define STR(x) STR_HELPER(x)
4c272b9a Salvatore Mesoraca 2017-06-12 25
4c272b9a Salvatore Mesoraca 2017-06-12 26 static struct dentry *fs_root;
4c272b9a Salvatore Mesoraca 2017-06-12 27
4c272b9a Salvatore Mesoraca 2017-06-12 28 static inline bool check_config_write_access(void)
4c272b9a Salvatore Mesoraca 2017-06-12 29 {
4c272b9a Salvatore Mesoraca 2017-06-12 30 if (unlikely(sara_config_locked && !capable(CAP_MAC_ADMIN))) {
4c272b9a Salvatore Mesoraca 2017-06-12 31 pr_warn("config write access blocked.\n");
4c272b9a Salvatore Mesoraca 2017-06-12 32 return false;

:::::: The code at line 24 was first introduced by commit
:::::: 4c272b9af7b3b969359f7c0dc41fd2a8a03be9fc S.A.R.A. framework creation

:::::: TO: Salvatore Mesoraca <[email protected]>
:::::: CC: 0day robot <[email protected]>

---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation


Attachments:
(No filename) (3.54 kB)
.config.gz (41.62 kB)
Download all attachments

2017-06-13 03:11:26

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH 03/11] Creation of "usb_device_auth" LSM hook

Hi Salvatore,

[auto build test ERROR on security/next]
[also build test ERROR on v4.12-rc5]
[cannot apply to next-20170609]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url: https://github.com/0day-ci/linux/commits/Salvatore-Mesoraca/S-A-R-A-Documentation/20170613-050631
base: https://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security.git next
config: ia64-allmodconfig (attached as .config)
compiler: ia64-linux-gcc (GCC) 6.2.0
reproduce:
wget https://raw.githubusercontent.com/01org/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
make.cross ARCH=ia64

All errors (new ones prefixed by >>):

>> ERROR: "security_usb_device_auth" [drivers/usb/core/usbcore.ko] undefined!
ERROR: "ia64_delay_loop" [drivers/spi/spi-thunderx.ko] undefined!
ERROR: "ia64_delay_loop" [drivers/net/phy/mdio-cavium.ko] undefined!

---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation


Attachments:
(No filename) (1.13 kB)
.config.gz (46.65 kB)
Download all attachments

2017-06-13 06:34:51

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH 05/11] Creation of "check_vmflags" LSM hook

On Mon, Jun 12, 2017 at 06:56:54PM +0200, Salvatore Mesoraca 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".

Please always post the whole series including the users, thanks.

2017-06-13 07:41:32

by Salvatore Mesoraca

[permalink] [raw]
Subject: Re: [PATCH 08/11] Creation of "pagefault_handler_x86" LSM hook

2017-06-12 19:32 GMT+02:00 Thomas Gleixner <[email protected]>:
> That explains, what you could do with it, but it completely lacks any
> rationale WHY this is desired and good behaviour and how that is a security
> feature.

You are right, I could have been more descriptive.
This is not a security feature "per se", it's a way to soften some
unwanted side-effects of restrictive security features.
In particular I'm trying to introduce a feature that will prevent
the runtime creation of executable code in user-space programs:
it's something like the PaX's MPROTECT feature.
This hook is 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 "memory restriction" features to stay on even
when they should be turned off. And, even if this emulation
makes the feature less effective, it's still better than having
it 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.

Thank your for taking the time to review this.

2017-06-13 07:43:24

by Salvatore Mesoraca

[permalink] [raw]
Subject: Re: [kernel-hardening] [PATCH 01/11] S.A.R.A. Documentation

2017-06-12 19:49 GMT+02:00 Jann Horn <[email protected]>:
> On Mon, Jun 12, 2017 at 6:56 PM, Salvatore Mesoraca
> As far as I can tell, the wxprot interface in procfs, when used as
> /proc/PID/attr/sara/wxprot, actually only sets restrictions on one of the
> threads.
> The documentation doesn't seem to mention this.

Yes, you are right. I'll change it.

>> +.. [3] `saralib <https://github.com/smeso/saralib>`_
>
> This link is broken.

Oops, the correct link is https://github.com/smeso/libsara

Thank you very much for your time.

2017-06-13 07:47:36

by Salvatore Mesoraca

[permalink] [raw]
Subject: Re: [PATCH 03/11] Creation of "usb_device_auth" LSM hook

2017-06-12 19:35 GMT+02:00 Krzysztof Opasiak <[email protected]>:
> Could you please explain me why we need LSM for this?
>
> There are tools like usbguard[1] and as far as I can tell it looks like they
> can do everything for you...

I have to admit that this is the first time I read about usbguard and it made
me realize that this feature can be implemented in userspace, so no need
to modify the kernel. :)
You are right.

Thank you very much for taking the time to answer me.

2017-06-13 07:50:10

by Salvatore Mesoraca

[permalink] [raw]
Subject: Re: [PATCH 03/11] Creation of "usb_device_auth" LSM hook

2017-06-12 21:38 GMT+02:00 Greg Kroah-Hartman <[email protected]>:
> No, like Krzysztof said, you can already do this today, just fine, from
> userspace. I think that support has been there for over a decade now,
> why are you not taking advantage of this already?
> No need to add extra stuff to the kernel at all to do this

Honesty, now that you mention it, I agree with you.
This feature can be implemented in userspace and I'm the first one to
think that what can be implemented in userspace should stay in
userspace.

> sorry you
> implemented all of this for no reason :(

Don't worry, usb filtering is just a small part of this patchset.
Let's hope the rest will end up being more useful. :)

Thank you very much for taking the time to answer me.

2017-06-13 07:51:27

by Salvatore Mesoraca

[permalink] [raw]
Subject: Re: [PATCH 03/11] Creation of "usb_device_auth" LSM hook

2017-06-12 23:31 GMT+02:00 Casey Schaufler <[email protected]>:
> Return the error reported by the hook rather than -EPERM.

Agreed, anyway this part will be, probably, dropped in
the next version (read Greg and Krzysztof answers).
I'm sorry :(

Thank you very much for the time you spent on this.

2017-06-13 07:53:02

by Salvatore Mesoraca

[permalink] [raw]
Subject: Re: [PATCH 05/11] Creation of "check_vmflags" LSM hook

2017-06-13 8:34 GMT+02:00 Christoph Hellwig <[email protected]>:
> Please always post the whole series including the users, thanks.

I'm sorry for the inconvenience, it won't happen again.
Thank you for your comment.

2017-06-13 07:55:34

by Salvatore Mesoraca

[permalink] [raw]
Subject: Re: [PATCH 05/11] Creation of "check_vmflags" LSM hook

2017-06-12 23:31 GMT+02:00 Casey Schaufler <[email protected]>:
> Have the hook return a value and return that rather
> than -EPERM. That way a security module can choose an
> error that it determines is appropriate. It is possible
> that a module might want to deny the access for a reason
> other than lack of privilege.
> [...]
>
> Same here
>
> [...]
>
> And here.

Yes, I think you are right. I'll fix it in the next version.
Thank you very much for taking the time to review my patch.

2017-06-20 07:07:25

by Pavel Machek

[permalink] [raw]
Subject: Re: [PATCH 04/11] S.A.R.A. USB Filtering

On Mon 2017-06-12 18:56:53, Salvatore Mesoraca wrote:
> Introduction of S.A.R.A. USB Filtering.
> It uses the "usb_device_auth" LSM hook to provide a mechanism to decide
> which USB devices should be authorized to connect to the system and
> which shouldn't.
> The main goal is to narrow the attack surface for custom USB devices
> designed to exploit vulnerabilities found in some USB device drivers.
> Via configuration it's possible to allow or to deny authorization, based
> on one or more of: Vendor ID, Product ID, bus name and port number. There
> is also support for "trailing wildcards".

Hmm. Given that USB device provides vendor id/product id, this does
not really stop anyone, right?

AFAICT you can still get USB stick with vid/pid of logitech keyboard,
and kernel will recognize it as a usb stick.

So you should not really filter on vid/pid, but on device
types (sha sum of USB descriptor?).

> Depending on the configuration, it can work both as a white list or as a
> black list.

Blacklisting vid/pid is completely useless. Whitelisting vid/pid is
nearly so. Attacker able to plug USB devices sees devices already
attached, so he can guess right vid/pids quite easily.

Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html


Attachments:
(No filename) (1.31 kB)
signature.asc (181.00 B)
Digital signature
Download all attachments

2017-06-20 07:53:40

by Salvatore Mesoraca

[permalink] [raw]
Subject: Re: [PATCH 04/11] S.A.R.A. USB Filtering

2017-06-20 9:07 GMT+02:00 Pavel Machek <[email protected]>:
> Hmm. Given that USB device provides vendor id/product id, this does
> not really stop anyone, right?
>
> AFAICT you can still get USB stick with vid/pid of logitech keyboard,
> and kernel will recognize it as a usb stick.

There are a number of ways by which a device can be assigned to a driver.
vid/pid is the most common for the most esoteric devices: i.e. less used/most
dangerous.
However you are right, a final version of this feature should have included
"interface matching" too and even something much more specific like serial
matching.
Anyway, because of lack of interest in having this feature included in kernel
space (something similar can already be done in user-space) it has been
abandoned.
You are answering to an old version of S.A.R.A. that can now be considered
obsolete.
Thank you anyway for your comment.

Salvatore

2017-06-27 22:51:44

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH 01/11] S.A.R.A. Documentation

On Mon, Jun 12, 2017 at 9:56 AM, Salvatore Mesoraca
<[email protected]> wrote:
> Adding documentation for S.A.R.A. LSM.
>
> Signed-off-by: Salvatore Mesoraca <[email protected]>
> ---
> Documentation/admin-guide/kernel-parameters.txt | 40 +++++
> Documentation/security/00-INDEX | 2 +
> Documentation/security/SARA.rst | 192 ++++++++++++++++++++++++
> 3 files changed, 234 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..f3ee12d 100644
> --- a/Documentation/admin-guide/kernel-parameters.txt
> +++ b/Documentation/admin-guide/kernel-parameters.txt
> @@ -3702,6 +3702,46 @@
> 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_usb_filtering= [SARA]
> + Disable or enable S.A.R.A. USB Filtering at boot
> + time.
> + Format: { "0" | "1" }
> + See security/sara/Kconfig help text
> + 0 -- disable.
> + 1 -- enable.
> + Default value is 1.
> +
> + sara_usb_filtering_default= [SARA]
> + Set S.A.R.A. USB Filtering default action.
> + Format: { "a" | "d" }
> + See security/sara/Kconfig help text
> + a -- allow.
> + d -- deny.
> + 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.
> +

As an organizational note, I would suggest making these all regular
"module parameters", which would let them be automatically namespaced
under "sara". For example "sara.enabled", "sara.wxprot", etc. For
example, this is how LoadPin does it for "loadpin.enabled":

/* Should not be mutable after boot, so not listed in sysfs (perm == 0). */
module_param(enabled, int, 0);
MODULE_PARM_DESC(enabled, "Pin module/firmware loading (default: true)");

> +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 two main submodules:
> +
> +- USB Filtering
> +- WX Protection

The USB Filtering of SARA seems kind of out of place. Does it have
infrastructure in common with the WX protections? Should it be its own
stackable LSM?

-Kees

--
Kees Cook
Pixel Security

2017-06-27 22:54:57

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH 01/11] S.A.R.A. Documentation

On Tue, Jun 27, 2017 at 3:51 PM, Kees Cook <[email protected]> wrote:
> The USB Filtering of SARA seems kind of out of place. Does it have
> infrastructure in common with the WX protections? Should it be its own
> stackable LSM?

Oops, I was reading v1. I see USB has been dropped from v2 --
switching to reading the v2 now. :)

-Kees

--
Kees Cook
Pixel Security

2017-07-04 10:12:37

by Salvatore Mesoraca

[permalink] [raw]
Subject: Re: [PATCH 01/11] S.A.R.A. Documentation

2017-06-28 0:51 GMT+02:00 Kees Cook <[email protected]>:
> On Mon, Jun 12, 2017 at 9:56 AM, Salvatore Mesoraca
> <[email protected]> wrote:
>> Adding documentation for S.A.R.A. LSM.
>>
>> Signed-off-by: Salvatore Mesoraca <[email protected]>
>> ---
>> Documentation/admin-guide/kernel-parameters.txt | 40 +++++
>> Documentation/security/00-INDEX | 2 +
>> Documentation/security/SARA.rst | 192 ++++++++++++++++++++++++
>> 3 files changed, 234 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..f3ee12d 100644
>> --- a/Documentation/admin-guide/kernel-parameters.txt
>> +++ b/Documentation/admin-guide/kernel-parameters.txt
>> @@ -3702,6 +3702,46 @@
>> 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_usb_filtering= [SARA]
>> + Disable or enable S.A.R.A. USB Filtering at boot
>> + time.
>> + Format: { "0" | "1" }
>> + See security/sara/Kconfig help text
>> + 0 -- disable.
>> + 1 -- enable.
>> + Default value is 1.
>> +
>> + sara_usb_filtering_default= [SARA]
>> + Set S.A.R.A. USB Filtering default action.
>> + Format: { "a" | "d" }
>> + See security/sara/Kconfig help text
>> + a -- allow.
>> + d -- deny.
>> + 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.
>> +
>
> As an organizational note, I would suggest making these all regular
> "module parameters", which would let them be automatically namespaced
> under "sara". For example "sara.enabled", "sara.wxprot", etc. For
> example, this is how LoadPin does it for "loadpin.enabled":
>
> /* Should not be mutable after boot, so not listed in sysfs (perm == 0). */
> module_param(enabled, int, 0);
> MODULE_PARM_DESC(enabled, "Pin module/firmware loading (default: true)");

I apologize to be so late to answer you.
I completely missed this email.
I'll follow your suggestion in v3, thank you.

2017-07-09 19:36:00

by Mickaël Salaün

[permalink] [raw]
Subject: Re: [kernel-hardening] [PATCH 00/11] S.A.R.A. a new stacked LSM

Hi,

I think it make sense to merge the W^X features with the TPE/shebang LSM
[1].

Regards,
Micka?l

[1]
https://lkml.kernel.org/r/[email protected]

On 12/06/2017 18:56, Salvatore Mesoraca wrote:
> 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.
>
> S.A.R.A. submodules introduced in this patchset are: USB Filtering and
> 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.
>
> USB Filtering aims to provide a mechanism to decide which USB devices
> should be authorized to connect to the system and which shouldn't. The main
> goal is to narrow the attack surface for custom USB devices designed to
> exploit vulnerabilities found in some USB device drivers.
> Via configuration it's possible to allow or to deny authorization, based
> on one or more of: Vendor ID, Product ID, bus name and port number. There
> is also limited support for wildcards.
> Depending on the configuration, it can work both as a white list or as a
> black list.
> With the help of "saractl" it's also possible to completely disable new
> USB devices when the screen is "locked".
> The original idea is inspired by the Grsecurity "Deny USB" feature.
>
> 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.
>
> [1] https://github.com/smeso/saractl
> [2] https://github.com/smeso/sara-test
>
> Salvatore Mesoraca (11):
> S.A.R.A. Documentation
> S.A.R.A. framework creation
> Creation of "usb_device_auth" LSM hook
> S.A.R.A. USB Filtering
> 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 | 40 ++
> Documentation/security/00-INDEX | 2 +
> Documentation/security/SARA.rst | 192 +++++
> arch/x86/mm/fault.c | 6 +
> drivers/usb/core/hub.c | 4 +
> drivers/usb/core/sysfs.c | 6 +-
> fs/proc/base.c | 38 +
> include/linux/cred.h | 3 +
> include/linux/lsm_hooks.h | 26 +
> include/linux/security.h | 24 +
> mm/mmap.c | 9 +
> security/Kconfig | 1 +
> security/Makefile | 2 +
> security/sara/Kconfig | 175 +++++
> security/sara/Makefile | 5 +
> 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/usb_filtering.h | 27 +
> security/sara/include/utils.h | 69 ++
> security/sara/include/wxprot.h | 27 +
> security/sara/main.c | 113 +++
> security/sara/sara_data.c | 79 +++
> security/sara/securityfs.c | 558 +++++++++++++++
> security/sara/usb_filtering.c | 410 +++++++++++
> security/sara/utils.c | 151 ++++
> security/sara/wxprot.c | 902 ++++++++++++++++++++++++
> security/security.c | 42 +-
> 29 files changed, 3214 insertions(+), 3 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/usb_filtering.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/usb_filtering.c
> create mode 100644 security/sara/utils.c
> create mode 100644 security/sara/wxprot.c
>


Attachments:
signature.asc (488.00 B)
OpenPGP digital signature

2017-07-10 07:59:22

by Salvatore Mesoraca

[permalink] [raw]
Subject: Re: [kernel-hardening] [PATCH 00/11] S.A.R.A. a new stacked LSM

2017-07-09 21:35 GMT+02:00 Mickaël Salaün <[email protected]>:
> Hi,
>
> I think it make sense to merge the W^X features with the TPE/shebang LSM
> [1].
>
> Regards,
> Mickaël
>
> [1]
> https://lkml.kernel.org/r/[email protected]

Hi,
Can you elaborate why it would be an advantage to have those features merged?
They seem quite unrelated.
Also, they work in rather different ways in respect to how they are configured.
I'm not sure what would be a reasonable way to merge them.
Thank you for your comment,

Salvatore

2017-07-10 23:41:38

by Mickaël Salaün

[permalink] [raw]
Subject: Re: [kernel-hardening] [PATCH 00/11] S.A.R.A. a new stacked LSM


On 10/07/2017 09:59, Salvatore Mesoraca wrote:
> 2017-07-09 21:35 GMT+02:00 Mickaël Salaün <[email protected]>:
>> Hi,
>>
>> I think it make sense to merge the W^X features with the TPE/shebang LSM
>> [1].
>>
>> Regards,
>> Mickaël
>>
>> [1]
>> https://lkml.kernel.org/r/[email protected]
>
> Hi,
> Can you elaborate why it would be an advantage to have those features merged?
> They seem quite unrelated.
> Also, they work in rather different ways in respect to how they are configured.
> I'm not sure what would be a reasonable way to merge them.
> Thank you for your comment,
>
> Salvatore
>

The aim of the Trusted Path Execution is to constraint calls to execve
(e.g. forbid an user to execute his own binaries, i.e. apply a W^X
security policy). This should handle binaries and could handle scripts
too [1]. However, there is always a way for a process to mmap/mprotect
arbitrary data and make it executable, be it intentional or not. PaX and
the W^X part of your LSM can handle this, or make exceptions by marking
a file with dedicated xattr values. This kind of exception fit well with
TPE to get a more hardened executable security policy (e.g. forbid an
user to execute his own binaries or to mmap arbitrary executable code).
Moreover, TPE could handle some part of its configuration from some
xattr values (e.g. allow scripts/interpreters, a whitelist of
environment variables, additional memory restrictions…) as you do with
SARA thanks to your tools.

Mickaël

[1]
https://lkml.kernel.org/r/[email protected]


Attachments:
signature.asc (488.00 B)
OpenPGP digital signature

2017-07-11 16:58:16

by Salvatore Mesoraca

[permalink] [raw]
Subject: Re: [kernel-hardening] [PATCH 00/11] S.A.R.A. a new stacked LSM

2017-07-11 1:40 GMT+02:00 Mickaël Salaün <[email protected]>:
>
> On 10/07/2017 09:59, Salvatore Mesoraca wrote:
>> 2017-07-09 21:35 GMT+02:00 Mickaël Salaün <[email protected]>:
>>> Hi,
>>>
>>> I think it make sense to merge the W^X features with the TPE/shebang LSM
>>> [1].
>>>
>>> Regards,
>>> Mickaël
>>>
>>> [1]
>>> https://lkml.kernel.org/r/[email protected]
>>
>> Hi,
>> Can you elaborate why it would be an advantage to have those features merged?
>> They seem quite unrelated.
>> Also, they work in rather different ways in respect to how they are configured.
>> I'm not sure what would be a reasonable way to merge them.
>> Thank you for your comment,
>>
>> Salvatore
>>
>
> The aim of the Trusted Path Execution is to constraint calls to execve
> (e.g. forbid an user to execute his own binaries, i.e. apply a W^X
> security policy). This should handle binaries and could handle scripts
> too [1]. However, there is always a way for a process to mmap/mprotect
> arbitrary data and make it executable, be it intentional or not. PaX and
> the W^X part of your LSM can handle this, or make exceptions by marking
> a file with dedicated xattr values. This kind of exception fit well with
> TPE to get a more hardened executable security policy (e.g. forbid an
> user to execute his own binaries or to mmap arbitrary executable code).
> Moreover, TPE could handle some part of its configuration from some
> xattr values (e.g. allow scripts/interpreters, a whitelist of
> environment variables, additional memory restrictions…) as you do with
> SARA thanks to your tools.

I understand your point. They complement each other in some sense.
On the other hand, I'm still worried about the suitability of merging,
under the same LSM, two features that are managed in two
completely different ways.
IMHO, if they have to be merged, they should be "integrated".
As I see it, there are only 3 possible solutions to this problem:
1 - SARA gives up its configuration mechanics and starts using xattrs
2 - TPE/shebang gives up xattrs and starts using SARA-style configurations
3 - SARA adds xattrs support to its quiver *and* TPE/shebang adds SARA-style
configuration support.

The solution number 1 is the one I'm less inclined to, as you can imagine.
I'm in favor of solutions 2 and 3, but of course we need to know Mimi Zohar and
Matt Brown opinion on this matter.
If we can find a consensus on the best way to merge them, I'm not against
the merge.
Anyway, these LSMs are stackable and they can be used together even if they
don't get merged. So I think that merging them is not a "must".

Salvatore

2017-07-11 17:48:46

by Matt Brown

[permalink] [raw]
Subject: Re: [kernel-hardening] [PATCH 00/11] S.A.R.A. a new stacked LSM

On 7/11/17 12:58 PM, Salvatore Mesoraca wrote:
> 2017-07-11 1:40 GMT+02:00 Mickaël Salaün <[email protected]>:
>>
>> On 10/07/2017 09:59, Salvatore Mesoraca wrote:
>>> 2017-07-09 21:35 GMT+02:00 Mickaël Salaün <[email protected]>:
>>>> Hi,
>>>>
>>>> I think it make sense to merge the W^X features with the TPE/shebang LSM
>>>> [1].
>>>>
>>>> Regards,
>>>> Mickaël
>>>>
>>>> [1]
>>>> https://lkml.kernel.org/r/[email protected]
>>>
>>> Hi,
>>> Can you elaborate why it would be an advantage to have those features merged?
>>> They seem quite unrelated.
>>> Also, they work in rather different ways in respect to how they are configured.
>>> I'm not sure what would be a reasonable way to merge them.
>>> Thank you for your comment,
>>>
>>> Salvatore
>>>
>>
>> The aim of the Trusted Path Execution is to constraint calls to execve
>> (e.g. forbid an user to execute his own binaries, i.e. apply a W^X
>> security policy). This should handle binaries and could handle scripts
>> too [1]. However, there is always a way for a process to mmap/mprotect
>> arbitrary data and make it executable, be it intentional or not. PaX and
>> the W^X part of your LSM can handle this, or make exceptions by marking
>> a file with dedicated xattr values. This kind of exception fit well with
>> TPE to get a more hardened executable security policy (e.g. forbid an
>> user to execute his own binaries or to mmap arbitrary executable code).
>> Moreover, TPE could handle some part of its configuration from some
>> xattr values (e.g. allow scripts/interpreters, a whitelist of
>> environment variables, additional memory restrictions…) as you do with
>> SARA thanks to your tools.
>
> I understand your point. They complement each other in some sense.
> On the other hand, I'm still worried about the suitability of merging,
> under the same LSM, two features that are managed in two
> completely different ways.
> IMHO, if they have to be merged, they should be "integrated".
> As I see it, there are only 3 possible solutions to this problem:
> 1 - SARA gives up its configuration mechanics and starts using xattrs
> 2 - TPE/shebang gives up xattrs and starts using SARA-style configurations
> 3 - SARA adds xattrs support to its quiver *and* TPE/shebang adds SARA-style
> configuration support.
>
> The solution number 1 is the one I'm less inclined to, as you can imagine.
> I'm in favor of solutions 2 and 3, but of course we need to know Mimi Zohar and
> Matt Brown opinion on this matter.
> If we can find a consensus on the best way to merge them, I'm not against
> the merge.
> Anyway, these LSMs are stackable and they can be used together even if they
> don't get merged. So I think that merging them is not a "must".
>
> Salvatore
>

I have merged my TPE LSM with Mimi Zohar's shebang LSM and will be
releasing a version 3 soon. I have also added securityfs support to
shebang that will allow users to update the interpreter list at run
time. This allows for user's to configure TPE/Shebang without any
xattrs. For a preview of my version 3 you can check out my dev tree
here:
https://github.com/nmatt0/linux-security/tree/tpe/security/tpe

Note: that git tree is WIP and may not have all of the attribution and
documentation needed.

Matt Brown

2017-07-11 19:31:30

by Mimi Zohar

[permalink] [raw]
Subject: Re: [kernel-hardening] [PATCH 00/11] S.A.R.A. a new stacked LSM

On Tue, 2017-07-11 at 13:49 -0400, Matt Brown wrote:

> I have merged my TPE LSM with Mimi Zohar's shebang LSM and will be
> releasing a version 3 soon. I have also added securityfs support to
> shebang that will allow users to update the interpreter list at run
> time. This allows for user's to configure TPE/Shebang without any
> xattrs. For a preview of my version 3 you can check out my dev tree
> here:
> https://github.com/nmatt0/linux-security/tree/tpe/security/tpe
>
> Note: that git tree is WIP and may not have all of the attribution and
> documentation needed.

You'll want to detect when an interpreter is deleted or renamed.  I
would define security_inode_rename, security_path_rename,
security_inode_unlink and security_path_unlink hooks.

"rename" could be an indication that the existing interpreter is being
updated. "unlink" indicates that the interpreter has been deleted.  At
either of these points, you'll want to start checking for the creation
of a new file with the expected pathname.

Mimi

2017-07-13 12:40:00

by Matt Brown

[permalink] [raw]
Subject: Re: [kernel-hardening] [PATCH 00/11] S.A.R.A. a new stacked LSM

On 7/11/17 3:31 PM, Mimi Zohar wrote:
> On Tue, 2017-07-11 at 13:49 -0400, Matt Brown wrote:
>
>> I have merged my TPE LSM with Mimi Zohar's shebang LSM and will be
>> releasing a version 3 soon. I have also added securityfs support to
>> shebang that will allow users to update the interpreter list at run
>> time. This allows for user's to configure TPE/Shebang without any
>> xattrs. For a preview of my version 3 you can check out my dev tree
>> here:
>> https://github.com/nmatt0/linux-security/tree/tpe/security/tpe
>>
>> Note: that git tree is WIP and may not have all of the attribution and
>> documentation needed.
>
> You'll want to detect when an interpreter is deleted or renamed. I
> would define security_inode_rename, security_path_rename,
> security_inode_unlink and security_path_unlink hooks.
>
> "rename" could be an indication that the existing interpreter is being
> updated. "unlink" indicates that the interpreter has been deleted. At
> either of these points, you'll want to start checking for the creation
> of a new file with the expected pathname.
>
> Mimi
>

Would it be better just to check for paths rather than inodes?

Matt

2017-07-13 15:19:58

by Mimi Zohar

[permalink] [raw]
Subject: Re: [kernel-hardening] [PATCH 00/11] S.A.R.A. a new stacked LSM

On Thu, 2017-07-13 at 08:39 -0400, Matt Brown wrote:
> On 7/11/17 3:31 PM, Mimi Zohar wrote:
> > On Tue, 2017-07-11 at 13:49 -0400, Matt Brown wrote:
> >
> >> I have merged my TPE LSM with Mimi Zohar's shebang LSM and will be
> >> releasing a version 3 soon. I have also added securityfs support to
> >> shebang that will allow users to update the interpreter list at run
> >> time. This allows for user's to configure TPE/Shebang without any
> >> xattrs. For a preview of my version 3 you can check out my dev tree
> >> here:
> >> https://github.com/nmatt0/linux-security/tree/tpe/security/tpe
> >>
> >> Note: that git tree is WIP and may not have all of the attribution and
> >> documentation needed.
> >
> > You'll want to detect when an interpreter is deleted or renamed. I
> > would define security_inode_rename, security_path_rename,
> > security_inode_unlink and security_path_unlink hooks.
> >
> > "rename" could be an indication that the existing interpreter is being
> > updated. "unlink" indicates that the interpreter has been deleted. At
> > either of these points, you'll want to start checking for the creation
> > of a new file with the expected pathname.
> >
> > Mimi
> >
>
> Would it be better just to check for paths rather than inodes?

Below is a simple example on my laptop of the same inode with
different path names.  In this case, a list of pathnames would be
double the number of inodes.  I'm not sure from a performance
perspective if this really matters, or if doing string compares
matters.  We're not talking about thousands or even hundreds of files,
though this is on the LSM bprm check hook.

The question is really from a security perspective which is better?
 Obviously, as v2 of the patch set changed from using pathnames to
inodes, it's pretty clear that I think inodes would be better.  Kees,
Serge, Casey any comments?

example:
$ rpm -qf /usr/bin/bash
bash-4.3.42-7.fc24.x86_64

$ rpm -q --dump bash | grep \/bin\/bash
/usr/bin/bash 1071984 1475233750
84c7d10de5f9c729f9d60e95bc97197a5e029a14e07820bc45ad309da91703f9
0100755 root root 0 0 0 X
/usr/bin/bashbug 10 1475233749
0000000000000000000000000000000000000000000000000000000000000000
0120777 root root 0 0 0 bashbug-64
/usr/bin/bashbug-64 7110 1475233745
0dd706d221569a7966f0246fcb40dd7f3bff4550c4ad0ee70ae8b0c6bc350157
0100755 root root 0 0 0 X

$ stat /bin/bash
  File: '/bin/bash'
  Size: 1071984    Blocks: 2104       IO Block: 4096   regular
file
Device: 804h/2052d Inode: 1180420     Links: 1
Access: (0755/-rwxr-xr-x)  Uid: (    0/    root)   Gid:
(    0/    root)
Context: system_u:object_r:shell_exec_t:s0
Access: 2017-07-12 21:56:28.125594261 -0400
Modify: 2016-09-30 07:09:10.000000000 -0400
Change: 2017-01-25 10:10:18.784719749 -0500
 Birth: -

$ stat /usr/bin/bash
  File: '/usr/bin/bash'
  Size: 1071984    Blocks: 2104       IO Block: 4096   regular
file
Device: 804h/2052d Inode: 1180420     Links: 1
Access: (0755/-rwxr-xr-x)  Uid: (    0/    root)   Gid:
(    0/    root)
Context: system_u:object_r:shell_exec_t:s0
Access: 2017-07-12 21:56:28.125594261 -0400
Modify: 2016-09-30 07:09:10.000000000 -0400
Change: 2017-01-25 10:10:18.784719749 -0500
 Birth: -

$ ls -lat / | grep bin
lrwxrwxrwx.   1 root root     7 Feb  3  2016 bin -> usr/bin
lrwxrwxrwx.   1 root root     8 Feb  3  2016 sbin -> usr/sbin

Mimi

2017-07-13 19:51:09

by Serge E. Hallyn

[permalink] [raw]
Subject: Re: [kernel-hardening] [PATCH 00/11] S.A.R.A. a new stacked LSM

Quoting Mimi Zohar ([email protected]):
> On Thu, 2017-07-13 at 08:39 -0400, Matt Brown wrote:
> The question is really from a security perspective which is better?
> ?Obviously, as v2 of the patch set changed from using pathnames to
> inodes, it's pretty clear that I think inodes would be better. ?Kees,
> Serge, Casey any comments?

Yes, inode seems clearly better. Paths are too easily worked around.

2017-07-13 22:33:56

by Matt Brown

[permalink] [raw]
Subject: Re: [kernel-hardening] [PATCH 00/11] S.A.R.A. a new stacked LSM

On 7/13/17 3:51 PM, Serge E. Hallyn wrote:
> Quoting Mimi Zohar ([email protected]):
>> On Thu, 2017-07-13 at 08:39 -0400, Matt Brown wrote:
>> The question is really from a security perspective which is better?
>> Obviously, as v2 of the patch set changed from using pathnames to
>> inodes, it's pretty clear that I think inodes would be better. Kees,
>> Serge, Casey any comments?
>
> Yes, inode seems clearly better. Paths are too easily worked around.
>

Sounds good. Do we think a rb_tree would be better than a list to store
the inodes in?

Matt

2017-07-24 00:59:19

by Casey Schaufler

[permalink] [raw]
Subject: Re: [kernel-hardening] [PATCH 00/11] S.A.R.A. a new stacked LSM

On 7/13/2017 12:51 PM, Serge E. Hallyn wrote:
> Quoting Mimi Zohar ([email protected]):
>> On Thu, 2017-07-13 at 08:39 -0400, Matt Brown wrote:
>> The question is really from a security perspective which is better?
>> Obviously, as v2 of the patch set changed from using pathnames to
>> inodes, it's pretty clear that I think inodes would be better. Kees,
>> Serge, Casey any comments?
> Yes, inode seems clearly better. Paths are too easily worked around.

An inode identifies the object, while a pathname identifies the intent.
Using the inode will be easier to code and easier to model. Using the
pathname will be much more likely to reflect what the human means to
accomplish, provided all the idiosyncrasies of the Linux filesystem
namespace are taken into account. Ever since the link count on an inode
was allowed to exceed 1* this has been difficult to accomplish.

----
* The link count has always been allowed to exceed 1. Then there
are symlinks, mount points, overlay filesystems and all manner
of other slick features that make the filesystem namespace
difficult to deal with from the security standpoint.

> --
> To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>