From patchwork Sat Jul 6 10:54:42 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Salvatore Mesoraca X-Patchwork-Id: 11033581 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 198781510 for ; Sat, 6 Jul 2019 10:55:15 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 094CC2881B for ; Sat, 6 Jul 2019 10:55:15 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id F17C528994; Sat, 6 Jul 2019 10:55:14 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,GAPPY_SUBJECT,RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 990ED2886A for ; Sat, 6 Jul 2019 10:55:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726248AbfGFKzN (ORCPT ); Sat, 6 Jul 2019 06:55:13 -0400 Received: from mail-wm1-f66.google.com ([209.85.128.66]:50400 "EHLO mail-wm1-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725990AbfGFKzM (ORCPT ); Sat, 6 Jul 2019 06:55:12 -0400 Received: by mail-wm1-f66.google.com with SMTP id v15so1040187wml.0; Sat, 06 Jul 2019 03:55:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=LSDyibP1XVZpeGT3DZYqEdlxulsdBlWaxtrr4VNl6oY=; b=c02v5eGeCj5HbIEAdudQQjgbzUrq82XXgqCQ02NBUHo1JB4D/SfUo+Z7GnKdKCG/Av 1TtgEsaynVkfr+x9paW1rL3z7p2ESzYOOaqNhFI3/QxVW7oCHm6H+PtJF20Bz7JZfP96 tvXanRd/k0QIHCtd/nmbdcG4n/dA95D7A8BhmxrBxtmLrkg/W+EbSrV62ApXeI4qX1X+ tFRNas3NgtrAATMdKmVNQC/UT5t4rXrFeF7rSbH390TA/WC2eUBLRgq8Z8p/F/YVkew9 LJ0Nt0lHbZakmefJNnCwKjtqkdNRKOHI5AA2BtLw7ifnr7IRYZ2GZWFiBYXeytfe6PS+ o3dw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=LSDyibP1XVZpeGT3DZYqEdlxulsdBlWaxtrr4VNl6oY=; b=hvNuFwERsR5H+bX+S3G8SfyWtN6//Zw7Tb6o7bEONLnLCpAgy0r2i2I3XHTZNBppzA J8aBREbGvX3ru4kmUnmjXbRDkPb/nOvTTE8izyNdA+ng12t/FpJqAS948vNPKxgnvOJz o6NhbJlEn6IoHsoNI1h3+s3YXoCGbo51fqdc/viVNi301TWMlj0cZbY+awXTaYYGrW6w SOj3jYnrgaozHUJOqlBrN+6jQCUkCCPx7gyRqWH/5drSvtygF0eSAtCXfRM1UWUC37CI HVssfISMNNqbIQQ8D5a/WDJBUKON/kdPfC4tmrLbHOhk/gkDbP4UZmawCokrgtQ1Gcps 0thw== X-Gm-Message-State: APjAAAU2YPjQEMW7s7Kyucv2lyVf5FykAy2F6NZ5UzmMZffZulDFu1ih Uv19ZiCJMAWVcrkC1blpaDXSOiUE3TaNBw== X-Google-Smtp-Source: APXvYqyxuovPbriEIhLSokjppZ1C87fwAjiJaQfHFYTH//jwFs40w+Dju5jKKRQe/vcnd9FsB8CHMA== X-Received: by 2002:a7b:c954:: with SMTP id i20mr6912798wml.169.1562410509333; Sat, 06 Jul 2019 03:55:09 -0700 (PDT) Received: from localhost (net-93-71-3-102.cust.vodafonedsl.it. [93.71.3.102]) by smtp.gmail.com with ESMTPSA id h11sm12578794wrx.93.2019.07.06.03.55.08 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sat, 06 Jul 2019 03:55:08 -0700 (PDT) From: Salvatore Mesoraca To: linux-kernel@vger.kernel.org Cc: kernel-hardening@lists.openwall.com, linux-mm@kvack.org, linux-security-module@vger.kernel.org, Alexander Viro , Brad Spengler , Casey Schaufler , Christoph Hellwig , James Morris , Jann Horn , Kees Cook , PaX Team , Salvatore Mesoraca , "Serge E. Hallyn" , Thomas Gleixner Subject: [PATCH v5 01/12] S.A.R.A.: add documentation Date: Sat, 6 Jul 2019 12:54:42 +0200 Message-Id: <1562410493-8661-2-git-send-email-s.mesoraca16@gmail.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1562410493-8661-1-git-send-email-s.mesoraca16@gmail.com> References: <1562410493-8661-1-git-send-email-s.mesoraca16@gmail.com> Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: X-Virus-Scanned: ClamAV using ClamSMTP Adding documentation for S.A.R.A. LSM. Signed-off-by: Salvatore Mesoraca --- Documentation/admin-guide/LSM/SARA.rst | 177 ++++++++++++++++++++++++ Documentation/admin-guide/LSM/index.rst | 1 + Documentation/admin-guide/kernel-parameters.txt | 24 ++++ 3 files changed, 202 insertions(+) create mode 100644 Documentation/admin-guide/LSM/SARA.rst diff --git a/Documentation/admin-guide/LSM/SARA.rst b/Documentation/admin-guide/LSM/SARA.rst new file mode 100644 index 0000000..fdde04c --- /dev/null +++ b/Documentation/admin-guide/LSM/SARA.rst @@ -0,0 +1,177 @@ +.. SPDX-License-Identifier: GPL-2.0 + +======== +S.A.R.A. +======== + +S.A.R.A. (S.A.R.A. is Another Recursive Acronym) is a stacked Linux Security +Module that aims to collect heterogeneous security measures, providing a common +interface to manage them. +As of today it consists of one submodule: + +- WX Protection + + +The kernel-space part is complemented by its user-space counterpart: `saractl` +[2]_. +A test suite for WX Protection, called `sara-test` [4]_, is also available. +More information about where to find these tools and the full S.A.R.A. +documentation are in the `External Links and Documentation`_ section. + +------------------------------------------------------------------------------- + +S.A.R.A.'s Submodules +===================== + +WX Protection +------------- +WX Protection aims to improve user-space programs security by applying: + +- `W^X enforcement`_ +- `W!->X (once writable never executable) mprotect restriction`_ +- `Executable MMAP prevention`_ + +All of the above features can be enabled or disabled both system wide +or on a per executable basis through the use of configuration files managed by +`saractl` [2]_. + +It is important to note that some programs may have issues working with +WX Protection. In particular: + +- **W^X enforcement** will cause problems to any programs that needs + memory pages mapped both as writable and executable at the same time e.g. + programs with executable stack markings in the *PT_GNU_STACK* segment. +- **W!->X mprotect restriction** will cause problems to any program that + needs to generate executable code at run time or to modify executable + pages e.g. programs with a *JIT* compiler built-in or linked against a + *non-PIC* library. +- **Executable MMAP prevention** can work only with programs that have at least + partial *RELRO* support. It's disabled automatically for programs that + lack this feature. It will cause problems to any program that uses *dlopen* + or tries to do an executable mmap. Unfortunately this feature is the one + that could create most problems and should be enabled only after careful + evaluation. + +To extend the scope of the above features, despite the issues that they may +cause, they are complemented by **/proc/PID/attr/sara/wxprot** interface +and **trampoline emulation**. + +At the moment, WX Protection (unless specified otherwise) should work on +any architecture supporting the NX bit, including, but not limited to: +`x86_64`, `x86_32` (with PAE), `ARM` and `ARM64`. + +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. +This feature also blocks accesses to /proc/*/mem files that would allow to +write the current process read-only memory, bypassing any protection. + +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 from the current thread. +Obviously, this feature does not prevent cases in which an attacker uses an +*execve* to start a completely new program. This kind of restriction, if +needed, can be applied using one of the other LSM that focuses on MAC. +Please be aware that this feature can break many programs and so it should be +enabled after careful evaluation. + +/proc/PID/attr/sara/wxprot interface +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The `procattr` interface can be used by a thread to discover which +WX Protection features are enabled and/or to tighten them: protection +can't be softened via procattr. +The interface is simple: it's a text file with an hexadecimal +number in it representing enabled features (more information can be +found in the `Flags values`_ section). Via this interface it is also +possible to perform a complete memory scan to remove the write permission +from pages that are both writable and executable, please note that this +change will also affect other threads of the same process. + +Protections that prevent the runtime creation of executable code +can be troublesome for all those programs that actually need to do it +e.g. programs shipping with a JIT compiler built-in. +This feature can be use to run the JIT compiler with few restrictions +while enforcing full WX Protection in the rest of the program. + +The preferred way to access this interface is via `libsara` [3]_. +If you don't want it as a dependency, you can just statically link it +in your project or copy/paste parts of it. +To make things simpler `libsara` is the only part of S.A.R.A. released under +*CC0 - No Rights Reserved* license. + +Trampoline emulation +^^^^^^^^^^^^^^^^^^^^ +Some programs need to generate part of their code at runtime. Luckily enough, +in some cases they only generate well-known code sequences (the +*trampolines*) that can be easily recognized and emulated by the kernel. +This way WX Protection can still be active, so a potential attacker won't be +able to generate arbitrary sequences of code, but just those that are +explicitly allowed. This is not ideal, but it's still better than having WX +Protection completely disabled. + +In particular S.A.R.A. is able to recognize trampolines used by GCC for nested +C functions and libffi's trampolines. +This feature is available only on `x86_32` and `x86_64`. + +Flags values +^^^^^^^^^^^^ +Flags are represented as a 16 bit unsigned integer in which every bit indicates +the status of a given feature: + ++------------------------------+----------+ +| Feature | Value | ++==============================+==========+ +| W!->X Heap | 0x0001 | ++------------------------------+----------+ +| W!->X Stack | 0x0002 | ++------------------------------+----------+ +| W!->X Other memory | 0x0004 | ++------------------------------+----------+ +| W^X | 0x0008 | ++------------------------------+----------+ +| Don't enforce, just complain | 0x0010 | ++------------------------------+----------+ +| Be Verbose | 0x0020 | ++------------------------------+----------+ +| Executable MMAP prevention | 0x0040 | ++------------------------------+----------+ +| Force W^X on setprocattr | 0x0080 | ++------------------------------+----------+ +| Trampoline emulation | 0x0100 | ++------------------------------+----------+ +| Children will inherit flags | 0x0200 | ++------------------------------+----------+ + +------------------------------------------------------------------------------- + +External Links and Documentation +================================ + +.. [1] `Documentation `_ +.. [2] `saractl `_ +.. [3] `libsara `_ +.. [4] `sara-test `_ diff --git a/Documentation/admin-guide/LSM/index.rst b/Documentation/admin-guide/LSM/index.rst index a6ba95f..81b50e4 100644 --- a/Documentation/admin-guide/LSM/index.rst +++ b/Documentation/admin-guide/LSM/index.rst @@ -47,3 +47,4 @@ subdirectories. tomoyo Yama SafeSetID + SARA diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 138f666..3d6e86d 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -4230,6 +4230,30 @@ 1 -- enable. Default value is set via kernel config option. + sara.enabled= [SARA] Disable or enable S.A.R.A. at boot time. + If disabled this way S.A.R.A. can't be enabled + again. + Format: { "0" | "1" } + See security/sara/Kconfig help text + 0 -- disable. + 1 -- enable. + Default value is set via kernel config option. + + sara.wxprot_enabled= [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 set via kernel config option. + + sara.wxprot_default_flags= [SARA] + Set S.A.R.A. WX Protection default flags. + Format: + See S.A.R.A. documentation. + Default value is set via kernel config option. + serialnumber [BUGS=X86-32] shapers= [NET] From patchwork Sat Jul 6 10:54:43 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Salvatore Mesoraca X-Patchwork-Id: 11033631 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 5B9F214F6 for ; Sat, 6 Jul 2019 10:56:03 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 4C3352881B for ; Sat, 6 Jul 2019 10:56:03 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 4072428A3F; Sat, 6 Jul 2019 10:56:03 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,GAPPY_SUBJECT,RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id AAC8C28A3A for ; Sat, 6 Jul 2019 10:56:01 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726683AbfGFKzS (ORCPT ); Sat, 6 Jul 2019 06:55:18 -0400 Received: from mail-wr1-f66.google.com ([209.85.221.66]:40434 "EHLO mail-wr1-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725990AbfGFKzS (ORCPT ); Sat, 6 Jul 2019 06:55:18 -0400 Received: by mail-wr1-f66.google.com with SMTP id r1so5913963wrl.7; Sat, 06 Jul 2019 03:55:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=BW+8xpyQbi7Tl8ElYIH8BHwSiQojAb3nUmPsO54K1pU=; b=Gy5TPt6/txGbLsMhEqym/m39JJLOPOcUp33pKsBXIoOfib1/thpFoahS2bYRfcTk3z YNCV71Qgi/dESSZ5Cid+OeLB1kL5AdVmRTO1vtsKAzliLqu7E7guphMsmUBivmlCBN9g xXzIOQiEkfK/fwXB37ZloNWywr4ijHxf18F1oIlU9mdS91Ib1dAwG4QSrlNu3fnRfpnt 3p7hdKEu8kHcCxudsUWLovvcISlvkL1gmsmZp3MnaoXR7hhnPpsk7hzk0La7U/SOhyJv xup3QV0T0CwQ7M6Z2iZnyYkyuKJsml23601/JLFeKW/SNDL3jpiboPC6IzfY0EGKVst3 ko2g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=BW+8xpyQbi7Tl8ElYIH8BHwSiQojAb3nUmPsO54K1pU=; b=WkXnv7QMGGipI+9GHpHmLaPWsTrw9bVQcCuvXeSbIt72Sm/BkXSWolDMbjMBhiBWKn /YKQ7Fu20K6YLbvKsJzRIvPUOKxW4drfn4g23TM96k82uOf5EA1mvruX6mgBJ+v5iMCV vZBJpyxTvPtUD95p3A1LhEU3FhteVLtDWbn+J3tGWCnN8y2/65VuAtJ+lZ5nbGzFa1sC xK0cPoW3c7SXG7v9YPVevDaoNYR63jznVBzmOeC0DQwRfS59pN9qNdyytOEZB1ADbY23 ZwXX1ULT0O4lYkajfKkhdri0Elsst/trxPryujY3G9eLj4nqi9WGihjbU2dvvqmPA0KM M/fA== X-Gm-Message-State: APjAAAUrBf49n/eSa62Vxw+wczTQG9DGTqcN4lbCWrcTvAAxaOuY/yMf wS6mgMoAqvdaO3drXkeiekXCiwuSBgzTVQ== X-Google-Smtp-Source: APXvYqy1ogO8dZ6NgaXXKK6DDQbYjudAU/lxFkXrHqpSoq4/f+IV71mZjPvf6fXPReqkMC7l8mLNCQ== X-Received: by 2002:a5d:468a:: with SMTP id u10mr8850925wrq.177.1562410514014; Sat, 06 Jul 2019 03:55:14 -0700 (PDT) Received: from localhost (net-93-71-3-102.cust.vodafonedsl.it. [93.71.3.102]) by smtp.gmail.com with ESMTPSA id h11sm12578794wrx.93.2019.07.06.03.55.12 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sat, 06 Jul 2019 03:55:13 -0700 (PDT) From: Salvatore Mesoraca To: linux-kernel@vger.kernel.org Cc: kernel-hardening@lists.openwall.com, linux-mm@kvack.org, linux-security-module@vger.kernel.org, Alexander Viro , Brad Spengler , Casey Schaufler , Christoph Hellwig , James Morris , Jann Horn , Kees Cook , PaX Team , Salvatore Mesoraca , "Serge E. Hallyn" , Thomas Gleixner Subject: [PATCH v5 02/12] S.A.R.A.: create framework Date: Sat, 6 Jul 2019 12:54:43 +0200 Message-Id: <1562410493-8661-3-git-send-email-s.mesoraca16@gmail.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1562410493-8661-1-git-send-email-s.mesoraca16@gmail.com> References: <1562410493-8661-1-git-send-email-s.mesoraca16@gmail.com> Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: X-Virus-Scanned: ClamAV using ClamSMTP 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 --- security/Kconfig | 11 +- security/Makefile | 2 + security/sara/Kconfig | 40 +++ security/sara/Makefile | 3 + security/sara/include/sara.h | 29 ++ security/sara/include/securityfs.h | 61 ++++ security/sara/include/utils.h | 80 ++++++ security/sara/main.c | 115 ++++++++ security/sara/securityfs.c | 565 +++++++++++++++++++++++++++++++++++++ security/sara/utils.c | 92 ++++++ 10 files changed, 993 insertions(+), 5 deletions(-) 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/security/Kconfig b/security/Kconfig index 466cc1f..4cae0ec 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -237,6 +237,7 @@ source "security/apparmor/Kconfig" source "security/loadpin/Kconfig" source "security/yama/Kconfig" source "security/safesetid/Kconfig" +source "security/sara/Kconfig" source "security/integrity/Kconfig" @@ -276,11 +277,11 @@ endchoice config LSM string "Ordered list of enabled LSMs" - default "yama,loadpin,safesetid,integrity,smack,selinux,tomoyo,apparmor" if DEFAULT_SECURITY_SMACK - default "yama,loadpin,safesetid,integrity,apparmor,selinux,smack,tomoyo" if DEFAULT_SECURITY_APPARMOR - default "yama,loadpin,safesetid,integrity,tomoyo" if DEFAULT_SECURITY_TOMOYO - default "yama,loadpin,safesetid,integrity" if DEFAULT_SECURITY_DAC - default "yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor" + default "yama,loadpin,safesetid,integrity,sara,smack,selinux,tomoyo,apparmor" if DEFAULT_SECURITY_SMACK + default "yama,loadpin,safesetid,integrity,sara,apparmor,selinux,smack,tomoyo" if DEFAULT_SECURITY_APPARMOR + default "yama,loadpin,safesetid,integrity,sara,tomoyo" if DEFAULT_SECURITY_TOMOYO + default "yama,loadpin,safesetid,integrity,sara" if DEFAULT_SECURITY_DAC + default "yama,loadpin,safesetid,integrity,sara,selinux,smack,tomoyo,apparmor" help A comma-separated list of LSMs, in initialization order. Any LSMs left off this list will be ignored. This can be diff --git a/security/Makefile b/security/Makefile index c598b90..4b0fd11 100644 --- a/security/Makefile +++ b/security/Makefile @@ -11,6 +11,7 @@ subdir-$(CONFIG_SECURITY_APPARMOR) += apparmor subdir-$(CONFIG_SECURITY_YAMA) += yama subdir-$(CONFIG_SECURITY_LOADPIN) += loadpin subdir-$(CONFIG_SECURITY_SAFESETID) += safesetid +subdir-$(CONFIG_SECURITY_SARA) += sara # always enable default capabilities obj-y += commoncap.o @@ -27,6 +28,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/ obj-$(CONFIG_SECURITY_YAMA) += yama/ obj-$(CONFIG_SECURITY_LOADPIN) += loadpin/ obj-$(CONFIG_SECURITY_SAFESETID) += safesetid/ +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..0456220 --- /dev/null +++ b/security/sara/Kconfig @@ -0,0 +1,40 @@ +menuconfig SECURITY_SARA + bool "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/admin-guide/LSM/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.enabled=[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.enabled=1" kernel parameter. + This option is functionally equivalent to "sara.enabled=0" kernel + parameter. This option is useful for distro kernels. + + If unsure, answer Y. + 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..cd12f52 --- /dev/null +++ b/security/sara/include/sara.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * S.A.R.A. Linux Security Module + * + * Copyright (C) 2017 Salvatore Mesoraca + * + * 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 +#include + +#define SARA_VERSION 0 +#define SARA_PATH_MAX PATH_MAX + +#undef pr_fmt +#define pr_fmt(fmt) "SARA: " fmt + +extern int sara_config_locked __read_mostly; +extern int sara_enabled __read_mostly; + +#endif /* __SARA_H */ diff --git a/security/sara/include/securityfs.h b/security/sara/include/securityfs.h new file mode 100644 index 0000000..92d6180 --- /dev/null +++ b/security/sara/include/securityfs.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * S.A.R.A. Linux Security Module + * + * Copyright (C) 2017 Salvatore Mesoraca + * + * 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 + +#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]; + int *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..ce9d5fb --- /dev/null +++ b/security/sara/include/utils.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * S.A.R.A. Linux Security Module + * + * Copyright (C) 2017 Salvatore Mesoraca + * + * 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 +#include +#include + +char *get_absolute_path(const struct path *spath, char **buf); +char *get_current_path(char **buf); + +static inline void release_entry(struct kref *ref) +{ + /* All work is done after the return from kref_put(). */ +} + + +/* + * The following macros must be used to access S.A.R.A. configuration + * structures. + * They are thread-safe under the assumption that a configuration + * won't ever be deleted but just replaced using SARA_CONFIG_REPLACE, + * possibly using an empty configuration. + * i.e. every call to SARA_CONFIG_PUT *must* be preceded by a matching + * SARA_CONFIG_GET invocation. + */ + +#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..52e6d18 --- /dev/null +++ b/security/sara/main.c @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * S.A.R.A. Linux Security Module + * + * Copyright (C) 2017 Salvatore Mesoraca + * + * 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 +#include +#include +#include + +#include "include/sara.h" +#include "include/securityfs.h" + +static const int sara_version = SARA_VERSION; + +#ifdef CONFIG_SECURITY_SARA_NO_RUNTIME_ENABLE +int sara_config_locked __read_mostly = true; +#else +int sara_config_locked __read_mostly; +#endif + +#ifdef CONFIG_SECURITY_SARA_DEFAULT_DISABLED +int sara_enabled __read_mostly; +#else +int 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 param_set_senabled(const char *val, const struct kernel_param *kp) +{ + if (!val) + return 0; + if (strtobool(val, kp->arg)) + return -EINVAL; + /* config must by locked when S.A.R.A. is disabled at boot + * and unlocked when it's enabled + */ + sara_config_locked = !(*(int *) kp->arg); + return 0; +} + +static struct kernel_param_ops param_ops_senabled = { + .set = param_set_senabled, +}; + +#define param_check_senabled(name, p) __param_check(name, p, int) + +module_param_named(enabled, sara_enabled, senabled, 0000); +MODULE_PARM_DESC(enabled, "Disable or enable S.A.R.A. at boot time. If disabled this way S.A.R.A. can't be enabled again."); + +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, + }, +}; + +static int __init sara_init(void) +{ + if (!sara_enabled && sara_config_locked) { + pr_notice("permanently disabled.\n"); + return 0; + } + + 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 0; + +error: + sara_enabled = false; + sara_config_locked = true; + pr_crit("permanently disabled.\n"); + return 1; +} + +DEFINE_LSM(sara) = { + .name = "sara", + .enabled = &sara_enabled, + .init = sara_init, +}; diff --git a/security/sara/securityfs.c b/security/sara/securityfs.c new file mode 100644 index 0000000..f6b152c --- /dev/null +++ b/security/sara/securityfs.c @@ -0,0 +1,565 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * S.A.R.A. Linux Security Module + * + * Copyright (C) 2017 Salvatore Mesoraca + * + * 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 +#include +#include +#include +#include +#include +#include + +#include "include/sara.h" +#include "include/utils.h" +#include "include/securityfs.h" + +#define __SARA_STR_HELPER(x) #x +#define SARA_STR(x) __SARA_STR_HELPER(x) + +static struct dentry *fs_root; + +static inline bool check_config_write_access(void) +{ + if (unlikely(sara_config_locked)) { + pr_warn("config write access blocked.\n"); + return false; + } + return true; +} + +static bool check_config_access(const struct file *file) +{ + if (!capable(CAP_MAC_ADMIN)) + return false; + 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) +{ + int *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'}; + int 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 = kvmalloc(buf_size, GFP_KERNEL_ACCOUNT); + if (unlikely(kbuf == NULL)) + goto out; + + ret = -EFAULT; + if (unlikely(copy_from_user(kbuf, ubuf, buf_size))) + goto out; + + ret = fptrs->load(kbuf, buf_size); + + if (unlikely(ret)) + goto out; + + ret = buf_size; + +out: + kvfree(kbuf); + return ret; +} + +static int file_config_loader_open(struct inode *inode, struct file *file) +{ + if (unlikely(!check_config_access(file))) + return -EACCES; + file->private_data = inode->i_private; + return 0; +} + +static const struct file_operations file_config_loader = { + .owner = THIS_MODULE, + .open = file_config_loader_open, + .write = file_config_loader_write, +}; + +static int file_config_show(struct seq_file *seq, void *v) +{ + const struct sara_secfs_fptrs *fptrs = seq->private; + char *buf = NULL; + ssize_t ret; + + ret = fptrs->dump(&buf); + if (unlikely(ret <= 0)) + goto out; + seq_write(seq, buf, ret); + kvfree(buf); + ret = 0; +out: + return ret; +} + +static int file_dumper_open(struct inode *inode, struct file *file) +{ + if (unlikely(!check_config_access(file))) + return -EACCES; + return single_open(file, file_config_show, inode->i_private); +} + +static const struct file_operations file_config_dumper = { + .owner = THIS_MODULE, + .open = file_dumper_open, + .read = seq_read, + .release = single_release, +}; + +static int file_hash_show(struct seq_file *seq, void *v) +{ + const struct sara_secfs_fptrs *fptrs = seq->private; + char *buf = NULL; + int ret; + + ret = fptrs->hash(&buf); + if (unlikely(ret)) + goto out; + seq_printf(seq, "%" SARA_STR(SARA_CONFIG_HASH_LEN) "phN\n", buf); + kvfree(buf); + ret = 0; +out: + return ret; +} + +static int file_hash_open(struct inode *inode, struct file *file) +{ + if (unlikely(!check_config_access(file))) + return -EACCES; + return single_open(file, file_hash_show, inode->i_private); +} + +static const struct file_operations file_hash = { + .owner = THIS_MODULE, + .open = file_hash_open, + .read = seq_read, + .release = single_release, +}; + +static int mk_dir(struct dentry *parent, + const char *dir_name, + struct dentry **dir_out) +{ + int ret = 0; + + *dir_out = securityfs_create_dir(dir_name, parent); + if (IS_ERR(*dir_out)) { + ret = -PTR_ERR(*dir_out); + *dir_out = NULL; + } + return ret; +} + +static int mk_bool_flag(struct dentry *parent, + const char *file_name, + struct dentry **dir_out, + void *flag) +{ + int ret = 0; + + *dir_out = securityfs_create_file(file_name, + 0600, + parent, + flag, + &file_flag); + if (IS_ERR(*dir_out)) { + ret = -PTR_ERR(*dir_out); + *dir_out = NULL; + } + return ret; +} + +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 ret; +} + +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 ret; +} + +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 ret; +} + +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 ret; +} + +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->name[sizeof(subtree->name)-1] = '\0'; + 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..d63febb --- /dev/null +++ b/security/sara/utils.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * S.A.R.A. Linux Security Module + * + * Copyright (C) 2017 Salvatore Mesoraca + * + * 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 +#include +#include +#include +#include +#include + +#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 = kvmalloc(size, GFP_KERNEL); + 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(TASK_COMM_LEN, GFP_KERNEL); + __get_task_comm(*buf, TASK_COMM_LEN, current); + path = *buf; + } + return path; +} From patchwork Sat Jul 6 10:54:44 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Salvatore Mesoraca X-Patchwork-Id: 11033587 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id D0F461510 for ; Sat, 6 Jul 2019 10:55:19 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id C0F882881B for ; Sat, 6 Jul 2019 10:55:19 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id AE36C28A30; Sat, 6 Jul 2019 10:55:19 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,GAPPY_SUBJECT,RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 2B10C2886A for ; Sat, 6 Jul 2019 10:55:19 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726576AbfGFKzS (ORCPT ); Sat, 6 Jul 2019 06:55:18 -0400 Received: from mail-wm1-f65.google.com ([209.85.128.65]:40872 "EHLO mail-wm1-f65.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726559AbfGFKzS (ORCPT ); Sat, 6 Jul 2019 06:55:18 -0400 Received: by mail-wm1-f65.google.com with SMTP id v19so11941179wmj.5; Sat, 06 Jul 2019 03:55:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=tntZg8r5S9LwiMO7vss45dOAl1fEVW4KEshHq7hSndk=; b=kc2cJKhDmxsorvbYBJN4L4LP1TLlMTnFPLFX6zgkZebJ/WsFIMzWWYWhtSgVtP7zMQ 0cOB3K0BaSp0V5QtNPW6SGBa+vrt3q/HL19vYWaeS7qrfH+dWKn9nfeK/a+K8xeUEotp 5ZmpQDndExIKqNYdSvxCEi9NPZMxIGZK9+ornVkxCqrBSuxArZeonMC6E7vUvtjhX4VT 5szWyZvmeouxknBYxB9Wbjm9LbY7t21Y+4zZ9LjC/eMDRTlMMT+GuGm2SSyALI9h5Hng oeLX0GzYFtaj78gVuuV1vufxWfJg/xwaAdrvfKBhwc/EYZdO2fu2pXy4SOCaITR4HIIa gRdw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=tntZg8r5S9LwiMO7vss45dOAl1fEVW4KEshHq7hSndk=; b=NFOL/YE4Wsu5HD9kmsFQn4vHaLaIj7vTd4rEZ6j96+ITKdV2wzd2bF+bCAmMzpp5hU B6n2nGYWWHghWRpT0kz3126s0FC2vdKYti/7KP+bcdjsEWR3ae2Wrf2zps/IsZQkUZw6 LgaX2TH4XuEL5ZUg96C0E5zHxzRHArZU9vSKzdk4dqW43OYY9Ek4f6I+jbTeZE7lQU3o FSJrTXKjQhf5l23mFwyjfuoV9XlE8SJC0xAkm5ZUr76+3+VIkJK2dY7xliP3FhKOuEAu 7oe64yiT7fRO5EU9ttvx8g/pwtU2+f9+w8HulDURxvAor1qQeVsGIjps29q/dqxhnMGY 6r+w== X-Gm-Message-State: APjAAAVV58V274DPlV7EfnwgEU7WyvDrDK5U/6r9yB53nKRRwPQHnVfJ X8Bbc4A4tug5Fv+ow4MmemQwnOHpv1Qm1g== X-Google-Smtp-Source: APXvYqyT5FvHevvfORCP5Opw4y7yO1x8K1YKmTYR/PUKpHFg48nVRxzDqlQiuBD7tLbShBXcjqdbVA== X-Received: by 2002:a1c:b706:: with SMTP id h6mr7598997wmf.119.1562410514962; Sat, 06 Jul 2019 03:55:14 -0700 (PDT) Received: from localhost (net-93-71-3-102.cust.vodafonedsl.it. [93.71.3.102]) by smtp.gmail.com with ESMTPSA id h11sm12578794wrx.93.2019.07.06.03.55.14 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sat, 06 Jul 2019 03:55:14 -0700 (PDT) From: Salvatore Mesoraca To: linux-kernel@vger.kernel.org Cc: kernel-hardening@lists.openwall.com, linux-mm@kvack.org, linux-security-module@vger.kernel.org, Alexander Viro , Brad Spengler , Casey Schaufler , Christoph Hellwig , James Morris , Jann Horn , Kees Cook , PaX Team , Salvatore Mesoraca , "Serge E. Hallyn" , Thomas Gleixner Subject: [PATCH v5 03/12] S.A.R.A.: cred blob management Date: Sat, 6 Jul 2019 12:54:44 +0200 Message-Id: <1562410493-8661-4-git-send-email-s.mesoraca16@gmail.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1562410493-8661-1-git-send-email-s.mesoraca16@gmail.com> References: <1562410493-8661-1-git-send-email-s.mesoraca16@gmail.com> Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: X-Virus-Scanned: ClamAV using ClamSMTP 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 --- security/sara/Makefile | 2 +- security/sara/include/sara_data.h | 84 +++++++++++++++++++++++++++++++++++++++ security/sara/main.c | 7 ++++ security/sara/sara_data.c | 69 ++++++++++++++++++++++++++++++++ 4 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 security/sara/include/sara_data.h create mode 100644 security/sara/sara_data.c diff --git a/security/sara/Makefile b/security/sara/Makefile index 8acd291..14bf7a8 100644 --- a/security/sara/Makefile +++ b/security/sara/Makefile @@ -1,3 +1,3 @@ obj-$(CONFIG_SECURITY_SARA) := sara.o -sara-y := main.o securityfs.o utils.o +sara-y := main.o securityfs.o utils.o sara_data.o diff --git a/security/sara/include/sara_data.h b/security/sara/include/sara_data.h new file mode 100644 index 0000000..9216c47 --- /dev/null +++ b/security/sara/include/sara_data.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * S.A.R.A. Linux Security Module + * + * Copyright (C) 2017 Salvatore Mesoraca + * + * 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 +#include +#include +#include + +int sara_data_init(void) __init; + +extern struct lsm_blob_sizes sara_blob_sizes __lsm_ro_after_init; + +#ifdef CONFIG_SECURITY_SARA_WXPROT + +struct sara_data { + unsigned long relro_page; + struct file *relro_file; + u16 wxp_flags; + u16 execve_flags; + bool relro_page_found; + bool mmap_blocked; +}; + +struct sara_shm_data { + bool no_exec; + bool no_write; + spinlock_t lock; +}; + + +static inline struct sara_data *get_sara_data(const struct cred *cred) +{ + return cred->security + sara_blob_sizes.lbs_cred; +} + +#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_execve_flags(X) (get_sara_data((X))->execve_flags) +#define get_current_sara_execve_flags() get_sara_execve_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_file(X) (get_sara_data((X))->relro_file) +#define get_current_sara_relro_file() get_sara_relro_file(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()) + + +static inline struct sara_shm_data *get_sara_shm_data( + const struct kern_ipc_perm *ipc) +{ + return ipc->security + sara_blob_sizes.lbs_ipc; +} + +#define get_sara_shm_no_exec(X) (get_sara_shm_data((X))->no_exec) +#define get_sara_shm_no_write(X) (get_sara_shm_data((X))->no_write) +#define lock_sara_shm(X) (spin_lock(&get_sara_shm_data((X))->lock)) +#define unlock_sara_shm(X) (spin_unlock(&get_sara_shm_data((X))->lock)) + +#endif + +#endif /* __SARA_H */ diff --git a/security/sara/main.c b/security/sara/main.c index 52e6d18..dc5dda4 100644 --- a/security/sara/main.c +++ b/security/sara/main.c @@ -18,6 +18,7 @@ #include #include "include/sara.h" +#include "include/sara_data.h" #include "include/securityfs.h" static const int sara_version = SARA_VERSION; @@ -93,6 +94,11 @@ static int __init sara_init(void) goto error; } + if (sara_data_init()) { + pr_crit("impossible to initialize creds.\n"); + goto error; + } + pr_debug("initialized.\n"); if (sara_enabled) @@ -112,4 +118,5 @@ static int __init sara_init(void) .name = "sara", .enabled = &sara_enabled, .init = sara_init, + .blobs = &sara_blob_sizes, }; diff --git a/security/sara/sara_data.c b/security/sara/sara_data.c new file mode 100644 index 0000000..9afca37 --- /dev/null +++ b/security/sara/sara_data.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * S.A.R.A. Linux Security Module + * + * Copyright (C) 2017 Salvatore Mesoraca + * + * 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 +#include +#include +#include + +static int sara_cred_prepare(struct cred *new, const struct cred *old, + gfp_t gfp) +{ + *get_sara_data(new) = *get_sara_data(old); + return 0; +} + +static void sara_cred_transfer(struct cred *new, const struct cred *old) +{ + *get_sara_data(new) = *get_sara_data(old); +} + +static int sara_shm_alloc_security(struct kern_ipc_perm *shp) +{ + struct sara_shm_data *d; + + d = get_sara_shm_data(shp); + spin_lock_init(&d->lock); + return 0; +} + +static struct security_hook_list data_hooks[] __lsm_ro_after_init = { + LSM_HOOK_INIT(cred_prepare, sara_cred_prepare), + LSM_HOOK_INIT(cred_transfer, sara_cred_transfer), + LSM_HOOK_INIT(shm_alloc_security, sara_shm_alloc_security), +}; + +struct lsm_blob_sizes sara_blob_sizes __lsm_ro_after_init = { + .lbs_cred = sizeof(struct sara_data), + .lbs_ipc = sizeof(struct sara_shm_data), +}; + +int __init sara_data_init(void) +{ + security_add_hooks(data_hooks, ARRAY_SIZE(data_hooks), "sara"); + return 0; +} + +#else /* CONFIG_SECURITY_SARA_WXPROT */ + +struct lsm_blob_sizes sara_blob_sizes __lsm_ro_after_init = { }; + +int __init sara_data_init(void) +{ + return 0; +} + +#endif From patchwork Sat Jul 6 10:54:45 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Salvatore Mesoraca X-Patchwork-Id: 11033629 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 1F97013BD for ; Sat, 6 Jul 2019 10:56:02 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 0FADD2881B for ; Sat, 6 Jul 2019 10:56:02 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 03E0628A45; Sat, 6 Jul 2019 10:56:02 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,GAPPY_SUBJECT,RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D75B92881B for ; Sat, 6 Jul 2019 10:56:00 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727189AbfGFKzz (ORCPT ); Sat, 6 Jul 2019 06:55:55 -0400 Received: from mail-wr1-f68.google.com ([209.85.221.68]:40438 "EHLO mail-wr1-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726565AbfGFKzT (ORCPT ); Sat, 6 Jul 2019 06:55:19 -0400 Received: by mail-wr1-f68.google.com with SMTP id r1so5914008wrl.7; Sat, 06 Jul 2019 03:55:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=HMLlUTvH7NymWaOjPDDSuVqGYq2w/v7gPHlts+YCCcY=; b=K5QOWWJF4uULt5fhx/WTlCd03sw83j2/BUkVXgohygderzW3Yt/f6QRhLm2eLDTQR3 xl+1XbsuQqiJAk4SXGU6K0j5eIrmywleN3fQ6cHF5HfkUqWwu1jZhobuDMpeV45Ffk6A vaZ1MOkxmFNd5+GEc+kizOsk+IvmkjVSn1xU+C4usl+oMLtBR1O1BuMEEPSsXl9sX6Rk FhwQMcRdiGIs38g45n1NmlKmC69JLrl7SoJBdFoN99AUiviKYx27dWS0XlOoqXpmSULf TLxKu1OgktRPTQAgODy06VNXmQ9MyDej9YHpgbswqpGwsZAATTgyXBskdkA4DXCQJ1sZ d8JQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=HMLlUTvH7NymWaOjPDDSuVqGYq2w/v7gPHlts+YCCcY=; b=LHTv9S5Uznu3q5Ibn4ceYPK630vhrBWAfrdrAigcywiNQ6fhx0hu9zTAJyJNF1u5rP EZwqs0dbQZbJRpUF7qUjZBxwl0WnU++nNhenH7tgVksjydPYEvhk9wjuWcW9MoQamZ8B slZGXAVKIBVQwBn0xrkgq6drgLP5DNy5lZcfVyG10LoJbC+1ztAPjXmjhIkvk00/J0lB Wk7lkNIRu0DBnIVX7mYC/gPEGIAgX0U4Jo3kHCWXSNOIMgaFZCcDQAHzga/epkYmfiss yl4HhLnrLxawkAGXbt/X9hHXGVVSdhQaH3MB71PbotrQyAXdwPREvo6tB1R0tjTylYEg jEXQ== X-Gm-Message-State: APjAAAVghfNp4MqBR8c850nH/X3QwxvTmdVsPBarjaGiTmWxvM5/UMf0 ePWA2kHsbl68H9Yb6XeLBaDttZmBdnch9A== X-Google-Smtp-Source: APXvYqxjW7TL1355+CnHJ4vlClfGDq4A6u03xqUecwa9PCLIfnLUO69I+8Q4hEKf748yiZXv3SX3UA== X-Received: by 2002:a5d:6284:: with SMTP id k4mr8982680wru.179.1562410515966; Sat, 06 Jul 2019 03:55:15 -0700 (PDT) Received: from localhost (net-93-71-3-102.cust.vodafonedsl.it. [93.71.3.102]) by smtp.gmail.com with ESMTPSA id h11sm12578794wrx.93.2019.07.06.03.55.15 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sat, 06 Jul 2019 03:55:15 -0700 (PDT) From: Salvatore Mesoraca To: linux-kernel@vger.kernel.org Cc: kernel-hardening@lists.openwall.com, linux-mm@kvack.org, linux-security-module@vger.kernel.org, Alexander Viro , Brad Spengler , Casey Schaufler , Christoph Hellwig , James Morris , Jann Horn , Kees Cook , PaX Team , Salvatore Mesoraca , "Serge E. Hallyn" , Thomas Gleixner Subject: [PATCH v5 04/12] S.A.R.A.: generic DFA for string matching Date: Sat, 6 Jul 2019 12:54:45 +0200 Message-Id: <1562410493-8661-5-git-send-email-s.mesoraca16@gmail.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1562410493-8661-1-git-send-email-s.mesoraca16@gmail.com> References: <1562410493-8661-1-git-send-email-s.mesoraca16@gmail.com> Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: X-Virus-Scanned: ClamAV using ClamSMTP Creation of a generic Discrete Finite Automata implementation for string matching. The transition tables have to be produced in user-space. This allows us to possibly support advanced string matching patterns like regular expressions, but they need to be supported by user-space tools. Signed-off-by: Salvatore Mesoraca --- security/sara/Kconfig | 22 +++ security/sara/Makefile | 3 +- security/sara/dfa.c | 335 +++++++++++++++++++++++++++++++++++++++ security/sara/dfa_test.c | 135 ++++++++++++++++ security/sara/include/dfa.h | 52 ++++++ security/sara/include/dfa_test.h | 29 ++++ security/sara/main.c | 6 + 7 files changed, 581 insertions(+), 1 deletion(-) create mode 100644 security/sara/dfa.c create mode 100644 security/sara/dfa_test.c create mode 100644 security/sara/include/dfa.h create mode 100644 security/sara/include/dfa_test.h diff --git a/security/sara/Kconfig b/security/sara/Kconfig index 0456220..b98cf27 100644 --- a/security/sara/Kconfig +++ b/security/sara/Kconfig @@ -13,6 +13,28 @@ menuconfig SECURITY_SARA If unsure, answer N. +config SECURITY_SARA_DFA_32BIT + bool "Use 32 bits instead of 16 bits for DFA states' id" + depends on SECURITY_SARA + default n + help + If you say Y here S.A.R.A. will use more memory, but you will be + able to configure more rules. + See Documentation/admin-guide/LSM/SARA.rst. for further information. + + If unsure, answer N. + +config SECURITY_SARA_DFA_TEST + bool "Enable test interface for the internal DFA engine" + depends on SECURITY_SARA + default n + help + If you say Y here S.A.R.A. will enable a user-space interface + that can be used to test the DFA engine (e.g. via `saractl test`). + See Documentation/admin-guide/LSM/SARA.rst. for further information. + + If unsure, answer N. + config SECURITY_SARA_DEFAULT_DISABLED bool "S.A.R.A. will be disabled at boot." depends on SECURITY_SARA diff --git a/security/sara/Makefile b/security/sara/Makefile index 14bf7a8..ffa1be1 100644 --- a/security/sara/Makefile +++ b/security/sara/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_SECURITY_SARA) := sara.o -sara-y := main.o securityfs.o utils.o sara_data.o +sara-y := main.o securityfs.o utils.o sara_data.o dfa.o +sara-$(CONFIG_SECURITY_SARA_DFA_TEST) += dfa_test.o diff --git a/security/sara/dfa.c b/security/sara/dfa.c new file mode 100644 index 0000000..e39b27e --- /dev/null +++ b/security/sara/dfa.c @@ -0,0 +1,335 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * S.A.R.A. Linux Security Module + * + * Copyright (C) 2017 Salvatore Mesoraca + * + * 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 +#include +#include + +#include "include/sara.h" +#include "include/dfa.h" +#include "include/securityfs.h" + +#define DFA_MAGIC_SIZE 8 +#define DFA_MAGIC "SARADFAT" + +#define DFA_INPUTS 255 + +#ifndef CONFIG_SECURITY_SARA_DFA_32BIT +#define pr_err_dfa_size() \ + pr_err_ratelimited("DFA: too many states. Recompile kernel with CONFIG_SARA_DFA_32BIT.\n") +#else +#define pr_err_dfa_size() pr_err_ratelimited("DFA: too many states.\n") +#endif + +void sara_dfa_free_tables(struct sara_dfa_tables *dfa) +{ + if (dfa) { + kvfree(dfa->output); + kvfree(dfa->def); + kvfree(dfa->base); + kvfree(dfa->next); + kvfree(dfa->check); + kvfree(dfa); + } +} + +static struct sara_dfa_tables *sara_dfa_alloc_tables(sara_dfa_state states, + sara_dfa_state cmp_states) +{ + struct sara_dfa_tables *tmp = NULL; + + tmp = kvzalloc(sizeof(*tmp), GFP_KERNEL_ACCOUNT); + if (!tmp) + goto err; + tmp->output = kvcalloc(states, + sizeof(*tmp->output), + GFP_KERNEL_ACCOUNT); + if (!tmp->output) + goto err; + tmp->def = kvcalloc(states, + sizeof(*tmp->def), + GFP_KERNEL_ACCOUNT); + if (!tmp->def) + goto err; + tmp->base = kvcalloc(states, + sizeof(*tmp->base), + GFP_KERNEL_ACCOUNT); + if (!tmp->base) + goto err; + tmp->next = kvcalloc(cmp_states, + sizeof(*tmp->next) * DFA_INPUTS, + GFP_KERNEL_ACCOUNT); + if (!tmp->next) + goto err; + tmp->check = kvcalloc(cmp_states, + sizeof(*tmp->check) * DFA_INPUTS, + GFP_KERNEL_ACCOUNT); + if (!tmp->check) + goto err; + tmp->states = states; + tmp->cmp_states = cmp_states; + return tmp; + +err: + sara_dfa_free_tables(tmp); + return ERR_PTR(-ENOMEM); +} + +int sara_dfa_match(struct sara_dfa_tables *dfa, + const unsigned char *s, + sara_dfa_output *output) +{ + sara_dfa_state i, j; + sara_dfa_state c_state = 0; + + /* Max s[x] value must be == DFA_INPUTS */ + BUILD_BUG_ON((((1ULL << (sizeof(*s) * 8)) - 1) != DFA_INPUTS)); + + /* + * The DFA transition table is compressed using 5 linear arrays + * as shown in the Dragon Book. + * These arrays are: default, base, next, check and output. + * default, base and output have the same size and are indexed by + * state number. + * next and check tables have the same size and are indexed by + * the value from base for a given state and the input symbol. + * To match a string against this set of arrays we need to: + * - Use the base arrays to recover the index to use + * with check and next arrays for the current state and symbol. + * - If the value in the check array matches the current state + * number the next state should be retrieved from the next array, + * otherwise we take it from the default array. + * - If the next state is not valid we should return immediately + * - If the input sequence is over and the value in the output array + * is valid, the string matches, and we should return the output + * value. + */ + + for (i = 0; s[i]; i++) { + j = (dfa->base[c_state] * DFA_INPUTS) + s[i] - 1; + if (dfa->check[j] != c_state) + c_state = dfa->def[c_state]; + else + c_state = dfa->next[j]; + if (c_state == SARA_INVALID_DFA_VALUE) + return 0; + } + + if (dfa->output[c_state] != SARA_INVALID_DFA_VALUE) { + *output = dfa->output[c_state]; + return 1; + } + return 0; +} + +struct sara_dfa_tables *sara_dfa_make_null(void) +{ + int i; + struct sara_dfa_tables *dfa = NULL; + + dfa = sara_dfa_alloc_tables(1, 1); + if (unlikely(IS_ERR_OR_NULL(dfa))) + return NULL; + dfa->output[0] = SARA_INVALID_DFA_VALUE; + dfa->def[0] = SARA_INVALID_DFA_VALUE; + dfa->base[0] = 0; + for (i = 0; i < DFA_INPUTS; ++i) + dfa->next[i] = SARA_INVALID_DFA_VALUE; + for (i = 0; i < DFA_INPUTS; ++i) + dfa->check[i] = 0; + memset(dfa->hash, 0, SARA_CONFIG_HASH_LEN); + return dfa; +} + +struct binary_dfa_header { + char magic[DFA_MAGIC_SIZE]; + __le32 version; + __le32 states; + __le32 cmp_states; + char hash[SARA_CONFIG_HASH_LEN]; +} __packed; + +#define SARA_INVALID_DFA_VALUE_LOAD 0xffffffffu + +struct sara_dfa_tables *sara_dfa_load(const char *buf, + size_t buf_len, + bool (*is_valid)(sara_dfa_output)) +{ + int ret; + struct sara_dfa_tables *dfa = NULL; + struct binary_dfa_header *h = (struct binary_dfa_header *) buf; + __le32 *p; + uint64_t i; + u32 version, states, cmp_states, tmp; + + ret = -EINVAL; + if (unlikely(buf_len < sizeof(*h))) + goto out; + + ret = -EINVAL; + if (unlikely(memcmp(h->magic, DFA_MAGIC, DFA_MAGIC_SIZE) != 0)) + goto out; + version = le32_to_cpu(h->version); + states = le32_to_cpu(h->states); + cmp_states = le32_to_cpu(h->cmp_states); + if (unlikely(version != SARA_DFA_VERSION)) { + pr_err_ratelimited("DFA: unsupported version\n"); + goto out; + } + if (unlikely(states >= SARA_INVALID_DFA_VALUE || + cmp_states >= SARA_INVALID_DFA_VALUE)) { + pr_err_dfa_size(); + goto out; + } + if (unlikely(states == 0 || + cmp_states == 0)) + goto out; + if (unlikely(((states * sizeof(u32) * 3) + + (cmp_states * sizeof(u32) * 2 * DFA_INPUTS) + + sizeof(*h)) != buf_len)) + goto out; + + ret = -ENOMEM; + dfa = sara_dfa_alloc_tables(h->states, h->cmp_states); + if (unlikely(IS_ERR_OR_NULL(dfa))) + goto out; + + dfa->states = states; + dfa->cmp_states = cmp_states; + + ret = -EINVAL; + p = (__le32 *) (buf + sizeof(*h)); + for (i = 0; i < dfa->states; i++) { + tmp = le32_to_cpu(*p); + if (unlikely(tmp != SARA_INVALID_DFA_VALUE_LOAD && + tmp >= dfa->states)) + goto out_alloc; + dfa->def[i] = (sara_dfa_state) tmp; + ++p; + } + for (i = 0; i < dfa->states; i++) { + tmp = le32_to_cpu(*p); + if (unlikely(tmp >= dfa->cmp_states)) + goto out_alloc; + dfa->base[i] = (sara_dfa_state) tmp; + ++p; + } + for (i = 0; i < (dfa->cmp_states * DFA_INPUTS); i++) { + tmp = le32_to_cpu(*p); + if (unlikely(tmp != SARA_INVALID_DFA_VALUE_LOAD && + tmp >= dfa->states)) + goto out_alloc; + dfa->next[i] = (sara_dfa_state) tmp; + ++p; + } + for (i = 0; i < (dfa->cmp_states * DFA_INPUTS); i++) { + tmp = le32_to_cpu(*p); + if (unlikely(tmp != SARA_INVALID_DFA_VALUE_LOAD && + tmp >= dfa->states)) + goto out_alloc; + dfa->check[i] = (sara_dfa_state) tmp; + ++p; + } + for (i = 0; i < dfa->states; i++) { + tmp = le32_to_cpu(*p); + if (unlikely(tmp != SARA_INVALID_DFA_VALUE_LOAD && + !is_valid(tmp))) + goto out_alloc; + dfa->output[i] = (sara_dfa_state) tmp; + ++p; + } + if (unlikely((void *) p != (void *) (buf + buf_len))) + goto out_alloc; + + BUILD_BUG_ON(sizeof(dfa->hash) != sizeof(h->hash)); + memcpy(dfa->hash, h->hash, sizeof(dfa->hash)); + + return dfa; +out_alloc: + sara_dfa_free_tables(dfa); +out: + pr_err_ratelimited("DFA: invalid load\n"); + return ERR_PTR(ret); +} + +ssize_t sara_dfa_dump(const struct sara_dfa_tables *dfa, char **buffer) +{ + char *buf; + size_t buf_len = 0; + struct binary_dfa_header *h; + __le32 *p; + int i; + + buf_len = sizeof(*h) + + dfa->states * sizeof(__le32) * 3 + + dfa->cmp_states * sizeof(__le32) * DFA_INPUTS * 2; + buf = kvmalloc(buf_len, GFP_KERNEL_ACCOUNT); + if (unlikely(!buf)) + return -ENOMEM; + + h = (struct binary_dfa_header *) buf; + memcpy(h->magic, DFA_MAGIC, DFA_MAGIC_SIZE); + h->version = cpu_to_le32(SARA_DFA_VERSION); + h->states = cpu_to_le32(dfa->states); + h->cmp_states = cpu_to_le32(dfa->cmp_states); + BUILD_BUG_ON(sizeof(dfa->hash) != sizeof(h->hash)); + memcpy(h->hash, dfa->hash, sizeof(dfa->hash)); + + p = (__le32 *) (buf + sizeof(*h)); + for (i = 0; i < dfa->states; i++) { + if (dfa->def[i] == SARA_INVALID_DFA_VALUE) + *p++ = cpu_to_le32(SARA_INVALID_DFA_VALUE_LOAD); + else + *p++ = cpu_to_le32(dfa->def[i]); + } + for (i = 0; i < dfa->states; i++) { + if (dfa->base[i] == SARA_INVALID_DFA_VALUE) + *p++ = cpu_to_le32(SARA_INVALID_DFA_VALUE_LOAD); + else + *p++ = cpu_to_le32(dfa->base[i]); + } + for (i = 0; i < (dfa->cmp_states * DFA_INPUTS); i++) { + if (dfa->next[i] == SARA_INVALID_DFA_VALUE) + *p++ = cpu_to_le32(SARA_INVALID_DFA_VALUE_LOAD); + else + *p++ = cpu_to_le32(dfa->next[i]); + } + for (i = 0; i < (dfa->cmp_states * DFA_INPUTS); i++) { + if (dfa->check[i] == SARA_INVALID_DFA_VALUE) + *p++ = cpu_to_le32(SARA_INVALID_DFA_VALUE_LOAD); + else + *p++ = cpu_to_le32(dfa->check[i]); + } + for (i = 0; i < dfa->states; i++) { + if (dfa->output[i] == SARA_INVALID_DFA_VALUE) + *p++ = cpu_to_le32(SARA_INVALID_DFA_VALUE_LOAD); + else + *p++ = cpu_to_le32(dfa->output[i]); + } + + if (unlikely((void *) p != (void *) (buf + buf_len))) { + /* + * We can calculate the correct buffer size upfront. + * This should never happen. + */ + kvfree(buf); + pr_crit("memory corruption in %s\n", __func__); + return 0; + } + + *buffer = buf; + return buf_len; +} + +#undef SARA_INVALID_DFA_VALUE_LOAD diff --git a/security/sara/dfa_test.c b/security/sara/dfa_test.c new file mode 100644 index 0000000..9c06414 --- /dev/null +++ b/security/sara/dfa_test.c @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * S.A.R.A. Linux Security Module + * + * Copyright (C) 2017 Salvatore Mesoraca + * + * 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/dfa.h" +#include "include/securityfs.h" +#include +#include +#include + +#define SARA_DFA_MAX_RES_SIZE 20 + +struct sara_dfa_tables *table; + +static DEFINE_MUTEX(test_lock); +static sara_dfa_output result; + +static bool is_valid_output(sara_dfa_output output) +{ + return true; +} + +static int config_load(const char *buf, size_t buf_len) +{ + struct sara_dfa_tables *dfa, *tmp; + + dfa = sara_dfa_load(buf, buf_len, is_valid_output); + if (unlikely(IS_ERR_OR_NULL(dfa))) { + if (unlikely(dfa == NULL)) + return -EINVAL; + else + return PTR_ERR(dfa); + } + mutex_lock(&test_lock); + tmp = table; + table = dfa; + mutex_unlock(&test_lock); + sara_dfa_free_tables(tmp); + return 0; +} + +static int config_load_str(const char *buf, size_t buf_len) +{ + char *s; + + s = kmalloc(buf_len+1, GFP_KERNEL_ACCOUNT); + if (unlikely(s == NULL)) + return -ENOMEM; + s[buf_len] = '\0'; + memcpy(s, buf, buf_len); + + mutex_lock(&test_lock); + result = SARA_INVALID_DFA_VALUE; + sara_dfa_match(table, s, &result); + mutex_unlock(&test_lock); + + kfree(s); + + return 0; +} + +static ssize_t config_dump_result(char **buf) +{ + char *s; + + s = kzalloc(SARA_DFA_MAX_RES_SIZE, GFP_KERNEL_ACCOUNT); + if (unlikely(s == NULL)) + return -ENOMEM; + mutex_lock(&test_lock); + if (result == SARA_INVALID_DFA_VALUE) + snprintf(s, SARA_DFA_MAX_RES_SIZE, "%u\n", 0xffffffff); + else + snprintf(s, + SARA_DFA_MAX_RES_SIZE, + "%u\n", + (unsigned int) result); + mutex_unlock(&test_lock); + *buf = s; + return strlen(s); +} + +static struct sara_secfs_fptrs fptrs __lsm_ro_after_init = { + .load = config_load, +}; + +static struct sara_secfs_fptrs teststr __lsm_ro_after_init = { + .load = config_load_str, + .dump = config_dump_result, +}; + +static const struct sara_secfs_node dfa_test_fs[] __initconst = { + { + .name = ".load", + .type = SARA_SECFS_CONFIG_LOAD, + .data = &fptrs, + }, + { + .name = "test", + .type = SARA_SECFS_CONFIG_LOAD, + .data = &teststr, + }, + { + .name = "result", + .type = SARA_SECFS_CONFIG_DUMP, + .data = &teststr, + }, +}; + +int __init sara_dfa_test_init(void) +{ + int ret; + + table = sara_dfa_make_null(); + if (unlikely(!table)) + return -ENOMEM; + ret = sara_secfs_subtree_register("dfa_test", + dfa_test_fs, + ARRAY_SIZE(dfa_test_fs)); + if (unlikely(ret)) + goto out_fail; + return 0; + +out_fail: + sara_dfa_free_tables(table); + return ret; +} diff --git a/security/sara/include/dfa.h b/security/sara/include/dfa.h new file mode 100644 index 0000000..a536b60 --- /dev/null +++ b/security/sara/include/dfa.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * S.A.R.A. Linux Security Module + * + * Copyright (C) 2017 Salvatore Mesoraca + * + * 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_DFA_H +#define __SARA_DFA_H + +#include "securityfs.h" + +#ifdef CONFIG_SARA_DFA_32BIT +typedef uint32_t sara_dfa_state; +typedef uint32_t sara_dfa_output; +#define SARA_INVALID_DFA_VALUE 0xffffffffu +#else +typedef uint16_t sara_dfa_state; +typedef uint16_t sara_dfa_output; +#define SARA_INVALID_DFA_VALUE 0xffffu +#endif + +#define SARA_DFA_VERSION 2 + +struct sara_dfa_tables { + sara_dfa_state states; + sara_dfa_state cmp_states; + sara_dfa_output *output; + sara_dfa_state *def; + sara_dfa_state *base; + sara_dfa_state *next; + sara_dfa_state *check; + char hash[SARA_CONFIG_HASH_LEN]; +}; + +int sara_dfa_match(struct sara_dfa_tables *dfa, + const unsigned char *s, + sara_dfa_output *output); +struct sara_dfa_tables *sara_dfa_make_null(void); +struct sara_dfa_tables *sara_dfa_load(const char *buf, + size_t buf_len, + bool (*is_valid)(sara_dfa_output)); +ssize_t sara_dfa_dump(const struct sara_dfa_tables *dfa, char **buffer); +void sara_dfa_free_tables(struct sara_dfa_tables *dfa); + +#endif /* __SARA_DFA_H */ diff --git a/security/sara/include/dfa_test.h b/security/sara/include/dfa_test.h new file mode 100644 index 0000000..f10f78a --- /dev/null +++ b/security/sara/include/dfa_test.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * S.A.R.A. Linux Security Module + * + * Copyright (C) 2017 Salvatore Mesoraca + * + * 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_DFA_TEST_H +#define __SARA_DFA_TEST_H + +#ifdef CONFIG_SECURITY_SARA_DFA_TEST + +#include +int sara_dfa_test_init(void) __init; + +#else /* CONFIG_SECURITY_SARA_DFA_TEST */ +inline int sara_dfa_test_init(void) +{ + return 0; +} +#endif /* CONFIG_SECURITY_SARA_DFA_TEST */ + +#endif /* __SARA_DFA_TEST_H */ diff --git a/security/sara/main.c b/security/sara/main.c index dc5dda4..6b09500 100644 --- a/security/sara/main.c +++ b/security/sara/main.c @@ -17,6 +17,7 @@ #include #include +#include "include/dfa_test.h" #include "include/sara.h" #include "include/sara_data.h" #include "include/securityfs.h" @@ -99,6 +100,11 @@ static int __init sara_init(void) goto error; } + if (sara_dfa_test_init()) { + pr_crit("impossible to initialize DFA test interface.\n"); + goto error; + } + pr_debug("initialized.\n"); if (sara_enabled) From patchwork Sat Jul 6 10:54:46 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Salvatore Mesoraca X-Patchwork-Id: 11033627 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 7CC2813BD for ; Sat, 6 Jul 2019 10:55:55 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 6DA7B2881B for ; Sat, 6 Jul 2019 10:55:55 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 613C2289D2; Sat, 6 Jul 2019 10:55:55 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D0FE22881B for ; Sat, 6 Jul 2019 10:55:54 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726880AbfGFKzV (ORCPT ); Sat, 6 Jul 2019 06:55:21 -0400 Received: from mail-wm1-f66.google.com ([209.85.128.66]:53935 "EHLO mail-wm1-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726568AbfGFKzU (ORCPT ); Sat, 6 Jul 2019 06:55:20 -0400 Received: by mail-wm1-f66.google.com with SMTP id x15so11410168wmj.3; Sat, 06 Jul 2019 03:55:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=tc9nxOt0YynZlg4DtgqDwIM4b55l1zqGz9yWCnOUqxc=; b=H/hv3oqlvBbndX96qLPf5yWClDQ6sRuTVt5tRUmkZdBjqfNT8QP6eSS48eMgnLnX3M WMQ4grRnDFsHfzcmJCa2NZW22qa3lXOGIluOW5jgh2LpcgL7DkJ54y1w0C6Xhkpp2ntZ I/av/JjLtKAzbQq7e3mH2IiGg67xxHpyAjtACqs2raUphqFhtSD3uoAU+89+iqorUH8i Taggjzu3zY8qh5GVcr/ruUdLkoTpT4zqR8vtjxaqQtGYSk/tQjq/DHcYhOTPMA3P0Zf2 4XSF8AjFUnGcVP26TnVi4uep/PBucOaPpgJiMZ2o/nHHHTtddc59KmP2z9DdGZLZZfYC 2KfA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=tc9nxOt0YynZlg4DtgqDwIM4b55l1zqGz9yWCnOUqxc=; b=XcbYNAfwegS4BdpitcHNLlKBTGZShEIrSoHaF7jzyoTJucsA7NtOu/fdOKG/TNxREh dKuPcbw7ojNQTbwNwgoxV++SSqdRGMohIllVnRBStdv+/g8ctroHOVTc4ydUjNQbxcyQ ubYqqR3sxpp5AKvtmTBGAKhiKLxOFoi2YrLxT8f71ovaV7uWv3nJaVbqWu8nn86gIuOE 26seZ/tmf/Zl0M4KsTvThftoZF6EC0OozSQz+22ttUOiMT7xgx0GX1oekzL35937/BE6 sz3cu/NO4g0AEBoWZVQdDtHkRzXWhO8Y8Z5cqeRcPBe9cTtHMK6CJZxYuIWOLdRUdSRx oJGw== X-Gm-Message-State: APjAAAXVhxsGlXYXIESJE6M+Zh8MbcotmE8ZuqfYB7XHJo5uoZqcay5n +gKinUD3YDyTC0soA9Qm2r4742Ib7qoidw== X-Google-Smtp-Source: APXvYqwjn5IyS6SWM65IBzeh/bdnAqkvGqGa6i9TXN3uin56hMILK/3JcH3obevfA3uL9Qb6/jMB4g== X-Received: by 2002:a1c:4d6:: with SMTP id 205mr7201683wme.148.1562410517083; Sat, 06 Jul 2019 03:55:17 -0700 (PDT) Received: from localhost (net-93-71-3-102.cust.vodafonedsl.it. [93.71.3.102]) by smtp.gmail.com with ESMTPSA id h11sm12578794wrx.93.2019.07.06.03.55.16 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sat, 06 Jul 2019 03:55:16 -0700 (PDT) From: Salvatore Mesoraca To: linux-kernel@vger.kernel.org Cc: kernel-hardening@lists.openwall.com, linux-mm@kvack.org, linux-security-module@vger.kernel.org, Alexander Viro , Brad Spengler , Casey Schaufler , Christoph Hellwig , James Morris , Jann Horn , Kees Cook , PaX Team , Salvatore Mesoraca , "Serge E. Hallyn" , Thomas Gleixner Subject: [PATCH v5 05/12] LSM: creation of "check_vmflags" LSM hook Date: Sat, 6 Jul 2019 12:54:46 +0200 Message-Id: <1562410493-8661-6-git-send-email-s.mesoraca16@gmail.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1562410493-8661-1-git-send-email-s.mesoraca16@gmail.com> References: <1562410493-8661-1-git-send-email-s.mesoraca16@gmail.com> Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: X-Virus-Scanned: ClamAV using ClamSMTP 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", "__install_special_mapping" and "setup_arg_pages". When loading an ELF, this hook is also used to determine what to do with an RWE PT_GNU_STACK header. This allows LSM to force the loader to silently ignore executable stack markings, which is useful a thing to do when trampoline emulation is available. Signed-off-by: Salvatore Mesoraca --- fs/binfmt_elf.c | 3 ++- fs/binfmt_elf_fdpic.c | 3 ++- fs/exec.c | 4 ++++ include/linux/lsm_hooks.h | 7 +++++++ include/linux/security.h | 6 ++++++ mm/mmap.c | 13 +++++++++++++ security/security.c | 5 +++++ 7 files changed, 39 insertions(+), 2 deletions(-) diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 8264b46..1d98737 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -806,7 +806,8 @@ static int load_elf_binary(struct linux_binprm *bprm) for (i = 0; i < loc->elf_ex.e_phnum; i++, elf_ppnt++) switch (elf_ppnt->p_type) { case PT_GNU_STACK: - if (elf_ppnt->p_flags & PF_X) + if (elf_ppnt->p_flags & PF_X && + !security_check_vmflags(VM_EXEC|VM_READ|VM_WRITE)) executable_stack = EXSTACK_ENABLE_X; else executable_stack = EXSTACK_DISABLE_X; diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c index d86ebd0d..6e0dee1 100644 --- a/fs/binfmt_elf_fdpic.c +++ b/fs/binfmt_elf_fdpic.c @@ -163,7 +163,8 @@ static int elf_fdpic_fetch_phdrs(struct elf_fdpic_params *params, if (phdr->p_type != PT_GNU_STACK) continue; - if (phdr->p_flags & PF_X) + if (phdr->p_flags & PF_X && + !security_check_vmflags(VM_EXEC|VM_READ|VM_WRITE)) params->flags |= ELF_FDPIC_FLAG_EXEC_STACK; else params->flags |= ELF_FDPIC_FLAG_NOEXEC_STACK; diff --git a/fs/exec.c b/fs/exec.c index 89a500b..abf770a 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -756,6 +756,10 @@ int setup_arg_pages(struct linux_binprm *bprm, vm_flags |= mm->def_flags; vm_flags |= VM_STACK_INCOMPLETE_SETUP; + ret = security_check_vmflags(vm_flags); + if (ret) + goto out_unlock; + ret = mprotect_fixup(vma, &prev, vma->vm_start, vma->vm_end, vm_flags); if (ret) diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 47f58cf..12ce609 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -513,6 +513,11 @@ * @reqprot contains the protection requested by the application. * @prot contains the protection that will be applied by the kernel. * Return 0 if permission is granted. + * @check_vmflags: + * Check if the requested @vmflags are allowed. + * @vmflags contains the requested vmflags. + * Return 0 if the operation is allowed to continue otherwise return + * the appropriate error code. * @file_lock: * Check permission before performing file locking operations. * Note the hook mediates both flock and fcntl style locks. @@ -1597,6 +1602,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); @@ -1897,6 +1903,7 @@ struct security_hook_heads { struct hlist_head mmap_addr; struct hlist_head mmap_file; struct hlist_head file_mprotect; + struct hlist_head check_vmflags; struct hlist_head file_lock; struct hlist_head file_fcntl; struct hlist_head file_set_fowner; diff --git a/include/linux/security.h b/include/linux/security.h index 659071c..aed78eb 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -312,6 +312,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); @@ -859,6 +860,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 7e8c3e8a..ec9c0e3d 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1390,6 +1390,7 @@ unsigned long do_mmap(struct file *file, unsigned long addr, { struct mm_struct *mm = current->mm; int pkey = 0; + int error; *populate = 0; @@ -1453,6 +1454,10 @@ unsigned long do_mmap(struct file *file, unsigned long addr, vm_flags |= calc_vm_prot_bits(prot, pkey) | calc_vm_flag_bits(flags) | mm->def_flags | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC; + error = security_check_vmflags(vm_flags); + if (error) + return error; + if (flags & MAP_LOCKED) if (!can_do_mlock()) return -EPERM; @@ -2996,6 +3001,10 @@ static int do_brk_flags(unsigned long addr, unsigned long len, unsigned long fla return -EINVAL; flags |= VM_DATA_DEFAULT_FLAGS | VM_ACCOUNT | mm->def_flags; + error = security_check_vmflags(flags); + if (error) + return error; + error = get_unmapped_area(NULL, addr, len, 0, MAP_FIXED); if (offset_in_page(error)) return error; @@ -3393,6 +3402,10 @@ static struct vm_area_struct *__install_special_mapping( int ret; struct vm_area_struct *vma; + ret = security_check_vmflags(vm_flags); + if (ret) + return ERR_PTR(ret); + vma = vm_area_alloc(mm); if (unlikely(vma == NULL)) return ERR_PTR(-ENOMEM); diff --git a/security/security.c b/security/security.c index f493db0..3308e89 100644 --- a/security/security.c +++ b/security/security.c @@ -1421,6 +1421,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); From patchwork Sat Jul 6 10:54:47 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Salvatore Mesoraca X-Patchwork-Id: 11033621 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 921CC13BD for ; Sat, 6 Jul 2019 10:55:51 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 7FBF12881B for ; Sat, 6 Jul 2019 10:55:51 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 7358C289D2; Sat, 6 Jul 2019 10:55:51 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,GAPPY_SUBJECT,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E0A872881B for ; Sat, 6 Jul 2019 10:55:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726915AbfGFKzW (ORCPT ); Sat, 6 Jul 2019 06:55:22 -0400 Received: from mail-wm1-f67.google.com ([209.85.128.67]:38362 "EHLO mail-wm1-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726851AbfGFKzV (ORCPT ); Sat, 6 Jul 2019 06:55:21 -0400 Received: by mail-wm1-f67.google.com with SMTP id s15so11970136wmj.3; Sat, 06 Jul 2019 03:55:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=neSAZr0qudklbC2ocAszdo5nTo2RaPTIwxDnwB9Bfh4=; b=NXUxTn17ctcRPNOUPFeMmXyn0eMc5qTzqtuYVVw1v6dvr165UiWXaMs4DE0Gnh5MVv bSmAUM0J9LX84qgbxFb7OMWQbgk4Zj9SnFo31GaRvl4xate/2l889tHMRthL7t6O8+v0 YWrdWLxLQeTuX8FAPQ6I2Erh3ZZCnjvGJh4LJ9pTVBsjevF6plRFoJRt34JSKYSyIw4a F3dHpzvh68ih2JX84M6FjbQgrFB+SF1w71RWnV66YQ8fNEU1Ev/V4e3ZKjlrM4cnl98V FRv2Jw10/VLaxiwa3tk2CmV6tWHEFHWFLtVsaE76fgROy/oxNLZL4oYCldIhKoIb0NTY 5FSg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=neSAZr0qudklbC2ocAszdo5nTo2RaPTIwxDnwB9Bfh4=; b=LOWjX8vo/ByzMOWOxe8Fp9DU3mHRDDTFZHHkgFjWcmRoRqA9F7QAUrFd4u/v6wnrG0 BrjI+S1V6QyEBQe7VstlvE2jpE2WkGMrkk1TRefGzaY89SRzYd3+I9shBjz6zRbSxjFC JmyIppW6jec4I2q/rRwORR2AU15Tn2CfSWmeQBZnGLX5VA2diy7D/IXrSyu3hV082lbL F0ezPmRadEAdF64uBtnB1raabsvs6cwC2rOsO3jmyfgyOcSIZulGZ1PUt2HIyAIRyat8 u0QozuY1NDsKRenWWofUeJ+kYrKnbVzTH9wB9dEIMsqthhBu2uoJZhvXi4TriFEoHM/U wh3g== X-Gm-Message-State: APjAAAVz67kV+SKztJvhifiemYjN9KHTdhklrJe3r17wVU2aVlHopMPr lITk4j4ftURSZmDjpWzBZc306aKzyZ5cjw== X-Google-Smtp-Source: APXvYqyj3Y0pdXAI8GwWuFskykzQj2FjjaVQ1LYXAEXUqG5HS9biwJxY0j1BLyCPepJq5qXexxxIzQ== X-Received: by 2002:a1c:67c3:: with SMTP id b186mr7380497wmc.34.1562410518164; Sat, 06 Jul 2019 03:55:18 -0700 (PDT) Received: from localhost (net-93-71-3-102.cust.vodafonedsl.it. [93.71.3.102]) by smtp.gmail.com with ESMTPSA id h11sm12578794wrx.93.2019.07.06.03.55.17 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sat, 06 Jul 2019 03:55:17 -0700 (PDT) From: Salvatore Mesoraca To: linux-kernel@vger.kernel.org Cc: kernel-hardening@lists.openwall.com, linux-mm@kvack.org, linux-security-module@vger.kernel.org, Alexander Viro , Brad Spengler , Casey Schaufler , Christoph Hellwig , James Morris , Jann Horn , Kees Cook , PaX Team , Salvatore Mesoraca , "Serge E. Hallyn" , Thomas Gleixner Subject: [PATCH v5 06/12] S.A.R.A.: WX protection Date: Sat, 6 Jul 2019 12:54:47 +0200 Message-Id: <1562410493-8661-7-git-send-email-s.mesoraca16@gmail.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1562410493-8661-1-git-send-email-s.mesoraca16@gmail.com> References: <1562410493-8661-1-git-send-email-s.mesoraca16@gmail.com> Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: X-Virus-Scanned: ClamAV using ClamSMTP 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. Additional restrictions are in place for System V shared memory segments: if a segment was attached as writable (executable) in the past it won't be allowed to be attached as executable (writable) in the future. 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 according to my understanding of the code. Changes or omissions from the original code are mine and don't reflect the original grsecurity/PaX code. Signed-off-by: Salvatore Mesoraca --- security/sara/Kconfig | 74 +++++ security/sara/Makefile | 1 + security/sara/include/wxprot.h | 29 ++ security/sara/main.c | 6 + security/sara/wxprot.c | 679 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 789 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 b98cf27..54a96e0 100644 --- a/security/sara/Kconfig +++ b/security/sara/Kconfig @@ -60,3 +60,77 @@ config SECURITY_SARA_NO_RUNTIME_ENABLE If unsure, answer Y. +config SECURITY_SARA_WXPROT + bool "WX Protection: W^X and W!->X protections" + depends on SECURITY_SARA + default y + help + WX Protection aims to improve user-space programs security by applying: + - W^X memory restriction + - W!->X (once writable never executable) mprotect restriction + - Executable MMAP prevention + See Documentation/admin-guide/LSM/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/admin-guide/LSM/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/admin-guide/LSM/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/admin-guide/LSM/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/admin-guide/LSM/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/admin-guide/LSM/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_enabled=[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 diff --git a/security/sara/Makefile b/security/sara/Makefile index ffa1be1..07751a5 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 dfa.o sara-$(CONFIG_SECURITY_SARA_DFA_TEST) += dfa_test.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..df8705a --- /dev/null +++ b/security/sara/include/wxprot.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * S.A.R.A. Linux Security Module + * + * Copyright (C) 2017 Salvatore Mesoraca + * + * 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 +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 6b09500..676e901 100644 --- a/security/sara/main.c +++ b/security/sara/main.c @@ -21,6 +21,7 @@ #include "include/sara.h" #include "include/sara_data.h" #include "include/securityfs.h" +#include "include/wxprot.h" static const int sara_version = SARA_VERSION; @@ -105,6 +106,11 @@ static int __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..8a3d002 --- /dev/null +++ b/security/sara/wxprot.c @@ -0,0 +1,679 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * S.A.R.A. Linux Security Module + * + * Copyright (C) 2017 Salvatore Mesoraca + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "include/dfa.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_config_container { + struct sara_dfa_tables *dfa; + struct kref refcount; +}; + +static struct wxprot_config_container __rcu *wxprot_config; + +static const int wxprot_config_version = SARA_WXPROT_CONFIG_VERSION; +static int wxprot_enabled __read_mostly = true; +static DEFINE_SPINLOCK(wxprot_config_lock); + +static u16 default_flags __lsm_ro_after_init = + CONFIG_SECURITY_SARA_WXPROT_DEFAULT_FLAGS; + +static const bool wxprot_emutramp; + +static void pr_wxp(char *msg) +{ + char *buf, *path; + + path = get_current_path(&buf); + pr_notice_ratelimited("WXP: %s in '%s' (%d).\n", + msg, path, current->pid); + kvfree(buf); +} + +/** + * are_flags_valid - check whether the given combination of flags is valid + * @flags: the flags to be checked + * + * Returns true if flags are valid, false otherwise. + * + * Rules checked: + * - Unused bits must be set to 0. + * - Any feature in the "MPROTECT" group require "WXORX". + * - "COMPLAIN" and "VERBOSE" can only be used if some other feature is + * enabled. + * - Trampoline emulation can only be used when all "MPROTECT" + * features are active. + * - "MMAP" protection requires SARA_WXP_OTHER + */ +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; + if (unlikely(flags & SARA_WXP_MMAP && + !(flags & SARA_WXP_OTHER))) + return false; + return true; +} + +module_param(wxprot_enabled, int, 0); +MODULE_PARM_DESC(wxprot_enabled, + "Disable or enable S.A.R.A. WX Protection at boot time."); + +static int param_set_wxpflags(const char *val, const struct kernel_param *kp) +{ + u16 flags; + + if (!val || kstrtou16(val, 0, &flags) != 0 || !are_flags_valid(flags)) + return -EINVAL; + *(u16 *) kp->arg = flags; + return 0; +} + +static struct kernel_param_ops param_ops_wxpflags = { + .set = param_set_wxpflags, +}; + +#define param_check_wxpflags(name, p) __param_check(name, p, u16) + +module_param_named(wxprot_default_flags, default_flags, wxpflags, 0); +MODULE_PARM_DESC(wxprot_default_flags, + "Disable or enable S.A.R.A. WX Protection at boot time."); + +/* + * 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; \ + loff_t _pos = 0; \ + if (ELFH.e_type == ET_DYN || ELFH.e_type == ET_EXEC) { \ + for (i = 0; i < ELFH.e_phnum; ++i) { \ + _pos = ELFH.e_phoff + i*sizeof(ELFP); \ + _tmp = kernel_read(FILE, &ELFP, sizeof(ELFP), \ + &_pos); \ + 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; + loff_t pos = 0; + + ret = kernel_read(bprm->file, &elf_h, sizeof(elf_h), &pos); + 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_64BIT) && + elf_h.c64.e_ident[EI_CLASS] == ELFCLASS64) { + find_relro_section(elf_h.c64, + elf_p.c64, + bprm->file, + relro_page, + relro_page_found); + } + } else + return 2; /* It isn't an ELF */ + + if (relro_page_found) { + get_sara_relro_page(bprm->cred) = relro_page; + get_sara_relro_page_found(bprm->cred) = relro_page_found; + get_sara_relro_file(bprm->cred) = bprm->file; + return 0; + } else + return 1; /* It's an ELF without a RELRO section */ +} + +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 && + get_current_sara_relro_file() == vma->vm_file) + 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; + sara_dfa_output outfl; + char *buf = NULL; + char *path = NULL; + struct cred *current_new; + + 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_relro_page(bprm->cred) = 0; + get_sara_relro_file(bprm->cred) = NULL; + get_sara_wxp_flags(bprm->cred) = SARA_WXP_NONE; + get_sara_execve_flags(bprm->cred) = SARA_WXP_NONE; + + if (!sara_enabled || !wxprot_enabled) + return 0; + + /* + * SARA_WXP_TRANSFER means that the parent + * wants this child to inherit its flags. + */ + 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); + if (sara_dfa_match(c->dfa, path, &outfl)) + sara_wxp_flags = (u16) outfl; + SARA_CONFIG_PUT_RCU(c); + } else + path = (char *) bprm->interp; + + if (sara_wxp_flags != default_flags && + sara_wxp_flags & SARA_WXP_VERBOSE) + pr_debug_ratelimited("WXP: '%s' run with flags '0x%x'.\n", + path, sara_wxp_flags); + +skip_flags: + i = set_relro_page(bprm); + /* + * i != 0 means no relro segment + * i == 1 means the file wasn't an ELF + * + * We want to disable SARA_WXP_MMAP when the file is missing + * the RELRO segment. + * We want to verbosely report this case only if the file + * was an ELF. + * + */ + if (i != 0) { + if (sara_wxp_flags & SARA_WXP_VERBOSE && + sara_wxp_flags & SARA_WXP_MMAP && + i == 1) + 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; + + /* + * Set the flags to be used for validation + * during the execve and discard SARA_WXP_MMAP: + * it doesn't make sense to prevent executable + * mmap during execve. + */ + current_new = prepare_creds(); + if (unlikely(current_new == NULL)) + return -ENOMEM; + get_sara_execve_flags(current_new) = sara_wxp_flags & ~SARA_WXP_MMAP; + commit_creds(current_new); + + return 0; +} + +#define sara_warn_or_return(err, msg) do { \ + if ((sara_wxp_flags & SARA_WXP_VERBOSE)) \ + pr_wxp(msg); \ + if (!(sara_wxp_flags & SARA_WXP_COMPLAIN)) \ + return -err; \ +} while (0) + +#define sara_warn_or_goto(label, msg) do { \ + if ((sara_wxp_flags & SARA_WXP_VERBOSE)) \ + pr_wxp(msg); \ + if (!(sara_wxp_flags & SARA_WXP_COMPLAIN)) \ + goto label; \ +} while (0) + +static int sara_check_vmflags(vm_flags_t vm_flags) +{ + u16 sara_wxp_flags; + + if (!sara_enabled || !wxprot_enabled) + return 0; + + /* + * Memory allocations done during an execve should be + * checked against the rules of the new executable, + * instead of those of the current one. + */ + if (current->in_execve) + sara_wxp_flags = get_current_sara_execve_flags(); + else + sara_wxp_flags = get_current_sara_wxp_flags(); + + /* + * Be quiet when using security_check_vmflags to decide + * what to do with a PT_GNU_STACK header + */ + if (current->in_execve && vm_flags == (VM_EXEC|VM_READ|VM_WRITE)) + sara_wxp_flags &= ~SARA_WXP_VERBOSE; + + /* + * If "W xor X" is active for the current thread + * this function must not allow new allocations that + * have both the VM_WRITE and the VM_EXEC flags. + */ + if (unlikely(sara_wxp_flags & SARA_WXP_WXORX && + vm_flags & VM_WRITE && + vm_flags & VM_EXEC)) + sara_warn_or_return(EPERM, "W^X"); + /* + * When the "MMAP" protection is on and shared libraries have + * been already loaded (i.e. get_current_sara_mmap_blocked + * returns true), this function must not allow: + * - new executable allocations + * - new non-executable allocations that may become + * executable bypassing the "MPROTECT" restriction; + * the "MPROTECT" protection will prevent a non-executable + * area to became executable only if it has the + * "VM_MAYWRITE" flag on. + */ + if (unlikely(sara_wxp_flags & SARA_WXP_MMAP && + (vm_flags & VM_EXEC || + (!(vm_flags & VM_MAYWRITE) && (vm_flags & VM_MAYEXEC))) && + get_current_sara_mmap_blocked())) + sara_warn_or_return(EPERM, "executable mmap"); + + return 0; +} + +static int sara_shm_shmat(struct kern_ipc_perm *shp, + char __user *shmaddr, + int shmflg) +{ + int block = 0; + u16 sara_wxp_flags; + char buf[TASK_COMM_LEN]; + + if (!sara_enabled || !wxprot_enabled) + return 0; + + sara_wxp_flags = get_current_sara_wxp_flags(); + + /* + * Allow executable mappings if and only if this shm + * was never attached as writable. + * + * Allow writable mappings if and only if this shm + * was never attached as executable. + * + * We don't need to handle the case in which this + * shm is attached as both writable and executable: + * sara_check_vmflags takes care of that. + */ + if (sara_wxp_flags & SARA_WXP_OTHER) { + if (shmflg & SHM_EXEC && shmflg & SHM_RDONLY) { + lock_sara_shm(shp); + if (unlikely(get_sara_shm_no_exec(shp))) + block = 1; + else + get_sara_shm_no_write(shp) = true; + unlock_sara_shm(shp); + } else if (!(shmflg & (SHM_EXEC | SHM_RDONLY))) { + lock_sara_shm(shp); + if (unlikely(get_sara_shm_no_write(shp))) + block = 2; + else + get_sara_shm_no_exec(shp) = true; + unlock_sara_shm(shp); + } + } + + if ((sara_wxp_flags & SARA_WXP_VERBOSE)) { + if (unlikely(block)) { + get_task_comm(buf, current); + if (block == 1) + pr_notice_ratelimited("WXP: executable SHM in '%s' (%d).\n", + buf, current->pid); + else if (block == 2) + pr_notice_ratelimited("WXP: writable SHM in '%s' (%d).\n", + buf, current->pid); + } + } + if (unlikely(block) && !(sara_wxp_flags & SARA_WXP_COMPLAIN)) + return -EACCES; + return 0; +} + +static int sara_file_mprotect(struct vm_area_struct *vma, + unsigned long reqprot, + unsigned long prot) +{ + u16 sara_wxp_flags; + + if (!sara_enabled || !wxprot_enabled) + return 0; + + if (current->in_execve) + sara_wxp_flags = get_current_sara_execve_flags(); + else + sara_wxp_flags = get_current_sara_wxp_flags(); + + /* + * vmas that may have been writable at some time in the past + * (i.e. have the VM_MAYWRITE flag on) shouldn't be allowed + * to be marked executable, unless they already are. + */ + if (unlikely(sara_wxp_flags & SARA_WXP_MPROTECT && + prot & PROT_EXEC && + !(vma->vm_flags & VM_EXEC) && + vma->vm_flags & VM_MAYWRITE)) { + /* + * If every MPROTECT flag is on and verbose reporting + * isn't needed, skip checking where the vma points to. + * Otherwise check if it points to a file mapping, + * to heap, to stack or to anywhere else. + */ + 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) + sara_warn_or_return(EACCES, + "mprotect on file mmap"); + } else if (vma->vm_start >= vma->vm_mm->start_brk && + vma->vm_end <= vma->vm_mm->brk) { + if (sara_wxp_flags & SARA_WXP_HEAP) + sara_warn_or_return(EACCES, + "mprotect on heap"); + } 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) + sara_warn_or_return(EACCES, + "mprotect on stack"); + } else if (sara_wxp_flags & SARA_WXP_OTHER) + sara_warn_or_return(EACCES, + "mprotect on anon mmap"); + } + + /* + * If "W xor X" is active for the current thread + * VM_EXEC and VM_WRITE can't be turned on at + * the same time, unless they already are. + */ + if (unlikely(sara_wxp_flags & SARA_WXP_WXORX && + prot & PROT_EXEC && + prot & PROT_WRITE && + (!(vma->vm_flags & VM_EXEC) || + !(vma->vm_flags & VM_WRITE)))) + sara_warn_or_return(EACCES, "W^X"); + + /* + * If the dynamic loader marks the "relro section" as + * read-only then it has finished loading shared libraries + * and, if the SARA_WXP_MMAP flag is on, new executable + * mmaps will be blocked from now on. + */ + if (unlikely(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[] __lsm_ro_after_init = { + LSM_HOOK_INIT(bprm_set_creds, sara_bprm_set_creds), + LSM_HOOK_INIT(check_vmflags, sara_check_vmflags), + LSM_HOOK_INIT(shm_shmat, sara_shm_shmat), + LSM_HOOK_INIT(file_mprotect, sara_file_mprotect), +}; + +static void config_free(struct wxprot_config_container *data) +{ + sara_dfa_free_tables(data->dfa); + kfree(data); +} + +static bool are_flags_valid_dfa(sara_dfa_output flags) +{ + return are_flags_valid((u16) flags); +} + +static int config_load(const char *buf, size_t buf_len) +{ + struct sara_dfa_tables *dfa = NULL; + struct wxprot_config_container *new; + int ret; + + dfa = sara_dfa_load(buf, buf_len, are_flags_valid_dfa); + if (unlikely(IS_ERR_OR_NULL(dfa))) { + if (IS_ERR(dfa)) + ret = PTR_ERR(dfa); + else + ret = -EINVAL; + goto error; + } + new = kmalloc(sizeof(*new), GFP_KERNEL); + if (unlikely(!new)) { + ret = -ENOMEM; + goto error_dfa; + } + kref_init(&new->refcount); + new->dfa = dfa; + SARA_CONFIG_REPLACE(wxprot_config, + new, + config_free, + &wxprot_config_lock); + pr_notice("WXP: new rules loaded.\n"); + return 0; +error_dfa: + sara_dfa_free_tables(dfa); +error: + pr_notice("WXP: failed to load rules.\n"); + return ret; +} + +static ssize_t config_dump(char **buf) +{ + ssize_t ret; + struct wxprot_config_container *c; + + SARA_CONFIG_GET(c, wxprot_config); + ret = sara_dfa_dump(c->dfa, buf); + 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->dfa->hash), GFP_KERNEL); + if (unlikely(*buf == NULL)) + goto out; + + SARA_CONFIG_GET_RCU(config, wxprot_config); + memcpy(*buf, config->dfa->hash, sizeof(config->dfa->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 __lsm_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 = NULL; + + 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; + kref_init(&tmpc->refcount); + tmpc->dfa = sara_dfa_make_null(); + if (unlikely(!tmpc->dfa)) + goto out_fail; + 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_dfa; + security_add_hooks(wxprot_hooks, ARRAY_SIZE(wxprot_hooks), "sara"); + return 0; + +out_fail_dfa: + sara_dfa_free_tables(tmpc->dfa); +out_fail: + kfree(tmpc); + return ret; +} + +#endif /* CONFIG_SECURITY_SARA_WXPROT */ From patchwork Sat Jul 6 10:54:48 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Salvatore Mesoraca X-Patchwork-Id: 11033623 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 6C83213BD for ; Sat, 6 Jul 2019 10:55:54 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5EC1E28994 for ; Sat, 6 Jul 2019 10:55:54 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 5316F28A27; Sat, 6 Jul 2019 10:55:54 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id BDF2F28994 for ; Sat, 6 Jul 2019 10:55:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726116AbfGFKzt (ORCPT ); Sat, 6 Jul 2019 06:55:49 -0400 Received: from mail-wm1-f65.google.com ([209.85.128.65]:50411 "EHLO mail-wm1-f65.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725990AbfGFKzW (ORCPT ); Sat, 6 Jul 2019 06:55:22 -0400 Received: by mail-wm1-f65.google.com with SMTP id v15so1040441wml.0; Sat, 06 Jul 2019 03:55:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=HSNnsoi40sXC9a5NBIzV0J+z/SUw5gqWPuXVFawRMYc=; b=V7AvK11reWJTiJ1DMourV1U4KNoutKJGDoiTGDXXM3R+PR5LpFynIPMuHGWbUmIT6N 62QOnfL7Spg5I7UppXlkG9bH566FlMkdYX2c5mITv+X49REEJ7TzTRD5C+iQfoN/4rPl UgKMTPNUamuI84pgZfGnM+KdecKNvQnLCdW9YiA5ieNyU+3CJjFBApwua+MKXWYTWR3W cDjDfWnzPiKTGrdUhnTZwNc+HFKvMUxWxCZ7CxTIxdI37rHw++WBf8TzZNY7DbiPJbPx Vi4i+Oh39vDEODfCMLRruFPTteb2P7fgj4MRU+BMXpmRVkxdZYPg4w23p2aEvnIp8KBu br5g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=HSNnsoi40sXC9a5NBIzV0J+z/SUw5gqWPuXVFawRMYc=; b=alHoR7aS+a2OjMWB1mlH8/CwwhHX+kw8FGpma7M+jkEbLTPHfpq9FtuWOhreRVRwld 19z5cesZDMsKeYcWZO+HS8DQodGEaX0IvOmeVhESHxcizaVYjpM6rSOdZJQkghAbcHZB Nz1m3HqIeVTEs+h4/ZWOpgq57bTO1KvZ5toDCHoPY1M1hpv7TNgTkR2D2koVjzFAj5Ty s1OjPu/rfLN2KurWDFokrSZUmae9OJS1CLDaUsZPBAjLgONqaIp/7TBh/fWUvwwijiqg VdW5kNDGzwNwA0fvkadwDOoo5qI1c2bU+GDT1Ux84kihPyHwOqMspJeQm49GMG5P6nvG Zh4g== X-Gm-Message-State: APjAAAXs6SyAPCat3RxKXA+ObWfivpGPVGDL263AG6+FXQ9wG/BkC/cu sMv7Cs8aJxS8OxJEl9bzQbwQEL79o6RcmQ== X-Google-Smtp-Source: APXvYqxTOz8ZdoRBiRrROUWhnI6SyewPlaUzoSCGW9GiCDm2WtNdoe4eOEEH3/a8B3ek3jvZ4FdMSg== X-Received: by 2002:a7b:cbc6:: with SMTP id n6mr8125351wmi.14.1562410519789; Sat, 06 Jul 2019 03:55:19 -0700 (PDT) Received: from localhost (net-93-71-3-102.cust.vodafonedsl.it. [93.71.3.102]) by smtp.gmail.com with ESMTPSA id h11sm12578794wrx.93.2019.07.06.03.55.18 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sat, 06 Jul 2019 03:55:18 -0700 (PDT) From: Salvatore Mesoraca To: linux-kernel@vger.kernel.org Cc: kernel-hardening@lists.openwall.com, linux-mm@kvack.org, linux-security-module@vger.kernel.org, Alexander Viro , Brad Spengler , Casey Schaufler , Christoph Hellwig , James Morris , Jann Horn , Kees Cook , PaX Team , Salvatore Mesoraca , "Serge E. Hallyn" , Thomas Gleixner Subject: [PATCH v5 07/12] LSM: creation of "pagefault_handler" LSM hook Date: Sat, 6 Jul 2019 12:54:48 +0200 Message-Id: <1562410493-8661-8-git-send-email-s.mesoraca16@gmail.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1562410493-8661-1-git-send-email-s.mesoraca16@gmail.com> References: <1562410493-8661-1-git-send-email-s.mesoraca16@gmail.com> Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: X-Virus-Scanned: ClamAV using ClamSMTP Creation of a new hook to let LSM modules handle user-space pagefaults on x86. It can be used to avoid segfaulting the originating process. If it's the case it can modify process registers before returning. This is not a security feature by itself, it's a way to soften some unwanted side-effects of restrictive security features. In particular this is used by S.A.R.A. to implement what PaX call "trampoline emulation" that, in practice, allows for some specific code sequences to be executed even if they are in non executable memory. This may look like a bad thing at first, but you have to consider that: - This allows for strict memory restrictions (e.g. W^X) to stay on even when they should be turned off. And, even if this emulation makes those features less effective, it's still better than having them turned off completely. - The only code sequences emulated are trampolines used to make function calls. In many cases, when you have the chance to make arbitrary memory writes, you can already manipulate the control flow of the program by overwriting function pointers or return values. So, in many cases, "trampoline emulation" doesn't introduce new exploit vectors. - It's a feature that can be turned on only if needed, on a per executable file basis. Signed-off-by: Salvatore Mesoraca --- arch/Kconfig | 6 ++++++ arch/x86/Kconfig | 1 + arch/x86/mm/fault.c | 6 ++++++ include/linux/lsm_hooks.h | 12 ++++++++++++ include/linux/security.h | 11 +++++++++++ security/security.c | 11 +++++++++++ 6 files changed, 47 insertions(+) diff --git a/arch/Kconfig b/arch/Kconfig index c47b328..16997c3 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -252,6 +252,12 @@ config ARCH_HAS_FORTIFY_SOURCE config ARCH_HAS_KEEPINITRD bool +config ARCH_HAS_LSM_PAGEFAULT + bool + help + An architecture should select this if it supports + "pagefault_handler" LSM hook. + # Select if arch has all set_memory_ro/rw/x/nx() functions in asm/cacheflush.h config ARCH_HAS_SET_MEMORY bool diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 2bbbd4d..a3c7660 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -67,6 +67,7 @@ config X86 select ARCH_HAS_FORTIFY_SOURCE select ARCH_HAS_GCOV_PROFILE_ALL select ARCH_HAS_KCOV if X86_64 + select ARCH_HAS_LSM_PAGEFAULT select ARCH_HAS_MEMBARRIER_SYNC_CORE select ARCH_HAS_PMEM_API if X86_64 select ARCH_HAS_PTE_SPECIAL diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index 46df4c6..7fe36f1 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c @@ -18,6 +18,7 @@ #include /* faulthandler_disabled() */ #include /* efi_recover_from_page_fault()*/ #include +#include /* security_pagefault_handler */ #include /* boot_cpu_has, ... */ #include /* dotraplinkage, ... */ @@ -1360,6 +1361,11 @@ void do_user_addr_fault(struct pt_regs *regs, local_irq_enable(); } + if (unlikely(security_pagefault_handler(regs, + hw_error_code, + address))) + return; + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address); if (hw_error_code & X86_PF_WRITE) diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 12ce609..478a187 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -518,6 +518,14 @@ * @vmflags contains the requested vmflags. * Return 0 if the operation is allowed to continue otherwise return * the appropriate error code. + * @pagefault_handler: + * Handle pagefaults on supported architectures, that is any architecture + * which defines CONFIG_ARCH_HAS_LSM_PAGEFAULT. + * @regs contains process' registers. + * @error_code contains error code for the pagefault. + * @address contains the address that caused the pagefault. + * Return 0 to let the kernel handle the pagefault as usually, any other + * value to let the process continue its execution. * @file_lock: * Check permission before performing file locking operations. * Note the hook mediates both flock and fcntl style locks. @@ -1603,6 +1611,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)(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); @@ -1904,6 +1915,7 @@ struct security_hook_heads { struct hlist_head mmap_file; struct hlist_head file_mprotect; struct hlist_head check_vmflags; + struct hlist_head pagefault_handler; struct hlist_head file_lock; struct hlist_head file_fcntl; struct hlist_head file_set_fowner; diff --git a/include/linux/security.h b/include/linux/security.h index aed78eb..c287eb2 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -313,6 +313,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(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); @@ -865,6 +868,14 @@ static inline int security_check_vmflags(vm_flags_t vmflags) return 0; } +static inline int __maybe_unused security_pagefault_handler( + 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 3308e89..a8bdcf3 100644 --- a/security/security.c +++ b/security/security.c @@ -1426,6 +1426,17 @@ int security_check_vmflags(vm_flags_t vmflags) return call_int_hook(check_vmflags, 0, vmflags); } +int __maybe_unused security_pagefault_handler(struct pt_regs *regs, + unsigned long error_code, + unsigned long address) +{ + return call_int_hook(pagefault_handler, + 0, + regs, + error_code, + address); +} + int security_file_lock(struct file *file, unsigned int cmd) { return call_int_hook(file_lock, 0, file, cmd); From patchwork Sat Jul 6 10:54:49 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Salvatore Mesoraca X-Patchwork-Id: 11033617 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id DC71414F6 for ; Sat, 6 Jul 2019 10:55:46 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id CD9D328994 for ; Sat, 6 Jul 2019 10:55:46 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id C13AC28A27; Sat, 6 Jul 2019 10:55:46 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,GAPPY_SUBJECT,RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id B1D7E28994 for ; Sat, 6 Jul 2019 10:55:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727050AbfGFKzZ (ORCPT ); Sat, 6 Jul 2019 06:55:25 -0400 Received: from mail-wm1-f66.google.com ([209.85.128.66]:36152 "EHLO mail-wm1-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726907AbfGFKzY (ORCPT ); Sat, 6 Jul 2019 06:55:24 -0400 Received: by mail-wm1-f66.google.com with SMTP id g67so5769648wme.1; Sat, 06 Jul 2019 03:55:21 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=GuxALAFQ/TNai2yWaUBZYrBAIJmgHr1PeMz07TcuHEk=; b=nXuWwFeaFB10NA4V6lB4djfCG4L03Q6YVTn+kObhLt+JnC9zkbzXGcCXqxJ+khdjo0 uwJBElmo7sJB/OZhWNONnRz0FNfsGlRBcU13eybEa3in1yWNnQhM2ZVWqPKflMf8G9q8 yJqcXaTPODZ09JeyF2MJJbCLKwcqRKf9n0m5xpF62VdZNkdS7DTRXY1KsNuFyyu6eWg+ lTH/lyd0NHCcCvuVNS69Bl8/tPzx//CMr/uVPkJbMAvW2/fasALxnJQ69pGmUAuxZX9M Iso4mDoYZ17y8anniEhUvdE3SAAb8m0iakYlkfo5QCYApCOkWwUB3c3re0pm1oEW0nq7 Q8Yg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=GuxALAFQ/TNai2yWaUBZYrBAIJmgHr1PeMz07TcuHEk=; b=Cj4l14YMKEtEZcw1ewx6eH1Mh0eZwL4tPuAcZ5Yi5mX4K1p59ncAq0Zau8oSBTXPml NHZJTWuvo9sRZ5+J4pVp5w3h9uvlucLBMb/yodcJL6ZrB8jlKxsy/L0gitI9mvmmyC/7 +Estc3+tRcNQJwKUFYlpH3nc6TdQ+urDXGJ54qmpMossRtZiTmu9VK+AjKOEpPx/tvEc 4HTVxHhP2l7d9DBtzIbbWW1sinjwNfwo1Du4Am4jENru1jQZuNB+kPHknbuJPBGsgUBT sEvL1FW/9Dko+gGRW8bSvuqqsko/zG/QsWDe6O3gW2JWSH0hMK5jkqo5f56/3pEpXkTk l+Sg== X-Gm-Message-State: APjAAAXmsM2VxY25XSTNZ6oTo6Crt3LssJ1qs99u4Mp/aoo0PH33Ehce vFR+Mw67qSV1c87SXuVElDarMFewv9iDAw== X-Google-Smtp-Source: APXvYqy1+ynai1OSOQkeLSv5Es/MREYsXma7ZlHF4OC5iwOUmuCSj70NPI+2aNwlq+0KMTWYYdT8cw== X-Received: by 2002:a1c:c5c2:: with SMTP id v185mr4017391wmf.161.1562410520881; Sat, 06 Jul 2019 03:55:20 -0700 (PDT) Received: from localhost (net-93-71-3-102.cust.vodafonedsl.it. [93.71.3.102]) by smtp.gmail.com with ESMTPSA id h11sm12578794wrx.93.2019.07.06.03.55.19 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sat, 06 Jul 2019 03:55:20 -0700 (PDT) From: Salvatore Mesoraca To: linux-kernel@vger.kernel.org Cc: kernel-hardening@lists.openwall.com, linux-mm@kvack.org, linux-security-module@vger.kernel.org, Alexander Viro , Brad Spengler , Casey Schaufler , Christoph Hellwig , James Morris , Jann Horn , Kees Cook , PaX Team , Salvatore Mesoraca , "Serge E. Hallyn" , Thomas Gleixner Subject: [PATCH v5 08/12] S.A.R.A.: trampoline emulation Date: Sat, 6 Jul 2019 12:54:49 +0200 Message-Id: <1562410493-8661-9-git-send-email-s.mesoraca16@gmail.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1562410493-8661-1-git-send-email-s.mesoraca16@gmail.com> References: <1562410493-8661-1-git-send-email-s.mesoraca16@gmail.com> Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: X-Virus-Scanned: ClamAV using ClamSMTP 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. Trampoline emulation is modified from Brad Spengler/PaX Team's code in the last public patch of grsecurity/PaX based on my understanding of the code. Changes or omissions from the original code are mine and don't reflect the original grsecurity/PaX code. Signed-off-by: Salvatore Mesoraca --- arch/x86/Kbuild | 2 + arch/x86/security/Makefile | 2 + arch/x86/security/sara/Makefile | 1 + arch/x86/security/sara/emutramp.c | 57 ++++++++++++ arch/x86/security/sara/trampolines32.h | 137 +++++++++++++++++++++++++++ arch/x86/security/sara/trampolines64.h | 164 +++++++++++++++++++++++++++++++++ security/sara/Kconfig | 18 ++++ security/sara/include/emutramp.h | 35 +++++++ security/sara/wxprot.c | 29 ++++++ 9 files changed, 445 insertions(+) create mode 100644 arch/x86/security/Makefile create mode 100644 arch/x86/security/sara/Makefile create mode 100644 arch/x86/security/sara/emutramp.c create mode 100644 arch/x86/security/sara/trampolines32.h create mode 100644 arch/x86/security/sara/trampolines64.h create mode 100644 security/sara/include/emutramp.h diff --git a/arch/x86/Kbuild b/arch/x86/Kbuild index 30dec01..4fea778 100644 --- a/arch/x86/Kbuild +++ b/arch/x86/Kbuild @@ -25,3 +25,5 @@ obj-y += platform/ obj-y += net/ obj-$(CONFIG_KEXEC_FILE) += purgatory/ + +obj-y += security/ diff --git a/arch/x86/security/Makefile b/arch/x86/security/Makefile new file mode 100644 index 0000000..ba4be4c --- /dev/null +++ b/arch/x86/security/Makefile @@ -0,0 +1,2 @@ +subdir-$(CONFIG_SECURITY_SARA) += sara +obj-$(CONFIG_SECURITY_SARA) += sara/ diff --git a/arch/x86/security/sara/Makefile b/arch/x86/security/sara/Makefile new file mode 100644 index 0000000..a4a76217 --- /dev/null +++ b/arch/x86/security/sara/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_SECURITY_SARA_WXPROT_EMUTRAMP) := emutramp.o diff --git a/arch/x86/security/sara/emutramp.c b/arch/x86/security/sara/emutramp.c new file mode 100644 index 0000000..45122e5 --- /dev/null +++ b/arch/x86/security/sara/emutramp.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * S.A.R.A. Linux Security Module + * + * Copyright (C) 2017 Salvatore Mesoraca + * + * 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 + * Being just hexadecimal constants, they are not subject to + * any copyright. + * + */ + +#define PF_PROT (1 << 0) +#define PF_USER (1 << 2) +#define PF_INSTR (1 << 4) + +#ifdef CONFIG_X86_32 + +#include "trampolines32.h" +static inline int trampoline_emulator(struct pt_regs *regs, + unsigned long address) +{ + return sara_trampoline_emulator_x86_32(regs); +} + +#else /* CONFIG_X86_32 */ + +#include "trampolines64.h" +static inline int trampoline_emulator(struct pt_regs *regs, + unsigned long address) +{ + return sara_trampoline_emulator_x86_64(regs, address); +} + +#endif /* CONFIG_X86_32 */ + + +int sara_trampoline_emulator(struct pt_regs *regs, + unsigned long error_code, + unsigned long address) +{ + if (!(error_code & PF_USER) || + !(error_code & PF_INSTR) || + !(error_code & PF_PROT)) + return 0; + + local_irq_enable(); + might_sleep(); + might_fault(); + return trampoline_emulator(regs, address); +} diff --git a/arch/x86/security/sara/trampolines32.h b/arch/x86/security/sara/trampolines32.h new file mode 100644 index 0000000..b3622d0 --- /dev/null +++ b/arch/x86/security/sara/trampolines32.h @@ -0,0 +1,137 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * S.A.R.A. Linux Security Module + * + * Copyright (C) 2017 Salvatore Mesoraca + * + * 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 + * Being just hexadecimal constants, they are not subject to + * any copyright. + * + */ + +#ifndef __SARA_TRAMPOLINES32_H +#define __SARA_TRAMPOLINES32_H + +#include +#include + +struct libffi_trampoline_x86_32 { + unsigned char mov; + unsigned int addr1; + unsigned char jmp; + unsigned int addr2; +} __packed; + +struct gcc_trampoline_x86_32_t1 { + unsigned char mov1; + unsigned int addr1; + unsigned char mov2; + unsigned int addr2; + unsigned short jmp; +} __packed; + +struct gcc_trampoline_x86_32_t2 { + 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_t1 g1; + struct gcc_trampoline_x86_32_t2 g2; +}; + +static inline int is_libffi_tramp_x86_32(const union trampolines_x86_32 *u) +{ + return (u->lf.mov == 0xB8 && u->lf.jmp == 0xE9); +} + +static inline void emu_libffi_tramp_x86_32(const union trampolines_x86_32 *u, + struct pt_regs *regs) +{ + regs->ax = u->lf.addr1; + regs->ip = (unsigned int) (regs->ip + + u->lf.addr2 + + sizeof(u->lf)); +} + +static inline int is_gcc_tramp_x86_32_t1(const union trampolines_x86_32 *u, + const struct pt_regs *regs) +{ + return (u->g1.mov1 == 0xB9 && + u->g1.mov2 == 0xB8 && + u->g1.jmp == 0xE0FF && + regs->ip > regs->sp); +} + +static inline void emu_gcc_tramp_x86_32_t1(const union trampolines_x86_32 *u, + struct pt_regs *regs) +{ + regs->cx = u->g1.addr1; + regs->ax = u->g1.addr2; + regs->ip = u->g1.addr2; +} + +static inline int is_gcc_tramp_x86_32_t2(const union trampolines_x86_32 *u, + const struct pt_regs *regs) +{ + return (u->g2.mov == 0xB9 && + u->g2.jmp == 0xE9 && + regs->ip > regs->sp); +} + +static inline void emu_gcc_tramp_x86_32_t2(const union trampolines_x86_32 *u, + struct pt_regs *regs) +{ + regs->cx = u->g2.addr1; + regs->ip = (unsigned int) (regs->ip + + u->g2.addr2 + + sizeof(u->g2)); +} + +static inline int sara_trampoline_emulator_x86_32(struct pt_regs *regs) +{ + int ret; + void __user *ip = (void __user *) regs->ip; + union trampolines_x86_32 t; //zero init + + 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_gcc_tramp_x86_32_t1(&t, regs)) { + pr_debug("Trampoline: gcc1 x86_32.\n"); + emu_gcc_tramp_x86_32_t1(&t, regs); + return 1; + } else if (is_libffi_tramp_x86_32(&t)) { + pr_debug("Trampoline: libffi x86_32.\n"); + emu_libffi_tramp_x86_32(&t, regs); + return 1; + } else if (is_gcc_tramp_x86_32_t2(&t, regs)) { + pr_debug("Trampoline: gcc2 x86_32.\n"); + emu_gcc_tramp_x86_32_t2(&t, regs); + return 1; + } + + pr_debug("Not a trampoline (x86_32).\n"); + + return 0; +} + +#endif /* __SARA_TRAMPOLINES32_H */ diff --git a/arch/x86/security/sara/trampolines64.h b/arch/x86/security/sara/trampolines64.h new file mode 100644 index 0000000..c9aaa03 --- /dev/null +++ b/arch/x86/security/sara/trampolines64.h @@ -0,0 +1,164 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * S.A.R.A. Linux Security Module + * + * Copyright (C) 2017 Salvatore Mesoraca + * + * 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 + * Being just hexadecimal constants, they are not subject to + * any copyright. + * + */ + +#ifndef __SARA_TRAMPOLINES64_H +#define __SARA_TRAMPOLINES64_H + +#include +#include + +#include "trampolines32.h" + +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; +}; + +static inline int is_libffi_tramp_x86_64(const union trampolines_x86_64 *u) +{ + return (u->lf.mov1 == 0xBB49 && + u->lf.mov2 == 0xBA49 && + (u->lf.stcclc == 0xF8 || + u->lf.stcclc == 0xF9) && + u->lf.jmp1 == 0xFF49 && + u->lf.jmp2 == 0xE3); +} + +static inline void emu_libffi_tramp_x86_64(const union trampolines_x86_64 *u, + struct pt_regs *regs) +{ + regs->r11 = u->lf.addr1; + regs->r10 = u->lf.addr2; + regs->ip = u->lf.addr1; + if (u->lf.stcclc == 0xF8) + regs->flags &= ~X86_EFLAGS_CF; + else + regs->flags |= X86_EFLAGS_CF; +} + +static inline int is_gcc_tramp_x86_64_t1(const union trampolines_x86_64 *u, + const struct pt_regs *regs) +{ + return (u->g1.mov1 == 0xBB49 && + u->g1.mov2 == 0xBA49 && + u->g1.jmp1 == 0xFF49 && + u->g1.jmp2 == 0xE3 && + regs->ip > regs->sp); +} + +static inline void emu_gcc_tramp_x86_64_t1(const union trampolines_x86_64 *u, + struct pt_regs *regs) +{ + regs->r11 = u->g1.addr1; + regs->r10 = u->g1.addr2; + regs->ip = u->g1.addr1; +} + +static inline int is_gcc_tramp_x86_64_t2(const union trampolines_x86_64 *u, + const struct pt_regs *regs) +{ + return (u->g2.mov1 == 0xBB41 && + u->g2.mov2 == 0xBA49 && + u->g2.jmp1 == 0xFF49 && + u->g2.jmp2 == 0xE3 && + regs->ip > regs->sp); +} + +static inline void emu_gcc_tramp_x86_64_t2(const union trampolines_x86_64 *u, + struct pt_regs *regs) +{ + regs->r11 = u->g2.addr1; + regs->r10 = u->g2.addr2; + regs->ip = u->g2.addr1; +} + +static inline int sara_trampoline_emulator_x86_64(struct pt_regs *regs, + unsigned long address) +{ + 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)); + + if (regs->cs == __USER32_CS || + regs->cs & (1<<2)) { + if (address >> 32) /* K8 erratum #100 */ + return 0; + return sara_trampoline_emulator_x86_32(regs); + } + + 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_libffi_tramp_x86_64(&t)) { + pr_debug("Trampoline: libffi x86_64.\n"); + emu_libffi_tramp_x86_64(&t, regs); + return 1; + } else if (is_gcc_tramp_x86_64_t1(&t, regs)) { + pr_debug("Trampoline: gcc1 x86_64.\n"); + emu_gcc_tramp_x86_64_t1(&t, regs); + return 1; + } else if (is_gcc_tramp_x86_64_t2(&t, regs)) { + pr_debug("Trampoline: gcc2 x86_64.\n"); + emu_gcc_tramp_x86_64_t2(&t, regs); + return 1; + } + + pr_debug("Not a trampoline (x86_64).\n"); + + return 0; + +} + +#endif /* __SARA_TRAMPOLINES64_H */ diff --git a/security/sara/Kconfig b/security/sara/Kconfig index 54a96e0..458e0e8 100644 --- a/security/sara/Kconfig +++ b/security/sara/Kconfig @@ -117,6 +117,24 @@ choice Documentation/admin-guide/LSM/SARA.rst. endchoice +config SECURITY_SARA_WXPROT_EMUTRAMP + bool "Enable emulation for some types of trampolines" + depends on SECURITY_SARA_WXPROT + depends on ARCH_HAS_LSM_PAGEFAULT + 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/admin-guide/LSM/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/emutramp.h b/security/sara/include/emutramp.h new file mode 100644 index 0000000..d82f92d --- /dev/null +++ b/security/sara/include/emutramp.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * S.A.R.A. Linux Security Module + * + * Copyright (C) 2017 Salvatore Mesoraca + * + * 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 + * Being just hexadecimal constants, they are not subject to + * any copyright. + * + */ + +#ifndef __SARA_EMUTRAMP_H +#define __SARA_EMUTRAMP_H + +#ifdef CONFIG_SECURITY_SARA_WXPROT_EMUTRAMP +int sara_trampoline_emulator(struct pt_regs *regs, + unsigned long error_code, + unsigned long address); +#else +inline int sara_trampoline_emulator(struct pt_regs *regs, + unsigned long error_code, + unsigned long address) +{ + return 0; +} +#endif /* CONFIG_SECURITY_SARA_WXPROT_EMUTRAMP */ + +#endif /* __SARA_EMUTRAMP_H */ diff --git a/security/sara/wxprot.c b/security/sara/wxprot.c index 8a3d002..9c42bfc 100644 --- a/security/sara/wxprot.c +++ b/security/sara/wxprot.c @@ -31,6 +31,7 @@ #include "include/utils.h" #include "include/securityfs.h" #include "include/wxprot.h" +#include "include/emutramp.h" #define SARA_WXPROT_CONFIG_VERSION 0 @@ -41,6 +42,7 @@ #define SARA_WXP_COMPLAIN 0x0010 #define SARA_WXP_VERBOSE 0x0020 #define SARA_WXP_MMAP 0x0040 +#define SARA_WXP_EMUTRAMP 0x0100 #define SARA_WXP_TRANSFER 0x0200 #define SARA_WXP_NONE 0x0000 #define SARA_WXP_MPROTECT (SARA_WXP_HEAP | \ @@ -51,7 +53,12 @@ SARA_WXP_WXORX | \ SARA_WXP_COMPLAIN | \ SARA_WXP_VERBOSE) +#ifdef CONFIG_SECURITY_SARA_WXPROT_EMUTRAMP +#define SARA_WXP_ALL (__SARA_WXP_ALL | \ + SARA_WXP_EMUTRAMP) +#else /* CONFIG_SECURITY_SARA_WXPROT_EMUTRAMP */ #define SARA_WXP_ALL __SARA_WXP_ALL +#endif /* CONFIG_SECURITY_SARA_WXPROT_EMUTRAMP */ struct wxprot_config_container { struct sara_dfa_tables *dfa; @@ -67,7 +74,11 @@ struct wxprot_config_container { static u16 default_flags __lsm_ro_after_init = CONFIG_SECURITY_SARA_WXPROT_DEFAULT_FLAGS; +#ifdef CONFIG_SECURITY_SARA_WXPROT_EMUTRAMP +static const bool wxprot_emutramp = true; +#else static const bool wxprot_emutramp; +#endif static void pr_wxp(char *msg) { @@ -110,6 +121,9 @@ static bool are_flags_valid(u16 flags) if (unlikely(flags & SARA_WXP_MMAP && !(flags & SARA_WXP_OTHER))) return false; + if (unlikely(flags & SARA_WXP_EMUTRAMP && + ((flags & SARA_WXP_MPROTECT) != SARA_WXP_MPROTECT))) + return false; return true; } @@ -514,11 +528,26 @@ static int sara_file_mprotect(struct vm_area_struct *vma, return 0; } +#ifdef CONFIG_SECURITY_SARA_WXPROT_EMUTRAMP +static int sara_pagefault_handler(struct pt_regs *regs, + unsigned long error_code, + unsigned long address) +{ + if (!sara_enabled || !wxprot_enabled || + likely(!(get_current_sara_wxp_flags() & SARA_WXP_EMUTRAMP))) + return 0; + return sara_trampoline_emulator(regs, error_code, address); +} +#endif + static struct security_hook_list wxprot_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(bprm_set_creds, sara_bprm_set_creds), LSM_HOOK_INIT(check_vmflags, sara_check_vmflags), LSM_HOOK_INIT(shm_shmat, sara_shm_shmat), LSM_HOOK_INIT(file_mprotect, sara_file_mprotect), +#ifdef CONFIG_SECURITY_SARA_WXPROT_EMUTRAMP + LSM_HOOK_INIT(pagefault_handler, sara_pagefault_handler), +#endif }; static void config_free(struct wxprot_config_container *data) From patchwork Sat Jul 6 10:54:50 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Salvatore Mesoraca X-Patchwork-Id: 11033595 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 34B7513BD for ; Sat, 6 Jul 2019 10:55:27 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 23AD92881B for ; Sat, 6 Jul 2019 10:55:27 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 176E228994; Sat, 6 Jul 2019 10:55:27 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,GAPPY_SUBJECT,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 6579A2881B for ; Sat, 6 Jul 2019 10:55:26 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727063AbfGFKzZ (ORCPT ); Sat, 6 Jul 2019 06:55:25 -0400 Received: from mail-wr1-f66.google.com ([209.85.221.66]:34918 "EHLO mail-wr1-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726973AbfGFKzY (ORCPT ); Sat, 6 Jul 2019 06:55:24 -0400 Received: by mail-wr1-f66.google.com with SMTP id y4so3639794wrm.2; Sat, 06 Jul 2019 03:55:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=2o5CRslS3uRS8RVi/y7cIRyfxfb3HvwyRL/v1ZeEIno=; b=rPg5B9TqS7krvMJJliXmsCRezH5NtE0qwL7MshANq/BMIQk+fLCGGDvdN0TqTFS/Hg 6+JesQatpm70HrH1/sKDHhJW5NuJ9lbPo/Pt3diMYQHeYiUTvped3PnPQZHI+4YJ0jN8 9/g0eLOYr5OMGYidjZj/2dAXPCxNHpShNsdocDH/i6xFouRHR8c8cHVZsIJgLYUcb0DD FQpwJKoiJ419jrwGoHRUHCLQTGHHWLdYLwHvsLFGedgjUlQUYEHsbPU+I9QqdvMXnfZ9 v9/8/wdff2V5mB4KBlxcXIQv7e6lcDopq/KTB5+TyDiIp+3dMLe+RN3aV4nQOiuYU/ye mN5g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=2o5CRslS3uRS8RVi/y7cIRyfxfb3HvwyRL/v1ZeEIno=; b=GhAHh4+6TBt+mpjLravDWR5lyYQfYlnRmUAF1MnFiS5cnspXl0JNM2Sg6OxV+7VWCf +lBpfq7skAKIUawjIbn/tkm7uTHvZ3GWCf9MFRyRKAzctF51bWvQm7bFa4yE1qEYcKSo VkLcrIytf7FblNZCs3+QsA+8gb3Swwj+SrrEUPaf3/Ycow+MTn/T3vsTCeMr/0LWaEyb pQMvb0+ACWWNl/vT89KHzgcVbbqKdD/0LYiU8XkAm8QB6GD9V68Edoh2T1Bw26hA8k9s 4PQ/qYMeqGPG22FUq9PNs3j3nDwfD7Q8+6kZ+ljo34Eusq4b6j5ly+eEx9iqMe0ze/nr rQlQ== X-Gm-Message-State: APjAAAXxjZ+EEkH1+naKZT8qF9MdnYB2UQGZe3VgU+WavvCG1xUgw4Kv 3Vip+r//kne0cLCHpmByfVty5OaLaHUfnQ== X-Google-Smtp-Source: APXvYqztD1m1u9aJTdBW5y6FhM7gZEDkT1ZI7+xUeEaha6acs1qD6E71wBnezSnp1EbFzIl12Fdq9g== X-Received: by 2002:a5d:42c5:: with SMTP id t5mr8328348wrr.5.1562410521889; Sat, 06 Jul 2019 03:55:21 -0700 (PDT) Received: from localhost (net-93-71-3-102.cust.vodafonedsl.it. [93.71.3.102]) by smtp.gmail.com with ESMTPSA id h11sm12578794wrx.93.2019.07.06.03.55.20 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sat, 06 Jul 2019 03:55:21 -0700 (PDT) From: Salvatore Mesoraca To: linux-kernel@vger.kernel.org Cc: kernel-hardening@lists.openwall.com, linux-mm@kvack.org, linux-security-module@vger.kernel.org, Alexander Viro , Brad Spengler , Casey Schaufler , Christoph Hellwig , James Morris , Jann Horn , Kees Cook , PaX Team , Salvatore Mesoraca , "Serge E. Hallyn" , Thomas Gleixner Subject: [PATCH v5 09/12] S.A.R.A.: WX protection procattr interface Date: Sat, 6 Jul 2019 12:54:50 +0200 Message-Id: <1562410493-8661-10-git-send-email-s.mesoraca16@gmail.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1562410493-8661-1-git-send-email-s.mesoraca16@gmail.com> References: <1562410493-8661-1-git-send-email-s.mesoraca16@gmail.com> Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: X-Virus-Scanned: ClamAV using ClamSMTP This allow threads to get current WX Protection flags for themselves or for other threads (if they have CAP_MAC_ADMIN). It also allow a thread to set itself flags to a stricter set of rules than the current one. Via a new wxprot flag (SARA_WXP_FORCE_WXORX) is it possible to ask the kernel to rescan the memory and remove the VM_WRITE flag from any area that is marked both writable and executable. Protections that prevent the runtime creation of executable code can be troublesome for all those programs that actually need to do it e.g. programs shipping with a JIT compiler built-in. This feature can be use to run the JIT compiler with few restrictions while enforcing full WX Protection in the rest of the program. To simplify access to this interface a CC0 licensed library is available here: https://github.com/smeso/libsara Signed-off-by: Salvatore Mesoraca --- fs/proc/base.c | 11 ++++ security/sara/wxprot.c | 150 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+) diff --git a/fs/proc/base.c b/fs/proc/base.c index 255f675..7873d27 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -2612,6 +2612,13 @@ static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf, LSM_DIR_OPS(smack); #endif +#ifdef CONFIG_SECURITY_SARA +static const struct pid_entry sara_attr_dir_stuff[] = { + ATTR("sara", "wxprot", 0666), +}; +LSM_DIR_OPS(sara); +#endif + static const struct pid_entry attr_dir_stuff[] = { ATTR(NULL, "current", 0666), ATTR(NULL, "prev", 0444), @@ -2623,6 +2630,10 @@ static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf, DIR("smack", 0555, proc_smack_attr_dir_inode_ops, proc_smack_attr_dir_ops), #endif +#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/sara/wxprot.c b/security/sara/wxprot.c index 9c42bfc..84f7b1e 100644 --- a/security/sara/wxprot.c +++ b/security/sara/wxprot.c @@ -14,6 +14,7 @@ #ifdef CONFIG_SECURITY_SARA_WXPROT #include +#include #include #include #include @@ -42,6 +43,7 @@ #define SARA_WXP_COMPLAIN 0x0010 #define SARA_WXP_VERBOSE 0x0020 #define SARA_WXP_MMAP 0x0040 +#define SARA_WXP_FORCE_WXORX 0x0080 #define SARA_WXP_EMUTRAMP 0x0100 #define SARA_WXP_TRANSFER 0x0200 #define SARA_WXP_NONE 0x0000 @@ -540,6 +542,152 @@ static int sara_pagefault_handler(struct pt_regs *regs, } #endif +static int sara_getprocattr(struct task_struct *p, char *name, char **value) +{ + int ret; + u16 flags; + char *buf; + + ret = -EINVAL; + if (strcmp(name, "wxprot") != 0) + goto out; + + ret = -EACCES; + if (unlikely(current != p && + !capable(CAP_MAC_ADMIN))) + goto out; + + ret = -ENOMEM; + buf = kzalloc(8, GFP_KERNEL); + if (unlikely(buf == NULL)) + goto out; + + if (!sara_enabled || !wxprot_enabled) { + flags = 0x0; + } else { + rcu_read_lock(); + flags = get_sara_wxp_flags(__task_cred(p)); + rcu_read_unlock(); + } + + snprintf(buf, 8, "0x%04x\n", flags); + ret = strlen(buf); + *value = buf; + +out: + return ret; +} + +static int sara_setprocattr(const char *name, void *value, size_t size) +{ + int ret; + struct vm_area_struct *vma; + struct cred *new = prepare_creds(); + u16 cur_flags; + u16 req_flags; + char *buf = NULL; + + ret = -EINVAL; + if (!sara_enabled || !wxprot_enabled) + goto error; + if (unlikely(new == NULL)) + return -ENOMEM; + if (strcmp(name, "wxprot") != 0) + goto error; + if (unlikely(value == NULL || size == 0 || size > 7)) + goto error; + ret = -ENOMEM; + buf = kmalloc(size+1, GFP_KERNEL); + if (unlikely(buf == NULL)) + goto error; + buf[size] = '\0'; + memcpy(buf, value, size); + ret = -EINVAL; + if (unlikely(strlen(buf) != size)) + goto error; + if (unlikely(kstrtou16(buf, 0, &req_flags) != 0)) + goto error; + /* + * SARA_WXP_FORCE_WXORX is a procattr only flag with a special + * meaning and it isn't recognized by are_flags_valid + */ + if (unlikely(!are_flags_valid(req_flags & ~SARA_WXP_FORCE_WXORX))) + goto error; + /* + * Extra checks on requested flags: + * - SARA_WXP_FORCE_WXORX requires SARA_WXP_WXORX + * - SARA_WXP_MMAP can only be activated if the program + * has a relro section + * - COMPLAIN mode can only be requested if it was already + * on (procattr can only be used to make protection stricter) + * - EMUTRAMP can only be activated if it was already on or + * if MPROTECT and WXORX weren't already on (procattr can + * only be used to make protection stricter) + * - VERBOSITY request is ignored + */ + 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; + /* + * Except SARA_WXP_COMPLAIN and SARA_WXP_EMUTRAMP, + * any other flag can't be removed (procattr can + * only be used to make protection stricter). + */ + if (unlikely(cur_flags & (req_flags ^ cur_flags) & + ~(SARA_WXP_COMPLAIN|SARA_WXP_EMUTRAMP))) + goto error; + ret = -EINTR; + /* + * When SARA_WXP_FORCE_WXORX is on we traverse all the + * memory and remove the write permission from any area + * that is both writable and executable. + */ + if (req_flags & SARA_WXP_FORCE_WXORX) { + if (down_write_killable(¤t->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(¤t->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[] __lsm_ro_after_init = { LSM_HOOK_INIT(bprm_set_creds, sara_bprm_set_creds), LSM_HOOK_INIT(check_vmflags, sara_check_vmflags), @@ -548,6 +696,8 @@ static int sara_pagefault_handler(struct pt_regs *regs, #ifdef CONFIG_SECURITY_SARA_WXPROT_EMUTRAMP LSM_HOOK_INIT(pagefault_handler, sara_pagefault_handler), #endif + LSM_HOOK_INIT(getprocattr, sara_getprocattr), + LSM_HOOK_INIT(setprocattr, sara_setprocattr), }; static void config_free(struct wxprot_config_container *data) From patchwork Sat Jul 6 10:54:51 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Salvatore Mesoraca X-Patchwork-Id: 11033613 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id C0BF813BD for ; Sat, 6 Jul 2019 10:55:44 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id B10A12881B for ; Sat, 6 Jul 2019 10:55:44 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id A44A3289D2; Sat, 6 Jul 2019 10:55:44 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,GAPPY_SUBJECT,RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 887C32881B for ; Sat, 6 Jul 2019 10:55:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726907AbfGFKz1 (ORCPT ); Sat, 6 Jul 2019 06:55:27 -0400 Received: from mail-wr1-f66.google.com ([209.85.221.66]:45262 "EHLO mail-wr1-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726851AbfGFKz0 (ORCPT ); Sat, 6 Jul 2019 06:55:26 -0400 Received: by mail-wr1-f66.google.com with SMTP id f9so12220783wre.12; Sat, 06 Jul 2019 03:55:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=yTf2cEJ/R1sIWwnnsJBizfOJ+ZTQkKILtxUL3GoVlWE=; b=ZLNuz0c29GG+6CNJGcOAjeOlpRMP9h7O99Pi/gakDKFGU3lhhHikq8a99CInc2k/XO x6Aq3KQ16oEDc3jolc38IPHfVlXaurELUSiUDHn05p6lfOFDkkvsNRWEIR/u9OIqM0Iv HtbBQ2B/Kq1vvvG4gn4LbVxWfdc/sXmvb7bh25PrgcUiOFGqI5qzgRkWR5JJ3qB8it29 nt6KXK7N3DC9FtosDIw8Yw2FSs9KbQjD6zMPBftq1aXOG2YvtlhuzxNjQd3NYN3DSsHP AO1doaXc1DmHasyn5SSmlIyHvB5/T78+l9uWIWn6bgSiY0WNtJcA4pqYF4yEJgMx128n jwQQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=yTf2cEJ/R1sIWwnnsJBizfOJ+ZTQkKILtxUL3GoVlWE=; b=RS48iXTel6QPwNCVYq4c/MlQvT+YbHFm3lkZAhdo3HUL9WjA7dZSRkE6fV43C0ChBX GtL+1HRUl580todqT/USeC7WY6l12XOndu0oSLLCP+qN8N3ET+U227APbeYAGb1BjI9W fyAmxUHbHyfeP01U4sVJfIENSKF63pPTC7R+wItfm+X9l7r5DLA2E3w/bYKruBobxnEW 35iCpvoZS1F0ubwSEs73h4uHVMiXO0UPQAfGXf3BHPg+kgDcj10T4Hz8WXvTsZ5lGWYY Pp+xo7OHtC56/kSNFgGy4DqtHs+S/0wFuEeyslOwqxW1IxBiknOAf//UGjOC5gOpVQW7 TcfA== X-Gm-Message-State: APjAAAUUH5ZDrsVCsYZiMCEhfO2jTRtLQt53ha4g4bk+8QIz6O4JaYkP F74LCiN7WdUFsJAUuvangP4f8xY3vV157A== X-Google-Smtp-Source: APXvYqy4CMl5RxszjxSvrEAMWtejw7CVXkEcAJC69QuAF9VdqwZsgSkOM4PQR4vCXHHgIdQEtRBcQw== X-Received: by 2002:adf:e483:: with SMTP id i3mr7749477wrm.210.1562410523298; Sat, 06 Jul 2019 03:55:23 -0700 (PDT) Received: from localhost (net-93-71-3-102.cust.vodafonedsl.it. [93.71.3.102]) by smtp.gmail.com with ESMTPSA id h11sm12578794wrx.93.2019.07.06.03.55.21 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sat, 06 Jul 2019 03:55:22 -0700 (PDT) From: Salvatore Mesoraca To: linux-kernel@vger.kernel.org Cc: kernel-hardening@lists.openwall.com, linux-mm@kvack.org, linux-security-module@vger.kernel.org, Alexander Viro , Brad Spengler , Casey Schaufler , Christoph Hellwig , James Morris , Jann Horn , Kees Cook , PaX Team , Salvatore Mesoraca , "Serge E. Hallyn" , Thomas Gleixner Subject: [PATCH v5 10/12] S.A.R.A.: XATTRs support Date: Sat, 6 Jul 2019 12:54:51 +0200 Message-Id: <1562410493-8661-11-git-send-email-s.mesoraca16@gmail.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1562410493-8661-1-git-send-email-s.mesoraca16@gmail.com> References: <1562410493-8661-1-git-send-email-s.mesoraca16@gmail.com> Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: X-Virus-Scanned: ClamAV using ClamSMTP Adds support for extended filesystem attributes in security and user namespaces. They can be used to override flags set via the centralized configuration, even when S.A.R.A. configuration is locked or saractl is not used at all. Signed-off-by: Salvatore Mesoraca --- Documentation/admin-guide/LSM/SARA.rst | 20 +++++ Documentation/admin-guide/kernel-parameters.txt | 16 ++++ include/uapi/linux/xattr.h | 4 + security/sara/Kconfig | 22 ++++++ security/sara/wxprot.c | 99 +++++++++++++++++++++++++ 5 files changed, 161 insertions(+) diff --git a/Documentation/admin-guide/LSM/SARA.rst b/Documentation/admin-guide/LSM/SARA.rst index fdde04c..47d9364 100644 --- a/Documentation/admin-guide/LSM/SARA.rst +++ b/Documentation/admin-guide/LSM/SARA.rst @@ -55,6 +55,8 @@ WX Protection. In particular: 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**. +It's also possible to override the centralized configuration via `Extended +filesystem attributes`_. At the moment, WX Protection (unless specified otherwise) should work on any architecture supporting the NX bit, including, but not limited to: @@ -123,6 +125,24 @@ in your project or copy/paste parts of it. To make things simpler `libsara` is the only part of S.A.R.A. released under *CC0 - No Rights Reserved* license. +Extended filesystem attributes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +When this functionality is enabled, it's possible to override +WX Protection flags set in the main configuration via extended attributes, +even when S.A.R.A.'s configuration is in "locked" mode. +If the user namespace is also enabled, its attributes will override settings +configured via the security namespace. +The xattrs currently in use are: + +- security.sara.wxprot +- user.sara.wxprot + +They can be manually set to the desired value as a decimal, hexadecimal or +octal number. When this functionality is enabled, S.A.R.A. can be easily used +without the help of its userspace tools. Though the preferred way to change +these attributes is `sara-xattr` which is part of `saractl` [2]_. + + Trampoline emulation ^^^^^^^^^^^^^^^^^^^^ Some programs need to generate part of their code at runtime. Luckily enough, diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 3d6e86d..af40f1b 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -4254,6 +4254,22 @@ See S.A.R.A. documentation. Default value is set via kernel config option. + sara.wxprot_xattrs_enabled= [SARA] + Enable support for security xattrs. + Format: { "0" | "1" } + See security/sara/Kconfig help text + 0 -- disable. + 1 -- enable. + Default value is set via kernel config option. + + sara.wxprot_xattrs_user= [SARA] + Enable support for user xattrs. + Format: { "0" | "1" } + See security/sara/Kconfig help text + 0 -- disable. + 1 -- enable. + Default value is set via kernel config option. + serialnumber [BUGS=X86-32] shapers= [NET] diff --git a/include/uapi/linux/xattr.h b/include/uapi/linux/xattr.h index c1395b5..45c0333 100644 --- a/include/uapi/linux/xattr.h +++ b/include/uapi/linux/xattr.h @@ -77,5 +77,9 @@ #define XATTR_POSIX_ACL_DEFAULT "posix_acl_default" #define XATTR_NAME_POSIX_ACL_DEFAULT XATTR_SYSTEM_PREFIX XATTR_POSIX_ACL_DEFAULT +#define XATTR_SARA_SUFFIX "sara." +#define XATTR_SARA_WXP_SUFFIX XATTR_SARA_SUFFIX "wxp" +#define XATTR_NAME_SEC_SARA_WXP XATTR_SECURITY_PREFIX XATTR_SARA_WXP_SUFFIX +#define XATTR_NAME_USR_SARA_WXP XATTR_USER_PREFIX XATTR_SARA_WXP_SUFFIX #endif /* _UAPI_LINUX_XATTR_H */ diff --git a/security/sara/Kconfig b/security/sara/Kconfig index 458e0e8..773256b 100644 --- a/security/sara/Kconfig +++ b/security/sara/Kconfig @@ -135,6 +135,28 @@ config SECURITY_SARA_WXPROT_EMUTRAMP If unsure, answer y. +config SECURITY_SARA_WXPROT_XATTRS_ENABLED + bool "xattrs support enabled by default." + depends on SECURITY_SARA_WXPROT + default n + help + If you say Y here it will be possible to override WX protection + configuration via extended attributes in the security namespace. + Even when S.A.R.A.'s configuration has been locked. + + If unsure, answer N. + +config CONFIG_SECURITY_SARA_WXPROT_XATTRS_USER + bool "'user' namespace xattrs support enabled by default." + depends on SECURITY_SARA_WXPROT_XATTRS_ENABLED + default n + help + If you say Y here it will be possible to override WX protection + configuration via extended attributes in the user namespace. + Even when S.A.R.A.'s configuration has been locked. + + If unsure, answer N. + config SECURITY_SARA_WXPROT_DISABLED bool "WX protection will be disabled at boot." depends on SECURITY_SARA_WXPROT diff --git a/security/sara/wxprot.c b/security/sara/wxprot.c index 84f7b1e..773d1fd 100644 --- a/security/sara/wxprot.c +++ b/security/sara/wxprot.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "include/dfa.h" #include "include/sara.h" @@ -82,6 +83,18 @@ struct wxprot_config_container { static const bool wxprot_emutramp; #endif +#ifdef CONFIG_SECURITY_SARA_WXPROT_XATTRS_ENABLED +static int wxprot_xattrs_enabled __read_mostly = true; +#else +static int wxprot_xattrs_enabled __read_mostly; +#endif + +#ifdef CONFIG_SECURITY_SARA_WXPROT_XATTRS_USER +static int wxprot_xattrs_user __read_mostly = true; +#else +static int wxprot_xattrs_user __read_mostly; +#endif + static void pr_wxp(char *msg) { char *buf, *path; @@ -133,6 +146,14 @@ static bool are_flags_valid(u16 flags) MODULE_PARM_DESC(wxprot_enabled, "Disable or enable S.A.R.A. WX Protection at boot time."); +module_param(wxprot_xattrs_enabled, int, 0); +MODULE_PARM_DESC(wxprot_xattrs_enabled, + "Disable or enable S.A.R.A. WXP extended attributes interfaces."); + +module_param(wxprot_xattrs_user, int, 0); +MODULE_PARM_DESC(wxprot_xattrs_user, + "Allow normal users to override S.A.R.A. WXP settings via extended attributes."); + static int param_set_wxpflags(const char *val, const struct kernel_param *kp) { u16 flags; @@ -236,6 +257,65 @@ static inline int is_relro_page(const struct vm_area_struct *vma) } /* + * Extended attributes handling + */ +static int sara_wxprot_xattrs_name(struct dentry *d, + const char *name, + u16 *flags) +{ + int rc; + char buffer[10]; + u16 tmp; + + if (!(d->d_inode->i_opflags & IOP_XATTR)) + return -EOPNOTSUPP; + + rc = __vfs_getxattr(d, d->d_inode, name, buffer, sizeof(buffer) - 1); + if (rc > 0) { + buffer[rc] = '\0'; + rc = kstrtou16(buffer, 0, &tmp); + if (rc) + return rc; + if (!are_flags_valid(tmp)) + return -EINVAL; + *flags = tmp; + return 0; + } else if (rc < 0) + return rc; + + return -ENODATA; +} + +#define sara_xattrs_may_return(RC, XATTRNAME, FNAME) do { \ + if (RC == -EINVAL || RC == -ERANGE) \ + pr_info_ratelimited( \ + "WXP: malformed xattr '%s' on '%s'\n", \ + XATTRNAME, \ + FNAME); \ + else if (RC == 0) \ + return 0; \ +} while (0) + +static inline int sara_wxprot_xattrs(struct dentry *d, + u16 *flags) +{ + int rc; + + if (!wxprot_xattrs_enabled) + return 1; + if (wxprot_xattrs_user) { + rc = sara_wxprot_xattrs_name(d, XATTR_NAME_USR_SARA_WXP, + flags); + sara_xattrs_may_return(rc, XATTR_NAME_USR_SARA_WXP, + d->d_name.name); + } + rc = sara_wxprot_xattrs_name(d, XATTR_NAME_SEC_SARA_WXP, flags); + sara_xattrs_may_return(rc, XATTR_NAME_SEC_SARA_WXP, d->d_name.name); + return 1; +} + + +/* * LSM hooks */ static int sara_bprm_set_creds(struct linux_binprm *bprm) @@ -259,6 +339,10 @@ static int sara_bprm_set_creds(struct linux_binprm *bprm) if (!sara_enabled || !wxprot_enabled) return 0; + if (sara_wxprot_xattrs(bprm->file->f_path.dentry, + &sara_wxp_flags) == 0) + goto flags_set; + /* * SARA_WXP_TRANSFER means that the parent * wants this child to inherit its flags. @@ -283,6 +367,7 @@ static int sara_bprm_set_creds(struct linux_binprm *bprm) } else path = (char *) bprm->interp; +flags_set: if (sara_wxp_flags != default_flags && sara_wxp_flags & SARA_WXP_VERBOSE) pr_debug_ratelimited("WXP: '%s' run with flags '0x%x'.\n", @@ -777,6 +862,10 @@ static int config_hash(char **buf) static DEFINE_SARA_SECFS_BOOL_FLAG(wxprot_enabled_data, wxprot_enabled); +static DEFINE_SARA_SECFS_BOOL_FLAG(wxprot_xattrs_enabled_data, + wxprot_xattrs_enabled); +static DEFINE_SARA_SECFS_BOOL_FLAG(wxprot_xattrs_user_data, + wxprot_xattrs_user); static struct sara_secfs_fptrs fptrs __lsm_ro_after_init = { .load = config_load, @@ -820,6 +909,16 @@ static DEFINE_SARA_SECFS_BOOL_FLAG(wxprot_enabled_data, .type = SARA_SECFS_CONFIG_HASH, .data = &fptrs, }, + { + .name = "xattr_enabled", + .type = SARA_SECFS_BOOL, + .data = (void *) &wxprot_xattrs_enabled_data, + }, + { + .name = "xattr_user_allowed", + .type = SARA_SECFS_BOOL, + .data = (void *) &wxprot_xattrs_user_data, + }, }; From patchwork Sat Jul 6 10:54:52 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Salvatore Mesoraca X-Patchwork-Id: 11033603 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 6095C13BD for ; Sat, 6 Jul 2019 10:55:36 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5048B2881B for ; Sat, 6 Jul 2019 10:55:36 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 44B89289D2; Sat, 6 Jul 2019 10:55:36 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,GAPPY_SUBJECT,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id B71E82881B for ; Sat, 6 Jul 2019 10:55:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727136AbfGFKze (ORCPT ); Sat, 6 Jul 2019 06:55:34 -0400 Received: from mail-wm1-f67.google.com ([209.85.128.67]:40883 "EHLO mail-wm1-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727057AbfGFKz1 (ORCPT ); Sat, 6 Jul 2019 06:55:27 -0400 Received: by mail-wm1-f67.google.com with SMTP id v19so11941394wmj.5; Sat, 06 Jul 2019 03:55:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=u2RjwdUFNE3OIK/3bCmlFikIsAzp11hkr0vwILrcAOk=; b=thNRn6TJGPSAjqCTr1tPi/WxernvhpJlmpA3TOqpdYJiE30WEHmKtQQLBhQkPRLOPw 6LdBMJiwQeRDj45LsQ+CyPeBPtz6Sm/DVPA/1jFuQwsFGykkDeEEAkecFk+eI93fAHD1 qYlH4joBKaM50/AAsI6LhYNjkSKoWjJohjBdSbGLIHp32dQu3tN7YZr1eYxcsB+bqXUy bX3ZLkExfr5FGb8+TAw/oOIVBNNg7ayqAkWV/3r6PYpzR3qHiM4xg4ORrndOJWzJXK+K OUQHzEOkfBbfQCpO4tDaUbNknqB74EkTe516YzXQIsCHfecezahJQP/7fVtAx2XeBD7c +9Fw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=u2RjwdUFNE3OIK/3bCmlFikIsAzp11hkr0vwILrcAOk=; b=eDrGL44MRUA86Mk8sjVGtgkww0yi39ok0Mn2EnsMtKpwyRNyJi73HvJZcxZYNg7TQ+ u4Gy/RJv7neDJnSInjCwsh4z8BptxWMsJ++2iXcW0CbTHwd61iS92mz7mu6FRxC1QB76 Rlb/6gKI34MbyMk2MQ7EfSnR8FtfCYFR1uevtyyl26Mx31Ei6zDLE539HKStZGHiXTXF gwqAezlNEY6tDElezDXgQ2icGvXZGBUnHqHu28ugFyAtIMsVbLYyM71NV+DLIIYX8RNq 6UfKhUfoUg0lYaV23z7wEkcBJhVItsqTFx6AMsbRgkr9x5KLKe7dZw+J5Cf43A0ddhds TimA== X-Gm-Message-State: APjAAAXpuhJXa390AkHldeITDMcT8zFQ0uDJTsG6o+FJJEh0I/vkjhIG KKqMX19TnfHAId0Boid/T/plGKboALI25g== X-Google-Smtp-Source: APXvYqxxhkCkToEoUM8bgIOUXQl0J7gFqXqis0ZuQI5jP94wYx8vI8XERZcndoRkmUp3pDS0ZQ935g== X-Received: by 2002:a1c:5f87:: with SMTP id t129mr8243038wmb.150.1562410524445; Sat, 06 Jul 2019 03:55:24 -0700 (PDT) Received: from localhost (net-93-71-3-102.cust.vodafonedsl.it. [93.71.3.102]) by smtp.gmail.com with ESMTPSA id h11sm12578794wrx.93.2019.07.06.03.55.23 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sat, 06 Jul 2019 03:55:23 -0700 (PDT) From: Salvatore Mesoraca To: linux-kernel@vger.kernel.org Cc: kernel-hardening@lists.openwall.com, linux-mm@kvack.org, linux-security-module@vger.kernel.org, Alexander Viro , Brad Spengler , Casey Schaufler , Christoph Hellwig , James Morris , Jann Horn , Kees Cook , PaX Team , Salvatore Mesoraca , "Serge E. Hallyn" , Thomas Gleixner Subject: [PATCH v5 11/12] S.A.R.A.: /proc/*/mem write limitation Date: Sat, 6 Jul 2019 12:54:52 +0200 Message-Id: <1562410493-8661-12-git-send-email-s.mesoraca16@gmail.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1562410493-8661-1-git-send-email-s.mesoraca16@gmail.com> References: <1562410493-8661-1-git-send-email-s.mesoraca16@gmail.com> Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: X-Virus-Scanned: ClamAV using ClamSMTP Prevent a task from opening, in "write" mode, any /proc/*/mem file that operates on the task's mm. A process could use it to overwrite read-only memory, bypassing S.A.R.A. restrictions. Signed-off-by: Salvatore Mesoraca --- security/sara/include/sara_data.h | 18 ++++++++++++++++- security/sara/sara_data.c | 8 ++++++++ security/sara/wxprot.c | 41 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 1 deletion(-) diff --git a/security/sara/include/sara_data.h b/security/sara/include/sara_data.h index 9216c47..ee95f74 100644 --- a/security/sara/include/sara_data.h +++ b/security/sara/include/sara_data.h @@ -15,6 +15,7 @@ #define __SARA_DATA_H #include +#include #include #include #include @@ -40,6 +41,10 @@ struct sara_shm_data { spinlock_t lock; }; +struct sara_inode_data { + struct task_struct *task; +}; + static inline struct sara_data *get_sara_data(const struct cred *cred) { @@ -79,6 +84,17 @@ static inline struct sara_shm_data *get_sara_shm_data( #define lock_sara_shm(X) (spin_lock(&get_sara_shm_data((X))->lock)) #define unlock_sara_shm(X) (spin_unlock(&get_sara_shm_data((X))->lock)) -#endif + +static inline struct sara_inode_data *get_sara_inode_data( + const struct inode *inode) +{ + if (unlikely(!inode->i_security)) + return NULL; + return inode->i_security + sara_blob_sizes.lbs_inode; +} + +#define get_sara_inode_task(X) (get_sara_inode_data((X))->task) + +#endif /* CONFIG_SECURITY_SARA_WXPROT */ #endif /* __SARA_H */ diff --git a/security/sara/sara_data.c b/security/sara/sara_data.c index 9afca37..e875cf0 100644 --- a/security/sara/sara_data.c +++ b/security/sara/sara_data.c @@ -17,6 +17,7 @@ #include #include #include +#include #include static int sara_cred_prepare(struct cred *new, const struct cred *old, @@ -40,15 +41,22 @@ static int sara_shm_alloc_security(struct kern_ipc_perm *shp) return 0; } +static void sara_task_to_inode(struct task_struct *t, struct inode *i) +{ + get_sara_inode_task(i) = t; +} + static struct security_hook_list data_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(cred_prepare, sara_cred_prepare), LSM_HOOK_INIT(cred_transfer, sara_cred_transfer), LSM_HOOK_INIT(shm_alloc_security, sara_shm_alloc_security), + LSM_HOOK_INIT(task_to_inode, sara_task_to_inode), }; struct lsm_blob_sizes sara_blob_sizes __lsm_ro_after_init = { .lbs_cred = sizeof(struct sara_data), .lbs_ipc = sizeof(struct sara_shm_data), + .lbs_inode = sizeof(struct sara_inode_data), }; int __init sara_data_init(void) diff --git a/security/sara/wxprot.c b/security/sara/wxprot.c index 773d1fd..1a8d132 100644 --- a/security/sara/wxprot.c +++ b/security/sara/wxprot.c @@ -22,8 +22,11 @@ #include #include #include +#include #include #include +#include +#include #include #include @@ -615,6 +618,43 @@ static int sara_file_mprotect(struct vm_area_struct *vma, return 0; } +static int sara_file_open(struct file *file) +{ + struct task_struct *t; + struct mm_struct *mm; + u16 sara_wxp_flags = get_current_sara_wxp_flags(); + + /* + * Prevent write access to /proc/.../mem + * if it operates on the mm_struct of the + * current process: it could be used to + * bypass W^X. + */ + + if (!sara_enabled || + !wxprot_enabled || + !(sara_wxp_flags & SARA_WXP_WXORX) || + !(file->f_mode & FMODE_WRITE)) + return 0; + + t = get_sara_inode_task(file_inode(file)); + if (unlikely(t != NULL && + strcmp(file->f_path.dentry->d_name.name, + "mem") == 0)) { + get_task_struct(t); + mm = get_task_mm(t); + put_task_struct(t); + if (unlikely(mm == current->mm)) + sara_warn_or_goto(error, + "write access to /proc/*/mem"); + mmput(mm); + } + return 0; +error: + mmput(mm); + return -EACCES; +} + #ifdef CONFIG_SECURITY_SARA_WXPROT_EMUTRAMP static int sara_pagefault_handler(struct pt_regs *regs, unsigned long error_code, @@ -778,6 +818,7 @@ static int sara_setprocattr(const char *name, void *value, size_t size) LSM_HOOK_INIT(check_vmflags, sara_check_vmflags), LSM_HOOK_INIT(shm_shmat, sara_shm_shmat), LSM_HOOK_INIT(file_mprotect, sara_file_mprotect), + LSM_HOOK_INIT(file_open, sara_file_open), #ifdef CONFIG_SECURITY_SARA_WXPROT_EMUTRAMP LSM_HOOK_INIT(pagefault_handler, sara_pagefault_handler), #endif From patchwork Sat Jul 6 10:54:53 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Salvatore Mesoraca X-Patchwork-Id: 11033609 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A6A5013BD for ; Sat, 6 Jul 2019 10:55:42 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9764B2881B for ; Sat, 6 Jul 2019 10:55:42 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 8ADDF28A26; Sat, 6 Jul 2019 10:55:42 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,GAPPY_SUBJECT,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 3D70D2881B for ; Sat, 6 Jul 2019 10:55:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726851AbfGFKze (ORCPT ); Sat, 6 Jul 2019 06:55:34 -0400 Received: from mail-wr1-f67.google.com ([209.85.221.67]:44388 "EHLO mail-wr1-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727079AbfGFKz1 (ORCPT ); Sat, 6 Jul 2019 06:55:27 -0400 Received: by mail-wr1-f67.google.com with SMTP id p17so1088797wrf.11; Sat, 06 Jul 2019 03:55:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=7bgA3KVSNsVJyluNyhiPajryn8BOsiL4nv8POO6e3hg=; b=U523Em2KEtwXXxdV/BGn5jTzA7rwZNHEXsm1OcyY68qXcXEIgtM7G0Cet6d7ccvPCH xbNCt3TDC5HduqU/DgmJHVWSZe3KHZA5S97dUyH+Nq+WuUGuXAX0ragXjruWpmzsAxwt ZI947WMH7fH0XqCrCOg3eUA/2YAFkYNqtSug+CIUNQQHYURAjMXd6h9N5ALvdD0NpxEB AK2lAV8bchfSrDSx25gT6m0BWN8JZu//SbcHOnuqBRH2eqm3G2johuskfOsaaGz6nZlh deThmGjYCWPUDpw151T+5llw2TOJGBzuERv8CkChSGkkIYR0JLd+dQLERH79C+pRIoUr 14dA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=7bgA3KVSNsVJyluNyhiPajryn8BOsiL4nv8POO6e3hg=; b=nNRln7yBC7okm2wMCweZiJkf9x/qlQ9VEEaAbR+OBqtjNlMIeYblh4mB8/ALkfe09Z vQZQo3YMLBncXjnD/ct8ruVaqUgcLZF5cRRW7lEdcuJzTnxgJ8FZQFl68ovyAUJoQJT2 ms14w+uR+kMXofW+n3vXYLTZZB96a8yQ2hyw6fHvSYR/kRuOEnda+wMwyhu0KljiMRgW L8pkvbg1WhhuuO4h8DFEcfxffUcgmFvoJp3ILjvWHznEay7E+0IE9Aa6byT/rqUmJLEl Wb/xJJO1Yz9fMi1rU4DJgmOCHX59bj+rndMq2RImlSnAQjaYVY56nd8L48DwgT4X8FEj 5ryw== X-Gm-Message-State: APjAAAWYPntvrsA4cspqBkH6mbnTacqtsj2osb0Qb3JuccEwjl9mbKSJ AwILX3ojkreMOQp0Ci45DFDZGgNsNNnxSw== X-Google-Smtp-Source: APXvYqzPbEUIxunmBrxyk9NAIxCXus8RCS7gBuXZApUJVMJ6c1dTQqt5X0xbeRxaG4n4nmyktigDXg== X-Received: by 2002:adf:e843:: with SMTP id d3mr9048922wrn.249.1562410525376; Sat, 06 Jul 2019 03:55:25 -0700 (PDT) Received: from localhost (net-93-71-3-102.cust.vodafonedsl.it. [93.71.3.102]) by smtp.gmail.com with ESMTPSA id h11sm12578794wrx.93.2019.07.06.03.55.24 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sat, 06 Jul 2019 03:55:25 -0700 (PDT) From: Salvatore Mesoraca To: linux-kernel@vger.kernel.org Cc: kernel-hardening@lists.openwall.com, linux-mm@kvack.org, linux-security-module@vger.kernel.org, Alexander Viro , Brad Spengler , Casey Schaufler , Christoph Hellwig , James Morris , Jann Horn , Kees Cook , PaX Team , Salvatore Mesoraca , "Serge E. Hallyn" , Thomas Gleixner Subject: [PATCH v5 12/12] MAINTAINERS: take maintainership for S.A.R.A. Date: Sat, 6 Jul 2019 12:54:53 +0200 Message-Id: <1562410493-8661-13-git-send-email-s.mesoraca16@gmail.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1562410493-8661-1-git-send-email-s.mesoraca16@gmail.com> References: <1562410493-8661-1-git-send-email-s.mesoraca16@gmail.com> Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: X-Virus-Scanned: ClamAV using ClamSMTP Signed-off-by: Salvatore Mesoraca --- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index f16e5d0..de6dab1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13925,6 +13925,15 @@ F: drivers/phy/samsung/phy-s5pv210-usb2.c F: drivers/phy/samsung/phy-samsung-usb2.c F: drivers/phy/samsung/phy-samsung-usb2.h +SARA SECURITY MODULE +M: Salvatore Mesoraca +T: git git://github.com/smeso/sara.git lsm/sara/master +W: https://sara.smeso.it +S: Maintained +F: security/sara/ +F: arch/x86/security/sara/ +F: Documentation/admin-guide/LSM/SARA.rst + SC1200 WDT DRIVER M: Zwane Mwaikambo S: Maintained