2020-07-23 17:14:38

by Mickaël Salaün

[permalink] [raw]
Subject: [PATCH v7 0/7] Add support for O_MAYEXEC

Hi,

This seventh patch series do not set __FMODE_EXEC for the sake of
simplicity. A notification feature could be added later if needed. The
handling of all file types is now well defined and tested: by default,
when opening a path, access to a directory is denied (with EISDIR),
access to a regular file depends on the sysctl policy, and access to
other file types (i.e. fifo, device, socket) is denied if there is any
enforced policy. There is new tests covering all these cases (cf.
test_file_types() ).

As requested by Mimi Zohar, I completed the series with one of her
patches for IMA. I also picked Kees Cook's patches to consolidate exec
permission checking into do_filp_open()'s flow.


# Goal of O_MAYEXEC

The goal of this patch series is to enable to control script execution
with interpreters help. A new O_MAYEXEC flag, usable through
openat2(2), is added to enable userspace script interpreters to delegate
to the kernel (and thus the system security policy) the permission to
interpret/execute scripts or other files containing what can be seen as
commands.

A simple system-wide security policy can be enforced by the system
administrator through a sysctl configuration consistent with the mount
points or the file access rights. The documentation patch explains the
prerequisites.

Furthermore, the security policy can also be delegated to an LSM, either
a MAC system or an integrity system. For instance, the new kernel
MAY_OPENEXEC flag closes a major IMA measurement/appraisal interpreter
integrity gap by bringing the ability to check the use of scripts [1].
Other uses are expected, such as for magic-links [2], SGX integration
[3], bpffs [4] or IPE [5].


# Prerequisite of its use

Userspace needs to adapt to take advantage of this new feature. For
example, the PEP 578 [6] (Runtime Audit Hooks) enables Python 3.8 to be
extended with policy enforcement points related to code interpretation,
which can be used to align with the PowerShell audit features.
Additional Python security improvements (e.g. a limited interpreter
withou -c, stdin piping of code) are on their way [7].


# Examples

The initial idea comes from CLIP OS 4 and the original implementation
has been used for more than 12 years:
https://github.com/clipos-archive/clipos4_doc
Chrome OS has a similar approach:
https://chromium.googlesource.com/chromiumos/docs/+/master/security/noexec_shell_scripts.md

Userland patches can be found here:
https://github.com/clipos-archive/clipos4_portage-overlay/search?q=O_MAYEXEC
Actually, there is more than the O_MAYEXEC changes (which matches this search)
e.g., to prevent Python interactive execution. There are patches for
Bash, Wine, Java (Icedtea), Busybox's ash, Perl and Python. There are
also some related patches which do not directly rely on O_MAYEXEC but
which restrict the use of browser plugins and extensions, which may be
seen as scripts too:
https://github.com/clipos-archive/clipos4_portage-overlay/tree/master/www-client

An introduction to O_MAYEXEC was given at the Linux Security Summit
Europe 2018 - Linux Kernel Security Contributions by ANSSI:
https://www.youtube.com/watch?v=chNjCRtPKQY&t=17m15s
The "write xor execute" principle was explained at Kernel Recipes 2018 -
CLIP OS: a defense-in-depth OS:
https://www.youtube.com/watch?v=PjRE0uBtkHU&t=11m14s
See also an overview article: https://lwn.net/Articles/820000/


This patch series can be applied on top of v5.8-rc5 . This can be tested
with CONFIG_SYSCTL. I would really appreciate constructive comments on
this patch series.

Previous version:
https://lore.kernel.org/lkml/[email protected]/


[1] https://lore.kernel.org/lkml/[email protected]/
[2] https://lore.kernel.org/lkml/[email protected]/
[3] https://lore.kernel.org/lkml/CALCETrVovr8XNZSroey7pHF46O=kj_c5D9K8h=z2T_cNrpvMig@mail.gmail.com/
[4] https://lore.kernel.org/lkml/CALCETrVeZ0eufFXwfhtaG_j+AdvbzEWE0M3wjXMWVEO7pj+xkw@mail.gmail.com/
[5] https://lore.kernel.org/lkml/[email protected]/
[6] https://www.python.org/dev/peps/pep-0578/
[7] https://lore.kernel.org/lkml/[email protected]/

Regards,

Kees Cook (3):
exec: Change uselib(2) IS_SREG() failure to EACCES
exec: Move S_ISREG() check earlier
exec: Move path_noexec() check earlier

Mickaël Salaün (3):
fs: Introduce O_MAYEXEC flag for openat2(2)
fs,doc: Enable to enforce noexec mounts or file exec through O_MAYEXEC
selftest/openat2: Add tests for O_MAYEXEC enforcing

Mimi Zohar (1):
ima: add policy support for the new file open MAY_OPENEXEC flag

Documentation/ABI/testing/ima_policy | 2 +-
Documentation/admin-guide/sysctl/fs.rst | 49 +++
fs/exec.c | 23 +-
fs/fcntl.c | 2 +-
fs/namei.c | 36 +-
fs/open.c | 12 +-
include/linux/fcntl.h | 2 +-
include/linux/fs.h | 3 +
include/uapi/asm-generic/fcntl.h | 7 +
kernel/sysctl.c | 12 +-
security/integrity/ima/ima_main.c | 3 +-
security/integrity/ima/ima_policy.c | 15 +-
tools/testing/selftests/kselftest_harness.h | 3 +
tools/testing/selftests/openat2/Makefile | 3 +-
tools/testing/selftests/openat2/config | 1 +
tools/testing/selftests/openat2/helpers.h | 1 +
.../testing/selftests/openat2/omayexec_test.c | 325 ++++++++++++++++++
17 files changed, 470 insertions(+), 29 deletions(-)
create mode 100644 tools/testing/selftests/openat2/config
create mode 100644 tools/testing/selftests/openat2/omayexec_test.c

--
2.27.0


2020-07-23 17:14:42

by Mickaël Salaün

[permalink] [raw]
Subject: [PATCH v7 7/7] ima: add policy support for the new file open MAY_OPENEXEC flag

From: Mimi Zohar <[email protected]>

The kernel has no way of differentiating between a file containing data
or code being opened by an interpreter. The proposed O_MAYEXEC
openat2(2) flag bridges this gap by defining and enabling the
MAY_OPENEXEC flag.

This patch adds IMA policy support for the new MAY_OPENEXEC flag.

Example:
measure func=FILE_CHECK mask=^MAY_OPENEXEC
appraise func=FILE_CHECK appraise_type=imasig mask=^MAY_OPENEXEC

Signed-off-by: Mickaël Salaün <[email protected]>
Signed-off-by: Mimi Zohar <[email protected]>
Reviewed-by: Lakshmi Ramasubramanian <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
---
Documentation/ABI/testing/ima_policy | 2 +-
security/integrity/ima/ima_main.c | 3 ++-
security/integrity/ima/ima_policy.c | 15 +++++++++++----
3 files changed, 14 insertions(+), 6 deletions(-)

diff --git a/Documentation/ABI/testing/ima_policy b/Documentation/ABI/testing/ima_policy
index cd572912c593..caca46125fe0 100644
--- a/Documentation/ABI/testing/ima_policy
+++ b/Documentation/ABI/testing/ima_policy
@@ -31,7 +31,7 @@ Description:
[KEXEC_KERNEL_CHECK] [KEXEC_INITRAMFS_CHECK]
[KEXEC_CMDLINE] [KEY_CHECK]
mask:= [[^]MAY_READ] [[^]MAY_WRITE] [[^]MAY_APPEND]
- [[^]MAY_EXEC]
+ [[^]MAY_EXEC] [[^]MAY_OPENEXEC]
fsmagic:= hex value
fsuuid:= file system UUID (e.g 8bcbe394-4f13-4144-be8e-5aa9ea2ce2f6)
uid:= decimal value
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index c1583d98c5e5..59fd1658a203 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -490,7 +490,8 @@ int ima_file_check(struct file *file, int mask)

security_task_getsecid(current, &secid);
return process_measurement(file, current_cred(), secid, NULL, 0,
- mask & (MAY_READ | MAY_WRITE | MAY_EXEC |
+ mask & (MAY_READ | MAY_WRITE |
+ MAY_EXEC | MAY_OPENEXEC |
MAY_APPEND), FILE_CHECK);
}
EXPORT_SYMBOL_GPL(ima_file_check);
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index e493063a3c34..6487f0b2afdd 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -406,7 +406,8 @@ static bool ima_match_keyring(struct ima_rule_entry *rule,
* @cred: a pointer to a credentials structure for user validation
* @secid: the secid of the task to be validated
* @func: LIM hook identifier
- * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
+ * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC |
+ * MAY_OPENEXEC)
* @keyring: keyring name to check in policy for KEY_CHECK func
*
* Returns true on rule match, false on failure.
@@ -527,7 +528,8 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
* being made
* @secid: LSM secid of the task to be validated
* @func: IMA hook identifier
- * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
+ * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC |
+ * MAY_OPENEXEC)
* @pcr: set the pcr to extend
* @template_desc: the template that should be used for this rule
* @keyring: the keyring name, if given, to be used to check in the policy.
@@ -1091,6 +1093,8 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
entry->mask = MAY_READ;
else if (strcmp(from, "MAY_APPEND") == 0)
entry->mask = MAY_APPEND;
+ else if (strcmp(from, "MAY_OPENEXEC") == 0)
+ entry->mask = MAY_OPENEXEC;
else
result = -EINVAL;
if (!result)
@@ -1422,14 +1426,15 @@ const char *const func_tokens[] = {

#ifdef CONFIG_IMA_READ_POLICY
enum {
- mask_exec = 0, mask_write, mask_read, mask_append
+ mask_exec = 0, mask_write, mask_read, mask_append, mask_openexec
};

static const char *const mask_tokens[] = {
"^MAY_EXEC",
"^MAY_WRITE",
"^MAY_READ",
- "^MAY_APPEND"
+ "^MAY_APPEND",
+ "^MAY_OPENEXEC"
};

void *ima_policy_start(struct seq_file *m, loff_t *pos)
@@ -1518,6 +1523,8 @@ int ima_policy_show(struct seq_file *m, void *v)
seq_printf(m, pt(Opt_mask), mt(mask_read) + offset);
if (entry->mask & MAY_APPEND)
seq_printf(m, pt(Opt_mask), mt(mask_append) + offset);
+ if (entry->mask & MAY_OPENEXEC)
+ seq_printf(m, pt(Opt_mask), mt(mask_openexec) + offset);
seq_puts(m, " ");
}

--
2.27.0

2020-07-23 17:14:58

by Mickaël Salaün

[permalink] [raw]
Subject: [PATCH v7 2/7] exec: Move S_ISREG() check earlier

From: Kees Cook <[email protected]>

The execve(2)/uselib(2) syscalls have always rejected non-regular
files. Recently, it was noticed that a deadlock was introduced when trying
to execute pipes, as the S_ISREG() test was happening too late. This was
fixed in commit 73601ea5b7b1 ("fs/open.c: allow opening only regular files
during execve()"), but it was added after inode_permission() had already
run, which meant LSMs could see bogus attempts to execute non-regular
files.

Move the test into the other inode type checks (which already look
for other pathological conditions[1]). Since there is no need to use
FMODE_EXEC while we still have access to "acc_mode", also switch the
test to MAY_EXEC.

Also include a comment with the redundant S_ISREG() checks at the end of
execve(2)/uselib(2) to note that they are present to avoid any mistakes.

My notes on the call path, and related arguments, checks, etc:

do_open_execat()
struct open_flags open_exec_flags = {
.open_flag = O_LARGEFILE | O_RDONLY | __FMODE_EXEC,
.acc_mode = MAY_EXEC,
...
do_filp_open(dfd, filename, open_flags)
path_openat(nameidata, open_flags, flags)
file = alloc_empty_file(open_flags, current_cred());
do_open(nameidata, file, open_flags)
may_open(path, acc_mode, open_flag)
/* new location of MAY_EXEC vs S_ISREG() test */
inode_permission(inode, MAY_OPEN | acc_mode)
security_inode_permission(inode, acc_mode)
vfs_open(path, file)
do_dentry_open(file, path->dentry->d_inode, open)
/* old location of FMODE_EXEC vs S_ISREG() test */
security_file_open(f)
open()

[1] https://lore.kernel.org/lkml/202006041910.9EF0C602@keescook/

Signed-off-by: Mickaël Salaün <[email protected]>
Signed-off-by: Kees Cook <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
---
fs/exec.c | 14 ++++++++++++--
fs/namei.c | 6 ++++--
fs/open.c | 6 ------
3 files changed, 16 insertions(+), 10 deletions(-)

diff --git a/fs/exec.c b/fs/exec.c
index d7c937044d10..bdc6a6eb5dce 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -141,8 +141,13 @@ SYSCALL_DEFINE1(uselib, const char __user *, library)
if (IS_ERR(file))
goto out;

+ /*
+ * may_open() has already checked for this, so it should be
+ * impossible to trip now. But we need to be extra cautious
+ * and check again at the very end too.
+ */
error = -EACCES;
- if (!S_ISREG(file_inode(file)->i_mode))
+ if (WARN_ON_ONCE(!S_ISREG(file_inode(file)->i_mode)))
goto exit;

if (path_noexec(&file->f_path))
@@ -886,8 +891,13 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags)
if (IS_ERR(file))
goto out;

+ /*
+ * may_open() has already checked for this, so it should be
+ * impossible to trip now. But we need to be extra cautious
+ * and check again at the very end too.
+ */
err = -EACCES;
- if (!S_ISREG(file_inode(file)->i_mode))
+ if (WARN_ON_ONCE(!S_ISREG(file_inode(file)->i_mode)))
goto exit;

if (path_noexec(&file->f_path))
diff --git a/fs/namei.c b/fs/namei.c
index 72d4219c93ac..a559ad943970 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2849,16 +2849,18 @@ static int may_open(const struct path *path, int acc_mode, int flag)
case S_IFLNK:
return -ELOOP;
case S_IFDIR:
- if (acc_mode & MAY_WRITE)
+ if (acc_mode & (MAY_WRITE | MAY_EXEC))
return -EISDIR;
break;
case S_IFBLK:
case S_IFCHR:
if (!may_open_dev(path))
return -EACCES;
- /*FALLTHRU*/
+ fallthrough;
case S_IFIFO:
case S_IFSOCK:
+ if (acc_mode & MAY_EXEC)
+ return -EACCES;
flag &= ~O_TRUNC;
break;
}
diff --git a/fs/open.c b/fs/open.c
index 6cd48a61cda3..623b7506a6db 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -784,12 +784,6 @@ static int do_dentry_open(struct file *f,
return 0;
}

