E Eric W. Biederman #
Message details and related content
Mickaël Salaün [off-list ref] 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/1e2f6913-42f2-3578-28ed-567f6a4bdda1@digikod.net/ 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 <redacted> Signed-off-by: Vincent Strubel <redacted> Co-developed-by: Thibaut Sautereau <redacted> Signed-off-by: Thibaut Sautereau <redacted> Signed-off-by: Mickaël Salaün <mic@digikod.net> Cc: Aleksa Sarai <redacted> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: Deven Bowers <redacted> Cc: Kees Cook <redacted> --- 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/20200430015429.wuob7m5ofdewubui@yavin.dot.cyphar.com/ 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.