From patchwork Wed Dec 12 08:17:08 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= X-Patchwork-Id: 10725661 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 7517B6C5 for ; Wed, 12 Dec 2018 08:18:08 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 673512AF73 for ; Wed, 12 Dec 2018 08:18:08 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 5A8072AF87; Wed, 12 Dec 2018 08:18:08 +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=-5.2 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from mother.openwall.net (mother.openwall.net [195.42.179.200]) by mail.wl.linuxfoundation.org (Postfix) with SMTP id 63F542AF73 for ; Wed, 12 Dec 2018 08:18:06 +0000 (UTC) Received: (qmail 17480 invoked by uid 550); 12 Dec 2018 08:17:59 -0000 Mailing-List: contact kernel-hardening-help@lists.openwall.com; run by ezmlm Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-ID: Delivered-To: mailing list kernel-hardening@lists.openwall.com Received: (qmail 16361 invoked from network); 12 Dec 2018 08:17:59 -0000 From: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= To: linux-kernel@vger.kernel.org Cc: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= , Al Viro , James Morris , Jonathan Corbet , Kees Cook , Matthew Garrett , Michael Kerrisk , =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= , Mimi Zohar , =?utf-8?q?Philippe_Tr=C3=A9buchet?= , Shuah Khan , Thibaut Sautereau , Vincent Strubel , Yves-Alexis Perez , kernel-hardening@lists.openwall.com, linux-api@vger.kernel.org, linux-security-module@vger.kernel.org, linux-fsdevel@vger.kernel.org Subject: [RFC PATCH v1 1/5] fs: Add support for an O_MAYEXEC flag on sys_open() Date: Wed, 12 Dec 2018 09:17:08 +0100 Message-Id: <20181212081712.32347-2-mic@digikod.net> X-Mailer: git-send-email 2.20.0.rc2 In-Reply-To: <20181212081712.32347-1-mic@digikod.net> References: <20181212081712.32347-1-mic@digikod.net> MIME-Version: 1.0 X-Antivirus: Dr.Web (R) for Unix mail servers drweb plugin ver.6.0.2.8 X-Antivirus-Code: 0x100000 X-Virus-Scanned: ClamAV using ClamSMTP When the O_MAYEXEC flag is passed, sys_open() may be subject to additional restrictions depending on a security policy implemented by an LSM through the inode_permission hook. The underlying idea is to be able to restrict scripts interpretation according to a policy defined by the system administrator. For this to be possible, script interpreters must use the O_MAYEXEC flag appropriately. To be fully effective, these interpreters also need to handle the other ways to execute code (for which the kernel can't help): command line parameters (e.g., option -e for Perl), module loading (e.g., option -m for Python), stdin, file sourcing, environment variables, configuration files... According to the threat model, it may be acceptable to allow some script interpreters (e.g. Bash) to interpret commands from stdin, may it be a TTY or a pipe, because it may not be enough to (directly) perform syscalls. A simple security policy implementation is available in a following patch for Yama. This is an updated subset of the patch initially written by Vincent Strubel for CLIP OS: https://github.com/clipos-archive/src_platform_clip-patches/blob/f5cb330d6b684752e403b4e41b39f7004d88e561/1901_open_mayexec.patch This patch has been used for more than 10 years with customized script interpreters. Some examples can be found here: https://github.com/clipos-archive/clipos4_portage-overlay/search?q=O_MAYEXEC Signed-off-by: Mickaël Salaün Signed-off-by: Thibaut Sautereau Signed-off-by: Vincent Strubel Reviewed-by: Philippe Trébuchet Cc: Al Viro Cc: Kees Cook Cc: Mickaël Salaün --- fs/fcntl.c | 2 +- fs/open.c | 4 ++++ include/linux/fcntl.h | 2 +- include/linux/fs.h | 2 ++ include/uapi/asm-generic/fcntl.h | 3 +++ 5 files changed, 11 insertions(+), 2 deletions(-) diff --git a/fs/fcntl.c b/fs/fcntl.c index 083185174c6d..6c85c4d0c006 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -1031,7 +1031,7 @@ static int __init fcntl_init(void) * Exceptions: O_NONBLOCK is a two bit define on parisc; O_NDELAY * is defined as O_NONBLOCK on some platforms and not on others. */ - BUILD_BUG_ON(21 - 1 /* for O_RDONLY being 0 */ != + BUILD_BUG_ON(22 - 1 /* for O_RDONLY being 0 */ != HWEIGHT32( (VALID_OPEN_FLAGS & ~(O_NONBLOCK | O_NDELAY)) | __FMODE_EXEC | __FMODE_NONOTIFY)); diff --git a/fs/open.c b/fs/open.c index 0285ce7dbd51..75479b79a58f 100644 --- a/fs/open.c +++ b/fs/open.c @@ -974,6 +974,10 @@ static inline int build_open_flags(int flags, umode_t mode, struct open_flags *o if (flags & O_APPEND) acc_mode |= MAY_APPEND; + /* Check execution permissions on open. */ + if (flags & O_MAYEXEC) + acc_mode |= MAY_OPENEXEC; + op->acc_mode = acc_mode; op->intent = flags & O_PATH ? 0 : LOOKUP_OPEN; diff --git a/include/linux/fcntl.h b/include/linux/fcntl.h index 27dc7a60693e..1fc00cabe9ab 100644 --- a/include/linux/fcntl.h +++ b/include/linux/fcntl.h @@ -9,7 +9,7 @@ (O_RDONLY | O_WRONLY | O_RDWR | O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC | \ O_APPEND | O_NDELAY | O_NONBLOCK | O_NDELAY | __O_SYNC | O_DSYNC | \ FASYNC | O_DIRECT | O_LARGEFILE | O_DIRECTORY | O_NOFOLLOW | \ - O_NOATIME | O_CLOEXEC | O_PATH | __O_TMPFILE) + O_NOATIME | O_CLOEXEC | O_PATH | __O_TMPFILE | O_MAYEXEC) #ifndef force_o_largefile #define force_o_largefile() (BITS_PER_LONG != 32) diff --git a/include/linux/fs.h b/include/linux/fs.h index c95c0807471f..584c9329ad78 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -94,6 +94,8 @@ typedef int (dio_iodone_t)(struct kiocb *iocb, loff_t offset, #define MAY_CHDIR 0x00000040 /* called from RCU mode, don't block */ #define MAY_NOT_BLOCK 0x00000080 +/* the inode is opened with O_MAYEXEC */ +#define MAY_OPENEXEC 0x00000100 /* * flags in file.f_mode. Note that FMODE_READ and FMODE_WRITE must correspond diff --git a/include/uapi/asm-generic/fcntl.h b/include/uapi/asm-generic/fcntl.h index 9dc0bf0c5a6e..cbb9425d6e7c 100644 --- a/include/uapi/asm-generic/fcntl.h +++ b/include/uapi/asm-generic/fcntl.h @@ -97,6 +97,9 @@ #define O_NDELAY O_NONBLOCK #endif +/* command execution from file is intended, check exec permissions */ +#define O_MAYEXEC 040000000 + #define F_DUPFD 0 /* dup */ #define F_GETFD 1 /* get close_on_exec */ #define F_SETFD 2 /* set/clear close_on_exec */ From patchwork Wed Dec 12 08:17:09 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= X-Patchwork-Id: 10725663 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 51BF413BF for ; Wed, 12 Dec 2018 08:18:16 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 4357A2AF73 for ; Wed, 12 Dec 2018 08:18:16 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 345D62AF87; Wed, 12 Dec 2018 08:18:16 +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=-5.2 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from mother.openwall.net (mother.openwall.net [195.42.179.200]) by mail.wl.linuxfoundation.org (Postfix) with SMTP id 3B8222AF73 for ; Wed, 12 Dec 2018 08:18:14 +0000 (UTC) Received: (qmail 17722 invoked by uid 550); 12 Dec 2018 08:18:02 -0000 Mailing-List: contact kernel-hardening-help@lists.openwall.com; run by ezmlm Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-ID: Delivered-To: mailing list kernel-hardening@lists.openwall.com Received: (qmail 17585 invoked from network); 12 Dec 2018 08:18:01 -0000 From: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= To: linux-kernel@vger.kernel.org Cc: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= , Al Viro , James Morris , Jonathan Corbet , Kees Cook , Matthew Garrett , Michael Kerrisk , =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= , Mimi Zohar , =?utf-8?q?Philippe_Tr=C3=A9buchet?= , Shuah Khan , Thibaut Sautereau , Vincent Strubel , Yves-Alexis Perez , kernel-hardening@lists.openwall.com, linux-api@vger.kernel.org, linux-security-module@vger.kernel.org, linux-fsdevel@vger.kernel.org Subject: [RFC PATCH v1 2/5] fs: Add a MAY_EXECMOUNT flag to infer the noexec mount propertie Date: Wed, 12 Dec 2018 09:17:09 +0100 Message-Id: <20181212081712.32347-3-mic@digikod.net> X-Mailer: git-send-email 2.20.0.rc2 In-Reply-To: <20181212081712.32347-1-mic@digikod.net> References: <20181212081712.32347-1-mic@digikod.net> MIME-Version: 1.0 X-Antivirus: Dr.Web (R) for Unix mail servers drweb plugin ver.6.0.2.8 X-Antivirus-Code: 0x100000 X-Virus-Scanned: ClamAV using ClamSMTP An LSM doesn't get path information related to an access request to open an inode. This new (internal) MAY_EXECMOUNT flag enables an LSM to check if the underlying mount point of an inode is marked as executable. This is useful to implement a security policy taking advantage of the noexec mount option. This flag is set according to path_noexec(), which checks if a mount point is mounted with MNT_NOEXEC or if the underlying superblock is SB_I_NOEXEC. Signed-off-by: Mickaël Salaün Reviewed-by: Philippe Trébuchet Reviewed-by: Thibaut Sautereau Cc: Al Viro Cc: Kees Cook Cc: Mickaël Salaün --- fs/namei.c | 2 ++ include/linux/fs.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/fs/namei.c b/fs/namei.c index 0cab6494978c..de4f33b3f464 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2970,6 +2970,8 @@ static int may_open(const struct path *path, int acc_mode, int flag) break; } + /* Pass the mount point executability. */ + acc_mode |= path_noexec(path) ? 0 : MAY_EXECMOUNT; error = inode_permission(inode, MAY_OPEN | acc_mode); if (error) return error; diff --git a/include/linux/fs.h b/include/linux/fs.h index 584c9329ad78..083a31b8068e 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -96,6 +96,8 @@ typedef int (dio_iodone_t)(struct kiocb *iocb, loff_t offset, #define MAY_NOT_BLOCK 0x00000080 /* the inode is opened with O_MAYEXEC */ #define MAY_OPENEXEC 0x00000100 +/* the mount point is marked as executable */ +#define MAY_EXECMOUNT 0x00000200 /* * flags in file.f_mode. Note that FMODE_READ and FMODE_WRITE must correspond From patchwork Wed Dec 12 08:17:10 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= X-Patchwork-Id: 10725665 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 2703D6C5 for ; Wed, 12 Dec 2018 08:18:26 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 185D92AF73 for ; Wed, 12 Dec 2018 08:18:26 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 0BF652AF87; Wed, 12 Dec 2018 08:18:26 +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=-5.2 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from mother.openwall.net (mother.openwall.net [195.42.179.200]) by mail.wl.linuxfoundation.org (Postfix) with SMTP id B44B82AF73 for ; Wed, 12 Dec 2018 08:18:24 +0000 (UTC) Received: (qmail 17905 invoked by uid 550); 12 Dec 2018 08:18:03 -0000 Mailing-List: contact kernel-hardening-help@lists.openwall.com; run by ezmlm Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-ID: Delivered-To: mailing list kernel-hardening@lists.openwall.com Received: (qmail 17794 invoked from network); 12 Dec 2018 08:18:02 -0000 From: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= To: linux-kernel@vger.kernel.org Cc: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= , Al Viro , James Morris , Jonathan Corbet , Kees Cook , Matthew Garrett , Michael Kerrisk , =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= , Mimi Zohar , =?utf-8?q?Philippe_Tr=C3=A9buchet?= , Shuah Khan , Thibaut Sautereau , Vincent Strubel , Yves-Alexis Perez , kernel-hardening@lists.openwall.com, linux-api@vger.kernel.org, linux-security-module@vger.kernel.org, linux-fsdevel@vger.kernel.org Subject: [RFC PATCH v1 3/5] Yama: Enforces noexec mounts or file executability through O_MAYEXEC Date: Wed, 12 Dec 2018 09:17:10 +0100 Message-Id: <20181212081712.32347-4-mic@digikod.net> X-Mailer: git-send-email 2.20.0.rc2 In-Reply-To: <20181212081712.32347-1-mic@digikod.net> References: <20181212081712.32347-1-mic@digikod.net> MIME-Version: 1.0 X-Antivirus: Dr.Web (R) for Unix mail servers drweb plugin ver.6.0.2.8 X-Antivirus-Code: 0x100000 X-Virus-Scanned: ClamAV using ClamSMTP Enable to either propagate the mount options from the underlying VFS mount to prevent execution, or to propagate the file execute permission. This may allow a script interpreter to check execution permissions before reading commands from a file. The main goal is to be able to protect the kernel by restricting arbitrary syscalls that an attacker could perform with a crafted binary or certain script languages. It also improves multilevel isolation by reducing the ability of an attacker to use side channels with specific code. These restrictions can natively be enforced for ELF binaries (with the noexec mount option) but require this kernel extension to properly handle scripts (e.g., Python, Perl). Add a new sysctl kernel.yama.open_mayexec_enforce to control this behavior. A following patch adds documentation. Signed-off-by: Mickaël Salaün Reviewed-by: Philippe Trébuchet Reviewed-by: Thibaut Sautereau Cc: Kees Cook Cc: Mickaël Salaün --- security/yama/Kconfig | 3 +- security/yama/yama_lsm.c | 82 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 83 insertions(+), 2 deletions(-) diff --git a/security/yama/Kconfig b/security/yama/Kconfig index 96b27405558a..9457619fabd5 100644 --- a/security/yama/Kconfig +++ b/security/yama/Kconfig @@ -5,7 +5,8 @@ config SECURITY_YAMA help This selects Yama, which extends DAC support with additional system-wide security settings beyond regular Linux discretionary - access controls. Currently available is ptrace scope restriction. + access controls. Currently available are ptrace scope restriction and + enforcement of the O_MAYEXEC open flag. Like capabilities, this security module stacks with other LSMs. Further information can be found in Documentation/admin-guide/LSM/Yama.rst. diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c index ffda91a4a1aa..120664e94ee5 100644 --- a/security/yama/yama_lsm.c +++ b/security/yama/yama_lsm.c @@ -1,10 +1,12 @@ /* * Yama Linux Security Module * - * Author: Kees Cook + * Authors: Kees Cook + * Mickaël Salaün * * Copyright (C) 2010 Canonical, Ltd. * Copyright (C) 2011 The Chromium OS Authors. + * Copyright (C) 2018 ANSSI * * 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 @@ -28,7 +30,14 @@ #define YAMA_SCOPE_CAPABILITY 2 #define YAMA_SCOPE_NO_ATTACH 3 +#define YAMA_OMAYEXEC_ENFORCE_NONE 0 +#define YAMA_OMAYEXEC_ENFORCE_MOUNT (1 << 0) +#define YAMA_OMAYEXEC_ENFORCE_FILE (1 << 1) +#define _YAMA_OMAYEXEC_LAST YAMA_OMAYEXEC_ENFORCE_FILE +#define _YAMA_OMAYEXEC_MASK ((_YAMA_OMAYEXEC_LAST << 1) - 1) + static int ptrace_scope = YAMA_SCOPE_RELATIONAL; +static int open_mayexec_enforce = YAMA_OMAYEXEC_ENFORCE_NONE; /* describe a ptrace relationship for potential exception */ struct ptrace_relation { @@ -423,7 +432,40 @@ int yama_ptrace_traceme(struct task_struct *parent) return rc; } +/** + * yama_inode_permission - check O_MAYEXEC permission before accessing an inode + * @inode: inode structure to check + * @mask: permission mask + * + * Return 0 if access is permitted, -EACCES otherwise. + */ +int yama_inode_permission(struct inode *inode, int mask) +{ + if (!(mask & MAY_OPENEXEC)) + return 0; + /* + * Match regular files and directories to make it easier to + * modify script interpreters. + */ + if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode)) + return 0; + + if ((open_mayexec_enforce & YAMA_OMAYEXEC_ENFORCE_MOUNT) && + !(mask & MAY_EXECMOUNT)) + return -EACCES; + + /* + * May prefer acl_permission_check() instead of generic_permission(), + * to not be bypassable with CAP_DAC_READ_SEARCH. + */ + if (open_mayexec_enforce & YAMA_OMAYEXEC_ENFORCE_FILE) + return generic_permission(inode, MAY_EXEC); + + return 0; +} + static struct security_hook_list yama_hooks[] __lsm_ro_after_init = { + LSM_HOOK_INIT(inode_permission, yama_inode_permission), LSM_HOOK_INIT(ptrace_access_check, yama_ptrace_access_check), LSM_HOOK_INIT(ptrace_traceme, yama_ptrace_traceme), LSM_HOOK_INIT(task_prctl, yama_task_prctl), @@ -447,6 +489,37 @@ static int yama_dointvec_minmax(struct ctl_table *table, int write, return proc_dointvec_minmax(&table_copy, write, buffer, lenp, ppos); } +static int yama_dointvec_bitmask_macadmin(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, + loff_t *ppos) +{ + int error; + + if (write) { + struct ctl_table table_copy; + int tmp_mayexec_enforce; + + if (!capable(CAP_MAC_ADMIN)) + return -EPERM; + tmp_mayexec_enforce = *((int *)table->data); + table_copy = *table; + /* do not erase open_mayexec_enforce */ + table_copy.data = &tmp_mayexec_enforce; + error = proc_dointvec(&table_copy, write, buffer, lenp, ppos); + if (error) + return error; + if ((tmp_mayexec_enforce | _YAMA_OMAYEXEC_MASK) != + _YAMA_OMAYEXEC_MASK) + return -EINVAL; + *((int *)table->data) = tmp_mayexec_enforce; + } else { + error = proc_dointvec(table, write, buffer, lenp, ppos); + if (error) + return error; + } + return 0; +} + static int zero; static int max_scope = YAMA_SCOPE_NO_ATTACH; @@ -466,6 +539,13 @@ static struct ctl_table yama_sysctl_table[] = { .extra1 = &zero, .extra2 = &max_scope, }, + { + .procname = "open_mayexec_enforce", + .data = &open_mayexec_enforce, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = yama_dointvec_bitmask_macadmin, + }, { } }; static void __init yama_init_sysctl(void) From patchwork Wed Dec 12 08:17:11 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= X-Patchwork-Id: 10725667 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 AE2806C5 for ; Wed, 12 Dec 2018 08:18:38 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 99D3B28FA8 for ; Wed, 12 Dec 2018 08:18:38 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 8A36128FAE; Wed, 12 Dec 2018 08:18:38 +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=-5.2 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from mother.openwall.net (mother.openwall.net [195.42.179.200]) by mail.wl.linuxfoundation.org (Postfix) with SMTP id 69BAE2AF73 for ; Wed, 12 Dec 2018 08:18:36 +0000 (UTC) Received: (qmail 18183 invoked by uid 550); 12 Dec 2018 08:18:06 -0000 Mailing-List: contact kernel-hardening-help@lists.openwall.com; run by ezmlm Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-ID: Delivered-To: mailing list kernel-hardening@lists.openwall.com Received: (qmail 18071 invoked from network); 12 Dec 2018 08:18:05 -0000 From: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= To: linux-kernel@vger.kernel.org Cc: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= , Al Viro , James Morris , Jonathan Corbet , Kees Cook , Matthew Garrett , Michael Kerrisk , =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= , Mimi Zohar , =?utf-8?q?Philippe_Tr=C3=A9buchet?= , Shuah Khan , Thibaut Sautereau , Vincent Strubel , Yves-Alexis Perez , kernel-hardening@lists.openwall.com, linux-api@vger.kernel.org, linux-security-module@vger.kernel.org, linux-fsdevel@vger.kernel.org Subject: [RFC PATCH v1 4/5] selftest/yama: Add tests for O_MAYEXEC enforcing Date: Wed, 12 Dec 2018 09:17:11 +0100 Message-Id: <20181212081712.32347-5-mic@digikod.net> X-Mailer: git-send-email 2.20.0.rc2 In-Reply-To: <20181212081712.32347-1-mic@digikod.net> References: <20181212081712.32347-1-mic@digikod.net> MIME-Version: 1.0 X-Antivirus: Dr.Web (R) for Unix mail servers drweb plugin ver.6.0.2.8 X-Antivirus-Code: 0x100000 X-Virus-Scanned: ClamAV using ClamSMTP Test propagation of noexec mount points or file executability through files open with or without O_MAYEXEC. Signed-off-by: Mickaël Salaün Cc: Kees Cook Cc: Mickaël Salaün Cc: Shuah Khan --- MAINTAINERS | 1 + tools/testing/selftests/Makefile | 1 + tools/testing/selftests/yama/.gitignore | 1 + tools/testing/selftests/yama/Makefile | 19 ++ tools/testing/selftests/yama/config | 2 + tools/testing/selftests/yama/test_omayexec.c | 276 +++++++++++++++++++ 6 files changed, 300 insertions(+) create mode 100644 tools/testing/selftests/yama/.gitignore create mode 100644 tools/testing/selftests/yama/Makefile create mode 100644 tools/testing/selftests/yama/config create mode 100644 tools/testing/selftests/yama/test_omayexec.c diff --git a/MAINTAINERS b/MAINTAINERS index 8119141a926f..a1d01a81b283 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16591,6 +16591,7 @@ M: Kees Cook T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git yama/tip S: Supported F: security/yama/ +F: tools/testing/selftests/yama/ F: Documentation/admin-guide/LSM/Yama.rst YEALINK PHONE DRIVER diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index f0017c831e57..608f31167aa6 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -46,6 +46,7 @@ endif TARGETS += user TARGETS += vm TARGETS += x86 +TARGETS += yama TARGETS += zram #Please keep the TARGETS list alphabetically sorted # Run "make quicktest=1 run_tests" or diff --git a/tools/testing/selftests/yama/.gitignore b/tools/testing/selftests/yama/.gitignore new file mode 100644 index 000000000000..6e8d5cfb48d0 --- /dev/null +++ b/tools/testing/selftests/yama/.gitignore @@ -0,0 +1 @@ +/test_omayexec diff --git a/tools/testing/selftests/yama/Makefile b/tools/testing/selftests/yama/Makefile new file mode 100644 index 000000000000..d411f1615b60 --- /dev/null +++ b/tools/testing/selftests/yama/Makefile @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0 + +all: + +include ../lib.mk + +.PHONY: all clean + +BINARIES := test_omayexec +CFLAGS += -Wl,-no-as-needed -Wall -Werror +LDFLAGS += -lcap + +test_omayexec: test_omayexec.c ../kselftest_harness.h + $(CC) $(CFLAGS) $(LDFLAGS) $< -o $@ + +TEST_PROGS += $(BINARIES) +EXTRA_CLEAN := $(BINARIES) + +all: $(BINARIES) diff --git a/tools/testing/selftests/yama/config b/tools/testing/selftests/yama/config new file mode 100644 index 000000000000..9d375bfc465b --- /dev/null +++ b/tools/testing/selftests/yama/config @@ -0,0 +1,2 @@ +CONFIG_SECURITY=y +CONFIG_SECURITY_YAMA=y diff --git a/tools/testing/selftests/yama/test_omayexec.c b/tools/testing/selftests/yama/test_omayexec.c new file mode 100644 index 000000000000..7d41097f0e89 --- /dev/null +++ b/tools/testing/selftests/yama/test_omayexec.c @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Yama tests - O_MAYEXEC + * + * Copyright © 2018 ANSSI + * + * Author: Mickaël Salaün + */ + +#include +#include /* O_CLOEXEC */ +#include +#include +#include /* strlen */ +#include +#include +#include /* mkdir */ +#include /* unlink, rmdir */ + +#include "../kselftest_harness.h" + +#ifndef O_MAYEXEC +#define O_MAYEXEC 040000000 +#endif + +#define SYSCTL_MAYEXEC "/proc/sys/kernel/yama/open_mayexec_enforce" + +#define BIN_DIR "./test-mount" +#define BIN_PATH BIN_DIR "/file" +#define DIR_PATH BIN_DIR "/directory" + +#define ALLOWED 1 +#define DENIED 0 + +static void test_omx(struct __test_metadata *_metadata, + const char *const path, const int exec_allowed) +{ + int fd; + + /* without O_MAYEXEC */ + fd = open(path, O_RDONLY | O_CLOEXEC); + ASSERT_NE(-1, fd); + EXPECT_FALSE(close(fd)); + + /* with O_MAYEXEC */ + fd = open(path, O_RDONLY | O_CLOEXEC | O_MAYEXEC); + if (exec_allowed) { + /* open should succeed */ + ASSERT_NE(-1, fd); + EXPECT_FALSE(close(fd)); + } else { + /* open should return EACCES */ + ASSERT_EQ(-1, fd); + ASSERT_EQ(EACCES, errno); + } +} + +static void ignore_dac(struct __test_metadata *_metadata, int override) +{ + cap_t caps; + const cap_value_t cap_val[2] = { + CAP_DAC_OVERRIDE, + CAP_DAC_READ_SEARCH, + }; + + caps = cap_get_proc(); + ASSERT_TRUE(!!caps); + ASSERT_FALSE(cap_set_flag(caps, CAP_EFFECTIVE, 2, cap_val, + override ? CAP_SET : CAP_CLEAR)); + ASSERT_FALSE(cap_set_proc(caps)); + EXPECT_FALSE(cap_free(caps)); +} + +static void test_dir_file(struct __test_metadata *_metadata, + const char *const dir_path, const char *const file_path, + const int exec_allowed, const int only_file_perm) +{ + if (only_file_perm) { + /* test as root */ + ignore_dac(_metadata, 1); + /* always allowed because of generic_permission() use */ + test_omx(_metadata, dir_path, ALLOWED); + } + + /* without bypass */ + ignore_dac(_metadata, 0); + test_omx(_metadata, dir_path, exec_allowed); + test_omx(_metadata, file_path, exec_allowed); +} + +static void sysctl_write(struct __test_metadata *_metadata, + const char *path, const char *value) +{ + int fd; + size_t len_value; + ssize_t len_wrote; + + fd = open(path, O_WRONLY | O_CLOEXEC); + ASSERT_NE(-1, fd); + len_value = strlen(value); + len_wrote = write(fd, value, len_value); + ASSERT_EQ(len_wrote, len_value); + EXPECT_FALSE(close(fd)); +} + +static void create_workspace(struct __test_metadata *_metadata, + int mount_exec, int file_exec) +{ + int fd; + + /* + * Cleanup previous workspace if any error previously happened (don't + * check errors). + */ + umount(BIN_DIR); + rmdir(BIN_DIR); + + /* create a clean mount point */ + ASSERT_FALSE(mkdir(BIN_DIR, 00700)); + ASSERT_FALSE(mount("test", BIN_DIR, "tmpfs", + MS_MGC_VAL | (mount_exec ? 0 : MS_NOEXEC), + "mode=0700,size=4k")); + + /* create a test file */ + fd = open(BIN_PATH, O_CREAT | O_RDONLY | O_CLOEXEC, + file_exec ? 00500 : 00400); + ASSERT_NE(-1, fd); + EXPECT_NE(-1, close(fd)); + + /* create a test directory */ + ASSERT_FALSE(mkdir(DIR_PATH, file_exec ? 00500 : 00400)); +} + +static void delete_workspace(struct __test_metadata *_metadata) +{ + ignore_dac(_metadata, 1); + sysctl_write(_metadata, SYSCTL_MAYEXEC, "0"); + + /* no need to unlink BIN_PATH nor DIR_PATH */ + ASSERT_FALSE(umount(BIN_DIR)); + ASSERT_FALSE(rmdir(BIN_DIR)); +} + +FIXTURE_DATA(mount_exec_file_exec) { }; + +FIXTURE_SETUP(mount_exec_file_exec) +{ + create_workspace(_metadata, 1, 1); +} + +FIXTURE_TEARDOWN(mount_exec_file_exec) +{ + delete_workspace(_metadata); +} + +TEST_F(mount_exec_file_exec, mount) +{ + /* enforce mount exec check */ + sysctl_write(_metadata, SYSCTL_MAYEXEC, "1"); + test_dir_file(_metadata, DIR_PATH, BIN_PATH, ALLOWED, 0); +} + +TEST_F(mount_exec_file_exec, file) +{ + /* enforce file exec check */ + sysctl_write(_metadata, SYSCTL_MAYEXEC, "2"); + test_dir_file(_metadata, DIR_PATH, BIN_PATH, ALLOWED, 0); +} + +TEST_F(mount_exec_file_exec, mount_file) +{ + /* enforce mount and file exec check */ + sysctl_write(_metadata, SYSCTL_MAYEXEC, "3"); + test_dir_file(_metadata, DIR_PATH, BIN_PATH, ALLOWED, 0); +} + +FIXTURE_DATA(mount_exec_file_noexec) { }; + +FIXTURE_SETUP(mount_exec_file_noexec) +{ + create_workspace(_metadata, 1, 0); +} + +FIXTURE_TEARDOWN(mount_exec_file_noexec) +{ + delete_workspace(_metadata); +} + +TEST_F(mount_exec_file_noexec, mount) +{ + /* enforce mount exec check */ + sysctl_write(_metadata, SYSCTL_MAYEXEC, "1"); + test_dir_file(_metadata, DIR_PATH, BIN_PATH, ALLOWED, 0); +} + +TEST_F(mount_exec_file_noexec, file) +{ + /* enforce file exec check */ + sysctl_write(_metadata, SYSCTL_MAYEXEC, "2"); + test_dir_file(_metadata, DIR_PATH, BIN_PATH, DENIED, 1); +} + +TEST_F(mount_exec_file_noexec, mount_file) +{ + /* enforce mount and file exec check */ + sysctl_write(_metadata, SYSCTL_MAYEXEC, "3"); + test_dir_file(_metadata, DIR_PATH, BIN_PATH, DENIED, 1); +} + +FIXTURE_DATA(mount_noexec_file_exec) { }; + +FIXTURE_SETUP(mount_noexec_file_exec) +{ + create_workspace(_metadata, 0, 1); +} + +FIXTURE_TEARDOWN(mount_noexec_file_exec) +{ + delete_workspace(_metadata); +} + +TEST_F(mount_noexec_file_exec, mount) +{ + /* enforce mount exec check */ + sysctl_write(_metadata, SYSCTL_MAYEXEC, "1"); + test_dir_file(_metadata, DIR_PATH, BIN_PATH, DENIED, 0); +} + +TEST_F(mount_noexec_file_exec, file) +{ + /* enforce file exec check */ + sysctl_write(_metadata, SYSCTL_MAYEXEC, "2"); + test_dir_file(_metadata, DIR_PATH, BIN_PATH, ALLOWED, 0); +} + +TEST_F(mount_noexec_file_exec, mount_file) +{ + /* enforce mount and file exec check */ + sysctl_write(_metadata, SYSCTL_MAYEXEC, "3"); + test_dir_file(_metadata, DIR_PATH, BIN_PATH, DENIED, 0); +} + +FIXTURE_DATA(mount_noexec_file_noexec) { }; + +FIXTURE_SETUP(mount_noexec_file_noexec) +{ + create_workspace(_metadata, 0, 0); +} + +FIXTURE_TEARDOWN(mount_noexec_file_noexec) +{ + delete_workspace(_metadata); +} + +TEST_F(mount_noexec_file_noexec, mount) +{ + /* enforce mount exec check */ + sysctl_write(_metadata, SYSCTL_MAYEXEC, "1"); + test_dir_file(_metadata, DIR_PATH, BIN_PATH, DENIED, 0); +} + +TEST_F(mount_noexec_file_noexec, file) +{ + /* enforce file exec check */ + sysctl_write(_metadata, SYSCTL_MAYEXEC, "2"); + test_dir_file(_metadata, DIR_PATH, BIN_PATH, DENIED, 1); +} + +TEST_F(mount_noexec_file_noexec, mount_file) +{ + /* enforce mount and file exec check */ + sysctl_write(_metadata, SYSCTL_MAYEXEC, "3"); + test_dir_file(_metadata, DIR_PATH, BIN_PATH, DENIED, 0); +} + +TEST_HARNESS_MAIN From patchwork Wed Dec 12 08:17:12 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= X-Patchwork-Id: 10725669 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 50D8E13BF for ; Wed, 12 Dec 2018 08:18:52 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 428202B021 for ; Wed, 12 Dec 2018 08:18:52 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 3265F2B030; Wed, 12 Dec 2018 08:18:52 +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=-5.2 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from mother.openwall.net (mother.openwall.net [195.42.179.200]) by mail.wl.linuxfoundation.org (Postfix) with SMTP id 392D42B021 for ; Wed, 12 Dec 2018 08:18:51 +0000 (UTC) Received: (qmail 18369 invoked by uid 550); 12 Dec 2018 08:18:08 -0000 Mailing-List: contact kernel-hardening-help@lists.openwall.com; run by ezmlm Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-ID: Delivered-To: mailing list kernel-hardening@lists.openwall.com Received: (qmail 18281 invoked from network); 12 Dec 2018 08:18:08 -0000 From: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= To: linux-kernel@vger.kernel.org Cc: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= , Al Viro , James Morris , Jonathan Corbet , Kees Cook , Matthew Garrett , Michael Kerrisk , =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= , Mimi Zohar , =?utf-8?q?Philippe_Tr=C3=A9buchet?= , Shuah Khan , Thibaut Sautereau , Vincent Strubel , Yves-Alexis Perez , kernel-hardening@lists.openwall.com, linux-api@vger.kernel.org, linux-security-module@vger.kernel.org, linux-fsdevel@vger.kernel.org Subject: [RFC PATCH v1 5/5] doc: Add documentation for Yama's open_mayexec_enforce Date: Wed, 12 Dec 2018 09:17:12 +0100 Message-Id: <20181212081712.32347-6-mic@digikod.net> X-Mailer: git-send-email 2.20.0.rc2 In-Reply-To: <20181212081712.32347-1-mic@digikod.net> References: <20181212081712.32347-1-mic@digikod.net> MIME-Version: 1.0 X-Antivirus: Dr.Web (R) for Unix mail servers drweb plugin ver.6.0.2.8 X-Antivirus-Code: 0x100000 X-Virus-Scanned: ClamAV using ClamSMTP Signed-off-by: Mickaël Salaün Reviewed-by: Philippe Trébuchet Reviewed-by: Thibaut Sautereau Cc: Jonathan Corbet Cc: Kees Cook Cc: Mickaël Salaün --- Documentation/admin-guide/LSM/Yama.rst | 41 ++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/Documentation/admin-guide/LSM/Yama.rst b/Documentation/admin-guide/LSM/Yama.rst index d0a060de3973..a72c86a24b35 100644 --- a/Documentation/admin-guide/LSM/Yama.rst +++ b/Documentation/admin-guide/LSM/Yama.rst @@ -72,3 +72,44 @@ The sysctl settings (writable only with ``CAP_SYS_PTRACE``) are: ``PTRACE_TRACEME``. Once set, this sysctl value cannot be changed. The original children-only logic was based on the restrictions in grsecurity. + +open_mayexec_enforce +==================== + +The ``O_MAYEXEC`` flag can be passed to :manpage:`open(2)` to only open files +(or directories) that are executable. If the file is not identified as +executable, then the syscall returns -EACCES. This may allow a script +interpreter to check executable permission before reading commands from a file. +One interesting use case is to enforce a "write xor execute" policy through +interpreters. + +Thanks to this flag, Yama enables to enforce the ``noexec`` mount option (i.e. +the underlying mount point of the file is mounted with MNT_NOEXEC or its +underlying superblock is SB_I_NOEXEC) not only on ELF binaries but also on +scripts. This may be possible thanks to script interpreters using the +``O_MAYEXEC`` flag. The executable permission is then checked before reading +commands from a file, and thus can enforce the ``noexec`` at the interpreter +level by propagating this security policy to the scripts. To be fully +effective, these interpreters also need to handle the other ways to execute +code (for which the kernel can't help): command line parameters (e.g., option +``-e`` for Perl), module loading (e.g., option ``-m`` for Python), stdin, file +sourcing, environment variables, configuration files... According to the +threat model, it may be acceptable to allow some script interpreters (e.g. +Bash) to interpret commands from stdin, may it be a TTY or a pipe, because it +may not be enough to (directly) perform syscalls. + +Yama implements two complementary security policies to propagate the ``noexec`` +mount option or the executable file permission. These policies are handled by +the ``kernel.yama.open_mayexec_enforce`` sysctl (writable only with +``CAP_MAC_ADMIN``) as a bitmask: + +1 - mount restriction: + check that the mount options for the underlying VFS mount do not prevent + execution. + +2 - file permission restriction: + check that the to-be-opened file is marked as executable for the current + process (e.g., POSIX permissions). + +Code samples can be found in tools/testing/selftests/yama/test_omayexec.c and +https://github.com/clipos-archive/clipos4_portage-overlay/search?q=O_MAYEXEC .