- /* Any file opened for execve()/uselib() has to be a regular file. */
- if (unlikely(f->f_flags & FMODE_EXEC && !S_ISREG(inode->i_mode))) {
- error = -EACCES;
- goto cleanup_file;
- }
-
if (f->f_mode & FMODE_WRITE && !special_file(inode->i_mode)) {
error = get_write_access(inode);
if (unlikely(error))
--
2.27.0

2020-07-23 17:15:22

by Mickaël Salaün

[permalink] [raw]
Subject: [PATCH v7 4/7] fs: Introduce O_MAYEXEC flag for openat2(2)

When the O_MAYEXEC flag is passed, openat2(2) may be subject to
additional restrictions depending on a security policy managed by the
kernel through a sysctl or implemented by an LSM thanks to the
inode_permission hook. This new flag is ignored by open(2) and
openat(2) because of their unspecified flags handling. When used with
openat2(2), the default behavior is only to forbid to open a directory.

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: command line parameters (e.g.,
option -e for Perl), module loading (e.g., option -m for Python), stdin,
file sourcing, environment variables, configuration files, etc.
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. Further documentation can be found in a following patch.

Even without enforced security policy, userland interpreters can set it
to enforce the system policy at their level, knowing that it will not
break anything on running systems which do not care about this feature.
However, on systems which want this feature enforced, there will be
knowledgeable people (i.e. sysadmins who enforced O_MAYEXEC
deliberately) to manage it. A simple security policy implementation,
configured through a dedicated sysctl, is available in a following
patch.

O_MAYEXEC should not be confused with the O_EXEC flag which is intended
for execute-only, which obviously doesn't work for scripts. However, a
similar behavior could be implemented in userland with O_PATH:
https://lore.kernel.org/lkml/[email protected]/

The implementation of O_MAYEXEC almost duplicates what execve(2) and
uselib(2) are already doing: setting MAY_OPENEXEC in acc_mode (which can
then be checked as MAY_EXEC, if enforced).

This is an updated subset of the patch initially written by Vincent
Strubel for CLIP OS 4:
https://github.com/clipos-archive/src_platform_clip-patches/blob/f5cb330d6b684752e403b4e41b39f7004d88e561/1901_open_mayexec.patch
This patch has been used for more than 12 years with customized script
interpreters. Some examples (with the original O_MAYEXEC) can be found
here:
https://github.com/clipos-archive/clipos4_portage-overlay/search?q=O_MAYEXEC

Co-developed-by: Vincent Strubel <[email protected]>
Signed-off-by: Vincent Strubel <[email protected]>
Co-developed-by: Thibaut Sautereau <[email protected]>
Signed-off-by: Thibaut Sautereau <[email protected]>
Signed-off-by: Mickaël Salaün <[email protected]>
Cc: Aleksa Sarai <[email protected]>
Cc: Al Viro <[email protected]>
Cc: Deven Bowers <[email protected]>
Cc: Kees Cook <[email protected]>
---

Changes since v6:
* Do not set __FMODE_EXEC for now because of inconsistent behavior:
https://lore.kernel.org/lkml/202007160822.CCDB5478@keescook/
* Returns EISDIR when opening a directory with O_MAYEXEC.
* Removed Deven Bowers and Kees Cook Reviewed-by tags because of the
current update.

Changes since v5:
* Update commit message.

Changes since v3:
* Switch back to O_MAYEXEC, but only handle it with openat2(2) which
checks unknown flags (suggested by Aleksa Sarai). Cf.
https://lore.kernel.org/lkml/[email protected]/

Changes since v2:
* Replace O_MAYEXEC with RESOLVE_MAYEXEC from openat2(2). This change
enables to not break existing application using bogus O_* flags that
may be ignored by current kernels by using a new dedicated flag, only
usable through openat2(2) (suggested by Jeff Layton). Using this flag
will results in an error if the running kernel does not support it.
User space needs to manage this case, as with other RESOLVE_* flags.
The best effort approach to security (for most common distros) will
simply consists of ignoring such an error and retry without
RESOLVE_MAYEXEC. However, a fully controlled system may which to
error out if such an inconsistency is detected.

Changes since v1:
* Set __FMODE_EXEC when using O_MAYEXEC to make this information
available through the new fanotify/FAN_OPEN_EXEC event (suggested by
Jan Kara and Matthew Bobrowski):
https://lore.kernel.org/lkml/[email protected]/
---
fs/fcntl.c | 2 +-
fs/namei.c | 4 ++--
fs/open.c | 6 ++++++
include/linux/fcntl.h | 2 +-
include/linux/fs.h | 2 ++
include/uapi/asm-generic/fcntl.h | 7 +++++++
6 files changed, 19 insertions(+), 4 deletions(-)

diff --git a/fs/fcntl.c b/fs/fcntl.c
index 2e4c0fa2074b..0357ad667563 100644
--- a/fs/fcntl.c
+++ b/fs/fcntl.c
@@ -1033,7 +1033,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/namei.c b/fs/namei.c
index ddc9b25540fe..3f074ec77390 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -428,7 +428,7 @@ static int sb_permission(struct super_block *sb, struct inode *inode, int mask)
/**
* inode_permission - Check for access rights to a given inode
* @inode: Inode to check permission on
- * @mask: Right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
+ * @mask: Right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC, %MAY_OPENEXEC)
*
* Check for read/write/execute permissions on an inode. We use fs[ug]id for
* this, letting us set arbitrary permissions for filesystem access without
@@ -2849,7 +2849,7 @@ static int may_open(const struct path *path, int acc_mode, int flag)
case S_IFLNK:
return -ELOOP;
case S_IFDIR:
- if (acc_mode & (MAY_WRITE | MAY_EXEC))
+ if (acc_mode & (MAY_WRITE | MAY_EXEC | MAY_OPENEXEC))
return -EISDIR;
break;
case S_IFBLK:
diff --git a/fs/open.c b/fs/open.c
index 623b7506a6db..21c2c1020574 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -987,6 +987,8 @@ inline struct open_how build_open_how(int flags, umode_t mode)
.mode = mode & S_IALLUGO,
};

+ /* O_MAYEXEC is ignored by syscalls relying on build_open_how(). */
+ how.flags &= ~O_MAYEXEC;
/* O_PATH beats everything else. */
if (how.flags & O_PATH)
how.flags &= O_PATH_FLAGS;
@@ -1054,6 +1056,10 @@ inline int build_open_flags(const struct open_how *how, struct open_flags *op)
if (flags & __O_SYNC)
flags |= O_DSYNC;

+ /* Checks execution permissions on open. */
+ if (flags & O_MAYEXEC)
+ acc_mode |= MAY_OPENEXEC;
+
op->open_flag = flags;

/* O_TRUNC implies we need access checks for write permissions */
diff --git a/include/linux/fcntl.h b/include/linux/fcntl.h
index 7bcdcf4f6ab2..e188a360fa5f 100644
--- a/include/linux/fcntl.h
+++ b/include/linux/fcntl.h
@@ -10,7 +10,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)

/* List of all valid flags for the how->upgrade_mask argument: */
#define VALID_UPGRADE_FLAGS \
diff --git a/include/linux/fs.h b/include/linux/fs.h
index f5abba86107d..56f835c9a87a 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -101,6 +101,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..bca90620119f 100644
--- a/include/uapi/asm-generic/fcntl.h
+++ b/include/uapi/asm-generic/fcntl.h
@@ -97,6 +97,13 @@
#define O_NDELAY O_NONBLOCK
#endif

+/*
+ * Code execution from file is intended, checks such permission. A simple
+ * policy can be enforced system-wide as explained in
+ * Documentation/admin-guide/sysctl/fs.rst .
+ */
+#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 */
--
2.27.0

2020-07-23 17:16:45

by Mickaël Salaün

[permalink] [raw]
Subject: [PATCH v7 3/7] exec: Move path_noexec() check earlier

From: Kees Cook <[email protected]>

The path_noexec() check, like the regular file check, was happening too
late, letting LSMs see impossible execve()s. Check it earlier as well
in may_open() and collect the redundant fs/exec.c path_noexec() test
under the same robustness comment as the S_ISREG() check.

My notes on the call path, and related arguments, checks, etc:

do_open_execat()
struct open_flags open_exec_flags = {
.open_flag = O_LARGEFILE | O_RDONLY | __FMODE_EXEC,
.acc_mode = MAY_EXEC,
...
do_filp_open(dfd, filename, open_flags)
path_openat(nameidata, open_flags, flags)
file = alloc_empty_file(open_flags, current_cred());
do_open(nameidata, file, open_flags)
may_open(path, acc_mode, open_flag)
/* new location of MAY_EXEC vs path_noexec() test */
inode_permission(inode, MAY_OPEN | acc_mode)
security_inode_permission(inode, acc_mode)
vfs_open(path, file)
do_dentry_open(file, path->dentry->d_inode, open)
security_file_open(f)
open()
/* old location of path_noexec() test */

Signed-off-by: Mickaël Salaün <[email protected]>
Signed-off-by: Kees Cook <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
---
fs/exec.c | 12 ++++--------
fs/namei.c | 4 ++++
2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/fs/exec.c b/fs/exec.c
index bdc6a6eb5dce..4eea20c27b01 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -147,10 +147,8 @@ SYSCALL_DEFINE1(uselib, const char __user *, library)
* and check again at the very end too.
*/
error = -EACCES;
- if (WARN_ON_ONCE(!S_ISREG(file_inode(file)->i_mode)))
- goto exit;
-
- if (path_noexec(&file->f_path))
+ if (WARN_ON_ONCE(!S_ISREG(file_inode(file)->i_mode) ||
+ path_noexec(&file->f_path)))
goto exit;

fsnotify_open(file);
@@ -897,10 +895,8 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags)
* and check again at the very end too.
*/
err = -EACCES;
- if (WARN_ON_ONCE(!S_ISREG(file_inode(file)->i_mode)))
- goto exit;
-
- if (path_noexec(&file->f_path))
+ if (WARN_ON_ONCE(!S_ISREG(file_inode(file)->i_mode) ||
+ path_noexec(&file->f_path)))
goto exit;

err = deny_write_access(file);
diff --git a/fs/namei.c b/fs/namei.c
index a559ad943970..ddc9b25540fe 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2863,6 +2863,10 @@ static int may_open(const struct path *path, int acc_mode, int flag)
return -EACCES;
flag &= ~O_TRUNC;
break;
+ case S_IFREG:
+ if ((acc_mode & MAY_EXEC) && path_noexec(path))
+ return -EACCES;
+ break;
}

error = inode_permission(inode, MAY_OPEN | acc_mode);
--
2.27.0

2020-07-23 17:16:55

by Mickaël Salaün

[permalink] [raw]
Subject: [PATCH v7 1/7] exec: Change uselib(2) IS_SREG() failure to EACCES

From: Kees Cook <[email protected]>

Change uselib(2)' S_ISREG() error return to EACCES instead of EINVAL so
the behavior matches execve(2), and the seemingly documented value.
The "not a regular file" failure mode of execve(2) is explicitly
documented[1], but it is not mentioned in uselib(2)[2] which does,
however, say that open(2) and mmap(2) errors may apply. The documentation
for open(2) does not include a "not a regular file" error[3], but mmap(2)
does[4], and it is EACCES.

[1] http://man7.org/linux/man-pages/man2/execve.2.html#ERRORS
[2] http://man7.org/linux/man-pages/man2/uselib.2.html#ERRORS
[3] http://man7.org/linux/man-pages/man2/open.2.html#ERRORS
[4] http://man7.org/linux/man-pages/man2/mmap.2.html#ERRORS

Signed-off-by: Mickaël Salaün <[email protected]>
Signed-off-by: Kees Cook <[email protected]>
Acked-by: Christian Brauner <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
---
fs/exec.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/fs/exec.c b/fs/exec.c
index e6e8a9a70327..d7c937044d10 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -141,11 +141,10 @@ SYSCALL_DEFINE1(uselib, const char __user *, library)
if (IS_ERR(file))
goto out;

- error = -EINVAL;
+ error = -EACCES;
if (!S_ISREG(file_inode(file)->i_mode))
goto exit;

- error = -EACCES;
if (path_noexec(&file->f_path))
goto exit;

--
2.27.0

2020-07-24 11:21:01

by Thibaut Sautereau

[permalink] [raw]
Subject: Re: [PATCH v7 0/7] Add support for O_MAYEXEC

On Thu, Jul 23, 2020 at 07:12:20PM +0200, Mickaël Salaün wrote:

> This patch series can be applied on top of v5.8-rc5 .

v5.8-rc6, actually.

> Previous version:
> https://lore.kernel.org/lkml/[email protected]/

This is v5.
v6 is at https://lore.kernel.org/lkml/[email protected]/

--
Thibaut Sautereau
CLIP OS developer

2020-07-24 19:03:50

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH v7 4/7] fs: Introduce O_MAYEXEC flag for openat2(2)

On Thu, Jul 23, 2020 at 07:12:24PM +0200, Micka?l Sala?n wrote:
> When the O_MAYEXEC flag is passed, openat2(2) may be subject to
> additional restrictions depending on a security policy managed by the
> kernel through a sysctl or implemented by an LSM thanks to the
> inode_permission hook. This new flag is ignored by open(2) and
> openat(2) because of their unspecified flags handling. When used with
> openat2(2), the default behavior is only to forbid to open a directory.
>
> 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: command line parameters (e.g.,
> option -e for Perl), module loading (e.g., option -m for Python), stdin,
> file sourcing, environment variables, configuration files, etc.
> 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. Further documentation can be found in a following patch.
>
> Even without enforced security policy, userland interpreters can set it
> to enforce the system policy at their level, knowing that it will not
> break anything on running systems which do not care about this feature.
> However, on systems which want this feature enforced, there will be
> knowledgeable people (i.e. sysadmins who enforced O_MAYEXEC
> deliberately) to manage it. A simple security policy implementation,
> configured through a dedicated sysctl, is available in a following
> patch.
>
> O_MAYEXEC should not be confused with the O_EXEC flag which is intended
> for execute-only, which obviously doesn't work for scripts. However, a
> similar behavior could be implemented in userland with O_PATH:
> https://lore.kernel.org/lkml/[email protected]/
>
> The implementation of O_MAYEXEC almost duplicates what execve(2) and
> uselib(2) are already doing: setting MAY_OPENEXEC in acc_mode (which can
> then be checked as MAY_EXEC, if enforced).
>
> This is an updated subset of the patch initially written by Vincent
> Strubel for CLIP OS 4:
> https://github.com/clipos-archive/src_platform_clip-patches/blob/f5cb330d6b684752e403b4e41b39f7004d88e561/1901_open_mayexec.patch
> This patch has been used for more than 12 years with customized script
> interpreters. Some examples (with the original O_MAYEXEC) can be found
> here:
> https://github.com/clipos-archive/clipos4_portage-overlay/search?q=O_MAYEXEC
>
> Co-developed-by: Vincent Strubel <[email protected]>
> Signed-off-by: Vincent Strubel <[email protected]>

Acked-by: Kees Cook <[email protected]>

--
Kees Cook

2020-07-24 19:06:05

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH v7 7/7] ima: add policy support for the new file open MAY_OPENEXEC flag

On Thu, Jul 23, 2020 at 07:12:27PM +0200, Micka?l Sala?n wrote:
> From: Mimi Zohar <[email protected]>
>
> The kernel has no way of differentiating between a file containing data
> or code being opened by an interpreter. The proposed O_MAYEXEC
> openat2(2) flag bridges this gap by defining and enabling the
> MAY_OPENEXEC flag.
>
> This patch adds IMA policy support for the new MAY_OPENEXEC flag.
>
> Example:
> measure func=FILE_CHECK mask=^MAY_OPENEXEC
> appraise func=FILE_CHECK appraise_type=imasig mask=^MAY_OPENEXEC
>
> Signed-off-by: Micka?l Sala?n <[email protected]>

^^^ this S-o-b should the last in the fields.

> Signed-off-by: Mimi Zohar <[email protected]>
> Reviewed-by: Lakshmi Ramasubramanian <[email protected]>
> Link: https://lore.kernel.org/r/[email protected]

Reviewed-by: Kees Cook <[email protected]>

--
Kees Cook

2020-07-24 19:10:01

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH v7 0/7] Add support for O_MAYEXEC

I think this looks good now.

Andrew, since you're already carrying my exec clean-ups (repeated here
in patch 1-3), can you pick the rest of this series too?

Thanks!

-Kees

On Thu, Jul 23, 2020 at 07:12:20PM +0200, Micka?l Sala?n wrote:
> Hi,
>
> This seventh patch series do not set __FMODE_EXEC for the sake of
> simplicity. A notification feature could be added later if needed. The
> handling of all file types is now well defined and tested: by default,
> when opening a path, access to a directory is denied (with EISDIR),
> access to a regular file depends on the sysctl policy, and access to
> other file types (i.e. fifo, device, socket) is denied if there is any
> enforced policy. There is new tests covering all these cases (cf.
> test_file_types() ).
>
> As requested by Mimi Zohar, I completed the series with one of her
> patches for IMA. I also picked Kees Cook's patches to consolidate exec
> permission checking into do_filp_open()'s flow.
>
>
> # Goal of O_MAYEXEC
>
> The goal of this patch series is to enable to control script execution
> with interpreters help. A new O_MAYEXEC flag, usable through
> openat2(2), is added to enable userspace script interpreters to delegate
> to the kernel (and thus the system security policy) the permission to
> interpret/execute scripts or other files containing what can be seen as
> commands.
>
> A simple system-wide security policy can be enforced by the system
> administrator through a sysctl configuration consistent with the mount
> points or the file access rights. The documentation patch explains the
> prerequisites.
>
> Furthermore, the security policy can also be delegated to an LSM, either
> a MAC system or an integrity system. For instance, the new kernel
> MAY_OPENEXEC flag closes a major IMA measurement/appraisal interpreter
> integrity gap by bringing the ability to check the use of scripts [1].
> Other uses are expected, such as for magic-links [2], SGX integration
> [3], bpffs [4] or IPE [5].
>
>
> # Prerequisite of its use
>
> Userspace needs to adapt to take advantage of this new feature. For
> example, the PEP 578 [6] (Runtime Audit Hooks) enables Python 3.8 to be
> extended with policy enforcement points related to code interpretation,
> which can be used to align with the PowerShell audit features.
> Additional Python security improvements (e.g. a limited interpreter
> withou -c, stdin piping of code) are on their way [7].
>
>
> # Examples
>
> The initial idea comes from CLIP OS 4 and the original implementation
> has been used for more than 12 years:
> https://github.com/clipos-archive/clipos4_doc
> Chrome OS has a similar approach:
> https://chromium.googlesource.com/chromiumos/docs/+/master/security/noexec_shell_scripts.md
>
> Userland patches can be found here:
> https://github.com/clipos-archive/clipos4_portage-overlay/search?q=O_MAYEXEC
> Actually, there is more than the O_MAYEXEC changes (which matches this search)
> e.g., to prevent Python interactive execution. There are patches for
> Bash, Wine, Java (Icedtea), Busybox's ash, Perl and Python. There are
> also some related patches which do not directly rely on O_MAYEXEC but
> which restrict the use of browser plugins and extensions, which may be
> seen as scripts too:
> https://github.com/clipos-archive/clipos4_portage-overlay/tree/master/www-client
>
> An introduction to O_MAYEXEC was given at the Linux Security Summit
> Europe 2018 - Linux Kernel Security Contributions by ANSSI:
> https://www.youtube.com/watch?v=chNjCRtPKQY&t=17m15s
> The "write xor execute" principle was explained at Kernel Recipes 2018 -
> CLIP OS: a defense-in-depth OS:
> https://www.youtube.com/watch?v=PjRE0uBtkHU&t=11m14s
> See also an overview article: https://lwn.net/Articles/820000/
>
>
> This patch series can be applied on top of v5.8-rc5 . This can be tested
> with CONFIG_SYSCTL. I would really appreciate constructive comments on
> this patch series.
>
> Previous version:
> https://lore.kernel.org/lkml/[email protected]/
>
>
> [1] https://lore.kernel.org/lkml/[email protected]/
> [2] https://lore.kernel.org/lkml/[email protected]/
> [3] https://lore.kernel.org/lkml/CALCETrVovr8XNZSroey7pHF46O=kj_c5D9K8h=z2T_cNrpvMig@mail.gmail.com/
> [4] https://lore.kernel.org/lkml/CALCETrVeZ0eufFXwfhtaG_j+AdvbzEWE0M3wjXMWVEO7pj+xkw@mail.gmail.com/
> [5] https://lore.kernel.org/lkml/[email protected]/
> [6] https://www.python.org/dev/peps/pep-0578/
> [7] https://lore.kernel.org/lkml/[email protected]/
>
> Regards,
>
> Kees Cook (3):
> exec: Change uselib(2) IS_SREG() failure to EACCES
> exec: Move S_ISREG() check earlier
> exec: Move path_noexec() check earlier
>
> Micka?l Sala?n (3):
> fs: Introduce O_MAYEXEC flag for openat2(2)
> fs,doc: Enable to enforce noexec mounts or file exec through O_MAYEXEC
> selftest/openat2: Add tests for O_MAYEXEC enforcing
>
> Mimi Zohar (1):
> ima: add policy support for the new file open MAY_OPENEXEC flag
>
> Documentation/ABI/testing/ima_policy | 2 +-
> Documentation/admin-guide/sysctl/fs.rst | 49 +++
> fs/exec.c | 23 +-
> fs/fcntl.c | 2 +-
> fs/namei.c | 36 +-
> fs/open.c | 12 +-
> include/linux/fcntl.h | 2 +-
> include/linux/fs.h | 3 +
> include/uapi/asm-generic/fcntl.h | 7 +
> kernel/sysctl.c | 12 +-
> security/integrity/ima/ima_main.c | 3 +-
> security/integrity/ima/ima_policy.c | 15 +-
> tools/testing/selftests/kselftest_harness.h | 3 +
> tools/testing/selftests/openat2/Makefile | 3 +-
> tools/testing/selftests/openat2/config | 1 +
> tools/testing/selftests/openat2/helpers.h | 1 +
> .../testing/selftests/openat2/omayexec_test.c | 325 ++++++++++++++++++
> 17 files changed, 470 insertions(+), 29 deletions(-)
> create mode 100644 tools/testing/selftests/openat2/config
> create mode 100644 tools/testing/selftests/openat2/omayexec_test.c
>
> --
> 2.27.0
>

--
Kees Cook

2020-07-25 11:21:31

by Christian Brauner

[permalink] [raw]
Subject: Re: [PATCH v7 0/7] Add support for O_MAYEXEC

On Fri, Jul 24, 2020 at 12:06:53PM -0700, Kees Cook wrote:
> I think this looks good now.
>
> Andrew, since you're already carrying my exec clean-ups (repeated here
> in patch 1-3), can you pick the rest of this series too?

Al,

Not sure if you have already re-surfaced from your
csum()/raw_copy_from_user() work completely yet but you had thoughts
about this series iirc.

Aleksa, thoughts?

Thanks!
Christian

2020-07-27 04:21:51

by Al Viro

[permalink] [raw]
Subject: Re: [PATCH v7 4/7] fs: Introduce O_MAYEXEC flag for openat2(2)

On Thu, Jul 23, 2020 at 07:12:24PM +0200, Micka?l Sala?n wrote:
> When the O_MAYEXEC flag is passed, openat2(2) may be subject to
> additional restrictions depending on a security policy managed by the
> kernel through a sysctl or implemented by an LSM thanks to the
> inode_permission hook. This new flag is ignored by open(2) and
> openat(2) because of their unspecified flags handling. When used with
> openat2(2), the default behavior is only to forbid to open a directory.

Correct me if I'm wrong, but it looks like you are introducing a magical
flag that would mean "let the Linux S&M take an extra special whip
for this open()".

Why is it done during open? If the caller is passing it deliberately,
why not have an explicit request to apply given torture device to an
already opened file? Why not sys_masochism(int fd, char *hurt_flavour),
for that matter?

2020-07-27 05:28:15

by Florian Weimer

[permalink] [raw]
Subject: Re: [PATCH v7 4/7] fs: Introduce O_MAYEXEC flag for openat2(2)

* Al Viro:

> On Thu, Jul 23, 2020 at 07:12:24PM +0200, Mickaël Salaün wrote:
>> When the O_MAYEXEC flag is passed, openat2(2) may be subject to
>> additional restrictions depending on a security policy managed by the
>> kernel through a sysctl or implemented by an LSM thanks to the
>> inode_permission hook. This new flag is ignored by open(2) and
>> openat(2) because of their unspecified flags handling. When used with
>> openat2(2), the default behavior is only to forbid to open a directory.
>
> Correct me if I'm wrong, but it looks like you are introducing a magical
> flag that would mean "let the Linux S&M take an extra special whip
> for this open()".
>
> Why is it done during open? If the caller is passing it deliberately,
> why not have an explicit request to apply given torture device to an
> already opened file? Why not sys_masochism(int fd, char *hurt_flavour),
> for that matter?

While I do not think this is appropriate language for a workplace, Al
has a point: If the auditing event can be generated on an already-open
descriptor, it would also cover scenarios like this one:

perl < /path/to/script

Where the process that opens the file does not (and cannot) know that it
will be used for execution purposes.

Thanks,
Florian

2020-07-27 19:49:20

by Mickaël Salaün

[permalink] [raw]
Subject: Re: [PATCH v7 4/7] fs: Introduce O_MAYEXEC flag for openat2(2)


On 27/07/2020 07:27, Florian Weimer wrote:
> * Al Viro:
>
>> On Thu, Jul 23, 2020 at 07:12:24PM +0200, Mickaël Salaün wrote:
>>> When the O_MAYEXEC flag is passed, openat2(2) may be subject to
>>> additional restrictions depending on a security policy managed by the
>>> kernel through a sysctl or implemented by an LSM thanks to the
>>> inode_permission hook. This new flag is ignored by open(2) and
>>> openat(2) because of their unspecified flags handling. When used with
>>> openat2(2), the default behavior is only to forbid to open a directory.
>>
>> Correct me if I'm wrong, but it looks like you are introducing a magical
>> flag that would mean "let the Linux S&M take an extra special whip
>> for this open()".

There is nothing magic, it doesn't only work with the LSM framework, and
there is nothing painful nor humiliating here (except maybe this language).

>>
>> Why is it done during open? If the caller is passing it deliberately,
>> why not have an explicit request to apply given torture device to an
>> already opened file? Why not sys_masochism(int fd, char *hurt_flavour),
>> for that matter?
>
> While I do not think this is appropriate language for a workplace, Al
> has a point: If the auditing event can be generated on an already-open
> descriptor, it would also cover scenarios like this one:
>
> perl < /path/to/script
>
> Where the process that opens the file does not (and cannot) know that it
> will be used for execution purposes.

The check is done during open because the goal of this patch series is
to address the problem of script execution when opening a script in well
controlled systems (e.g. to enforce a "write xor execute" policy, to do
an atomic integrity check [1], to check specific execute/read
permissions, etc.). As discussed multiple times, controlling other means
to interpret commands (stdin, environment variables, etc.) is out of
scope and should be handled by interpreters (in userspace). Someone
could still extend fcntl(2) to enable to check file descriptors, but it
is an independent change not required for now.
Specific audit features are also out of scope for now [2].

[1] https://lore.kernel.org/lkml/[email protected]/
[2] https://lore.kernel.org/lkml/202007160822.CCDB5478@keescook/

2020-08-10 20:14:33

by Mickaël Salaün

[permalink] [raw]
Subject: Re: [PATCH v7 0/7] Add support for O_MAYEXEC

It seems that there is no more complains nor questions. Do you want me
to send another series to fix the order of the S-o-b in patch 7?


On 24/07/2020 21:06, Kees Cook wrote:
> I think this looks good now.
>
> Andrew, since you're already carrying my exec clean-ups (repeated here
> in patch 1-3), can you pick the rest of this series too?
>
> Thanks!
>
> -Kees
>
> On Thu, Jul 23, 2020 at 07:12:20PM +0200, Micka?l Sala?n wrote:
>> Hi,
>>
>> This seventh patch series do not set __FMODE_EXEC for the sake of
>> simplicity. A notification feature could be added later if needed. The
>> handling of all file types is now well defined and tested: by default,
>> when opening a path, access to a directory is denied (with EISDIR),
>> access to a regular file depends on the sysctl policy, and access to
>> other file types (i.e. fifo, device, socket) is denied if there is any
>> enforced policy. There is new tests covering all these cases (cf.
>> test_file_types() ).
>>
>> As requested by Mimi Zohar, I completed the series with one of her
>> patches for IMA. I also picked Kees Cook's patches to consolidate exec
>> permission checking into do_filp_open()'s flow.
>>
>>
>> # Goal of O_MAYEXEC
>>
>> The goal of this patch series is to enable to control script execution
>> with interpreters help. A new O_MAYEXEC flag, usable through
>> openat2(2), is added to enable userspace script interpreters to delegate
>> to the kernel (and thus the system security policy) the permission to
>> interpret/execute scripts or other files containing what can be seen as
>> commands.
>>
>> A simple system-wide security policy can be enforced by the system
>> administrator through a sysctl configuration consistent with the mount
>> points or the file access rights. The documentation patch explains the
>> prerequisites.
>>
>> Furthermore, the security policy can also be delegated to an LSM, either
>> a MAC system or an integrity system. For instance, the new kernel
>> MAY_OPENEXEC flag closes a major IMA measurement/appraisal interpreter
>> integrity gap by bringing the ability to check the use of scripts [1].
>> Other uses are expected, such as for magic-links [2], SGX integration
>> [3], bpffs [4] or IPE [5].
>>
>>
>> # Prerequisite of its use
>>
>> Userspace needs to adapt to take advantage of this new feature. For
>> example, the PEP 578 [6] (Runtime Audit Hooks) enables Python 3.8 to be
>> extended with policy enforcement points related to code interpretation,
>> which can be used to align with the PowerShell audit features.
>> Additional Python security improvements (e.g. a limited interpreter
>> withou -c, stdin piping of code) are on their way [7].
>>
>>
>> # Examples
>>
>> The initial idea comes from CLIP OS 4 and the original implementation
>> has been used for more than 12 years:
>> https://github.com/clipos-archive/clipos4_doc
>> Chrome OS has a similar approach:
>> https://chromium.googlesource.com/chromiumos/docs/+/master/security/noexec_shell_scripts.md
>>
>> Userland patches can be found here:
>> https://github.com/clipos-archive/clipos4_portage-overlay/search?q=O_MAYEXEC
>> Actually, there is more than the O_MAYEXEC changes (which matches this search)
>> e.g., to prevent Python interactive execution. There are patches for
>> Bash, Wine, Java (Icedtea), Busybox's ash, Perl and Python. There are
>> also some related patches which do not directly rely on O_MAYEXEC but
>> which restrict the use of browser plugins and extensions, which may be
>> seen as scripts too:
>> https://github.com/clipos-archive/clipos4_portage-overlay/tree/master/www-client
>>
>> An introduction to O_MAYEXEC was given at the Linux Security Summit
>> Europe 2018 - Linux Kernel Security Contributions by ANSSI:
>> https://www.youtube.com/watch?v=chNjCRtPKQY&t=17m15s
>> The "write xor execute" principle was explained at Kernel Recipes 2018 -
>> CLIP OS: a defense-in-depth OS:
>> https://www.youtube.com/watch?v=PjRE0uBtkHU&t=11m14s
>> See also an overview article: https://lwn.net/Articles/820000/
>>
>>
>> This patch series can be applied on top of v5.8-rc5 . This can be tested
>> with CONFIG_SYSCTL. I would really appreciate constructive comments on
>> this patch series.
>>
>> Previous version:
>> https://lore.kernel.org/lkml/[email protected]/
>>
>>
>> [1] https://lore.kernel.org/lkml/[email protected]/
>> [2] https://lore.kernel.org/lkml/[email protected]/
>> [3] https://lore.kernel.org/lkml/CALCETrVovr8XNZSroey7pHF46O=kj_c5D9K8h=z2T_cNrpvMig@mail.gmail.com/
>> [4] https://lore.kernel.org/lkml/CALCETrVeZ0eufFXwfhtaG_j+AdvbzEWE0M3wjXMWVEO7pj+xkw@mail.gmail.com/
>> [5] https://lore.kernel.org/lkml/[email protected]/
>> [6] https://www.python.org/dev/peps/pep-0578/
>> [7] https://lore.kernel.org/lkml/[email protected]/
>>
>> Regards,
>>
>> Kees Cook (3):
>> exec: Change uselib(2) IS_SREG() failure to EACCES
>> exec: Move S_ISREG() check earlier
>> exec: Move path_noexec() check earlier
>>
>> Micka?l Sala?n (3):
>> fs: Introduce O_MAYEXEC flag for openat2(2)
>> fs,doc: Enable to enforce noexec mounts or file exec through O_MAYEXEC
>> selftest/openat2: Add tests for O_MAYEXEC enforcing
>>
>> Mimi Zohar (1):
>> ima: add policy support for the new file open MAY_OPENEXEC flag
>>
>> Documentation/ABI/testing/ima_policy | 2 +-
>> Documentation/admin-guide/sysctl/fs.rst | 49 +++
>> fs/exec.c | 23 +-
>> fs/fcntl.c | 2 +-
>> fs/namei.c | 36 +-
>> fs/open.c | 12 +-
>> include/linux/fcntl.h | 2 +-
>> include/linux/fs.h | 3 +
>> include/uapi/asm-generic/fcntl.h | 7 +
>> kernel/sysctl.c | 12 +-
>> security/integrity/ima/ima_main.c | 3 +-
>> security/integrity/ima/ima_policy.c | 15 +-
>> tools/testing/selftests/kselftest_harness.h | 3 +
>> tools/testing/selftests/openat2/Makefile | 3 +-
>> tools/testing/selftests/openat2/config | 1 +
>> tools/testing/selftests/openat2/helpers.h | 1 +
>> .../testing/selftests/openat2/omayexec_test.c | 325 ++++++++++++++++++
>> 17 files changed, 470 insertions(+), 29 deletions(-)
>> create mode 100644 tools/testing/selftests/openat2/config
>> create mode 100644 tools/testing/selftests/openat2/omayexec_test.c
>>
>> --
>> 2.27.0
>>
>

2020-08-10 20:25:05

by Al Viro

[permalink] [raw]
Subject: Re: [PATCH v7 0/7] Add support for O_MAYEXEC

On Mon, Aug 10, 2020 at 10:11:53PM +0200, Micka?l Sala?n wrote:
> It seems that there is no more complains nor questions. Do you want me
> to send another series to fix the order of the S-o-b in patch 7?

There is a major question regarding the API design and the choice of
hooking that stuff on open(). And I have not heard anything resembling
a coherent answer.

2020-08-10 22:10:38

by David Laight

[permalink] [raw]
Subject: RE: [PATCH v7 0/7] Add support for O_MAYEXEC

> On Mon, Aug 10, 2020 at 10:11:53PM +0200, Mickaël Salaün wrote:
> > It seems that there is no more complains nor questions. Do you want me
> > to send another series to fix the order of the S-o-b in patch 7?
>
> There is a major question regarding the API design and the choice of
> hooking that stuff on open(). And I have not heard anything resembling
> a coherent answer.

To me O_MAYEXEC is just the wrong name.
The bit would be (something like) O_INTERPRET to indicate
what you want to do with the contents.

The kernel 'policy' then decides whether that needs 'r-x'
access or whether 'r--' access in enough.

I think that is what you 100 line comment in 0/n means.

David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)

2020-08-10 22:30:25

by Al Viro

[permalink] [raw]
Subject: Re: [PATCH v7 0/7] Add support for O_MAYEXEC

On Mon, Aug 10, 2020 at 10:09:09PM +0000, David Laight wrote:
> > On Mon, Aug 10, 2020 at 10:11:53PM +0200, Micka?l Sala?n wrote:
> > > It seems that there is no more complains nor questions. Do you want me
> > > to send another series to fix the order of the S-o-b in patch 7?
> >
> > There is a major question regarding the API design and the choice of
> > hooking that stuff on open(). And I have not heard anything resembling
> > a coherent answer.
>
> To me O_MAYEXEC is just the wrong name.
> The bit would be (something like) O_INTERPRET to indicate
> what you want to do with the contents.

... which does not answer the question - name of constant is the least of
the worries here. Why the hell is "apply some unspecified checks to
file" combined with opening it, rather than being an independent primitive
you apply to an already opened file? Just in case - "'cuz that's how we'd
done it" does not make a good answer...

2020-08-10 22:47:11

by Mickaël Salaün

[permalink] [raw]
Subject: Re: [PATCH v7 0/7] Add support for O_MAYEXEC


On 10/08/2020 22:21, Al Viro wrote:
> On Mon, Aug 10, 2020 at 10:11:53PM +0200, Micka?l Sala?n wrote:
>> It seems that there is no more complains nor questions. Do you want me
>> to send another series to fix the order of the S-o-b in patch 7?
>
> There is a major question regarding the API design and the choice of
> hooking that stuff on open(). And I have not heard anything resembling
> a coherent answer.

Hooking on open is a simple design that enables processes to check files
they intend to open, before they open them. From an API point of view,
this series extends openat2(2) with one simple flag: O_MAYEXEC. The
enforcement is then subject to the system policy (e.g. mount points,
file access rights, IMA, etc.).

Checking on open enables to not open a file if it does not meet some
requirements, the same way as if the path doesn't exist or (for whatever
reasons, including execution permission) if access is denied. It is a
good practice to check as soon as possible such properties, and it may
enables to avoid (user space) time-of-check to time-of-use (TOCTOU)
attacks (i.e. misuse of already open resources). It is important to keep
in mind that the use cases we are addressing consider that the (user
space) script interpreters (or linkers) are trusted and unaltered (i.e.
integrity/authenticity checked). These are similar sought defensive
properties as for SUID/SGID binaries: attackers can still launch them
with malicious inputs (e.g. file paths, file descriptors, environment
variables, etc.), but the binaries can then have a way to check if they
can extend their trust to some file paths.

Checking file descriptors may help in some use cases, but not the ones
motivating this series. Checking (already) opened resources could be a
*complementary* way to check execute permission, but it is not in the
scope of this series.

2020-08-10 22:48:32

by Mickaël Salaün

[permalink] [raw]
Subject: Re: [PATCH v7 0/7] Add support for O_MAYEXEC



On 11/08/2020 00:28, Al Viro wrote:
> On Mon, Aug 10, 2020 at 10:09:09PM +0000, David Laight wrote:
>>> On Mon, Aug 10, 2020 at 10:11:53PM +0200, Micka?l Sala?n wrote:
>>>> It seems that there is no more complains nor questions. Do you want me
>>>> to send another series to fix the order of the S-o-b in patch 7?
>>>
>>> There is a major question regarding the API design and the choice of
>>> hooking that stuff on open(). And I have not heard anything resembling
>>> a coherent answer.
>>
>> To me O_MAYEXEC is just the wrong name.
>> The bit would be (something like) O_INTERPRET to indicate
>> what you want to do with the contents.

The properties is "execute permission". This can then be checked by
interpreters or other applications, then the generic O_MAYEXEC name.

>
> ... which does not answer the question - name of constant is the least of
> the worries here. Why the hell is "apply some unspecified checks to
> file" combined with opening it, rather than being an independent primitive
> you apply to an already opened file? Just in case - "'cuz that's how we'd
> done it" does not make a good answer...
>

That is not the case, see
https://lore.kernel.org/lkml/[email protected]/

2020-08-10 23:05:54

by Jann Horn

[permalink] [raw]
Subject: Re: [PATCH v7 0/7] Add support for O_MAYEXEC

On Tue, Aug 11, 2020 at 12:43 AM Mickaël Salaün <[email protected]> wrote:
> On 10/08/2020 22:21, Al Viro wrote:
> > On Mon, Aug 10, 2020 at 10:11:53PM +0200, Mickaël Salaün wrote:
> >> It seems that there is no more complains nor questions. Do you want me
> >> to send another series to fix the order of the S-o-b in patch 7?
> >
> > There is a major question regarding the API design and the choice of
> > hooking that stuff on open(). And I have not heard anything resembling
> > a coherent answer.
>
> Hooking on open is a simple design that enables processes to check files
> they intend to open, before they open them. From an API point of view,
> this series extends openat2(2) with one simple flag: O_MAYEXEC. The
> enforcement is then subject to the system policy (e.g. mount points,
> file access rights, IMA, etc.).
>
> Checking on open enables to not open a file if it does not meet some
> requirements, the same way as if the path doesn't exist or (for whatever
> reasons, including execution permission) if access is denied.

You can do exactly the same thing if you do the check in a separate
syscall though.

And it provides a greater degree of flexibility; for example, you can
use it in combination with fopen() without having to modify the
internals of fopen() or having to use fdopen().

> It is a
> good practice to check as soon as possible such properties, and it may
> enables to avoid (user space) time-of-check to time-of-use (TOCTOU)
> attacks (i.e. misuse of already open resources).

The assumption that security checks should happen as early as possible
can actually cause security problems. For example, because seccomp was
designed to do its checks as early as possible, including before
ptrace, we had an issue for a long time where the ptrace API could be
abused to bypass seccomp filters.

Please don't decide that a check must be ordered first _just_ because
it is a security check. While that can be good for limiting attack
surface, it can also create issues when the idea is applied too
broadly.

I don't see how TOCTOU issues are relevant in any way here. If someone
can turn a script that is considered a trusted file into an untrusted
file and then maliciously change its contents, you're going to have
issues either way because the modifications could still happen after
openat(); if this was possible, the whole thing would kind of fall
apart. And if that isn't possible, I don't see any TOCTOU.

> It is important to keep
> in mind that the use cases we are addressing consider that the (user
> space) script interpreters (or linkers) are trusted and unaltered (i.e.
> integrity/authenticity checked). These are similar sought defensive
> properties as for SUID/SGID binaries: attackers can still launch them
> with malicious inputs (e.g. file paths, file descriptors, environment
> variables, etc.), but the binaries can then have a way to check if they
> can extend their trust to some file paths.
>
> Checking file descriptors may help in some use cases, but not the ones
> motivating this series.

It actually provides a superset of the functionality that your
existing patches provide.

> Checking (already) opened resources could be a
> *complementary* way to check execute permission, but it is not in the
> scope of this series.

2020-08-10 23:13:35

by Al Viro

[permalink] [raw]
Subject: Re: [PATCH v7 0/7] Add support for O_MAYEXEC

On Tue, Aug 11, 2020 at 12:43:52AM +0200, Micka?l Sala?n wrote:

> Hooking on open is a simple design that enables processes to check files
> they intend to open, before they open them.

Which is a good thing, because...?

> From an API point of view,
> this series extends openat2(2) with one simple flag: O_MAYEXEC. The
> enforcement is then subject to the system policy (e.g. mount points,
> file access rights, IMA, etc.).

That's what "unspecified" means - as far as the kernel concerned, it's
"something completely opaque, will let these hooks to play, semantics is
entirely up to them".

> Checking on open enables to not open a file if it does not meet some
> requirements, the same way as if the path doesn't exist or (for whatever
> reasons, including execution permission) if access is denied. It is a
> good practice to check as soon as possible such properties, and it may
> enables to avoid (user space) time-of-check to time-of-use (TOCTOU)
> attacks (i.e. misuse of already open resources).

????? You explicitly assume a cooperating caller. If it can't be trusted
to issue the check between open and use, or can be manipulated (ptraced,
etc.) into not doing so, how can you rely upon the flag having been passed
in the first place? And TOCTOU window is definitely not wider that way.

If you want to have it done immediately after open(), bloody well do it
immediately after open. If attacker has subverted your control flow to the
extent that allows them to hit descriptor table in the interval between
these two syscalls, you have already lost - they'll simply prevent that
flag from being passed.

What's the point of burying it inside openat2()? A convenient multiplexor
to hook into? We already have one - it's called do_syscall_...

2020-08-11 08:12:29

by David Laight

[permalink] [raw]
Subject: RE: [PATCH v7 0/7] Add support for O_MAYEXEC

> On 11/08/2020 00:28, Al Viro wrote:
> > On Mon, Aug 10, 2020 at 10:09:09PM +0000, David Laight wrote:
> >>> On Mon, Aug 10, 2020 at 10:11:53PM +0200, Mickaël Salaün wrote:
> >>>> It seems that there is no more complains nor questions. Do you want me
> >>>> to send another series to fix the order of the S-o-b in patch 7?
> >>>
> >>> There is a major question regarding the API design and the choice of
> >>> hooking that stuff on open(). And I have not heard anything resembling
> >>> a coherent answer.
> >>
> >> To me O_MAYEXEC is just the wrong name.
> >> The bit would be (something like) O_INTERPRET to indicate
> >> what you want to do with the contents.
>
> The properties is "execute permission". This can then be checked by
> interpreters or other applications, then the generic O_MAYEXEC name.

The english sense of MAYEXEC is just wrong for what you are trying
to check.

> > ... which does not answer the question - name of constant is the least of
> > the worries here. Why the hell is "apply some unspecified checks to
> > file" combined with opening it, rather than being an independent primitive
> > you apply to an already opened file? Just in case - "'cuz that's how we'd
> > done it" does not make a good answer...

Maybe an access_ok() that acts on an open fd would be more
appropriate.
Which might end up being an fcntrl() action.
That would give you a full 32bit mask of options.

David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)

2020-08-11 08:50:11

by Mickaël Salaün

[permalink] [raw]
Subject: Re: [PATCH v7 0/7] Add support for O_MAYEXEC


On 11/08/2020 01:03, Jann Horn wrote:
> On Tue, Aug 11, 2020 at 12:43 AM Mickaël Salaün <[email protected]> wrote:
>> On 10/08/2020 22:21, Al Viro wrote:
>>> On Mon, Aug 10, 2020 at 10:11:53PM +0200, Mickaël Salaün wrote:
>>>> It seems that there is no more complains nor questions. Do you want me
>>>> to send another series to fix the order of the S-o-b in patch 7?
>>>
>>> There is a major question regarding the API design and the choice of
>>> hooking that stuff on open(). And I have not heard anything resembling
>>> a coherent answer.
>>
>> Hooking on open is a simple design that enables processes to check files
>> they intend to open, before they open them. From an API point of view,
>> this series extends openat2(2) with one simple flag: O_MAYEXEC. The
>> enforcement is then subject to the system policy (e.g. mount points,
>> file access rights, IMA, etc.).
>>
>> Checking on open enables to not open a file if it does not meet some
>> requirements, the same way as if the path doesn't exist or (for whatever
>> reasons, including execution permission) if access is denied.
>
> You can do exactly the same thing if you do the check in a separate
> syscall though.
>
> And it provides a greater degree of flexibility; for example, you can
> use it in combination with fopen() without having to modify the
> internals of fopen() or having to use fdopen().
>
>> It is a
>> good practice to check as soon as possible such properties, and it may
>> enables to avoid (user space) time-of-check to time-of-use (TOCTOU)
>> attacks (i.e. misuse of already open resources).
>
> The assumption that security checks should happen as early as possible
> can actually cause security problems. For example, because seccomp was
> designed to do its checks as early as possible, including before
> ptrace, we had an issue for a long time where the ptrace API could be
> abused to bypass seccomp filters.
>
> Please don't decide that a check must be ordered first _just_ because
> it is a security check. While that can be good for limiting attack
> surface, it can also create issues when the idea is applied too
> broadly.

I'd be interested with such security issue examples.

I hope that delaying checks will not be an issue for mechanisms such as
IMA or IPE:
https://lore.kernel.org/lkml/[email protected]/

Any though Mimi, Deven, Chrome OS folks?

>
> I don't see how TOCTOU issues are relevant in any way here. If someone
> can turn a script that is considered a trusted file into an untrusted
> file and then maliciously change its contents, you're going to have
> issues either way because the modifications could still happen after
> openat(); if this was possible, the whole thing would kind of fall
> apart. And if that isn't possible, I don't see any TOCTOU.

Sure, and if the scripts are not protected in some way there is no point
to check anything.

>
>> It is important to keep
>> in mind that the use cases we are addressing consider that the (user
>> space) script interpreters (or linkers) are trusted and unaltered (i.e.
>> integrity/authenticity checked). These are similar sought defensive
>> properties as for SUID/SGID binaries: attackers can still launch them
>> with malicious inputs (e.g. file paths, file descriptors, environment
>> variables, etc.), but the binaries can then have a way to check if they
>> can extend their trust to some file paths.
>>
>> Checking file descriptors may help in some use cases, but not the ones
>> motivating this series.
>
> It actually provides a superset of the functionality that your
> existing patches provide.

It also brings new issues with multiple file descriptor origins (e.g.
memfd_create).

>
>> Checking (already) opened resources could be a
>> *complementary* way to check execute permission, but it is not in the
>> scope of this series.

2020-08-11 08:51:11

by Mickaël Salaün

[permalink] [raw]
Subject: Re: [PATCH v7 0/7] Add support for O_MAYEXEC


On 11/08/2020 01:05, Al Viro wrote:
> On Tue, Aug 11, 2020 at 12:43:52AM +0200, Micka?l Sala?n wrote:
>
>> Hooking on open is a simple design that enables processes to check files
>> they intend to open, before they open them.
>
> Which is a good thing, because...?
>
>> From an API point of view,
>> this series extends openat2(2) with one simple flag: O_MAYEXEC. The
>> enforcement is then subject to the system policy (e.g. mount points,
>> file access rights, IMA, etc.).
>
> That's what "unspecified" means - as far as the kernel concerned, it's
> "something completely opaque, will let these hooks to play, semantics is
> entirely up to them".

I see it as an access controls mechanism; access may be granted or
denied, as for O_RDONLY, O_WRONLY or (non-Linux) O_EXEC. Even for common
access controls, there are capabilities to bypass them (i.e.
CAP_DAC_OVERRIDE), but multiple layers may enforce different
complementary policies.

>
>> Checking on open enables to not open a file if it does not meet some
>> requirements, the same way as if the path doesn't exist or (for whatever
>> reasons, including execution permission) if access is denied. It is a
>> good practice to check as soon as possible such properties, and it may
>> enables to avoid (user space) time-of-check to time-of-use (TOCTOU)
>> attacks (i.e. misuse of already open resources).
>
> ????? You explicitly assume a cooperating caller.

As said in the below (removed) reply, no, quite the contrary.

> If it can't be trusted
> to issue the check between open and use, or can be manipulated (ptraced,
> etc.) into not doing so, how can you rely upon the flag having been passed
> in the first place? And TOCTOU window is definitely not wider that way.

OK, I guess it would be considered a bug in the application (e.g. buggy
resource management between threads).

>
> If you want to have it done immediately after open(), bloody well do it
> immediately after open. If attacker has subverted your control flow to the
> extent that allows them to hit descriptor table in the interval between
> these two syscalls, you have already lost - they'll simply prevent that
> flag from being passed.
>
> What's the point of burying it inside openat2()? A convenient multiplexor
> to hook into? We already have one - it's called do_syscall_...
>

To check as soon as possible without opening something that should not
be opened in the first place.

Isn't a dedicated syscall a bit too much for this feature? What about
adding a new command/flag to fcntl(2)?

2020-08-11 08:52:02

by Mickaël Salaün

[permalink] [raw]
Subject: Re: [PATCH v7 0/7] Add support for O_MAYEXEC


On 11/08/2020 10:09, David Laight wrote:
>> On 11/08/2020 00:28, Al Viro wrote:
>>> On Mon, Aug 10, 2020 at 10:09:09PM +0000, David Laight wrote:
>>>>> On Mon, Aug 10, 2020 at 10:11:53PM +0200, Mickaël Salaün wrote:
>>>>>> It seems that there is no more complains nor questions. Do you want me
>>>>>> to send another series to fix the order of the S-o-b in patch 7?
>>>>>
>>>>> There is a major question regarding the API design and the choice of
>>>>> hooking that stuff on open(). And I have not heard anything resembling
>>>>> a coherent answer.
>>>>
>>>> To me O_MAYEXEC is just the wrong name.
>>>> The bit would be (something like) O_INTERPRET to indicate
>>>> what you want to do with the contents.
>>
>> The properties is "execute permission". This can then be checked by
>> interpreters or other applications, then the generic O_MAYEXEC name.
>
> The english sense of MAYEXEC is just wrong for what you are trying
> to check.

We think it reflects exactly what it's purpose is.

>
>>> ... which does not answer the question - name of constant is the least of
>>> the worries here. Why the hell is "apply some unspecified checks to
>>> file" combined with opening it, rather than being an independent primitive
>>> you apply to an already opened file? Just in case - "'cuz that's how we'd
>>> done it" does not make a good answer...
>
> Maybe an access_ok() that acts on an open fd would be more
> appropriate.
> Which might end up being an fcntrl() action.
> That would give you a full 32bit mask of options.

I previously talk about fcntl(2):
https://lore.kernel.org/lkml/[email protected]/

2020-08-11 14:02:08

by Mimi Zohar

[permalink] [raw]
Subject: Re: [PATCH v7 0/7] Add support for O_MAYEXEC

On Tue, 2020-08-11 at 10:48 +0200, Micka?l Sala?n wrote:
> On 11/08/2020 01:03, Jann Horn wrote:
> > On Tue, Aug 11, 2020 at 12:43 AM Micka?l Sala?n <[email protected]> wrote:
> > > On 10/08/2020 22:21, Al Viro wrote:
> > > > On Mon, Aug 10, 2020 at 10:11:53PM +0200, Micka?l Sala?n wrote:
> > > > > It seems that there is no more complains nor questions. Do you want me
> > > > > to send another series to fix the order of the S-o-b in patch 7?
> > > >
> > > > There is a major question regarding the API design and the choice of
> > > > hooking that stuff on open(). And I have not heard anything resembling
> > > > a coherent answer.
> > >
> > > Hooking on open is a simple design that enables processes to check files
> > > they intend to open, before they open them. From an API point of view,
> > > this series extends openat2(2) with one simple flag: O_MAYEXEC. The
> > > enforcement is then subject to the system policy (e.g. mount points,
> > > file access rights, IMA, etc.).
> > >
> > > Checking on open enables to not open a file if it does not meet some
> > > requirements, the same way as if the path doesn't exist or (for whatever
> > > reasons, including execution permission) if access is denied.
> >
> > You can do exactly the same thing if you do the check in a separate
> > syscall though.
> >
> > And it provides a greater degree of flexibility; for example, you can
> > use it in combination with fopen() without having to modify the
> > internals of fopen() or having to use fdopen().
> >
> > > It is a
> > > good practice to check as soon as possible such properties, and it may
> > > enables to avoid (user space) time-of-check to time-of-use (TOCTOU)
> > > attacks (i.e. misuse of already open resources).
> >
> > The assumption that security checks should happen as early as possible
> > can actually cause security problems. For example, because seccomp was
> > designed to do its checks as early as possible, including before
> > ptrace, we had an issue for a long time where the ptrace API could be
> > abused to bypass seccomp filters.
> >
> > Please don't decide that a check must be ordered first _just_ because
> > it is a security check. While that can be good for limiting attack
> > surface, it can also create issues when the idea is applied too
> > broadly.
>
> I'd be interested with such security issue examples.
>
> I hope that delaying checks will not be an issue for mechanisms such as
> IMA or IPE:
> https://lore.kernel.org/lkml/[email protected]/
>
> Any though Mimi, Deven, Chrome OS folks?

One of the major gaps, defining a system wide policy requiring all code
being executed to be signed, is interpreters. The kernel has no
context for the interpreter's opening the file. From an IMA
perspective, this information needs to be conveyed to the kernel prior
to ima_file_check(), which would allow IMA policy rules to be defined
in terms of O_MAYEXEC.

>
> > I don't see how TOCTOU issues are relevant in any way here. If someone
> > can turn a script that is considered a trusted file into an untrusted
> > file and then maliciously change its contents, you're going to have
> > issues either way because the modifications could still happen after
> > openat(); if this was possible, the whole thing would kind of fall
> > apart. And if that isn't possible, I don't see any TOCTOU.
>
> Sure, and if the scripts are not protected in some way there is no point
> to check anything.

The interpreter itself would be signed.

Mimi

>
> > > It is important to keep
> > > in mind that the use cases we are addressing consider that the (user
> > > space) script interpreters (or linkers) are trusted and unaltered (i.e.
> > > integrity/authenticity checked). These are similar sought defensive
> > > properties as for SUID/SGID binaries: attackers can still launch them
> > > with malicious inputs (e.g. file paths, file descriptors, environment
> > > variables, etc.), but the binaries can then have a way to check if they
> > > can extend their trust to some file paths.
> > >
> > > Checking file descriptors may help in some use cases, but not the ones
> > > motivating this series.
> >
> > It actually provides a superset of the functionality that your
> > existing patches provide.
>
> It also brings new issues with multiple file descriptor origins (e.g.
> memfd_create).
>
> > > Checking (already) opened resources could be a
> > > *complementary* way to check execute permission, but it is not in the
> > > scope of this series.


2020-08-11 14:03:32

by Matthew Wilcox

[permalink] [raw]
Subject: Re: [PATCH v7 0/7] Add support for O_MAYEXEC

On Tue, Aug 11, 2020 at 09:56:50AM -0400, Mimi Zohar wrote:
> On Tue, 2020-08-11 at 10:48 +0200, Micka?l Sala?n wrote:
> > On 11/08/2020 01:03, Jann Horn wrote:
> > > On Tue, Aug 11, 2020 at 12:43 AM Micka?l Sala?n <[email protected]> wrote:
> > > > On 10/08/2020 22:21, Al Viro wrote:
> > > > > On Mon, Aug 10, 2020 at 10:11:53PM +0200, Micka?l Sala?n wrote:
> > > > > > It seems that there is no more complains nor questions. Do you want me
> > > > > > to send another series to fix the order of the S-o-b in patch 7?
> > > > >
> > > > > There is a major question regarding the API design and the choice of
> > > > > hooking that stuff on open(). And I have not heard anything resembling
> > > > > a coherent answer.
> > > >
> > > > Hooking on open is a simple design that enables processes to check files
> > > > they intend to open, before they open them. From an API point of view,
> > > > this series extends openat2(2) with one simple flag: O_MAYEXEC. The
> > > > enforcement is then subject to the system policy (e.g. mount points,
> > > > file access rights, IMA, etc.).
> > > >
> > > > Checking on open enables to not open a file if it does not meet some
> > > > requirements, the same way as if the path doesn't exist or (for whatever
> > > > reasons, including execution permission) if access is denied.
> > >
> > > You can do exactly the same thing if you do the check in a separate
> > > syscall though.
> > >
> > > And it provides a greater degree of flexibility; for example, you can
> > > use it in combination with fopen() without having to modify the
> > > internals of fopen() or having to use fdopen().
> > >
> > > > It is a
> > > > good practice to check as soon as possible such properties, and it may
> > > > enables to avoid (user space) time-of-check to time-of-use (TOCTOU)
> > > > attacks (i.e. misuse of already open resources).
> > >
> > > The assumption that security checks should happen as early as possible
> > > can actually cause security problems. For example, because seccomp was
> > > designed to do its checks as early as possible, including before
> > > ptrace, we had an issue for a long time where the ptrace API could be
> > > abused to bypass seccomp filters.
> > >
> > > Please don't decide that a check must be ordered first _just_ because
> > > it is a security check. While that can be good for limiting attack
> > > surface, it can also create issues when the idea is applied too
> > > broadly.
> >
> > I'd be interested with such security issue examples.
> >
> > I hope that delaying checks will not be an issue for mechanisms such as
> > IMA or IPE:
> > https://lore.kernel.org/lkml/[email protected]/
> >
> > Any though Mimi, Deven, Chrome OS folks?
>
> One of the major gaps, defining a system wide policy requiring all code
> being executed to be signed, is interpreters. The kernel has no
> context for the interpreter's opening the file. From an IMA
> perspective, this information needs to be conveyed to the kernel prior
> to ima_file_check(), which would allow IMA policy rules to be defined
> in terms of O_MAYEXEC.

This is kind of evading the point -- Micka?l is proposing a new flag
to open() to tell IMA to measure the file being opened before the fd
is returned to userspace, and Al is suggesting a new syscall to allow
a previously-obtained fd to be measured.

I think what you're saying is that you don't see any reason to prefer
one over the other.

2020-08-11 14:33:04

by Mimi Zohar

[permalink] [raw]
Subject: Re: [PATCH v7 0/7] Add support for O_MAYEXEC

On Tue, 2020-08-11 at 15:02 +0100, Matthew Wilcox wrote:
> On Tue, Aug 11, 2020 at 09:56:50AM -0400, Mimi Zohar wrote:
> > On Tue, 2020-08-11 at 10:48 +0200, Micka?l Sala?n wrote:
> > > On 11/08/2020 01:03, Jann Horn wrote:
> > > > On Tue, Aug 11, 2020 at 12:43 AM Micka?l Sala?n <[email protected]> wrote:
> > > > > On 10/08/2020 22:21, Al Viro wrote:
> > > > > > On Mon, Aug 10, 2020 at 10:11:53PM +0200, Micka?l Sala?n wrote:
> > > > > > > It seems that there is no more complains nor questions. Do you want me
> > > > > > > to send another series to fix the order of the S-o-b in patch 7?
> > > > > >
> > > > > > There is a major question regarding the API design and the choice of
> > > > > > hooking that stuff on open(). And I have not heard anything resembling
> > > > > > a coherent answer.
> > > > >
> > > > > Hooking on open is a simple design that enables processes to check files
> > > > > they intend to open, before they open them. From an API point of view,
> > > > > this series extends openat2(2) with one simple flag: O_MAYEXEC. The
> > > > > enforcement is then subject to the system policy (e.g. mount points,
> > > > > file access rights, IMA, etc.).
> > > > >
> > > > > Checking on open enables to not open a file if it does not meet some
> > > > > requirements, the same way as if the path doesn't exist or (for whatever
> > > > > reasons, including execution permission) if access is denied.
> > > >
> > > > You can do exactly the same thing if you do the check in a separate
> > > > syscall though.
> > > >
> > > > And it provides a greater degree of flexibility; for example, you can
> > > > use it in combination with fopen() without having to modify the
> > > > internals of fopen() or having to use fdopen().
> > > >
> > > > > It is a
> > > > > good practice to check as soon as possible such properties, and it may
> > > > > enables to avoid (user space) time-of-check to time-of-use (TOCTOU)
> > > > > attacks (i.e. misuse of already open resources).
> > > >
> > > > The assumption that security checks should happen as early as possible
> > > > can actually cause security problems. For example, because seccomp was
> > > > designed to do its checks as early as possible, including before
> > > > ptrace, we had an issue for a long time where the ptrace API could be
> > > > abused to bypass seccomp filters.
> > > >
> > > > Please don't decide that a check must be ordered first _just_ because
> > > > it is a security check. While that can be good for limiting attack
> > > > surface, it can also create issues when the idea is applied too
> > > > broadly.
> > >
> > > I'd be interested with such security issue examples.
> > >
> > > I hope that delaying checks will not be an issue for mechanisms such as
> > > IMA or IPE:
> > > https://lore.kernel.org/lkml/[email protected]/
> > >
> > > Any though Mimi, Deven, Chrome OS folks?
> >
> > One of the major gaps, defining a system wide policy requiring all code
> > being executed to be signed, is interpreters. The kernel has no
> > context for the interpreter's opening the file. From an IMA
> > perspective, this information needs to be conveyed to the kernel prior
> > to ima_file_check(), which would allow IMA policy rules to be defined
> > in terms of O_MAYEXEC.
>
> This is kind of evading the point -- Micka?l is proposing a new flag
> to open() to tell IMA to measure the file being opened before the fd
> is returned to userspace, and Al is suggesting a new syscall to allow
> a previously-obtained fd to be measured.
>
> I think what you're saying is that you don't see any reason to prefer
> one over the other.

Being able to define IMA appraise and measure file open
(func=FILE_CHECK) policy rules to prevent the interpreter from
executing unsigned files would be really nice. Otherwise, the file
would be measured and appraised multiple times, once on file open and
again at the point of this new syscall.

Mimi

2020-08-11 17:19:12

by Deven Bowers

[permalink] [raw]
Subject: Re: [PATCH v7 0/7] Add support for O_MAYEXEC



On 8/11/2020 1:48 AM, Mickaël Salaün wrote:

[...snip]

>>> It is a
>>> good practice to check as soon as possible such properties, and it may
>>> enables to avoid (user space) time-of-check to time-of-use (TOCTOU)
>>> attacks (i.e. misuse of already open resources).
>>
>> The assumption that security checks should happen as early as possible
>> can actually cause security problems. For example, because seccomp was
>> designed to do its checks as early as possible, including before
>> ptrace, we had an issue for a long time where the ptrace API could be
>> abused to bypass seccomp filters.
>>
>> Please don't decide that a check must be ordered first _just_ because
>> it is a security check. While that can be good for limiting attack
>> surface, it can also create issues when the idea is applied too
>> broadly.
>
> I'd be interested with such security issue examples.
>
> I hope that delaying checks will not be an issue for mechanisms such as
> IMA or IPE:
> https://lore.kernel.org/lkml/[email protected]/
>
> Any though Mimi, Deven, Chrome OS folks?
>

I don't see an issue with IPE. As long as the hypothetical new syscall
and associated security hook have the file struct available in the
hook, it should integrate fairly easily.

[...snip]

2020-08-11 19:04:21

by Eric W. Biederman

[permalink] [raw]
Subject: Re: [PATCH v7 1/7] exec: Change uselib(2) IS_SREG() failure to EACCES

Mickaël Salaün <[email protected]> writes:

> From: Kees Cook <[email protected]>
>
> Change uselib(2)' S_ISREG() error return to EACCES instead of EINVAL so
> the behavior matches execve(2), and the seemingly documented value.
> The "not a regular file" failure mode of execve(2) is explicitly
> documented[1], but it is not mentioned in uselib(2)[2] which does,
> however, say that open(2) and mmap(2) errors may apply. The documentation
> for open(2) does not include a "not a regular file" error[3], but mmap(2)
> does[4], and it is EACCES.

Do you have enough visibility into uselib to be certain this will change
will not cause regressions?

My sense of uselib is that it would be easier to remove the system call
entirely (I think it's last use was in libc5) than to validate that a
change like this won't cause problems for the users of uselib.

For the kernel what is important are real world users and the manpages
are only important as far as they suggest what the real world users do.

Eric


> [1] http://man7.org/linux/man-pages/man2/execve.2.html#ERRORS
> [2] http://man7.org/linux/man-pages/man2/uselib.2.html#ERRORS
> [3] http://man7.org/linux/man-pages/man2/open.2.html#ERRORS
> [4] http://man7.org/linux/man-pages/man2/mmap.2.html#ERRORS
>
> Signed-off-by: Mickaël Salaün <[email protected]>
> Signed-off-by: Kees Cook <[email protected]>
> Acked-by: Christian Brauner <[email protected]>
> Link: https://lore.kernel.org/r/[email protected]
> ---
> fs/exec.c | 3 +--
> 1 file changed, 1 insertion(+), 2 deletions(-)
>
> diff --git a/fs/exec.c b/fs/exec.c
> index e6e8a9a70327..d7c937044d10 100644
> --- a/fs/exec.c
> +++ b/fs/exec.c
> @@ -141,11 +141,10 @@ SYSCALL_DEFINE1(uselib, const char __user *, library)
> if (IS_ERR(file))
> goto out;
>
> - error = -EINVAL;
> + error = -EACCES;
> if (!S_ISREG(file_inode(file)->i_mode))
> goto exit;
>
> - error = -EACCES;
> if (path_noexec(&file->f_path))
> goto exit;

2020-08-11 19:19:17

by Eric W. Biederman

[permalink] [raw]
Subject: Re: [PATCH v7 1/7] exec: Change uselib(2) IS_SREG() failure to EACCES

[email protected] (Eric W. Biederman) writes:

> Mickaël Salaün <[email protected]> writes:
>
>> From: Kees Cook <[email protected]>
>>
>> Change uselib(2)' S_ISREG() error return to EACCES instead of EINVAL so
>> the behavior matches execve(2), and the seemingly documented value.
>> The "not a regular file" failure mode of execve(2) is explicitly
>> documented[1], but it is not mentioned in uselib(2)[2] which does,
>> however, say that open(2) and mmap(2) errors may apply. The documentation
>> for open(2) does not include a "not a regular file" error[3], but mmap(2)
>> does[4], and it is EACCES.
>
> Do you have enough visibility into uselib to be certain this will change
> will not cause regressions?
>
> My sense of uselib is that it would be easier to remove the system call
> entirely (I think it's last use was in libc5) than to validate that a
> change like this won't cause problems for the users of uselib.
>
> For the kernel what is important are real world users and the manpages
> are only important as far as they suggest what the real world users
> do.

Hmm.

My apologies. After reading the next patch I see that what really makes
this safe is: 73601ea5b7b1 ("fs/open.c: allow opening only regular files
during execve()").

As in practice this change has already been made and uselib simply
can not reach the !S_ISREG test.

It might make sense to drop this patch or include that reference
in the next posting of this patch.

Eric

2020-08-11 19:32:41

by Eric W. Biederman

[permalink] [raw]
Subject: Re: [PATCH v7 2/7] exec: Move S_ISREG() check earlier

Mickaël Salaün <[email protected]> writes:

> From: Kees Cook <[email protected]>
>
> The execve(2)/uselib(2) syscalls have always rejected non-regular
> files. Recently, it was noticed that a deadlock was introduced when trying
> to execute pipes, as the S_ISREG() test was happening too late. This was
> fixed in commit 73601ea5b7b1 ("fs/open.c: allow opening only regular files
> during execve()"), but it was added after inode_permission() had already
> run, which meant LSMs could see bogus attempts to execute non-regular
> files.
>
> Move the test into the other inode type checks (which already look
> for other pathological conditions[1]). Since there is no need to use
> FMODE_EXEC while we still have access to "acc_mode", also switch the
> test to MAY_EXEC.
>
> Also include a comment with the redundant S_ISREG() checks at the end of
> execve(2)/uselib(2) to note that they are present to avoid any mistakes.

The comment is:
> + /*
> + * may_open() has already checked for this, so it should be
> + * impossible to trip now. But we need to be extra cautious
> + * and check again at the very end too.
> + */
Those comments scare me. Why do you need to be extra cautious?
How can the file type possibly change between may_open and anywhere?
The type of a file is immutable after it's creation.

If the comment said check just in case something went wrong with
code maintenance I could understand but that isn't what the comment
says.

Also the fallthrough change below really should be broken out into
it's own change.


> My notes on the call path, and related arguments, checks, etc:
>
> do_open_execat()
> struct open_flags open_exec_flags = {
> .open_flag = O_LARGEFILE | O_RDONLY | __FMODE_EXEC,
> .acc_mode = MAY_EXEC,
> ...
> do_filp_open(dfd, filename, open_flags)
> path_openat(nameidata, open_flags, flags)
> file = alloc_empty_file(open_flags, current_cred());
> do_open(nameidata, file, open_flags)
> may_open(path, acc_mode, open_flag)
> /* new location of MAY_EXEC vs S_ISREG() test */
> inode_permission(inode, MAY_OPEN | acc_mode)
> security_inode_permission(inode, acc_mode)
> vfs_open(path, file)
> do_dentry_open(file, path->dentry->d_inode, open)
> /* old location of FMODE_EXEC vs S_ISREG() test */
> security_file_open(f)
> open()
>
> [1] https://lore.kernel.org/lkml/202006041910.9EF0C602@keescook/
>
> Signed-off-by: Mickaël Salaün <[email protected]>
> Signed-off-by: Kees Cook <[email protected]>
> Link: https://lore.kernel.org/r/[email protected]
> ---
> fs/exec.c | 14 ++++++++++++--
> fs/namei.c | 6 ++++--
> fs/open.c | 6 ------
> 3 files changed, 16 insertions(+), 10 deletions(-)
>
> diff --git a/fs/exec.c b/fs/exec.c
> index d7c937044d10..bdc6a6eb5dce 100644
> --- a/fs/exec.c
> +++ b/fs/exec.c
> @@ -141,8 +141,13 @@ SYSCALL_DEFINE1(uselib, const char __user *, library)
> if (IS_ERR(file))
> goto out;
>
> + /*
> + * may_open() has already checked for this, so it should be
> + * impossible to trip now. But we need to be extra cautious
> + * and check again at the very end too.
> + */
> error = -EACCES;
> - if (!S_ISREG(file_inode(file)->i_mode))
> + if (WARN_ON_ONCE(!S_ISREG(file_inode(file)->i_mode)))
> goto exit;
>
> if (path_noexec(&file->f_path))
> @@ -886,8 +891,13 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags)
> if (IS_ERR(file))
> goto out;
>
> + /*
> + * may_open() has already checked for this, so it should be
> + * impossible to trip now. But we need to be extra cautious
> + * and check again at the very end too.
> + */
> err = -EACCES;
> - if (!S_ISREG(file_inode(file)->i_mode))
> + if (WARN_ON_ONCE(!S_ISREG(file_inode(file)->i_mode)))
> goto exit;
>
> if (path_noexec(&file->f_path))
> diff --git a/fs/namei.c b/fs/namei.c
> index 72d4219c93ac..a559ad943970 100644
> --- a/fs/namei.c
> +++ b/fs/namei.c
> @@ -2849,16 +2849,18 @@ static int may_open(const struct path *path, int acc_mode, int flag)
> case S_IFLNK:
> return -ELOOP;
> case S_IFDIR:
> - if (acc_mode & MAY_WRITE)
> + if (acc_mode & (MAY_WRITE | MAY_EXEC))
> return -EISDIR;
> break;
> case S_IFBLK:
> case S_IFCHR:
> if (!may_open_dev(path))
> return -EACCES;
> - /*FALLTHRU*/
> + fallthrough;
^^^^^^^^^^^
That is an unrelated change and should be sent separately.

> case S_IFIFO:
> case S_IFSOCK:
> + if (acc_mode & MAY_EXEC)
> + return -EACCES;
> flag &= ~O_TRUNC;
> break;
> }
> diff --git a/fs/open.c b/fs/open.c
> index 6cd48a61cda3..623b7506a6db 100644
> --- a/fs/open.c
> +++ b/fs/open.c
> @@ -784,12 +784,6 @@ static int do_dentry_open(struct file *f,
> return 0;
> }
>
> - /* Any file opened for execve()/uselib() has to be a regular file. */
> - if (unlikely(f->f_flags & FMODE_EXEC && !S_ISREG(inode->i_mode))) {
> - error = -EACCES;
> - goto cleanup_file;
> - }
> -
> if (f->f_mode & FMODE_WRITE && !special_file(inode->i_mode)) {
> error = get_write_access(inode);
> if (unlikely(error))

2020-08-11 19:43:35

by Eric W. Biederman

[permalink] [raw]
Subject: Re: [PATCH v7 3/7] exec: Move path_noexec() check earlier

Mickaël Salaün <[email protected]> writes:

> From: Kees Cook <[email protected]>
>
> The path_noexec() check, like the regular file check, was happening too
> late, letting LSMs see impossible execve()s. Check it earlier as well
> in may_open() and collect the redundant fs/exec.c path_noexec() test
> under the same robustness comment as the S_ISREG() check.
>
> My notes on the call path, and related arguments, checks, etc:

A big question arises, that I think someone already asked.

Why perform this test in may_open directly instead of moving
it into inode_permission. That way the code can be shared with
faccessat, and any other code path that wants it?

That would look to provide a more maintainable kernel.

Eric


> do_open_execat()
> struct open_flags open_exec_flags = {
> .open_flag = O_LARGEFILE | O_RDONLY | __FMODE_EXEC,
> .acc_mode = MAY_EXEC,
> ...
> do_filp_open(dfd, filename, open_flags)
> path_openat(nameidata, open_flags, flags)
> file = alloc_empty_file(open_flags, current_cred());
> do_open(nameidata, file, open_flags)
> may_open(path, acc_mode, open_flag)
> /* new location of MAY_EXEC vs path_noexec() test */
> inode_permission(inode, MAY_OPEN | acc_mode)
> security_inode_permission(inode, acc_mode)
> vfs_open(path, file)
> do_dentry_open(file, path->dentry->d_inode, open)
> security_file_open(f)
> open()
> /* old location of path_noexec() test */
>
> Signed-off-by: Mickaël Salaün <[email protected]>
> Signed-off-by: Kees Cook <[email protected]>
> Link: https://lore.kernel.org/r/[email protected]
> ---
> fs/exec.c | 12 ++++--------
> fs/namei.c | 4 ++++
> 2 files changed, 8 insertions(+), 8 deletions(-)
>
> diff --git a/fs/exec.c b/fs/exec.c
> index bdc6a6eb5dce..4eea20c27b01 100644
> --- a/fs/exec.c
> +++ b/fs/exec.c
> @@ -147,10 +147,8 @@ SYSCALL_DEFINE1(uselib, const char __user *, library)
> * and check again at the very end too.
> */
> error = -EACCES;
> - if (WARN_ON_ONCE(!S_ISREG(file_inode(file)->i_mode)))
> - goto exit;
> -
> - if (path_noexec(&file->f_path))
> + if (WARN_ON_ONCE(!S_ISREG(file_inode(file)->i_mode) ||
> + path_noexec(&file->f_path)))
> goto exit;
>
> fsnotify_open(file);
> @@ -897,10 +895,8 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags)
> * and check again at the very end too.
> */
> err = -EACCES;
> - if (WARN_ON_ONCE(!S_ISREG(file_inode(file)->i_mode)))
> - goto exit;
> -
> - if (path_noexec(&file->f_path))
> + if (WARN_ON_ONCE(!S_ISREG(file_inode(file)->i_mode) ||
> + path_noexec(&file->f_path)))
> goto exit;
>
> err = deny_write_access(file);
> diff --git a/fs/namei.c b/fs/namei.c
> index a559ad943970..ddc9b25540fe 100644
> --- a/fs/namei.c
> +++ b/fs/namei.c
> @@ -2863,6 +2863,10 @@ static int may_open(const struct path *path, int acc_mode, int flag)
> return -EACCES;
> flag &= ~O_TRUNC;
> break;
> + case S_IFREG:
> + if ((acc_mode & MAY_EXEC) && path_noexec(path))
> + return -EACCES;
> + break;
> }
>
> error = inode_permission(inode, MAY_OPEN | acc_mode);

2020-08-11 19:57:58

by Eric W. Biederman

[permalink] [raw]
Subject: Re: [PATCH v7 4/7] fs: Introduce O_MAYEXEC flag for openat2(2)

Mickaël Salaün <[email protected]> writes:

> When the O_MAYEXEC flag is passed, openat2(2) may be subject to
> additional restrictions depending on a security policy managed by the
> kernel through a sysctl or implemented by an LSM thanks to the
> inode_permission hook. This new flag is ignored by open(2) and
> openat(2) because of their unspecified flags handling. When used with
> openat2(2), the default behavior is only to forbid to open a directory.
>
> 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: command line parameters (e.g.,
> option -e for Perl), module loading (e.g., option -m for Python), stdin,
> file sourcing, environment variables, configuration files, etc.
> 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. Further documentation can be found in a following patch.
>
> Even without enforced security policy, userland interpreters can set it
> to enforce the system policy at their level, knowing that it will not
> break anything on running systems which do not care about this feature.
> However, on systems which want this feature enforced, there will be
> knowledgeable people (i.e. sysadmins who enforced O_MAYEXEC
> deliberately) to manage it. A simple security policy implementation,
> configured through a dedicated sysctl, is available in a following
> patch.
>
> O_MAYEXEC should not be confused with the O_EXEC flag which is intended
> for execute-only, which obviously doesn't work for scripts. However, a
> similar behavior could be implemented in userland with O_PATH:
> https://lore.kernel.org/lkml/[email protected]/
>
> The implementation of O_MAYEXEC almost duplicates what execve(2) and
> uselib(2) are already doing: setting MAY_OPENEXEC in acc_mode (which can
> then be checked as MAY_EXEC, if enforced).

You are allowing S_IFBLK, S_IFCHR, S_IFIFO, S_IFSOCK as targets for
O_MAYEXEC?

You are not requiring the opened script be executable?

You are not requring path_noexec? Despite the original patch that
inspired this was checking path_noexec?

I honestly think this patch is buggy. If you could reuse MAY_EXEC in
the kernel and mean what exec means when it says MAY_EXEC that would be
useful.

As it is this patch appears wrong and dangerously confusing as it implies
execness but does not implement execness.

If you were simply defining O_EXEC and reusing MAY_EXEC as it exists
or exists with cleanups in the kernel this would be a small change that
would seem to make reasonable sense. But as you are not reusing
anything from MAY_EXEC this code does not make any sense as I am reading
it.

Eric


> This is an updated subset of the patch initially written by Vincent
> Strubel for CLIP OS 4:
> https://github.com/clipos-archive/src_platform_clip-patches/blob/f5cb330d6b684752e403b4e41b39f7004d88e561/1901_open_mayexec.patch
> This patch has been used for more than 12 years with customized script
> interpreters. Some examples (with the original O_MAYEXEC) can be found
> here:
> https://github.com/clipos-archive/clipos4_portage-overlay/search?q=O_MAYEXEC
>
> Co-developed-by: Vincent Strubel <[email protected]>
> Signed-off-by: Vincent Strubel <[email protected]>
> Co-developed-by: Thibaut Sautereau <[email protected]>
> Signed-off-by: Thibaut Sautereau <[email protected]>
> Signed-off-by: Mickaël Salaün <[email protected]>
> Cc: Aleksa Sarai <[email protected]>
> Cc: Al Viro <[email protected]>
> Cc: Deven Bowers <[email protected]>
> Cc: Kees Cook <[email protected]>
> ---
>
> Changes since v6:
> * Do not set __FMODE_EXEC for now because of inconsistent behavior:
> https://lore.kernel.org/lkml/202007160822.CCDB5478@keescook/
> * Returns EISDIR when opening a directory with O_MAYEXEC.
> * Removed Deven Bowers and Kees Cook Reviewed-by tags because of the
> current update.
>
> Changes since v5:
> * Update commit message.
>
> Changes since v3:
> * Switch back to O_MAYEXEC, but only handle it with openat2(2) which
> checks unknown flags (suggested by Aleksa Sarai). Cf.
> https://lore.kernel.org/lkml/[email protected]/
>
> Changes since v2:
> * Replace O_MAYEXEC with RESOLVE_MAYEXEC from openat2(2). This change
> enables to not break existing application using bogus O_* flags that
> may be ignored by current kernels by using a new dedicated flag, only
> usable through openat2(2) (suggested by Jeff Layton). Using this flag
> will results in an error if the running kernel does not support it.
> User space needs to manage this case, as with other RESOLVE_* flags.
> The best effort approach to security (for most common distros) will
> simply consists of ignoring such an error and retry without
> RESOLVE_MAYEXEC. However, a fully controlled system may which to
> error out if such an inconsistency is detected.
>
> Changes since v1:
> * Set __FMODE_EXEC when using O_MAYEXEC to make this information
> available through the new fanotify/FAN_OPEN_EXEC event (suggested by
> Jan Kara and Matthew Bobrowski):
> https://lore.kernel.org/lkml/[email protected]/
> ---
> fs/fcntl.c | 2 +-
> fs/namei.c | 4 ++--
> fs/open.c | 6 ++++++
> include/linux/fcntl.h | 2 +-
> include/linux/fs.h | 2 ++
> include/uapi/asm-generic/fcntl.h | 7 +++++++
> 6 files changed, 19 insertions(+), 4 deletions(-)
>
> diff --git a/fs/fcntl.c b/fs/fcntl.c
> index 2e4c0fa2074b..0357ad667563 100644
> --- a/fs/fcntl.c
> +++ b/fs/fcntl.c
> @@ -1033,7 +1033,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/namei.c b/fs/namei.c
> index ddc9b25540fe..3f074ec77390 100644
> --- a/fs/namei.c
> +++ b/fs/namei.c
> @@ -428,7 +428,7 @@ static int sb_permission(struct super_block *sb, struct inode *inode, int mask)
> /**
> * inode_permission - Check for access rights to a given inode
> * @inode: Inode to check permission on
> - * @mask: Right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
> + * @mask: Right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC, %MAY_OPENEXEC)
> *
> * Check for read/write/execute permissions on an inode. We use fs[ug]id for
> * this, letting us set arbitrary permissions for filesystem access without
> @@ -2849,7 +2849,7 @@ static int may_open(const struct path *path, int acc_mode, int flag)
> case S_IFLNK:
> return -ELOOP;
> case S_IFDIR:
> - if (acc_mode & (MAY_WRITE | MAY_EXEC))
> + if (acc_mode & (MAY_WRITE | MAY_EXEC | MAY_OPENEXEC))
> return -EISDIR;
> break;
> case S_IFBLK:
> diff --git a/fs/open.c b/fs/open.c
> index 623b7506a6db..21c2c1020574 100644
> --- a/fs/open.c
> +++ b/fs/open.c
> @@ -987,6 +987,8 @@ inline struct open_how build_open_how(int flags, umode_t mode)
> .mode = mode & S_IALLUGO,
> };
>
> + /* O_MAYEXEC is ignored by syscalls relying on build_open_how(). */
> + how.flags &= ~O_MAYEXEC;
> /* O_PATH beats everything else. */
> if (how.flags & O_PATH)
> how.flags &= O_PATH_FLAGS;
> @@ -1054,6 +1056,10 @@ inline int build_open_flags(const struct open_how *how, struct open_flags *op)
> if (flags & __O_SYNC)
> flags |= O_DSYNC;
>
> + /* Checks execution permissions on open. */
> + if (flags & O_MAYEXEC)
> + acc_mode |= MAY_OPENEXEC;
> +
> op->open_flag = flags;
>
> /* O_TRUNC implies we need access checks for write permissions */
> diff --git a/include/linux/fcntl.h b/include/linux/fcntl.h
> index 7bcdcf4f6ab2..e188a360fa5f 100644
> --- a/include/linux/fcntl.h
> +++ b/include/linux/fcntl.h
> @@ -10,7 +10,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)
>
> /* List of all valid flags for the how->upgrade_mask argument: */
> #define VALID_UPGRADE_FLAGS \
> diff --git a/include/linux/fs.h b/include/linux/fs.h
> index f5abba86107d..56f835c9a87a 100644
> --- a/include/linux/fs.h
> +++ b/include/linux/fs.h
> @@ -101,6 +101,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..bca90620119f 100644
> --- a/include/uapi/asm-generic/fcntl.h
> +++ b/include/uapi/asm-generic/fcntl.h
> @@ -97,6 +97,13 @@
> #define O_NDELAY O_NONBLOCK
> #endif
>
> +/*
> + * Code execution from file is intended, checks such permission. A simple
> + * policy can be enforced system-wide as explained in
> + * Documentation/admin-guide/sysctl/fs.rst .
> + */
> +#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 */

2020-08-13 14:38:37

by Mickaël Salaün

[permalink] [raw]
Subject: Re: [PATCH v7 4/7] fs: Introduce O_MAYEXEC flag for openat2(2)


On 11/08/2020 21:51, Eric W. Biederman wrote:
> Mickaël Salaün <[email protected]> writes:
>
>> When the O_MAYEXEC flag is passed, openat2(2) may be subject to
>> additional restrictions depending on a security policy managed by the
>> kernel through a sysctl or implemented by an LSM thanks to the
>> inode_permission hook. This new flag is ignored by open(2) and
>> openat(2) because of their unspecified flags handling. When used with
>> openat2(2), the default behavior is only to forbid to open a directory.
>>
>> 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: command line parameters (e.g.,
>> option -e for Perl), module loading (e.g., option -m for Python), stdin,
>> file sourcing, environment variables, configuration files, etc.
>> 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. Further documentation can be found in a following patch.
>>
>> Even without enforced security policy, userland interpreters can set it
>> to enforce the system policy at their level, knowing that it will not
>> break anything on running systems which do not care about this feature.
>> However, on systems which want this feature enforced, there will be
>> knowledgeable people (i.e. sysadmins who enforced O_MAYEXEC
>> deliberately) to manage it. A simple security policy implementation,
>> configured through a dedicated sysctl, is available in a following
>> patch.
>>
>> O_MAYEXEC should not be confused with the O_EXEC flag which is intended
>> for execute-only, which obviously doesn't work for scripts. However, a
>> similar behavior could be implemented in userland with O_PATH:
>> https://lore.kernel.org/lkml/[email protected]/
>>
>> The implementation of O_MAYEXEC almost duplicates what execve(2) and
>> uselib(2) are already doing: setting MAY_OPENEXEC in acc_mode (which can
>> then be checked as MAY_EXEC, if enforced).
>
> You are allowing S_IFBLK, S_IFCHR, S_IFIFO, S_IFSOCK as targets for
> O_MAYEXEC?

There is a switch case for each file type (in this patch and the next one).

>
> You are not requiring the opened script be executable?

The (conditional) enforcement is in the next patch, with the rational.

>
> You are not requring path_noexec? Despite the original patch that
> inspired this was checking path_noexec?

This patch just introduces the new flag and its default behavior. See
the next patch for a security policy configuration.

>
> I honestly think this patch is buggy. If you could reuse MAY_EXEC in
> the kernel and mean what exec means when it says MAY_EXEC that would be
> useful.

Yeah, but unfortunately this is not possible in practice because of
general Linux distro, as explained in the next patch.


>
> As it is this patch appears wrong and dangerously confusing as it implies
> execness but does not implement execness.

Please see next patch.

>
> If you were simply defining O_EXEC and reusing MAY_EXEC as it exists
> or exists with cleanups in the kernel this would be a small change that
> would seem to make reasonable sense. But as you are not reusing
> anything from MAY_EXEC this code does not make any sense as I am reading
> it.

As explained in this commit message, O_EXEC doesn't have the same
semantic. Also, see next patch.

>
> Eric
>
>
>> This is an updated subset of the patch initially written by Vincent
>> Strubel for CLIP OS 4:
>> https://github.com/clipos-archive/src_platform_clip-patches/blob/f5cb330d6b684752e403b4e41b39f7004d88e561/1901_open_mayexec.patch
>> This patch has been used for more than 12 years with customized script
>> interpreters. Some examples (with the original O_MAYEXEC) can be found
>> here:
>> https://github.com/clipos-archive/clipos4_portage-overlay/search?q=O_MAYEXEC
>>
>> Co-developed-by: Vincent Strubel <[email protected]>
>> Signed-off-by: Vincent Strubel <[email protected]>
>> Co-developed-by: Thibaut Sautereau <[email protected]>
>> Signed-off-by: Thibaut Sautereau <[email protected]>
>> Signed-off-by: Mickaël Salaün <[email protected]>
>> Cc: Aleksa Sarai <[email protected]>
>> Cc: Al Viro <[email protected]>
>> Cc: Deven Bowers <[email protected]>
>> Cc: Kees Cook <[email protected]>
>> ---
>>
>> Changes since v6:
>> * Do not set __FMODE_EXEC for now because of inconsistent behavior:
>> https://lore.kernel.org/lkml/202007160822.CCDB5478@keescook/
>> * Returns EISDIR when opening a directory with O_MAYEXEC.
>> * Removed Deven Bowers and Kees Cook Reviewed-by tags because of the
>> current update.
>>
>> Changes since v5:
>> * Update commit message.
>>
>> Changes since v3:
>> * Switch back to O_MAYEXEC, but only handle it with openat2(2) which
>> checks unknown flags (suggested by Aleksa Sarai). Cf.
>> https://lore.kernel.org/lkml/[email protected]/
>>
>> Changes since v2:
>> * Replace O_MAYEXEC with RESOLVE_MAYEXEC from openat2(2). This change
>> enables to not break existing application using bogus O_* flags that
>> may be ignored by current kernels by using a new dedicated flag, only
>> usable through openat2(2) (suggested by Jeff Layton). Using this flag
>> will results in an error if the running kernel does not support it.
>> User space needs to manage this case, as with other RESOLVE_* flags.
>> The best effort approach to security (for most common distros) will
>> simply consists of ignoring such an error and retry without
>> RESOLVE_MAYEXEC. However, a fully controlled system may which to
>> error out if such an inconsistency is detected.
>>
>> Changes since v1:
>> * Set __FMODE_EXEC when using O_MAYEXEC to make this information
>> available through the new fanotify/FAN_OPEN_EXEC event (suggested by
>> Jan Kara and Matthew Bobrowski):
>> https://lore.kernel.org/lkml/[email protected]/
>> ---
>> fs/fcntl.c | 2 +-
>> fs/namei.c | 4 ++--
>> fs/open.c | 6 ++++++
>> include/linux/fcntl.h | 2 +-
>> include/linux/fs.h | 2 ++
>> include/uapi/asm-generic/fcntl.h | 7 +++++++
>> 6 files changed, 19 insertions(+), 4 deletions(-)
>>
>> diff --git a/fs/fcntl.c b/fs/fcntl.c
>> index 2e4c0fa2074b..0357ad667563 100644
>> --- a/fs/fcntl.c
>> +++ b/fs/fcntl.c
>> @@ -1033,7 +1033,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/namei.c b/fs/namei.c
>> index ddc9b25540fe..3f074ec77390 100644
>> --- a/fs/namei.c
>> +++ b/fs/namei.c
>> @@ -428,7 +428,7 @@ static int sb_permission(struct super_block *sb, struct inode *inode, int mask)
>> /**
>> * inode_permission - Check for access rights to a given inode
>> * @inode: Inode to check permission on
>> - * @mask: Right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
>> + * @mask: Right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC, %MAY_OPENEXEC)
>> *
>> * Check for read/write/execute permissions on an inode. We use fs[ug]id for
>> * this, letting us set arbitrary permissions for filesystem access without
>> @@ -2849,7 +2849,7 @@ static int may_open(const struct path *path, int acc_mode, int flag)
>> case S_IFLNK:
>> return -ELOOP;
>> case S_IFDIR:
>> - if (acc_mode & (MAY_WRITE | MAY_EXEC))
>> + if (acc_mode & (MAY_WRITE | MAY_EXEC | MAY_OPENEXEC))
>> return -EISDIR;
>> break;
>> case S_IFBLK:
>> diff --git a/fs/open.c b/fs/open.c
>> index 623b7506a6db..21c2c1020574 100644
>> --- a/fs/open.c
>> +++ b/fs/open.c
>> @@ -987,6 +987,8 @@ inline struct open_how build_open_how(int flags, umode_t mode)
>> .mode = mode & S_IALLUGO,
>> };
>>
>> + /* O_MAYEXEC is ignored by syscalls relying on build_open_how(). */
>> + how.flags &= ~O_MAYEXEC;
>> /* O_PATH beats everything else. */
>> if (how.flags & O_PATH)
>> how.flags &= O_PATH_FLAGS;
>> @@ -1054,6 +1056,10 @@ inline int build_open_flags(const struct open_how *how, struct open_flags *op)
>> if (flags & __O_SYNC)
>> flags |= O_DSYNC;
>>
>> + /* Checks execution permissions on open. */
>> + if (flags & O_MAYEXEC)
>> + acc_mode |= MAY_OPENEXEC;
>> +
>> op->open_flag = flags;
>>
>> /* O_TRUNC implies we need access checks for write permissions */
>> diff --git a/include/linux/fcntl.h b/include/linux/fcntl.h
>> index 7bcdcf4f6ab2..e188a360fa5f 100644
>> --- a/include/linux/fcntl.h
>> +++ b/include/linux/fcntl.h
>> @@ -10,7 +10,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)
>>
>> /* List of all valid flags for the how->upgrade_mask argument: */
>> #define VALID_UPGRADE_FLAGS \
>> diff --git a/include/linux/fs.h b/include/linux/fs.h
>> index f5abba86107d..56f835c9a87a 100644
>> --- a/include/linux/fs.h
>> +++ b/include/linux/fs.h
>> @@ -101,6 +101,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..bca90620119f 100644
>> --- a/include/uapi/asm-generic/fcntl.h
>> +++ b/include/uapi/asm-generic/fcntl.h
>> @@ -97,6 +97,13 @@
>> #define O_NDELAY O_NONBLOCK
>> #endif
>>
>> +/*
>> + * Code execution from file is intended, checks such permission. A simple
>> + * policy can be enforced system-wide as explained in
>> + * Documentation/admin-guide/sysctl/fs.rst .
>> + */
>> +#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 */

2020-08-13 15:32:37

by Mickaël Salaün

[permalink] [raw]
Subject: Re: [PATCH v7 3/7] exec: Move path_noexec() check earlier

Kees Cook wrote this patch, which is in Andrew Morton's tree, but I
think you're talking about O_MAYEXEC, not this patch specifically.

On 11/08/2020 21:36, Eric W. Biederman wrote:
> Mickaël Salaün <[email protected]> writes:
>
>> From: Kees Cook <[email protected]>
>>
>> The path_noexec() check, like the regular file check, was happening too
>> late, letting LSMs see impossible execve()s. Check it earlier as well
>> in may_open() and collect the redundant fs/exec.c path_noexec() test
>> under the same robustness comment as the S_ISREG() check.
>>
>> My notes on the call path, and related arguments, checks, etc:
>
> A big question arises, that I think someone already asked.

Al Viro and Jann Horn expressed such concerns for O_MAYEXEC:
https://lore.kernel.org/lkml/[email protected]/

>
> Why perform this test in may_open directly instead of moving
> it into inode_permission. That way the code can be shared with
> faccessat, and any other code path that wants it?

This patch is just a refactoring.

About O_MAYEXEC, path-based LSM, IMA and IPE need to work on a struct
file, whereas inode_permission() only gives a struct inode. However,
faccessat2(2) (with extended flags) seems to be the perfect candidate if
we want to be able to check file descriptors.

>
> That would look to provide a more maintainable kernel.

Why would it be more maintainable?

>
> Eric
>
>
>> do_open_execat()
>> struct open_flags open_exec_flags = {
>> .open_flag = O_LARGEFILE | O_RDONLY | __FMODE_EXEC,
>> .acc_mode = MAY_EXEC,
>> ...
>> do_filp_open(dfd, filename, open_flags)
>> path_openat(nameidata, open_flags, flags)
>> file = alloc_empty_file(open_flags, current_cred());
>> do_open(nameidata, file, open_flags)
>> may_open(path, acc_mode, open_flag)
>> /* new location of MAY_EXEC vs path_noexec() test */
>> inode_permission(inode, MAY_OPEN | acc_mode)
>> security_inode_permission(inode, acc_mode)
>> vfs_open(path, file)
>> do_dentry_open(file, path->dentry->d_inode, open)
>> security_file_open(f)
>> open()
>> /* old location of path_noexec() test */
>>
>> Signed-off-by: Mickaël Salaün <[email protected]>
>> Signed-off-by: Kees Cook <[email protected]>
>> Link: https://lore.kernel.org/r/[email protected]
>> ---
>> fs/exec.c | 12 ++++--------
>> fs/namei.c | 4 ++++
>> 2 files changed, 8 insertions(+), 8 deletions(-)
>>
>> diff --git a/fs/exec.c b/fs/exec.c
>> index bdc6a6eb5dce..4eea20c27b01 100644
>> --- a/fs/exec.c
>> +++ b/fs/exec.c
>> @@ -147,10 +147,8 @@ SYSCALL_DEFINE1(uselib, const char __user *, library)
>> * and check again at the very end too.
>> */
>> error = -EACCES;
>> - if (WARN_ON_ONCE(!S_ISREG(file_inode(file)->i_mode)))
>> - goto exit;
>> -
>> - if (path_noexec(&file->f_path))
>> + if (WARN_ON_ONCE(!S_ISREG(file_inode(file)->i_mode) ||
>> + path_noexec(&file->f_path)))
>> goto exit;
>>
>> fsnotify_open(file);
>> @@ -897,10 +895,8 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags)
>> * and check again at the very end too.
>> */
>> err = -EACCES;
>> - if (WARN_ON_ONCE(!S_ISREG(file_inode(file)->i_mode)))
>> - goto exit;
>> -
>> - if (path_noexec(&file->f_path))
>> + if (WARN_ON_ONCE(!S_ISREG(file_inode(file)->i_mode) ||
>> + path_noexec(&file->f_path)))
>> goto exit;
>>
>> err = deny_write_access(file);
>> diff --git a/fs/namei.c b/fs/namei.c
>> index a559ad943970..ddc9b25540fe 100644
>> --- a/fs/namei.c
>> +++ b/fs/namei.c
>> @@ -2863,6 +2863,10 @@ static int may_open(const struct path *path, int acc_mode, int flag)
>> return -EACCES;
>> flag &= ~O_TRUNC;
>> break;
>> + case S_IFREG:
>> + if ((acc_mode & MAY_EXEC) && path_noexec(path))
>> + return -EACCES;
>> + break;
>> }
>>
>> error = inode_permission(inode, MAY_OPEN | acc_mode);