* Re: [RFC PATCH v19 2/5] security: Add new SHOULD_EXEC_CHECK and SHOULD_EXEC_RESTRICT securebits
From: Mickaël Salaün @ 2024-07-16 15:15 UTC (permalink / raw)
To: Jeff Xu
Cc: Kees Cook, Steve Dower, Al Viro, Christian Brauner,
Linus Torvalds, Paul Moore, Theodore Ts'o, Alejandro Colomar,
Aleksa Sarai, Andrew Morton, Andy Lutomirski, Arnd Bergmann,
Casey Schaufler, Christian Heimes, Dmitry Vyukov, Eric Biggers,
Eric Chiang, Fan Wu, Florian Weimer, Geert Uytterhoeven,
James Morris, Jan Kara, Jann Horn, Jonathan Corbet,
Jordan R Abrahams, Lakshmi Ramasubramanian, Luca Boccassi,
Luis Chamberlain, Madhavan T . Venkataraman, Matt Bobrowski,
Matthew Garrett, Matthew Wilcox, Miklos Szeredi, Mimi Zohar,
Nicolas Bouchinet, Scott Shell, Shuah Khan, Stephen Rothwell,
Steve Grubb, Thibaut Sautereau, Vincent Strubel, Xiaoming Ni,
Yin Fengwei, kernel-hardening, linux-api, linux-fsdevel,
linux-integrity, linux-kernel, linux-security-module
In-Reply-To: <CALmYWFt7X0v8k1N9=aX6BuT2gCiC9SeWwPEBckvBk8GQtb0rqQ@mail.gmail.com>
On Tue, Jul 16, 2024 at 08:02:37AM -0700, Jeff Xu wrote:
> On Thu, Jul 11, 2024 at 1:57 AM Mickaël Salaün <mic@digikod.net> wrote:
> >
> > On Wed, Jul 10, 2024 at 09:26:14AM -0700, Kees Cook wrote:
> > > On Wed, Jul 10, 2024 at 11:58:25AM +0200, Mickaël Salaün wrote:
> > > > Here is another proposal:
> > > >
> > > > We can change a bit the semantic by making it the norm to always check
> > > > file executability with AT_CHECK, and using the securebits to restrict
> > > > file interpretation and/or command injection (e.g. user supplied shell
> > > > commands). Non-executable checked files can be reported/logged at the
> > > > kernel level, with audit, configured by sysadmins.
> > > >
> > > > New securebits (feel free to propose better names):
> > > >
> > > > - SECBIT_EXEC_RESTRICT_FILE: requires AT_CHECK to pass.
> > >
> > > Would you want the enforcement of this bit done by userspace or the
> > > kernel?
> > >
> > > IIUC, userspace would always perform AT_CHECK regardless of
> > > SECBIT_EXEC_RESTRICT_FILE, and then which would happen?
> > >
> > > 1) userspace would ignore errors from AT_CHECK when
> > > SECBIT_EXEC_RESTRICT_FILE is unset
> >
> > Yes, that's the idea.
> >
> > >
> > > or
> > >
> > > 2) kernel would allow all AT_CHECK when SECBIT_EXEC_RESTRICT_FILE is
> > > unset
> > >
> > > I suspect 1 is best and what you intend, given that
> > > SECBIT_EXEC_DENY_INTERACTIVE can only be enforced by userspace.
> >
> > Indeed. We don't want AT_CHECK's behavior to change according to
> > securebits.
> >
> One bit is good.
>
> > >
> > > > - SECBIT_EXEC_DENY_INTERACTIVE: deny any command injection via
> > > > command line arguments, environment variables, or configuration files.
> > > > This should be ignored by dynamic linkers. We could also have an
> > > > allow-list of shells for which this bit is not set, managed by an
> > > > LSM's policy, if the native securebits scoping approach is not enough.
> > > >
> > > > Different modes for script interpreters:
> > > >
> > > > 1. RESTRICT_FILE=0 DENY_INTERACTIVE=0 (default)
> > > > Always interpret scripts, and allow arbitrary user commands.
> > > > => No threat, everyone and everything is trusted, but we can get
> > > > ahead of potential issues with logs to prepare for a migration to a
> > > > restrictive mode.
> > > >
> > > > 2. RESTRICT_FILE=1 DENY_INTERACTIVE=0
> > > > Deny script interpretation if they are not executable, and allow
> > > > arbitrary user commands.
> > > > => Threat: (potential) malicious scripts run by trusted (and not
> > > > fooled) users. That could protect against unintended script
> > > > executions (e.g. sh /tmp/*.sh).
> > > > ==> Makes sense for (semi-restricted) user sessions.
> > > >
> > > > 3. RESTRICT_FILE=1 DENY_INTERACTIVE=1
> > > > Deny script interpretation if they are not executable, and also deny
> > > > any arbitrary user commands.
> > > > => Threat: malicious scripts run by untrusted users.
> > > > ==> Makes sense for system services executing scripts.
> > > >
> > > > 4. RESTRICT_FILE=0 DENY_INTERACTIVE=1
> > > > Always interpret scripts, but deny arbitrary user commands.
> > > > => Goal: monitor/measure/assess script content (e.g. with IMA/EVM) in
> > > > a system where the access rights are not (yet) ready. Arbitrary
> > > > user commands would be much more difficult to monitor.
> > > > ==> First step of restricting system services that should not
> > > > directly pass arbitrary commands to shells.
> > >
> > > I like these bits!
> >
> > Good! Jeff, Steve, Florian, Matt, others, what do you think?
>
> For below two cases: will they be restricted by one (or some) mode above ?
>
> 1> cat /tmp/a.sh | sh
>
> 2> sh -c "$(cat /tmp/a.sh)"
Yes, DENY_INTERACTIVE=1 is to deny both of these cases (i.e. arbitrary
user command).
These other examples should be allowed with AT_CHECK and RESTRICT_FILE=1
if a.sh is executable though:
* sh /tmp/a.sh
* sh < /tmp/a.sh
^ permalink raw reply
* Re: [RFC PATCH v19 2/5] security: Add new SHOULD_EXEC_CHECK and SHOULD_EXEC_RESTRICT securebits
From: Jeff Xu @ 2024-07-16 15:18 UTC (permalink / raw)
To: Mickaël Salaün
Cc: Kees Cook, Steve Dower, Al Viro, Christian Brauner,
Linus Torvalds, Paul Moore, Theodore Ts'o, Alejandro Colomar,
Aleksa Sarai, Andrew Morton, Andy Lutomirski, Arnd Bergmann,
Casey Schaufler, Christian Heimes, Dmitry Vyukov, Eric Biggers,
Eric Chiang, Fan Wu, Florian Weimer, Geert Uytterhoeven,
James Morris, Jan Kara, Jann Horn, Jonathan Corbet,
Jordan R Abrahams, Lakshmi Ramasubramanian, Luca Boccassi,
Luis Chamberlain, Madhavan T . Venkataraman, Matt Bobrowski,
Matthew Garrett, Matthew Wilcox, Miklos Szeredi, Mimi Zohar,
Nicolas Bouchinet, Scott Shell, Shuah Khan, Stephen Rothwell,
Steve Grubb, Thibaut Sautereau, Vincent Strubel, Xiaoming Ni,
Yin Fengwei, kernel-hardening, linux-api, linux-fsdevel,
linux-integrity, linux-kernel, linux-security-module
In-Reply-To: <20240716.Zah8Phaiphae@digikod.net>
On Tue, Jul 16, 2024 at 8:15 AM Mickaël Salaün <mic@digikod.net> wrote:
>
> On Tue, Jul 16, 2024 at 08:02:37AM -0700, Jeff Xu wrote:
> > On Thu, Jul 11, 2024 at 1:57 AM Mickaël Salaün <mic@digikod.net> wrote:
> > >
> > > On Wed, Jul 10, 2024 at 09:26:14AM -0700, Kees Cook wrote:
> > > > On Wed, Jul 10, 2024 at 11:58:25AM +0200, Mickaël Salaün wrote:
> > > > > Here is another proposal:
> > > > >
> > > > > We can change a bit the semantic by making it the norm to always check
> > > > > file executability with AT_CHECK, and using the securebits to restrict
> > > > > file interpretation and/or command injection (e.g. user supplied shell
> > > > > commands). Non-executable checked files can be reported/logged at the
> > > > > kernel level, with audit, configured by sysadmins.
> > > > >
> > > > > New securebits (feel free to propose better names):
> > > > >
> > > > > - SECBIT_EXEC_RESTRICT_FILE: requires AT_CHECK to pass.
> > > >
> > > > Would you want the enforcement of this bit done by userspace or the
> > > > kernel?
> > > >
> > > > IIUC, userspace would always perform AT_CHECK regardless of
> > > > SECBIT_EXEC_RESTRICT_FILE, and then which would happen?
> > > >
> > > > 1) userspace would ignore errors from AT_CHECK when
> > > > SECBIT_EXEC_RESTRICT_FILE is unset
> > >
> > > Yes, that's the idea.
> > >
> > > >
> > > > or
> > > >
> > > > 2) kernel would allow all AT_CHECK when SECBIT_EXEC_RESTRICT_FILE is
> > > > unset
> > > >
> > > > I suspect 1 is best and what you intend, given that
> > > > SECBIT_EXEC_DENY_INTERACTIVE can only be enforced by userspace.
> > >
> > > Indeed. We don't want AT_CHECK's behavior to change according to
> > > securebits.
> > >
> > One bit is good.
> >
> > > >
> > > > > - SECBIT_EXEC_DENY_INTERACTIVE: deny any command injection via
> > > > > command line arguments, environment variables, or configuration files.
> > > > > This should be ignored by dynamic linkers. We could also have an
> > > > > allow-list of shells for which this bit is not set, managed by an
> > > > > LSM's policy, if the native securebits scoping approach is not enough.
> > > > >
> > > > > Different modes for script interpreters:
> > > > >
> > > > > 1. RESTRICT_FILE=0 DENY_INTERACTIVE=0 (default)
> > > > > Always interpret scripts, and allow arbitrary user commands.
> > > > > => No threat, everyone and everything is trusted, but we can get
> > > > > ahead of potential issues with logs to prepare for a migration to a
> > > > > restrictive mode.
> > > > >
> > > > > 2. RESTRICT_FILE=1 DENY_INTERACTIVE=0
> > > > > Deny script interpretation if they are not executable, and allow
> > > > > arbitrary user commands.
> > > > > => Threat: (potential) malicious scripts run by trusted (and not
> > > > > fooled) users. That could protect against unintended script
> > > > > executions (e.g. sh /tmp/*.sh).
> > > > > ==> Makes sense for (semi-restricted) user sessions.
> > > > >
> > > > > 3. RESTRICT_FILE=1 DENY_INTERACTIVE=1
> > > > > Deny script interpretation if they are not executable, and also deny
> > > > > any arbitrary user commands.
> > > > > => Threat: malicious scripts run by untrusted users.
> > > > > ==> Makes sense for system services executing scripts.
> > > > >
> > > > > 4. RESTRICT_FILE=0 DENY_INTERACTIVE=1
> > > > > Always interpret scripts, but deny arbitrary user commands.
> > > > > => Goal: monitor/measure/assess script content (e.g. with IMA/EVM) in
> > > > > a system where the access rights are not (yet) ready. Arbitrary
> > > > > user commands would be much more difficult to monitor.
> > > > > ==> First step of restricting system services that should not
> > > > > directly pass arbitrary commands to shells.
> > > >
> > > > I like these bits!
> > >
> > > Good! Jeff, Steve, Florian, Matt, others, what do you think?
> >
> > For below two cases: will they be restricted by one (or some) mode above ?
> >
> > 1> cat /tmp/a.sh | sh
> >
> > 2> sh -c "$(cat /tmp/a.sh)"
>
> Yes, DENY_INTERACTIVE=1 is to deny both of these cases (i.e. arbitrary
> user command).
>
> These other examples should be allowed with AT_CHECK and RESTRICT_FILE=1
> if a.sh is executable though:
> * sh /tmp/a.sh
> * sh < /tmp/a.sh
That looks good. Thanks for clarifying.
^ permalink raw reply
* Re: [RFC PATCH v19 0/5] Script execution control (was O_MAYEXEC)
From: Roberto Sassu @ 2024-07-16 15:57 UTC (permalink / raw)
To: Mickaël Salaün, Mimi Zohar
Cc: Al Viro, Christian Brauner, Kees Cook, Linus Torvalds, Paul Moore,
Theodore Ts'o, Alejandro Colomar, Aleksa Sarai, Andrew Morton,
Andy Lutomirski, Arnd Bergmann, Casey Schaufler, Christian Heimes,
Dmitry Vyukov, Eric Biggers, Eric Chiang, Fan Wu, Florian Weimer,
Geert Uytterhoeven, James Morris, Jan Kara, Jann Horn, Jeff Xu,
Jonathan Corbet, Jordan R Abrahams, Lakshmi Ramasubramanian,
Luca Boccassi, Luis Chamberlain, Madhavan T . Venkataraman,
Matt Bobrowski, Matthew Garrett, Matthew Wilcox, Miklos Szeredi,
Nicolas Bouchinet, Scott Shell, Shuah Khan, Stephen Rothwell,
Steve Dower, Steve Grubb, Thibaut Sautereau, Vincent Strubel,
Xiaoming Ni, Yin Fengwei, kernel-hardening, linux-api,
linux-fsdevel, linux-integrity, linux-kernel,
linux-security-module
In-Reply-To: <20240709.AhJ7oTh1biej@digikod.net>
On Tue, 2024-07-09 at 22:43 +0200, Mickaël Salaün wrote:
> On Mon, Jul 08, 2024 at 04:35:38PM -0400, Mimi Zohar wrote:
> > Hi Mickaël,
> >
> > On Thu, 2024-07-04 at 21:01 +0200, Mickaël Salaün wrote:
> > > Hi,
> > >
> > > The ultimate goal of this patch series is to be able to ensure that
> > > direct file execution (e.g. ./script.sh) and indirect file execution
> > > (e.g. sh script.sh) lead to the same result, especially from a security
> > > point of view.
> > >
> > > Overview
> > > --------
> > >
> > > This patch series is a new approach of the initial O_MAYEXEC feature,
> > > and a revamp of the previous patch series. Taking into account the last
> > > reviews [1], we now stick to the kernel semantic for file executability.
> > > One major change is the clear split between access check and policy
> > > management.
> > >
> > > The first patch brings the AT_CHECK flag to execveat(2). The goal is to
> > > enable user space to check if a file could be executed (by the kernel).
> > > Unlike stat(2) that only checks file permissions, execveat2(2) +
> > > AT_CHECK take into account the full context, including mount points
> > > (noexec), caller's limits, and all potential LSM extra checks (e.g.
> > > argv, envp, credentials).
> > >
> > > The second patch brings two new securebits used to set or get a security
> > > policy for a set of processes. For this to be meaningful, all
> > > executable code needs to be trusted. In practice, this means that
> > > (malicious) users can be restricted to only run scripts provided (and
> > > trusted) by the system.
> > >
> > > [1] https://lore.kernel.org/r/CAHk-=wjPGNLyzeBMWdQu+kUdQLHQugznwY7CvWjmvNW47D5sog@mail.gmail.com
> > >
> > > Script execution
> > > ----------------
> > >
> > > One important thing to keep in mind is that the goal of this patch
> > > series is to get the same security restrictions with these commands:
> > > * ./script.py
> > > * python script.py
> > > * python < script.py
> > > * python -m script.pyT
> >
> > This is really needed, but is it the "only" purpose of this patch set or can it
> > be used to also monitor files the script opens (for read) with the intention of
> > executing.
>
> This feature can indeed also be used to monitor files requested by
> scripts to be executed e.g. using
> https://docs.python.org/3/library/io.html#io.open_code
>
> IMA/EVM can include this check in its logs.
>
> >
> > >
> > > However, on secure systems, we should be able to forbid these commands
> > > because there is no way to reliably identify the origin of the script:
> > > * xargs -a script.py -d '\r' -- python -c
> > > * cat script.py | python
> > > * python
> > >
> > > Background
> > > ----------
> > >
> > > Compared to the previous patch series, there is no more dedicated
> > > syscall nor sysctl configuration. This new patch series only add new
> > > flags: one for execveat(2) and four for prctl(2).
> > >
> > > This kind of script interpreter restriction may already be used in
> > > hardened systems, which may need to fork interpreters and install
> > > different versions of the binaries. This mechanism should enable to
> > > avoid the use of duplicate binaries (and potential forked source code)
> > > for secure interpreters (e.g. secure Python [2]) by making it possible
> > > to dynamically enforce restrictions or not.
> > >
> > > The ability to control script execution is also required to close a
> > > major IMA measurement/appraisal interpreter integrity [3].
> >
> > Definitely. But it isn't limited to controlling script execution, but also
> > measuring the script. Will it be possible to measure and appraise the indirect
> > script calls with this patch set?
>
> Yes. You should only need to implement security_bprm_creds_for_exec()
> for IMA/EVM.
>
> BTW, I noticed that IMA only uses the security_bprm_check() hook (which
> can be called several times for one execve), but
> security_bprm_creds_for_exec() might be more appropriate.
Ok, I tried a trivial modification to have this working:
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index f04f43af651c..2a6b04c91601 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -554,6 +554,14 @@ static int ima_bprm_check(struct linux_binprm *bprm)
MAY_EXEC, CREDS_CHECK);
}
+static int ima_bprm_creds_for_exec(struct linux_binprm *bprm)
+{
+ if (!bprm->is_check)
+ return 0;
+
+ return ima_bprm_check(bprm);
+}
+
/**
* ima_file_check - based on policy, collect/store measurement.
* @file: pointer to the file to be measured
@@ -1177,6 +1185,7 @@ static int __init init_ima(void)
static struct security_hook_list ima_hooks[] __ro_after_init = {
LSM_HOOK_INIT(bprm_check_security, ima_bprm_check),
+ LSM_HOOK_INIT(bprm_creds_for_exec, ima_bprm_creds_for_exec),
LSM_HOOK_INIT(file_post_open, ima_file_check),
LSM_HOOK_INIT(inode_post_create_tmpfile, ima_post_create_tmpfile),
LSM_HOOK_INIT(file_release, ima_file_free),
I also adapted the Clip OS 4 patch for bash.
The result seems good so far:
# echo "measure fowner=2000 func=BPRM_CHECK" > /sys/kernel/security/ima/policy
# ./bash /root/test.sh
Hello World
# cat /sys/kernel/security/ima/ascii_runtime_measurements
10 35435d0858d895b90097306171a2e5fcc7f5da9e ima-ng sha256:0e4acf326a82c6bded9d86f48d272d7a036b6490081bb6466ecc2a0e416b244a boot_aggregate
10 4cd9df168a2cf8d18be46543e66c76a53ca6a03d ima-ng sha256:e7f3c2dab66f56fef963fbab55fc6d64bc22a5f900c29042e6ecd87e08f2b535 /root/test.sh
So, it is there.
It works only with +x permission. If not, I get:
# ./bash /root/test.sh
./bash: /root/test.sh: Permission denied
But the Clip OS 4 patch does not cover the redirection case:
# ./bash < /root/test.sh
Hello World
Do you have a more recent patch for that?
Thanks
Roberto
> >
> > Mimi
> >
> > > This new execveat + AT_CHECK should not be confused with the O_EXEC flag
> > > (for open) which is intended for execute-only, which obviously doesn't
> > > work for scripts.
> > >
> > > I gave a talk about controlling script execution where I explain the
> > > previous approaches [4]. The design of the WIP RFC I talked about
> > > changed quite a bit since then.
> > >
> > > [2] https://github.com/zooba/spython
> > > [3] https://lore.kernel.org/lkml/20211014130125.6991-1-zohar@linux.ibm.com/
> > > [4] https://lssna2023.sched.com/event/1K7bO
> > >
> >
> >
^ permalink raw reply related
* Re: [RFC PATCH v19 0/5] Script execution control (was O_MAYEXEC)
From: James Bottomley @ 2024-07-16 16:12 UTC (permalink / raw)
To: Roberto Sassu, Mickaël Salaün, Mimi Zohar
Cc: Al Viro, Christian Brauner, Kees Cook, Linus Torvalds, Paul Moore,
Theodore Ts'o, Alejandro Colomar, Aleksa Sarai, Andrew Morton,
Andy Lutomirski, Arnd Bergmann, Casey Schaufler, Christian Heimes,
Dmitry Vyukov, Eric Biggers, Eric Chiang, Fan Wu, Florian Weimer,
Geert Uytterhoeven, James Morris, Jan Kara, Jann Horn, Jeff Xu,
Jonathan Corbet, Jordan R Abrahams, Lakshmi Ramasubramanian,
Luca Boccassi, Luis Chamberlain, Madhavan T . Venkataraman,
Matt Bobrowski, Matthew Garrett, Matthew Wilcox, Miklos Szeredi,
Nicolas Bouchinet, Scott Shell, Shuah Khan, Stephen Rothwell,
Steve Dower, Steve Grubb, Thibaut Sautereau, Vincent Strubel,
Xiaoming Ni, Yin Fengwei, kernel-hardening, linux-api,
linux-fsdevel, linux-integrity, linux-kernel,
linux-security-module
In-Reply-To: <9e3df65c2bf060b5833558e9f8d82dcd2fe9325a.camel@huaweicloud.com>
On Tue, 2024-07-16 at 17:57 +0200, Roberto Sassu wrote:
> But the Clip OS 4 patch does not cover the redirection case:
>
> # ./bash < /root/test.sh
> Hello World
>
> Do you have a more recent patch for that?
How far down the rabbit hole do you want to go? You can't forbid a
shell from executing commands from stdin because logging in then won't
work. It may be possible to allow from a tty backed file and not from
a file backed one, but you still have the problem of the attacker
manually typing in the script.
The saving grace for this for shells is that they pretty much do
nothing on their own (unlike python) so you can still measure all the
executables they call out to, which provides reasonable safety.
James
^ permalink raw reply
* Re: mprotect PROT_SIGNAL
From: Soni "They/Them" L. @ 2024-07-16 16:16 UTC (permalink / raw)
To: Florian Weimer; +Cc: linux-api
In-Reply-To: <87ikx5z7xp.fsf@oldenburg.str.redhat.com>
On 2024-07-16 12:13, Florian Weimer wrote:
> * Soni L.:
>
> > we'd like an mprotect PROT_SIGNAL flag for a compiler we're making
> >
> > PROT_SIGNAL - marks the pages as unmapped while running signal handlers
> >
> > this would be very useful, do you think you could provide it?
> >
> > (not much more to say about it, it's supposed to do what it says on
> > the tin, we want to unmap pages in signal handlers so as to catch bugs
> > without causing memory corruption.)
>
> The challenge is going to be to detect abnormal exit from the signal
> handler.
>
> You can already get some of this behavior to today with memory
> protection keys, on x86-64 at least. (I consider this a glitch in the
> implementation, it makes it less useful.) Access is revoked
> automatically when the hander is invoked. However, access is not
> restored if you jump out of the handler using longjmp or by throwing an
> exception.
As it should be - POSIX specifies that longjmping out of a handler
remains in handler context, so you still can't call signal-unsafe functions.
Presumably, an API to manually map them back in would be necessary in
the case of e.g. the JVM, which uses signal handlers for
hardware-accelerated null checks.
> Thanks,
> Florian
>
^ permalink raw reply
* Re: [RFC PATCH v19 0/5] Script execution control (was O_MAYEXEC)
From: Mickaël Salaün @ 2024-07-16 17:31 UTC (permalink / raw)
To: James Bottomley
Cc: Roberto Sassu, Mimi Zohar, Al Viro, Christian Brauner, Kees Cook,
Linus Torvalds, Paul Moore, Theodore Ts'o, Alejandro Colomar,
Aleksa Sarai, Andrew Morton, Andy Lutomirski, Arnd Bergmann,
Casey Schaufler, Christian Heimes, Dmitry Vyukov, Eric Biggers,
Eric Chiang, Fan Wu, Florian Weimer, Geert Uytterhoeven,
James Morris, Jan Kara, Jann Horn, Jeff Xu, Jonathan Corbet,
Jordan R Abrahams, Lakshmi Ramasubramanian, Luca Boccassi,
Luis Chamberlain, Madhavan T . Venkataraman, Matt Bobrowski,
Matthew Garrett, Matthew Wilcox, Miklos Szeredi,
Nicolas Bouchinet, Scott Shell, Shuah Khan, Stephen Rothwell,
Steve Dower, Steve Grubb, Thibaut Sautereau, Vincent Strubel,
Xiaoming Ni, Yin Fengwei, kernel-hardening, linux-api,
linux-fsdevel, linux-integrity, linux-kernel,
linux-security-module
In-Reply-To: <ee1ae815b6e75021709612181a6a4415fda543a4.camel@HansenPartnership.com>
On Tue, Jul 16, 2024 at 12:12:49PM -0400, James Bottomley wrote:
> On Tue, 2024-07-16 at 17:57 +0200, Roberto Sassu wrote:
> > But the Clip OS 4 patch does not cover the redirection case:
> >
> > # ./bash < /root/test.sh
> > Hello World
> >
> > Do you have a more recent patch for that?
Bash was only partially restricted for CLIP OS because it was used for
administrative tasks (interactive shell).
Python was also restricted for user commands though:
https://github.com/clipos-archive/clipos4_portage-overlay/blob/master/dev-lang/python/files/python-2.7.9-clip-mayexec.patch
Steve and Christian could help with a better Python implementation.
>
> How far down the rabbit hole do you want to go? You can't forbid a
> shell from executing commands from stdin because logging in then won't
> work. It may be possible to allow from a tty backed file and not from
> a file backed one, but you still have the problem of the attacker
> manually typing in the script.
Yes, that's why we'll have the (optional) SECBIT_EXEC_DENY_INTERACTIVE:
https://lore.kernel.org/all/20240710.eiKohpa4Phai@digikod.net/
>
> The saving grace for this for shells is that they pretty much do
> nothing on their own (unlike python) so you can still measure all the
> executables they call out to, which provides reasonable safety.
Exactly. Python is a much more interesting target for attacker because
it opens the door for arbitrary syscalls (see the cover letter).
If we want to have a more advanced access control (e.g. allow Bash but
not Python), we should extend existing LSMs to manage the appropriate
securebits according to programs/subjects.
^ permalink raw reply
* Re: [RFC PATCH v19 0/5] Script execution control (was O_MAYEXEC)
From: Mickaël Salaün @ 2024-07-16 17:47 UTC (permalink / raw)
To: Boris Lukashev
Cc: James Bottomley, Roberto Sassu, Mimi Zohar, Al Viro,
Christian Brauner, Kees Cook, Linus Torvalds, Paul Moore,
Theodore Ts'o, Alejandro Colomar, Aleksa Sarai, Andrew Morton,
Andy Lutomirski, Arnd Bergmann, Casey Schaufler, Christian Heimes,
Dmitry Vyukov, Eric Biggers, Eric Chiang, Fan Wu, Florian Weimer,
Geert Uytterhoeven, James Morris, Jan Kara, Jann Horn, Jeff Xu,
Jonathan Corbet, Jordan R Abrahams, Lakshmi Ramasubramanian,
Luca Boccassi, Luis Chamberlain, Madhavan T . Venkataraman,
Matt Bobrowski, Matthew Garrett, Matthew Wilcox, Miklos Szeredi,
Nicolas Bouchinet, Scott Shell, Shuah Khan, Stephen Rothwell,
Steve Dower, Steve Grubb, Thibaut Sautereau, Vincent Strubel,
Xiaoming Ni, Yin Fengwei, kernel-hardening, linux-api,
linux-fsdevel, linux-integrity, linux-kernel,
linux-security-module
In-Reply-To: <E608EDB8-72E8-4791-AC9B-8FF9AC753FBE@sempervictus.com>
(adding back other people in Cc)
On Tue, Jul 16, 2024 at 01:29:43PM -0400, Boris Lukashev wrote:
> Wouldn't count those shell chickens - awk alone is enough and we can
> use ssh and openssl clients (all in metasploit public code). As one of
> the people who makes novel shell types, I can assure you that this
> effort is only going to slow skiddies and only until the rest of us
> publish mitigations for this mitigation :)
Security is not binary. :)
Not all Linux systems are equals. Some hardened systems need this kind
of feature and they can get guarantees because they fully control and
trust their executable binaries (e.g. CLIP OS, chromeOS) or they
properly sandbox them. See context in the cover letter.
awk is a script interpreter that should be patched too, like other Linux
tools.
>
> -Boris (RageLtMan)
>
> On July 16, 2024 12:12:49 PM EDT, James Bottomley <James.Bottomley@HansenPartnership.com> wrote:
> >On Tue, 2024-07-16 at 17:57 +0200, Roberto Sassu wrote:
> >> But the Clip OS 4 patch does not cover the redirection case:
> >>
> >> # ./bash < /root/test.sh
> >> Hello World
> >>
> >> Do you have a more recent patch for that?
> >
> >How far down the rabbit hole do you want to go? You can't forbid a
> >shell from executing commands from stdin because logging in then won't
> >work. It may be possible to allow from a tty backed file and not from
> >a file backed one, but you still have the problem of the attacker
> >manually typing in the script.
> >
> >The saving grace for this for shells is that they pretty much do
> >nothing on their own (unlike python) so you can still measure all the
> >executables they call out to, which provides reasonable safety.
> >
> >James
> >
^ permalink raw reply
* Re: [RFC PATCH v19 1/5] exec: Add a new AT_CHECK flag to execveat(2)
From: Jeff Xu @ 2024-07-17 6:33 UTC (permalink / raw)
To: Mickaël Salaün
Cc: Al Viro, Christian Brauner, Kees Cook, Linus Torvalds, Paul Moore,
Theodore Ts'o, Alejandro Colomar, Aleksa Sarai, Andrew Morton,
Andy Lutomirski, Arnd Bergmann, Casey Schaufler, Christian Heimes,
Dmitry Vyukov, Eric Biggers, Eric Chiang, Fan Wu, Florian Weimer,
Geert Uytterhoeven, James Morris, Jan Kara, Jann Horn,
Jonathan Corbet, Jordan R Abrahams, Lakshmi Ramasubramanian,
Luca Boccassi, Luis Chamberlain, Madhavan T . Venkataraman,
Matt Bobrowski, Matthew Garrett, Matthew Wilcox, Miklos Szeredi,
Mimi Zohar, Nicolas Bouchinet, Scott Shell, Shuah Khan,
Stephen Rothwell, Steve Dower, Steve Grubb, Thibaut Sautereau,
Vincent Strubel, Xiaoming Ni, Yin Fengwei, kernel-hardening,
linux-api, linux-fsdevel, linux-integrity, linux-kernel,
linux-security-module
In-Reply-To: <20240704190137.696169-2-mic@digikod.net>
On Thu, Jul 4, 2024 at 12:02 PM Mickaël Salaün <mic@digikod.net> wrote:
>
> Add a new AT_CHECK flag to execveat(2) to check if a file would be
> allowed for execution. The main use case is for script interpreters and
> dynamic linkers to check execution permission according to the kernel's
> security policy. Another use case is to add context to access logs e.g.,
> which script (instead of interpreter) accessed a file. As any
> executable code, scripts could also use this check [1].
>
> This is different than faccessat(2) which only checks file access
> rights, but not the full context e.g. mount point's noexec, stack limit,
> and all potential LSM extra checks (e.g. argv, envp, credentials).
> Since the use of AT_CHECK follows the exact kernel semantic as for a
> real execution, user space gets the same error codes.
>
So we concluded that execveat(AT_CHECK) will be used to check the
exec, shared object, script and config file (such as seccomp config),
I'm still thinking execveat(AT_CHECK) vs faccessat(AT_CHECK) in
different use cases:
execveat clearly has less code change, but that also means: we can't
add logic specific to exec (i.e. logic that can't be applied to
config) for this part (from do_execveat_common to
security_bprm_creds_for_exec) in future. This would require some
agreement/sign-off, I'm not sure from whom.
--------------------------
now looked at user cases (focus on elf for now)
1> ld.so /tmp/a.out, /tmp/a.out is on non-exec mount
dynamic linker will first call execveat(fd, AT_CHECK) then execveat(fd)
2> execve(/usr/bin/some.out) and some.out has dependency on /tmp/a.so
/usr/bin/some.out will pass AT_CHECK
3> execve(usr/bin/some.out) and some.out uses custom /tmp/ld.so
/usr/bin/some.out will pass AT_CHECK, however, it uses a custom
/tmp/ld.so (I assume this is possible for elf header will set the
path for ld.so because kernel has no knowledge of that, and
binfmt_elf.c allocate memory for ld.so during execveat call)
4> dlopen(/tmp/a.so)
I assume dynamic linker will call execveat(AT_CHECK), before map a.so
into memory.
For case 1>
Alternative solution: Because AT_CHECK is always called, I think we
can avoid the first AT_CHECK call, and check during execveat(fd),
this means the kernel will enforce SECBIT_EXEC_RESTRICT_FILE = 1, the
benefit is that there is no TOCTOU and save one round trip of syscall
for a succesful execveat() case.
For case 2>
dynamic linker will call execve(AT_CHECK), then mmap(fd) into memory.
However, the process can all open then mmap() directly, it seems
minimal effort for an attacker to walk around such a defence from
dynamic linker.
Alternative solution:
dynamic linker call AT_CHECK for each .so, kernel will save the state
(associated with fd)
kernel will check fd state at the time of mmap(fd, executable memory)
and enforce SECBIT_EXEC_RESTRICT_FILE = 1
Alternative solution 2:
a new syscall to load the .so and enforce the AT_CHECK in kernel
This also means, for the solution to be complete, we might want to
block creation of executable anonymous memory (e.g. by seccomp, ),
unless the user space can harden the creation of executable anonymous
memory in some way.
For case 3>
I think binfmt_elf.c in the kernel needs to check the ld.so to make
sure it passes AT_CHECK, before loading it into memory.
For case 4>
same as case 2.
Consider those cases: I think:
a> relying purely on userspace for enforcement does't seem to be
effective, e.g. it is trivial to call open(), then mmap() it into
executable memory.
b> if both user space and kernel need to call AT_CHECK, the faccessat
seems to be a better place for AT_CHECK, e.g. kernel can call
do_faccessat(AT_CHECK) and userspace can call faccessat(). This will
avoid complicating the execveat() code path.
What do you think ?
Thanks
-Jeff
> With the information that a script interpreter is about to interpret a
> script, an LSM security policy can adjust caller's access rights or log
> execution request as for native script execution (e.g. role transition).
> This is possible thanks to the call to security_bprm_creds_for_exec().
>
> Because LSMs may only change bprm's credentials, use of AT_CHECK with
> current kernel code should not be a security issue (e.g. unexpected role
> transition). LSMs willing to update the caller's credential could now
> do so when bprm->is_check is set. Of course, such policy change should
> be in line with the new user space code.
>
> Because AT_CHECK is dedicated to user space interpreters, it doesn't
> make sense for the kernel to parse the checked files, look for
> interpreters known to the kernel (e.g. ELF, shebang), and return ENOEXEC
> if the format is unknown. Because of that, security_bprm_check() is
> never called when AT_CHECK is used.
>
> It should be noted that script interpreters cannot directly use
> execveat(2) (without this new AT_CHECK flag) because this could lead to
> unexpected behaviors e.g., `python script.sh` could lead to Bash being
> executed to interpret the script. Unlike the kernel, script
> interpreters may just interpret the shebang as a simple comment, which
> should not change for backward compatibility reasons.
>
> Because scripts or libraries files might not currently have the
> executable permission set, or because we might want specific users to be
> allowed to run arbitrary scripts, the following patch provides a dynamic
> configuration mechanism with the SECBIT_SHOULD_EXEC_CHECK and
> SECBIT_SHOULD_EXEC_RESTRICT securebits.
>
> This is a redesign of the CLIP OS 4's O_MAYEXEC:
> https://github.com/clipos-archive/src_platform_clip-patches/blob/f5cb330d6b684752e403b4e41b39f7004d88e561/1901_open_mayexec.patch
> This patch has been used for more than a decade with customized script
> interpreters. Some examples can be found here:
> https://github.com/clipos-archive/clipos4_portage-overlay/search?q=O_MAYEXEC
>
> Cc: Al Viro <viro@zeniv.linux.org.uk>
> Cc: Christian Brauner <brauner@kernel.org>
> Cc: Kees Cook <keescook@chromium.org>
> Cc: Paul Moore <paul@paul-moore.com>
> Link: https://docs.python.org/3/library/io.html#io.open_code [1]
> Signed-off-by: Mickaël Salaün <mic@digikod.net>
> Link: https://lore.kernel.org/r/20240704190137.696169-2-mic@digikod.net
> ---
>
> New design since v18:
> https://lore.kernel.org/r/20220104155024.48023-3-mic@digikod.net
> ---
> fs/exec.c | 5 +++--
> include/linux/binfmts.h | 7 ++++++-
> include/uapi/linux/fcntl.h | 30 ++++++++++++++++++++++++++++++
> kernel/audit.h | 1 +
> kernel/auditsc.c | 1 +
> 5 files changed, 41 insertions(+), 3 deletions(-)
>
> diff --git a/fs/exec.c b/fs/exec.c
> index 40073142288f..ea2a1867afdc 100644
> --- a/fs/exec.c
> +++ b/fs/exec.c
> @@ -931,7 +931,7 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags)
> .lookup_flags = LOOKUP_FOLLOW,
> };
>
> - if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0)
> + if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH | AT_CHECK)) != 0)
> return ERR_PTR(-EINVAL);
> if (flags & AT_SYMLINK_NOFOLLOW)
> open_exec_flags.lookup_flags &= ~LOOKUP_FOLLOW;
> @@ -1595,6 +1595,7 @@ static struct linux_binprm *alloc_bprm(int fd, struct filename *filename, int fl
> bprm->filename = bprm->fdpath;
> }
> bprm->interp = bprm->filename;
> + bprm->is_check = !!(flags & AT_CHECK);
>
> retval = bprm_mm_init(bprm);
> if (!retval)
> @@ -1885,7 +1886,7 @@ static int bprm_execve(struct linux_binprm *bprm)
>
> /* Set the unchanging part of bprm->cred */
> retval = security_bprm_creds_for_exec(bprm);
> - if (retval)
> + if (retval || bprm->is_check)
> goto out;
>
> retval = exec_binprm(bprm);
> diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
> index 70f97f685bff..8ff9c9e33aed 100644
> --- a/include/linux/binfmts.h
> +++ b/include/linux/binfmts.h
> @@ -42,7 +42,12 @@ struct linux_binprm {
> * Set when errors can no longer be returned to the
> * original userspace.
> */
> - point_of_no_return:1;
> + point_of_no_return:1,
> + /*
> + * Set by user space to check executability according to the
> + * caller's environment.
> + */
> + is_check:1;
> struct file *executable; /* Executable to pass to the interpreter */
> struct file *interpreter;
> struct file *file;
> diff --git a/include/uapi/linux/fcntl.h b/include/uapi/linux/fcntl.h
> index c0bcc185fa48..bcd05c59b7df 100644
> --- a/include/uapi/linux/fcntl.h
> +++ b/include/uapi/linux/fcntl.h
> @@ -118,6 +118,36 @@
> #define AT_HANDLE_FID AT_REMOVEDIR /* file handle is needed to
> compare object identity and may not
> be usable to open_by_handle_at(2) */
> +
> +/*
> + * AT_CHECK only performs a check on a regular file and returns 0 if execution
> + * of this file would be allowed, ignoring the file format and then the related
> + * interpreter dependencies (e.g. ELF libraries, script's shebang). AT_CHECK
> + * should only be used if SECBIT_SHOULD_EXEC_CHECK is set for the calling
> + * thread. See securebits.h documentation.
> + *
> + * Programs should use this check to apply kernel-level checks against files
> + * that are not directly executed by the kernel but directly passed to a user
> + * space interpreter instead. All files that contain executable code, from the
> + * point of view of the interpreter, should be checked. The main purpose of
> + * this flag is to improve the security and consistency of an execution
> + * environment to ensure that direct file execution (e.g. ./script.sh) and
> + * indirect file execution (e.g. sh script.sh) lead to the same result. For
> + * instance, this can be used to check if a file is trustworthy according to
> + * the caller's environment.
> + *
> + * In a secure environment, libraries and any executable dependencies should
> + * also be checked. For instance dynamic linking should make sure that all
> + * libraries are allowed for execution to avoid trivial bypass (e.g. using
> + * LD_PRELOAD). For such secure execution environment to make sense, only
> + * trusted code should be executable, which also requires integrity guarantees.
> + *
> + * To avoid race conditions leading to time-of-check to time-of-use issues,
> + * AT_CHECK should be used with AT_EMPTY_PATH to check against a file
> + * descriptor instead of a path.
> + */
> +#define AT_CHECK 0x10000
> +
> #if defined(__KERNEL__)
> #define AT_GETATTR_NOSEC 0x80000000
> #endif
> diff --git a/kernel/audit.h b/kernel/audit.h
> index a60d2840559e..8ebdabd2ab81 100644
> --- a/kernel/audit.h
> +++ b/kernel/audit.h
> @@ -197,6 +197,7 @@ struct audit_context {
> struct open_how openat2;
> struct {
> int argc;
> + bool is_check;
> } execve;
> struct {
> char *name;
> diff --git a/kernel/auditsc.c b/kernel/auditsc.c
> index 6f0d6fb6523f..b6316e284342 100644
> --- a/kernel/auditsc.c
> +++ b/kernel/auditsc.c
> @@ -2662,6 +2662,7 @@ void __audit_bprm(struct linux_binprm *bprm)
>
> context->type = AUDIT_EXECVE;
> context->execve.argc = bprm->argc;
> + context->execve.is_check = bprm->is_check;
> }
>
>
> --
> 2.45.2
>
^ permalink raw reply
* Re: [RFC PATCH v19 1/5] exec: Add a new AT_CHECK flag to execveat(2)
From: Steve Dower @ 2024-07-17 8:26 UTC (permalink / raw)
To: Jeff Xu, Mickaël Salaün
Cc: Al Viro, Christian Brauner, Kees Cook, Linus Torvalds, Paul Moore,
Theodore Ts'o, Alejandro Colomar, Aleksa Sarai, Andrew Morton,
Andy Lutomirski, Arnd Bergmann, Casey Schaufler, Christian Heimes,
Dmitry Vyukov, Eric Biggers, Eric Chiang, Fan Wu, Florian Weimer,
Geert Uytterhoeven, James Morris, Jan Kara, Jann Horn,
Jonathan Corbet, Jordan R Abrahams, Lakshmi Ramasubramanian,
Luca Boccassi, Luis Chamberlain, Madhavan T . Venkataraman,
Matt Bobrowski, Matthew Garrett, Matthew Wilcox, Miklos Szeredi,
Mimi Zohar, Nicolas Bouchinet, Scott Shell, Shuah Khan,
Stephen Rothwell, Steve Grubb, Thibaut Sautereau, Vincent Strubel,
Xiaoming Ni, Yin Fengwei, kernel-hardening, linux-api,
linux-fsdevel, linux-integrity, linux-kernel,
linux-security-module
In-Reply-To: <CALmYWFss7qcpR9D_r3pbP_Orxs55t3y3yXJsac1Wz=Hk9Di0Nw@mail.gmail.com>
On 17/07/2024 07:33, Jeff Xu wrote:
> Consider those cases: I think:
> a> relying purely on userspace for enforcement does't seem to be
> effective, e.g. it is trivial to call open(), then mmap() it into
> executable memory.
If there's a way to do this without running executable code that had to
pass a previous execveat() check, then yeah, it's not effective (e.g. a
Python interpreter that *doesn't* enforce execveat() is a trivial way to
do it).
Once arbitrary code is running, all bets are off. So long as all
arbitrary code is being checked itself, it's allowed to do things that
would bypass later checks (and it's up to whoever audited it in the
first place to prevent this by not giving it the special mark that
allows it to pass the check).
> b> if both user space and kernel need to call AT_CHECK, the faccessat
> seems to be a better place for AT_CHECK, e.g. kernel can call
> do_faccessat(AT_CHECK) and userspace can call faccessat(). This will
> avoid complicating the execveat() code path.
>
> What do you think ?
>
> Thanks
> -Jeff
^ permalink raw reply
* Re: [RFC PATCH v19 1/5] exec: Add a new AT_CHECK flag to execveat(2)
From: Mickaël Salaün @ 2024-07-17 10:00 UTC (permalink / raw)
To: Steve Dower
Cc: Jeff Xu, Al Viro, Christian Brauner, Kees Cook, Linus Torvalds,
Paul Moore, Theodore Ts'o, Alejandro Colomar, Aleksa Sarai,
Andrew Morton, Andy Lutomirski, Arnd Bergmann, Casey Schaufler,
Christian Heimes, Dmitry Vyukov, Eric Biggers, Eric Chiang,
Fan Wu, Florian Weimer, Geert Uytterhoeven, James Morris,
Jan Kara, Jann Horn, Jonathan Corbet, Jordan R Abrahams,
Lakshmi Ramasubramanian, Luca Boccassi, Luis Chamberlain,
Madhavan T . Venkataraman, Matt Bobrowski, Matthew Garrett,
Matthew Wilcox, Miklos Szeredi, Mimi Zohar, Nicolas Bouchinet,
Scott Shell, Shuah Khan, Stephen Rothwell, Steve Grubb,
Thibaut Sautereau, Vincent Strubel, Xiaoming Ni, Yin Fengwei,
kernel-hardening, linux-api, linux-fsdevel, linux-integrity,
linux-kernel, linux-security-module
In-Reply-To: <a0da7702-dabe-49e4-87f4-5d6111f023a8@python.org>
On Wed, Jul 17, 2024 at 09:26:22AM +0100, Steve Dower wrote:
> On 17/07/2024 07:33, Jeff Xu wrote:
> > Consider those cases: I think:
> > a> relying purely on userspace for enforcement does't seem to be
> > effective, e.g. it is trivial to call open(), then mmap() it into
> > executable memory.
>
> If there's a way to do this without running executable code that had to pass
> a previous execveat() check, then yeah, it's not effective (e.g. a Python
> interpreter that *doesn't* enforce execveat() is a trivial way to do it).
>
> Once arbitrary code is running, all bets are off. So long as all arbitrary
> code is being checked itself, it's allowed to do things that would bypass
> later checks (and it's up to whoever audited it in the first place to
> prevent this by not giving it the special mark that allows it to pass the
> check).
Exactly. As explained in the patches, one crucial prerequisite is that
the executable code is trusted, and the system must provide integrity
guarantees. We cannot do anything without that. This patches series is
a building block to fix a blind spot on Linux systems to be able to
fully control executability.
^ permalink raw reply
* Re: [RFC PATCH v19 1/5] exec: Add a new AT_CHECK flag to execveat(2)
From: Mickaël Salaün @ 2024-07-17 10:01 UTC (permalink / raw)
To: Jeff Xu
Cc: Al Viro, Christian Brauner, Kees Cook, Linus Torvalds, Paul Moore,
Theodore Ts'o, Alejandro Colomar, Aleksa Sarai, Andrew Morton,
Andy Lutomirski, Arnd Bergmann, Casey Schaufler, Christian Heimes,
Dmitry Vyukov, Eric Biggers, Eric Chiang, Fan Wu, Florian Weimer,
Geert Uytterhoeven, James Morris, Jan Kara, Jann Horn,
Jonathan Corbet, Jordan R Abrahams, Lakshmi Ramasubramanian,
Luca Boccassi, Luis Chamberlain, Madhavan T . Venkataraman,
Matt Bobrowski, Matthew Garrett, Matthew Wilcox, Miklos Szeredi,
Mimi Zohar, Nicolas Bouchinet, Scott Shell, Shuah Khan,
Stephen Rothwell, Steve Dower, Steve Grubb, Thibaut Sautereau,
Vincent Strubel, Xiaoming Ni, Yin Fengwei, kernel-hardening,
linux-api, linux-fsdevel, linux-integrity, linux-kernel,
linux-security-module
In-Reply-To: <CALmYWFss7qcpR9D_r3pbP_Orxs55t3y3yXJsac1Wz=Hk9Di0Nw@mail.gmail.com>
On Tue, Jul 16, 2024 at 11:33:55PM -0700, Jeff Xu wrote:
> On Thu, Jul 4, 2024 at 12:02 PM Mickaël Salaün <mic@digikod.net> wrote:
> >
> > Add a new AT_CHECK flag to execveat(2) to check if a file would be
> > allowed for execution. The main use case is for script interpreters and
> > dynamic linkers to check execution permission according to the kernel's
> > security policy. Another use case is to add context to access logs e.g.,
> > which script (instead of interpreter) accessed a file. As any
> > executable code, scripts could also use this check [1].
> >
> > This is different than faccessat(2) which only checks file access
> > rights, but not the full context e.g. mount point's noexec, stack limit,
> > and all potential LSM extra checks (e.g. argv, envp, credentials).
> > Since the use of AT_CHECK follows the exact kernel semantic as for a
> > real execution, user space gets the same error codes.
> >
> So we concluded that execveat(AT_CHECK) will be used to check the
> exec, shared object, script and config file (such as seccomp config),
"config file" that contains executable code.
> I'm still thinking execveat(AT_CHECK) vs faccessat(AT_CHECK) in
> different use cases:
>
> execveat clearly has less code change, but that also means: we can't
> add logic specific to exec (i.e. logic that can't be applied to
> config) for this part (from do_execveat_common to
> security_bprm_creds_for_exec) in future. This would require some
> agreement/sign-off, I'm not sure from whom.
I'm not sure to follow. We could still add new flags, but for now I
don't see use cases. This patch series is not meant to handle all
possible "trust checks", only executable code, which makes sense for the
kernel.
If we want other checks, we'll need to clearly define their semantic and
align with the kernel. faccessat2(2) might be used to check other file
properties, but the executable property is not only defined by the file
attributes.
>
> --------------------------
> now looked at user cases (focus on elf for now)
>
> 1> ld.so /tmp/a.out, /tmp/a.out is on non-exec mount
> dynamic linker will first call execveat(fd, AT_CHECK) then execveat(fd)
>
> 2> execve(/usr/bin/some.out) and some.out has dependency on /tmp/a.so
> /usr/bin/some.out will pass AT_CHECK
>
> 3> execve(usr/bin/some.out) and some.out uses custom /tmp/ld.so
> /usr/bin/some.out will pass AT_CHECK, however, it uses a custom
> /tmp/ld.so (I assume this is possible for elf header will set the
> path for ld.so because kernel has no knowledge of that, and
> binfmt_elf.c allocate memory for ld.so during execveat call)
>
> 4> dlopen(/tmp/a.so)
> I assume dynamic linker will call execveat(AT_CHECK), before map a.so
> into memory.
>
> For case 1>
> Alternative solution: Because AT_CHECK is always called, I think we
> can avoid the first AT_CHECK call, and check during execveat(fd),
There is no need to use AT_CHECK if we're going to call execveat(2) on
the same file descriptor. By design, AT_CHECK is implicit for any
execve(2).
> this means the kernel will enforce SECBIT_EXEC_RESTRICT_FILE = 1, the
> benefit is that there is no TOCTOU and save one round trip of syscall
> for a succesful execveat() case.
As long as user space uses the same file descriptor, there is no TOCTOU.
SECBIT_EXEC_RESTRICT_FILE only makes sense for user space: it defines
the user space security policy. The kernel already enforces the same
security policy for any execve(2), whatever are the calling process's
securebits.
>
> For case 2>
> dynamic linker will call execve(AT_CHECK), then mmap(fd) into memory.
> However, the process can all open then mmap() directly, it seems
> minimal effort for an attacker to walk around such a defence from
> dynamic linker.
Which process? What do you mean by "can all open then mmap() directly"?
In this context the dynamic linker (like its parent processes) is
trusted (guaranteed by the system).
For case 2, the dynamic linker must check with AT_CHECK all files that
will be mapped, which include /usr/bin/some.out and /tmp/a.so
>
> Alternative solution:
> dynamic linker call AT_CHECK for each .so, kernel will save the state
> (associated with fd)
> kernel will check fd state at the time of mmap(fd, executable memory)
> and enforce SECBIT_EXEC_RESTRICT_FILE = 1
The idea with AT_CHECK is that there is no kernel side effect, no extra
kernel state, and the semantic is the same as with execve(2).
This also enables us to check file's executable permission and ignore
it, which is useful in a "permissive mode" when preparing for a
migration without breaking a system, or to do extra integrity checks.
BTW, this use case would also be more complex with a new openat2(2) flag
like the original O_MAYEXEC.
>
> Alternative solution 2:
> a new syscall to load the .so and enforce the AT_CHECK in kernel
A new syscall would be overkill for this feature. Please see Linus's
comment.
>
> This also means, for the solution to be complete, we might want to
> block creation of executable anonymous memory (e.g. by seccomp, ),
How seccomp could create anonymous memory in user space?
seccomp filters should be treated (and checked with AT_CHECK) as
executable code anyway.
> unless the user space can harden the creation of executable anonymous
> memory in some way.
User space is already in charge of mmapping its own memory. I don't see
what is missing.
>
> For case 3>
> I think binfmt_elf.c in the kernel needs to check the ld.so to make
> sure it passes AT_CHECK, before loading it into memory.
All ELF dependencies are opened and checked with open_exec(), which
perform the main executability checks (with the __FMODE_EXEC flag).
Did I miss something?
However, we must be careful with programs using the (deprecated)
uselib(2). They should also check with AT_CHECK because this syscall
opens the shared library without __FMODE_EXEC (similar to a simple file
open). See
https://lore.kernel.org/all/CAHk-=wiUwRG7LuR=z5sbkFVGQh+7qVB6_1NM0Ny9SVNL1Un4Sw@mail.gmail.com/
>
> For case 4>
> same as case 2.
>
> Consider those cases: I think:
> a> relying purely on userspace for enforcement does't seem to be
> effective, e.g. it is trivial to call open(), then mmap() it into
> executable memory.
As Steve explained (and is also explained in the patches), it is trivial
if the attacker can already execute its own code, which is too late to
enforce any script execution control.
> b> if both user space and kernel need to call AT_CHECK, the faccessat
> seems to be a better place for AT_CHECK, e.g. kernel can call
> do_faccessat(AT_CHECK) and userspace can call faccessat(). This will
> avoid complicating the execveat() code path.
A previous version of this patches series already patched faccessat(2),
but this is not the right place. faccessat2(2) is dedicated to check
file permissions, not executability (e.g. with mount's noexec).
>
> What do you think ?
I think there are some misunderstandings. Please let me know if it's
clearer now.
>
> Thanks
> -Jeff
>
> > With the information that a script interpreter is about to interpret a
> > script, an LSM security policy can adjust caller's access rights or log
> > execution request as for native script execution (e.g. role transition).
> > This is possible thanks to the call to security_bprm_creds_for_exec().
> >
> > Because LSMs may only change bprm's credentials, use of AT_CHECK with
> > current kernel code should not be a security issue (e.g. unexpected role
> > transition). LSMs willing to update the caller's credential could now
> > do so when bprm->is_check is set. Of course, such policy change should
> > be in line with the new user space code.
> >
> > Because AT_CHECK is dedicated to user space interpreters, it doesn't
> > make sense for the kernel to parse the checked files, look for
> > interpreters known to the kernel (e.g. ELF, shebang), and return ENOEXEC
> > if the format is unknown. Because of that, security_bprm_check() is
> > never called when AT_CHECK is used.
> >
> > It should be noted that script interpreters cannot directly use
> > execveat(2) (without this new AT_CHECK flag) because this could lead to
> > unexpected behaviors e.g., `python script.sh` could lead to Bash being
> > executed to interpret the script. Unlike the kernel, script
> > interpreters may just interpret the shebang as a simple comment, which
> > should not change for backward compatibility reasons.
> >
> > Because scripts or libraries files might not currently have the
> > executable permission set, or because we might want specific users to be
> > allowed to run arbitrary scripts, the following patch provides a dynamic
> > configuration mechanism with the SECBIT_SHOULD_EXEC_CHECK and
> > SECBIT_SHOULD_EXEC_RESTRICT securebits.
> >
> > This is a redesign of the CLIP OS 4's O_MAYEXEC:
> > https://github.com/clipos-archive/src_platform_clip-patches/blob/f5cb330d6b684752e403b4e41b39f7004d88e561/1901_open_mayexec.patch
> > This patch has been used for more than a decade with customized script
> > interpreters. Some examples can be found here:
> > https://github.com/clipos-archive/clipos4_portage-overlay/search?q=O_MAYEXEC
> >
> > Cc: Al Viro <viro@zeniv.linux.org.uk>
> > Cc: Christian Brauner <brauner@kernel.org>
> > Cc: Kees Cook <keescook@chromium.org>
> > Cc: Paul Moore <paul@paul-moore.com>
> > Link: https://docs.python.org/3/library/io.html#io.open_code [1]
> > Signed-off-by: Mickaël Salaün <mic@digikod.net>
> > Link: https://lore.kernel.org/r/20240704190137.696169-2-mic@digikod.net
> > ---
> >
> > New design since v18:
> > https://lore.kernel.org/r/20220104155024.48023-3-mic@digikod.net
> > ---
> > fs/exec.c | 5 +++--
> > include/linux/binfmts.h | 7 ++++++-
> > include/uapi/linux/fcntl.h | 30 ++++++++++++++++++++++++++++++
> > kernel/audit.h | 1 +
> > kernel/auditsc.c | 1 +
> > 5 files changed, 41 insertions(+), 3 deletions(-)
> >
> > diff --git a/fs/exec.c b/fs/exec.c
> > index 40073142288f..ea2a1867afdc 100644
> > --- a/fs/exec.c
> > +++ b/fs/exec.c
> > @@ -931,7 +931,7 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags)
> > .lookup_flags = LOOKUP_FOLLOW,
> > };
> >
> > - if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0)
> > + if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH | AT_CHECK)) != 0)
> > return ERR_PTR(-EINVAL);
> > if (flags & AT_SYMLINK_NOFOLLOW)
> > open_exec_flags.lookup_flags &= ~LOOKUP_FOLLOW;
> > @@ -1595,6 +1595,7 @@ static struct linux_binprm *alloc_bprm(int fd, struct filename *filename, int fl
> > bprm->filename = bprm->fdpath;
> > }
> > bprm->interp = bprm->filename;
> > + bprm->is_check = !!(flags & AT_CHECK);
> >
> > retval = bprm_mm_init(bprm);
> > if (!retval)
> > @@ -1885,7 +1886,7 @@ static int bprm_execve(struct linux_binprm *bprm)
> >
> > /* Set the unchanging part of bprm->cred */
> > retval = security_bprm_creds_for_exec(bprm);
> > - if (retval)
> > + if (retval || bprm->is_check)
> > goto out;
> >
> > retval = exec_binprm(bprm);
> > diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
> > index 70f97f685bff..8ff9c9e33aed 100644
> > --- a/include/linux/binfmts.h
> > +++ b/include/linux/binfmts.h
> > @@ -42,7 +42,12 @@ struct linux_binprm {
> > * Set when errors can no longer be returned to the
> > * original userspace.
> > */
> > - point_of_no_return:1;
> > + point_of_no_return:1,
> > + /*
> > + * Set by user space to check executability according to the
> > + * caller's environment.
> > + */
> > + is_check:1;
> > struct file *executable; /* Executable to pass to the interpreter */
> > struct file *interpreter;
> > struct file *file;
> > diff --git a/include/uapi/linux/fcntl.h b/include/uapi/linux/fcntl.h
> > index c0bcc185fa48..bcd05c59b7df 100644
> > --- a/include/uapi/linux/fcntl.h
> > +++ b/include/uapi/linux/fcntl.h
> > @@ -118,6 +118,36 @@
> > #define AT_HANDLE_FID AT_REMOVEDIR /* file handle is needed to
> > compare object identity and may not
> > be usable to open_by_handle_at(2) */
> > +
> > +/*
> > + * AT_CHECK only performs a check on a regular file and returns 0 if execution
> > + * of this file would be allowed, ignoring the file format and then the related
> > + * interpreter dependencies (e.g. ELF libraries, script's shebang). AT_CHECK
> > + * should only be used if SECBIT_SHOULD_EXEC_CHECK is set for the calling
> > + * thread. See securebits.h documentation.
> > + *
> > + * Programs should use this check to apply kernel-level checks against files
> > + * that are not directly executed by the kernel but directly passed to a user
> > + * space interpreter instead. All files that contain executable code, from the
> > + * point of view of the interpreter, should be checked. The main purpose of
> > + * this flag is to improve the security and consistency of an execution
> > + * environment to ensure that direct file execution (e.g. ./script.sh) and
> > + * indirect file execution (e.g. sh script.sh) lead to the same result. For
> > + * instance, this can be used to check if a file is trustworthy according to
> > + * the caller's environment.
> > + *
> > + * In a secure environment, libraries and any executable dependencies should
> > + * also be checked. For instance dynamic linking should make sure that all
> > + * libraries are allowed for execution to avoid trivial bypass (e.g. using
> > + * LD_PRELOAD). For such secure execution environment to make sense, only
> > + * trusted code should be executable, which also requires integrity guarantees.
> > + *
> > + * To avoid race conditions leading to time-of-check to time-of-use issues,
> > + * AT_CHECK should be used with AT_EMPTY_PATH to check against a file
> > + * descriptor instead of a path.
> > + */
> > +#define AT_CHECK 0x10000
> > +
> > #if defined(__KERNEL__)
> > #define AT_GETATTR_NOSEC 0x80000000
> > #endif
> > diff --git a/kernel/audit.h b/kernel/audit.h
> > index a60d2840559e..8ebdabd2ab81 100644
> > --- a/kernel/audit.h
> > +++ b/kernel/audit.h
> > @@ -197,6 +197,7 @@ struct audit_context {
> > struct open_how openat2;
> > struct {
> > int argc;
> > + bool is_check;
> > } execve;
> > struct {
> > char *name;
> > diff --git a/kernel/auditsc.c b/kernel/auditsc.c
> > index 6f0d6fb6523f..b6316e284342 100644
> > --- a/kernel/auditsc.c
> > +++ b/kernel/auditsc.c
> > @@ -2662,6 +2662,7 @@ void __audit_bprm(struct linux_binprm *bprm)
> >
> > context->type = AUDIT_EXECVE;
> > context->execve.argc = bprm->argc;
> > + context->execve.is_check = bprm->is_check;
> > }
> >
> >
> > --
> > 2.45.2
> >
>
^ permalink raw reply
* Re: [RFC PATCH v19 0/5] Script execution control (was O_MAYEXEC)
From: Boris Lukashev @ 2024-07-17 17:59 UTC (permalink / raw)
To: Mickaël Salaün
Cc: James Bottomley, Roberto Sassu, Mimi Zohar, Al Viro,
Christian Brauner, Kees Cook, Linus Torvalds, Paul Moore,
Theodore Ts'o, Alejandro Colomar, Aleksa Sarai, Andrew Morton,
Andy Lutomirski, Arnd Bergmann, Casey Schaufler, Christian Heimes,
Dmitry Vyukov, Eric Biggers, Eric Chiang, Fan Wu, Florian Weimer,
Geert Uytterhoeven, James Morris, Jan Kara, Jann Horn, Jeff Xu,
Jonathan Corbet, Jordan R Abrahams, Lakshmi Ramasubramanian,
Luca Boccassi, Luis Chamberlain, Madhavan T . Venkataraman,
Matt Bobrowski, Matthew Garrett, Matthew Wilcox, Miklos Szeredi,
Nicolas Bouchinet, Scott Shell, Shuah Khan, Stephen Rothwell,
Steve Dower, Steve Grubb, Thibaut Sautereau, Vincent Strubel,
Xiaoming Ni, Yin Fengwei, kernel-hardening, linux-api,
linux-fsdevel, linux-integrity, linux-kernel,
linux-security-module
In-Reply-To: <20240716.shaliZ2chohj@digikod.net>
Apologies, sent from phone so plain-text wasn't flying.
To elaborate a bit on the quick commentary there - i'm the happy
camper behind most of the SSL shells, SSH stuff, AWS shells, and so on
in Metasploit. So please take the following with a grain of
tinfoil-hat salt as i'm well aware that there is no perfect defense
against these things which covers all bases while permitting any level
of sane operation in a general-purpose linux system (also work w/
GrapheneOS which is a far more suitable context for this sort of
thing). Having loosely followed the discussion thread, my offsec-brain
$0.02 are:
Shells are the provenance of the post-exploitation world - it's what
we want to get as a result of the exploit succeeding. So i think we
want to keep clear delineation between exploit and post-exp mitigation
as they're actually separate concerns of the killchain.
1. Command shells tend to differentiate from interpreted or binary
execution environments in their use of POSIX file descriptor
primitives such as pipes. How those are marshalled, chained, and
maintained (in a loop or whatever, hiding args, etc) are the only real
IOCs available at this tier for interdiction as observation of data
flow through the pipes is too onerous and complex. Target systems vary
in the post-exp surfaces exposed (/dev/tcp for example) with the
mechanics of that exposure necessitating adaptation of marshalling,
chaining, and maintenance to fit the environment; but the basic
premise of what forms a command shell cannot be mitigated without
breaking POSIX mechanics themselves - offsec devs are no different
from anyone else, we want our code to utilize architectural primitives
instead of undefined behavior for longevity and ecosystem
persistence/relevance.
2. The conversation about interpreted languages is probably a dead-end
unless you want to neuter the interpreter - check out Spencer
McIntyre's work re Python meterpreter or HDs/mine/etc on the PHP side.
The stagers, loaded contexts, execution patterns, etc are all
trivially modified to avoid detection (private versions not submitted
for free ripping by lazy commercial entities to the FOSS ecosystem,
yet). Dynamic code loading of interpreted languages is trivial and
requires no syscalls, just text/serialized IL/etc. The complexity of
loaded context available permits much more advanced functionality than
we get in most basic command interpreter shells - <advanced evasions
go here before doing something that'll get you caught> sort of thing.
3. Lastly, binary payloads such as Mettle have their own advantages re
portability, skipping over libc, etc but need to be "harnessed-in"
from say a command-injection exploit via memfd or similar. We haven't
published our memfd stagers while the relevant sysctl gets adopted
more widely, but we've had them for a long time (meaning real bad guys
have as well) and have other ways to get binary content into
executable memory or make memory containing it executable
(to-the-gills Grsec/PaX systems notwithstanding). IMO, interdiction of
the harnessed injection from a command context is the last time when
anything of use can be done at this layer unless we're sure that we
can trace all related and potentially async (not within the process
tree anyway) syscalls emanating from what happens next. Subsequent
actions are separate "remedial" workflows which is a wholly separate
philosophical discussion about how to handle having been compromised
already.
Security is very much not binary and in that vein of logic i think
that we should probably define our shades of gray as ranges of what we
want to protect/how and at what operational cost to then permit
"dial-in" knobs to actually garner adoption from a broad range of
systems outside the "real hardened efforts." At some point this turns
into "limit users to sftp or git shells" which is a perfectly valid
approach when the context permits that level of draconian restriction
but the architectural breakdown of "native command, interpreted
context, fully binary" shell types is pretty universal with new ones
being API access into runtimes of clouds (SSM/serial/etc) which have
their own set of limitations at execution and interface layers.
Organizing defensive functions to handle the primitives necessary for
each of these shell classes would likely help stratify/simplify this
conversation and allow for more granular tasking toward those specific
objectives.
Thanks,
-Boris
On Tue, Jul 16, 2024 at 1:48 PM Mickaël Salaün <mic@digikod.net> wrote:
>
> (adding back other people in Cc)
>
> On Tue, Jul 16, 2024 at 01:29:43PM -0400, Boris Lukashev wrote:
> > Wouldn't count those shell chickens - awk alone is enough and we can
> > use ssh and openssl clients (all in metasploit public code). As one of
> > the people who makes novel shell types, I can assure you that this
> > effort is only going to slow skiddies and only until the rest of us
> > publish mitigations for this mitigation :)
>
> Security is not binary. :)
>
> Not all Linux systems are equals. Some hardened systems need this kind
> of feature and they can get guarantees because they fully control and
> trust their executable binaries (e.g. CLIP OS, chromeOS) or they
> properly sandbox them. See context in the cover letter.
>
> awk is a script interpreter that should be patched too, like other Linux
> tools.
>
> >
> > -Boris (RageLtMan)
> >
> > On July 16, 2024 12:12:49 PM EDT, James Bottomley <James.Bottomley@HansenPartnership.com> wrote:
> > >On Tue, 2024-07-16 at 17:57 +0200, Roberto Sassu wrote:
> > >> But the Clip OS 4 patch does not cover the redirection case:
> > >>
> > >> # ./bash < /root/test.sh
> > >> Hello World
> > >>
> > >> Do you have a more recent patch for that?
> > >
> > >How far down the rabbit hole do you want to go? You can't forbid a
> > >shell from executing commands from stdin because logging in then won't
> > >work. It may be possible to allow from a tty backed file and not from
> > >a file backed one, but you still have the problem of the attacker
> > >manually typing in the script.
> > >
> > >The saving grace for this for shells is that they pretty much do
> > >nothing on their own (unlike python) so you can still measure all the
> > >executables they call out to, which provides reasonable safety.
> > >
> > >James
> > >
^ permalink raw reply
* Re: [RFC PATCH v19 1/5] exec: Add a new AT_CHECK flag to execveat(2)
From: Andy Lutomirski @ 2024-07-18 1:02 UTC (permalink / raw)
To: Mickaël Salaün
Cc: Steve Dower, Jeff Xu, Al Viro, Christian Brauner, Kees Cook,
Linus Torvalds, Paul Moore, Theodore Ts'o, Alejandro Colomar,
Aleksa Sarai, Andrew Morton, Andy Lutomirski, Arnd Bergmann,
Casey Schaufler, Christian Heimes, Dmitry Vyukov, Eric Biggers,
Eric Chiang, Fan Wu, Florian Weimer, Geert Uytterhoeven,
James Morris, Jan Kara, Jann Horn, Jonathan Corbet,
Jordan R Abrahams, Lakshmi Ramasubramanian, Luca Boccassi,
Luis Chamberlain, Madhavan T . Venkataraman, Matt Bobrowski,
Matthew Garrett, Matthew Wilcox, Miklos Szeredi, Mimi Zohar,
Nicolas Bouchinet, Scott Shell, Shuah Khan, Stephen Rothwell,
Steve Grubb, Thibaut Sautereau, Vincent Strubel, Xiaoming Ni,
Yin Fengwei, kernel-hardening, linux-api, linux-fsdevel,
linux-integrity, linux-kernel, linux-security-module
In-Reply-To: <20240717.AGh2shahc9ee@digikod.net>
> On Jul 17, 2024, at 6:01 PM, Mickaël Salaün <mic@digikod.net> wrote:
>
> On Wed, Jul 17, 2024 at 09:26:22AM +0100, Steve Dower wrote:
>>> On 17/07/2024 07:33, Jeff Xu wrote:
>>> Consider those cases: I think:
>>> a> relying purely on userspace for enforcement does't seem to be
>>> effective, e.g. it is trivial to call open(), then mmap() it into
>>> executable memory.
>>
>> If there's a way to do this without running executable code that had to pass
>> a previous execveat() check, then yeah, it's not effective (e.g. a Python
>> interpreter that *doesn't* enforce execveat() is a trivial way to do it).
>>
>> Once arbitrary code is running, all bets are off. So long as all arbitrary
>> code is being checked itself, it's allowed to do things that would bypass
>> later checks (and it's up to whoever audited it in the first place to
>> prevent this by not giving it the special mark that allows it to pass the
>> check).
>
> Exactly. As explained in the patches, one crucial prerequisite is that
> the executable code is trusted, and the system must provide integrity
> guarantees. We cannot do anything without that. This patches series is
> a building block to fix a blind spot on Linux systems to be able to
> fully control executability.
Circling back to my previous comment (did that ever get noticed?), I
don’t think this is quite right:
https://lore.kernel.org/all/CALCETrWYu=PYJSgyJ-vaa+3BGAry8Jo8xErZLiGR3U5h6+U0tA@mail.gmail.com/
On a basic system configuration, a given path either may or may not be
executed. And maybe that path has some integrity check (dm-verity,
etc). So the kernel should tell the interpreter/loader whether the
target may be executed. All fine.
But I think the more complex cases are more interesting, and the
“execute a program” process IS NOT BINARY. An attempt to execute can
be rejected outright, or it can be allowed *with a change to creds or
security context*. It would be entirely reasonable to have a policy
that allows execution of non-integrity-checked files but in a very
locked down context only.
So… shouldn’t a patch series to this effect actually support this?
^ permalink raw reply
* Re: [RFC PATCH v19 1/5] exec: Add a new AT_CHECK flag to execveat(2)
From: Jeff Xu @ 2024-07-18 1:51 UTC (permalink / raw)
To: Mickaël Salaün
Cc: Steve Dower, Al Viro, Christian Brauner, Kees Cook,
Linus Torvalds, Paul Moore, Theodore Ts'o, Alejandro Colomar,
Aleksa Sarai, Andrew Morton, Andy Lutomirski, Arnd Bergmann,
Casey Schaufler, Christian Heimes, Dmitry Vyukov, Eric Biggers,
Eric Chiang, Fan Wu, Florian Weimer, Geert Uytterhoeven,
James Morris, Jan Kara, Jann Horn, Jonathan Corbet,
Jordan R Abrahams, Lakshmi Ramasubramanian, Luca Boccassi,
Luis Chamberlain, Madhavan T . Venkataraman, Matt Bobrowski,
Matthew Garrett, Matthew Wilcox, Miklos Szeredi, Mimi Zohar,
Nicolas Bouchinet, Scott Shell, Shuah Khan, Stephen Rothwell,
Steve Grubb, Thibaut Sautereau, Vincent Strubel, Xiaoming Ni,
Yin Fengwei, kernel-hardening, linux-api, linux-fsdevel,
linux-integrity, linux-kernel, linux-security-module,
Elliott Hughes
In-Reply-To: <20240717.AGh2shahc9ee@digikod.net>
On Wed, Jul 17, 2024 at 3:00 AM Mickaël Salaün <mic@digikod.net> wrote:
>
> On Wed, Jul 17, 2024 at 09:26:22AM +0100, Steve Dower wrote:
> > On 17/07/2024 07:33, Jeff Xu wrote:
> > > Consider those cases: I think:
> > > a> relying purely on userspace for enforcement does't seem to be
> > > effective, e.g. it is trivial to call open(), then mmap() it into
> > > executable memory.
> >
> > If there's a way to do this without running executable code that had to pass
> > a previous execveat() check, then yeah, it's not effective (e.g. a Python
> > interpreter that *doesn't* enforce execveat() is a trivial way to do it).
> >
> > Once arbitrary code is running, all bets are off. So long as all arbitrary
> > code is being checked itself, it's allowed to do things that would bypass
> > later checks (and it's up to whoever audited it in the first place to
> > prevent this by not giving it the special mark that allows it to pass the
> > check).
>
We will want to define what is considered as "arbitrary code is running"
Using an example of ROP, attackers change the return address in stack,
e.g. direct the execution flow to a gauge to call "ld.so /tmp/a.out",
do you consider "arbitrary code is running" when stack is overwritten
? or after execve() is called.
If it is later, this patch can prevent "ld.so /tmp/a.out".
> Exactly. As explained in the patches, one crucial prerequisite is that
> the executable code is trusted, and the system must provide integrity
> guarantees. We cannot do anything without that. This patches series is
> a building block to fix a blind spot on Linux systems to be able to
> fully control executability.
Even trusted executable can have a bug.
I'm thinking in the context of ChromeOS, where all its system services
are from trusted partitions, and legit code won't load .so from a
non-exec mount. But we want to sandbox those services, so even under
some kind of ROP attack, the service still won't be able to load .so
from /tmp. Of course, if an attacker can already write arbitrary
length of data into the stack, it is probably already a game over.
^ permalink raw reply
* Re: [RFC PATCH v19 1/5] exec: Add a new AT_CHECK flag to execveat(2)
From: Jeff Xu @ 2024-07-18 2:08 UTC (permalink / raw)
To: Mickaël Salaün
Cc: Al Viro, Christian Brauner, Kees Cook, Linus Torvalds, Paul Moore,
Theodore Ts'o, Alejandro Colomar, Aleksa Sarai, Andrew Morton,
Andy Lutomirski, Arnd Bergmann, Casey Schaufler, Christian Heimes,
Dmitry Vyukov, Eric Biggers, Eric Chiang, Fan Wu, Florian Weimer,
Geert Uytterhoeven, James Morris, Jan Kara, Jann Horn,
Jonathan Corbet, Jordan R Abrahams, Lakshmi Ramasubramanian,
Luca Boccassi, Luis Chamberlain, Madhavan T . Venkataraman,
Matt Bobrowski, Matthew Garrett, Matthew Wilcox, Miklos Szeredi,
Mimi Zohar, Nicolas Bouchinet, Scott Shell, Shuah Khan,
Stephen Rothwell, Steve Dower, Steve Grubb, Thibaut Sautereau,
Vincent Strubel, Xiaoming Ni, Yin Fengwei, kernel-hardening,
linux-api, linux-fsdevel, linux-integrity, linux-kernel,
linux-security-module, Elliott Hughes
In-Reply-To: <20240717.neaB5Aiy2zah@digikod.net>
On Wed, Jul 17, 2024 at 3:01 AM Mickaël Salaün <mic@digikod.net> wrote:
>
> On Tue, Jul 16, 2024 at 11:33:55PM -0700, Jeff Xu wrote:
> > On Thu, Jul 4, 2024 at 12:02 PM Mickaël Salaün <mic@digikod.net> wrote:
> > >
> > > Add a new AT_CHECK flag to execveat(2) to check if a file would be
> > > allowed for execution. The main use case is for script interpreters and
> > > dynamic linkers to check execution permission according to the kernel's
> > > security policy. Another use case is to add context to access logs e.g.,
> > > which script (instead of interpreter) accessed a file. As any
> > > executable code, scripts could also use this check [1].
> > >
> > > This is different than faccessat(2) which only checks file access
> > > rights, but not the full context e.g. mount point's noexec, stack limit,
> > > and all potential LSM extra checks (e.g. argv, envp, credentials).
> > > Since the use of AT_CHECK follows the exact kernel semantic as for a
> > > real execution, user space gets the same error codes.
> > >
> > So we concluded that execveat(AT_CHECK) will be used to check the
> > exec, shared object, script and config file (such as seccomp config),
>
> "config file" that contains executable code.
>
Is seccomp config considered as "contains executable code", seccomp
config is translated into bpf, so maybe yes ? but bpf is running in
the kernel.
> > I'm still thinking execveat(AT_CHECK) vs faccessat(AT_CHECK) in
> > different use cases:
> >
> > execveat clearly has less code change, but that also means: we can't
> > add logic specific to exec (i.e. logic that can't be applied to
> > config) for this part (from do_execveat_common to
> > security_bprm_creds_for_exec) in future. This would require some
> > agreement/sign-off, I'm not sure from whom.
>
> I'm not sure to follow. We could still add new flags, but for now I
> don't see use cases. This patch series is not meant to handle all
> possible "trust checks", only executable code, which makes sense for the
> kernel.
>
I guess the "configfile" discussion is where I get confused, at one
point, I think this would become a generic "trust checks" api for
everything related to "generating executable code", e.g. javascript,
java code, and more.
We will want to clearly define the scope of execveat(AT_CHECK)
> If we want other checks, we'll need to clearly define their semantic and
> align with the kernel. faccessat2(2) might be used to check other file
> properties, but the executable property is not only defined by the file
> attributes.
>
Agreed.
> >
> > --------------------------
> > now looked at user cases (focus on elf for now)
> >
> > 1> ld.so /tmp/a.out, /tmp/a.out is on non-exec mount
> > dynamic linker will first call execveat(fd, AT_CHECK) then execveat(fd)
> >
> > 2> execve(/usr/bin/some.out) and some.out has dependency on /tmp/a.so
> > /usr/bin/some.out will pass AT_CHECK
> >
> > 3> execve(usr/bin/some.out) and some.out uses custom /tmp/ld.so
> > /usr/bin/some.out will pass AT_CHECK, however, it uses a custom
> > /tmp/ld.so (I assume this is possible for elf header will set the
> > path for ld.so because kernel has no knowledge of that, and
> > binfmt_elf.c allocate memory for ld.so during execveat call)
> >
> > 4> dlopen(/tmp/a.so)
> > I assume dynamic linker will call execveat(AT_CHECK), before map a.so
> > into memory.
> >
> > For case 1>
> > Alternative solution: Because AT_CHECK is always called, I think we
> > can avoid the first AT_CHECK call, and check during execveat(fd),
>
> There is no need to use AT_CHECK if we're going to call execveat(2) on
> the same file descriptor. By design, AT_CHECK is implicit for any
> execve(2).
>
Yes. I realized I was wrong to say that ld.so will call execve() for
/tmp/a.out, there is no execve() call, otherwise it would have been
blocked already today.
The ld.so will mmap the /tmp/a.out directly. So case 1 is no
different than case 2 and 4. ( the elf objects are mapped to memory
by dynamic linker.)
I'm not familiar with dynamic linker, Florian is on this thread, and
can help to correct me if my guess is wrong.
> > this means the kernel will enforce SECBIT_EXEC_RESTRICT_FILE = 1, the
> > benefit is that there is no TOCTOU and save one round trip of syscall
> > for a succesful execveat() case.
>
> As long as user space uses the same file descriptor, there is no TOCTOU.
>
> SECBIT_EXEC_RESTRICT_FILE only makes sense for user space: it defines
> the user space security policy. The kernel already enforces the same
> security policy for any execve(2), whatever are the calling process's
> securebits.
>
> >
> > For case 2>
> > dynamic linker will call execve(AT_CHECK), then mmap(fd) into memory.
> > However, the process can all open then mmap() directly, it seems
> > minimal effort for an attacker to walk around such a defence from
> > dynamic linker.
>
> Which process? What do you mean by "can all open then mmap() directly"?
>
> In this context the dynamic linker (like its parent processes) is
> trusted (guaranteed by the system).
>
> For case 2, the dynamic linker must check with AT_CHECK all files that
> will be mapped, which include /usr/bin/some.out and /tmp/a.so
>
My point is that the process can work around this by mmap() the file directly.
> >
> > Alternative solution:
> > dynamic linker call AT_CHECK for each .so, kernel will save the state
> > (associated with fd)
> > kernel will check fd state at the time of mmap(fd, executable memory)
> > and enforce SECBIT_EXEC_RESTRICT_FILE = 1
>
> The idea with AT_CHECK is that there is no kernel side effect, no extra
> kernel state, and the semantic is the same as with execve(2).
>
> This also enables us to check file's executable permission and ignore
> it, which is useful in a "permissive mode" when preparing for a
> migration without breaking a system, or to do extra integrity checks.
For preparing a migration (detect all violations), this is useful.
But as a defense mechanism (SECBIT_EXEC_RESTRICT_FILE = 1) , this
seems to be weak, at least for elf loading case.
> BTW, this use case would also be more complex with a new openat2(2) flag
> like the original O_MAYEXEC.
>
> >
> > Alternative solution 2:
> > a new syscall to load the .so and enforce the AT_CHECK in kernel
>
> A new syscall would be overkill for this feature. Please see Linus's
> comment.
>
maybe, I was thinking on how to prevent "/tmp/a.o" from getting mmap()
to executable memory.
> >
> > This also means, for the solution to be complete, we might want to
> > block creation of executable anonymous memory (e.g. by seccomp, ),
>
> How seccomp could create anonymous memory in user space?
> seccomp filters should be treated (and checked with AT_CHECK) as
> executable code anyway.
>
> > unless the user space can harden the creation of executable anonymous
> > memory in some way.
>
> User space is already in charge of mmapping its own memory. I don't see
> what is missing.
>
> >
> > For case 3>
> > I think binfmt_elf.c in the kernel needs to check the ld.so to make
> > sure it passes AT_CHECK, before loading it into memory.
>
> All ELF dependencies are opened and checked with open_exec(), which
> perform the main executability checks (with the __FMODE_EXEC flag).
> Did I miss something?
>
I mean the ld-linux-x86-64.so.2 which is loaded by binfmt in the kernel.
The app can choose its own dynamic linker path during build, (maybe
even statically link one ?) This is another reason that relying on a
userspace only is not enough.
> However, we must be careful with programs using the (deprecated)
> uselib(2). They should also check with AT_CHECK because this syscall
> opens the shared library without __FMODE_EXEC (similar to a simple file
> open). See
> https://lore.kernel.org/all/CAHk-=wiUwRG7LuR=z5sbkFVGQh+7qVB6_1NM0Ny9SVNL1Un4Sw@mail.gmail.com/
>
> >
> > For case 4>
> > same as case 2.
> >
> > Consider those cases: I think:
> > a> relying purely on userspace for enforcement does't seem to be
> > effective, e.g. it is trivial to call open(), then mmap() it into
> > executable memory.
>
> As Steve explained (and is also explained in the patches), it is trivial
> if the attacker can already execute its own code, which is too late to
> enforce any script execution control.
>
> > b> if both user space and kernel need to call AT_CHECK, the faccessat
> > seems to be a better place for AT_CHECK, e.g. kernel can call
> > do_faccessat(AT_CHECK) and userspace can call faccessat(). This will
> > avoid complicating the execveat() code path.
>
> A previous version of this patches series already patched faccessat(2),
> but this is not the right place. faccessat2(2) is dedicated to check
> file permissions, not executability (e.g. with mount's noexec).
>
> >
> > What do you think ?
>
> I think there are some misunderstandings. Please let me know if it's
> clearer now.
>
I'm still not sure about the user case for dynamic linker (elf
loading) case. Maybe this patch is more suitable for scripts?
A detailed user case will help demonstrate the use case for dynamic
linker, e.g. what kind of app will benefit from
SECBIT_EXEC_RESTRICT_FILE = 1, what kind of threat model are we
dealing with , what kind of attack chain we blocked as a result.
> >
> > Thanks
> > -Jeff
> >
> > > With the information that a script interpreter is about to interpret a
> > > script, an LSM security policy can adjust caller's access rights or log
> > > execution request as for native script execution (e.g. role transition).
> > > This is possible thanks to the call to security_bprm_creds_for_exec().
> > >
> > > Because LSMs may only change bprm's credentials, use of AT_CHECK with
> > > current kernel code should not be a security issue (e.g. unexpected role
> > > transition). LSMs willing to update the caller's credential could now
> > > do so when bprm->is_check is set. Of course, such policy change should
> > > be in line with the new user space code.
> > >
> > > Because AT_CHECK is dedicated to user space interpreters, it doesn't
> > > make sense for the kernel to parse the checked files, look for
> > > interpreters known to the kernel (e.g. ELF, shebang), and return ENOEXEC
> > > if the format is unknown. Because of that, security_bprm_check() is
> > > never called when AT_CHECK is used.
> > >
> > > It should be noted that script interpreters cannot directly use
> > > execveat(2) (without this new AT_CHECK flag) because this could lead to
> > > unexpected behaviors e.g., `python script.sh` could lead to Bash being
> > > executed to interpret the script. Unlike the kernel, script
> > > interpreters may just interpret the shebang as a simple comment, which
> > > should not change for backward compatibility reasons.
> > >
> > > Because scripts or libraries files might not currently have the
> > > executable permission set, or because we might want specific users to be
> > > allowed to run arbitrary scripts, the following patch provides a dynamic
> > > configuration mechanism with the SECBIT_SHOULD_EXEC_CHECK and
> > > SECBIT_SHOULD_EXEC_RESTRICT securebits.
> > >
> > > This is a redesign of the CLIP OS 4's O_MAYEXEC:
> > > https://github.com/clipos-archive/src_platform_clip-patches/blob/f5cb330d6b684752e403b4e41b39f7004d88e561/1901_open_mayexec.patch
> > > This patch has been used for more than a decade with customized script
> > > interpreters. Some examples can be found here:
> > > https://github.com/clipos-archive/clipos4_portage-overlay/search?q=O_MAYEXEC
> > >
> > > Cc: Al Viro <viro@zeniv.linux.org.uk>
> > > Cc: Christian Brauner <brauner@kernel.org>
> > > Cc: Kees Cook <keescook@chromium.org>
> > > Cc: Paul Moore <paul@paul-moore.com>
> > > Link: https://docs.python.org/3/library/io.html#io.open_code [1]
> > > Signed-off-by: Mickaël Salaün <mic@digikod.net>
> > > Link: https://lore.kernel.org/r/20240704190137.696169-2-mic@digikod.net
> > > ---
> > >
> > > New design since v18:
> > > https://lore.kernel.org/r/20220104155024.48023-3-mic@digikod.net
> > > ---
> > > fs/exec.c | 5 +++--
> > > include/linux/binfmts.h | 7 ++++++-
> > > include/uapi/linux/fcntl.h | 30 ++++++++++++++++++++++++++++++
> > > kernel/audit.h | 1 +
> > > kernel/auditsc.c | 1 +
> > > 5 files changed, 41 insertions(+), 3 deletions(-)
> > >
> > > diff --git a/fs/exec.c b/fs/exec.c
> > > index 40073142288f..ea2a1867afdc 100644
> > > --- a/fs/exec.c
> > > +++ b/fs/exec.c
> > > @@ -931,7 +931,7 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags)
> > > .lookup_flags = LOOKUP_FOLLOW,
> > > };
> > >
> > > - if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0)
> > > + if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH | AT_CHECK)) != 0)
> > > return ERR_PTR(-EINVAL);
> > > if (flags & AT_SYMLINK_NOFOLLOW)
> > > open_exec_flags.lookup_flags &= ~LOOKUP_FOLLOW;
> > > @@ -1595,6 +1595,7 @@ static struct linux_binprm *alloc_bprm(int fd, struct filename *filename, int fl
> > > bprm->filename = bprm->fdpath;
> > > }
> > > bprm->interp = bprm->filename;
> > > + bprm->is_check = !!(flags & AT_CHECK);
> > >
> > > retval = bprm_mm_init(bprm);
> > > if (!retval)
> > > @@ -1885,7 +1886,7 @@ static int bprm_execve(struct linux_binprm *bprm)
> > >
> > > /* Set the unchanging part of bprm->cred */
> > > retval = security_bprm_creds_for_exec(bprm);
> > > - if (retval)
> > > + if (retval || bprm->is_check)
> > > goto out;
> > >
> > > retval = exec_binprm(bprm);
> > > diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
> > > index 70f97f685bff..8ff9c9e33aed 100644
> > > --- a/include/linux/binfmts.h
> > > +++ b/include/linux/binfmts.h
> > > @@ -42,7 +42,12 @@ struct linux_binprm {
> > > * Set when errors can no longer be returned to the
> > > * original userspace.
> > > */
> > > - point_of_no_return:1;
> > > + point_of_no_return:1,
> > > + /*
> > > + * Set by user space to check executability according to the
> > > + * caller's environment.
> > > + */
> > > + is_check:1;
> > > struct file *executable; /* Executable to pass to the interpreter */
> > > struct file *interpreter;
> > > struct file *file;
> > > diff --git a/include/uapi/linux/fcntl.h b/include/uapi/linux/fcntl.h
> > > index c0bcc185fa48..bcd05c59b7df 100644
> > > --- a/include/uapi/linux/fcntl.h
> > > +++ b/include/uapi/linux/fcntl.h
> > > @@ -118,6 +118,36 @@
> > > #define AT_HANDLE_FID AT_REMOVEDIR /* file handle is needed to
> > > compare object identity and may not
> > > be usable to open_by_handle_at(2) */
> > > +
> > > +/*
> > > + * AT_CHECK only performs a check on a regular file and returns 0 if execution
> > > + * of this file would be allowed, ignoring the file format and then the related
> > > + * interpreter dependencies (e.g. ELF libraries, script's shebang). AT_CHECK
> > > + * should only be used if SECBIT_SHOULD_EXEC_CHECK is set for the calling
> > > + * thread. See securebits.h documentation.
> > > + *
> > > + * Programs should use this check to apply kernel-level checks against files
> > > + * that are not directly executed by the kernel but directly passed to a user
> > > + * space interpreter instead. All files that contain executable code, from the
> > > + * point of view of the interpreter, should be checked. The main purpose of
> > > + * this flag is to improve the security and consistency of an execution
> > > + * environment to ensure that direct file execution (e.g. ./script.sh) and
> > > + * indirect file execution (e.g. sh script.sh) lead to the same result. For
> > > + * instance, this can be used to check if a file is trustworthy according to
> > > + * the caller's environment.
> > > + *
> > > + * In a secure environment, libraries and any executable dependencies should
> > > + * also be checked. For instance dynamic linking should make sure that all
> > > + * libraries are allowed for execution to avoid trivial bypass (e.g. using
> > > + * LD_PRELOAD). For such secure execution environment to make sense, only
> > > + * trusted code should be executable, which also requires integrity guarantees.
> > > + *
> > > + * To avoid race conditions leading to time-of-check to time-of-use issues,
> > > + * AT_CHECK should be used with AT_EMPTY_PATH to check against a file
> > > + * descriptor instead of a path.
> > > + */
> > > +#define AT_CHECK 0x10000
> > > +
> > > #if defined(__KERNEL__)
> > > #define AT_GETATTR_NOSEC 0x80000000
> > > #endif
> > > diff --git a/kernel/audit.h b/kernel/audit.h
> > > index a60d2840559e..8ebdabd2ab81 100644
> > > --- a/kernel/audit.h
> > > +++ b/kernel/audit.h
> > > @@ -197,6 +197,7 @@ struct audit_context {
> > > struct open_how openat2;
> > > struct {
> > > int argc;
> > > + bool is_check;
> > > } execve;
> > > struct {
> > > char *name;
> > > diff --git a/kernel/auditsc.c b/kernel/auditsc.c
> > > index 6f0d6fb6523f..b6316e284342 100644
> > > --- a/kernel/auditsc.c
> > > +++ b/kernel/auditsc.c
> > > @@ -2662,6 +2662,7 @@ void __audit_bprm(struct linux_binprm *bprm)
> > >
> > > context->type = AUDIT_EXECVE;
> > > context->execve.argc = bprm->argc;
> > > + context->execve.is_check = bprm->is_check;
> > > }
> > >
> > >
> > > --
> > > 2.45.2
> > >
> >
^ permalink raw reply
* Re: [RFC PATCH v19 1/5] exec: Add a new AT_CHECK flag to execveat(2)
From: Mickaël Salaün @ 2024-07-18 12:22 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Steve Dower, Jeff Xu, Al Viro, Christian Brauner, Kees Cook,
Linus Torvalds, Paul Moore, Theodore Ts'o, Alejandro Colomar,
Aleksa Sarai, Andrew Morton, Andy Lutomirski, Arnd Bergmann,
Casey Schaufler, Christian Heimes, Dmitry Vyukov, Eric Biggers,
Eric Chiang, Fan Wu, Florian Weimer, Geert Uytterhoeven,
James Morris, Jan Kara, Jann Horn, Jonathan Corbet,
Jordan R Abrahams, Lakshmi Ramasubramanian, Luca Boccassi,
Luis Chamberlain, Madhavan T . Venkataraman, Matt Bobrowski,
Matthew Garrett, Matthew Wilcox, Miklos Szeredi, Mimi Zohar,
Nicolas Bouchinet, Scott Shell, Shuah Khan, Stephen Rothwell,
Steve Grubb, Thibaut Sautereau, Vincent Strubel, Xiaoming Ni,
Yin Fengwei, kernel-hardening, linux-api, linux-fsdevel,
linux-integrity, linux-kernel, linux-security-module,
Elliott Hughes
In-Reply-To: <CALCETrUcr3p_APNazMro7Y9FX1zLAiQESvKZ5BDgd8X3PoCdFw@mail.gmail.com>
On Thu, Jul 18, 2024 at 09:02:56AM +0800, Andy Lutomirski wrote:
> > On Jul 17, 2024, at 6:01 PM, Mickaël Salaün <mic@digikod.net> wrote:
> >
> > On Wed, Jul 17, 2024 at 09:26:22AM +0100, Steve Dower wrote:
> >>> On 17/07/2024 07:33, Jeff Xu wrote:
> >>> Consider those cases: I think:
> >>> a> relying purely on userspace for enforcement does't seem to be
> >>> effective, e.g. it is trivial to call open(), then mmap() it into
> >>> executable memory.
> >>
> >> If there's a way to do this without running executable code that had to pass
> >> a previous execveat() check, then yeah, it's not effective (e.g. a Python
> >> interpreter that *doesn't* enforce execveat() is a trivial way to do it).
> >>
> >> Once arbitrary code is running, all bets are off. So long as all arbitrary
> >> code is being checked itself, it's allowed to do things that would bypass
> >> later checks (and it's up to whoever audited it in the first place to
> >> prevent this by not giving it the special mark that allows it to pass the
> >> check).
> >
> > Exactly. As explained in the patches, one crucial prerequisite is that
> > the executable code is trusted, and the system must provide integrity
> > guarantees. We cannot do anything without that. This patches series is
> > a building block to fix a blind spot on Linux systems to be able to
> > fully control executability.
>
> Circling back to my previous comment (did that ever get noticed?), I
Yes, I replied to your comments. Did I miss something?
> don’t think this is quite right:
>
> https://lore.kernel.org/all/CALCETrWYu=PYJSgyJ-vaa+3BGAry8Jo8xErZLiGR3U5h6+U0tA@mail.gmail.com/
>
> On a basic system configuration, a given path either may or may not be
> executed. And maybe that path has some integrity check (dm-verity,
> etc). So the kernel should tell the interpreter/loader whether the
> target may be executed. All fine.
>
> But I think the more complex cases are more interesting, and the
> “execute a program” process IS NOT BINARY. An attempt to execute can
> be rejected outright, or it can be allowed *with a change to creds or
> security context*. It would be entirely reasonable to have a policy
> that allows execution of non-integrity-checked files but in a very
> locked down context only.
I guess you mean to transition to a sandbox when executing an untrusted
file. This is a good idea. I talked about role transition in the
patch's description:
With the information that a script interpreter is about to interpret a
script, an LSM security policy can adjust caller's access rights or log
execution request as for native script execution (e.g. role transition).
This is possible thanks to the call to security_bprm_creds_for_exec().
>
> So… shouldn’t a patch series to this effect actually support this?
>
This patch series brings the minimal building blocks to have a
consistent execution environment. Role transitions for script execution
are left to LSMs. For instance, we could extend Landlock to
automatically sandbox untrusted scripts.
^ permalink raw reply
* Re: [RFC PATCH v19 1/5] exec: Add a new AT_CHECK flag to execveat(2)
From: Mickaël Salaün @ 2024-07-18 12:24 UTC (permalink / raw)
To: Jeff Xu
Cc: Al Viro, Christian Brauner, Kees Cook, Linus Torvalds, Paul Moore,
Theodore Ts'o, Alejandro Colomar, Aleksa Sarai, Andrew Morton,
Andy Lutomirski, Arnd Bergmann, Casey Schaufler, Christian Heimes,
Dmitry Vyukov, Eric Biggers, Eric Chiang, Fan Wu, Florian Weimer,
Geert Uytterhoeven, James Morris, Jan Kara, Jann Horn,
Jonathan Corbet, Jordan R Abrahams, Lakshmi Ramasubramanian,
Luca Boccassi, Luis Chamberlain, Madhavan T . Venkataraman,
Matt Bobrowski, Matthew Garrett, Matthew Wilcox, Miklos Szeredi,
Mimi Zohar, Nicolas Bouchinet, Scott Shell, Shuah Khan,
Stephen Rothwell, Steve Dower, Steve Grubb, Thibaut Sautereau,
Vincent Strubel, Xiaoming Ni, Yin Fengwei, kernel-hardening,
linux-api, linux-fsdevel, linux-integrity, linux-kernel,
linux-security-module, Elliott Hughes
In-Reply-To: <CALmYWFt=yXpzhS=HS9FjwVMvx6U1MoR31vK79wxNLhmJm9bBoA@mail.gmail.com>
On Wed, Jul 17, 2024 at 07:08:17PM -0700, Jeff Xu wrote:
> On Wed, Jul 17, 2024 at 3:01 AM Mickaël Salaün <mic@digikod.net> wrote:
> >
> > On Tue, Jul 16, 2024 at 11:33:55PM -0700, Jeff Xu wrote:
> > > On Thu, Jul 4, 2024 at 12:02 PM Mickaël Salaün <mic@digikod.net> wrote:
> > > >
> > > > Add a new AT_CHECK flag to execveat(2) to check if a file would be
> > > > allowed for execution. The main use case is for script interpreters and
> > > > dynamic linkers to check execution permission according to the kernel's
> > > > security policy. Another use case is to add context to access logs e.g.,
> > > > which script (instead of interpreter) accessed a file. As any
> > > > executable code, scripts could also use this check [1].
> > > >
> > > > This is different than faccessat(2) which only checks file access
> > > > rights, but not the full context e.g. mount point's noexec, stack limit,
> > > > and all potential LSM extra checks (e.g. argv, envp, credentials).
> > > > Since the use of AT_CHECK follows the exact kernel semantic as for a
> > > > real execution, user space gets the same error codes.
> > > >
> > > So we concluded that execveat(AT_CHECK) will be used to check the
> > > exec, shared object, script and config file (such as seccomp config),
> >
> > "config file" that contains executable code.
> >
> Is seccomp config considered as "contains executable code", seccomp
> config is translated into bpf, so maybe yes ? but bpf is running in
> the kernel.
Because seccomp filters alter syscalls, they are similar to code
injection.
>
> > > I'm still thinking execveat(AT_CHECK) vs faccessat(AT_CHECK) in
> > > different use cases:
> > >
> > > execveat clearly has less code change, but that also means: we can't
> > > add logic specific to exec (i.e. logic that can't be applied to
> > > config) for this part (from do_execveat_common to
> > > security_bprm_creds_for_exec) in future. This would require some
> > > agreement/sign-off, I'm not sure from whom.
> >
> > I'm not sure to follow. We could still add new flags, but for now I
> > don't see use cases. This patch series is not meant to handle all
> > possible "trust checks", only executable code, which makes sense for the
> > kernel.
> >
> I guess the "configfile" discussion is where I get confused, at one
> point, I think this would become a generic "trust checks" api for
> everything related to "generating executable code", e.g. javascript,
> java code, and more.
> We will want to clearly define the scope of execveat(AT_CHECK)
The line between data and code is blurry. For instance, a configuration
file can impact the execution flow of a program. So, where to draw the
line?
It might makes sense to follow the kernel and interpreter semantic: if a
file can be executed by the kernel (e.g. ELF binary, file containing a
shebang, or just configured with binfmt_misc), then this should be
considered as executable code. This applies to Bash, Python,
Javascript, NodeJS, PE, PHP... However, we can also make a picture
executable with binfmt_misc. So, again, where to draw the line?
I'd recommend to think about interaction with the outside, through
function calls, IPCs, syscalls... For instance, "running" an image
should not lead to reading or writing to arbitrary files, or accessing
the network, but in practice it is legitimate for some file formats...
PostScript is a programming language, but mostly used to draw pictures.
So, again, where to draw the line?
We should follow the principle of least astonishment. What most users
would expect? This should follow the *common usage* of executable
files. At the end, the script interpreters will be patched by security
folks for security reasons. I think the right question to ask should
be: could this file format be (ab)used to leak or modify arbitrary
files, or to perform arbitrary syscalls? If the answer is yes, then it
should be checked for executability. Of course, this excludes bugs
exploited in the file format parser.
I'll extend the next patch series with this rationale.
>
> > If we want other checks, we'll need to clearly define their semantic and
> > align with the kernel. faccessat2(2) might be used to check other file
> > properties, but the executable property is not only defined by the file
> > attributes.
> >
> Agreed.
>
> > >
> > > --------------------------
> > > now looked at user cases (focus on elf for now)
> > >
> > > 1> ld.so /tmp/a.out, /tmp/a.out is on non-exec mount
> > > dynamic linker will first call execveat(fd, AT_CHECK) then execveat(fd)
> > >
> > > 2> execve(/usr/bin/some.out) and some.out has dependency on /tmp/a.so
> > > /usr/bin/some.out will pass AT_CHECK
> > >
> > > 3> execve(usr/bin/some.out) and some.out uses custom /tmp/ld.so
> > > /usr/bin/some.out will pass AT_CHECK, however, it uses a custom
> > > /tmp/ld.so (I assume this is possible for elf header will set the
> > > path for ld.so because kernel has no knowledge of that, and
> > > binfmt_elf.c allocate memory for ld.so during execveat call)
> > >
> > > 4> dlopen(/tmp/a.so)
> > > I assume dynamic linker will call execveat(AT_CHECK), before map a.so
> > > into memory.
> > >
> > > For case 1>
> > > Alternative solution: Because AT_CHECK is always called, I think we
> > > can avoid the first AT_CHECK call, and check during execveat(fd),
> >
> > There is no need to use AT_CHECK if we're going to call execveat(2) on
> > the same file descriptor. By design, AT_CHECK is implicit for any
> > execve(2).
> >
> Yes. I realized I was wrong to say that ld.so will call execve() for
> /tmp/a.out, there is no execve() call, otherwise it would have been
> blocked already today.
> The ld.so will mmap the /tmp/a.out directly. So case 1 is no
> different than case 2 and 4. ( the elf objects are mapped to memory
> by dynamic linker.)
> I'm not familiar with dynamic linker, Florian is on this thread, and
> can help to correct me if my guess is wrong.
>
> > > this means the kernel will enforce SECBIT_EXEC_RESTRICT_FILE = 1, the
> > > benefit is that there is no TOCTOU and save one round trip of syscall
> > > for a succesful execveat() case.
> >
> > As long as user space uses the same file descriptor, there is no TOCTOU.
> >
> > SECBIT_EXEC_RESTRICT_FILE only makes sense for user space: it defines
> > the user space security policy. The kernel already enforces the same
> > security policy for any execve(2), whatever are the calling process's
> > securebits.
> >
> > >
> > > For case 2>
> > > dynamic linker will call execve(AT_CHECK), then mmap(fd) into memory.
> > > However, the process can all open then mmap() directly, it seems
> > > minimal effort for an attacker to walk around such a defence from
> > > dynamic linker.
> >
> > Which process? What do you mean by "can all open then mmap() directly"?
> >
> > In this context the dynamic linker (like its parent processes) is
> > trusted (guaranteed by the system).
> >
> > For case 2, the dynamic linker must check with AT_CHECK all files that
> > will be mapped, which include /usr/bin/some.out and /tmp/a.so
> >
> My point is that the process can work around this by mmap() the file directly.
Yes, see my answer in the other email. The process is trusted.
>
> > >
> > > Alternative solution:
> > > dynamic linker call AT_CHECK for each .so, kernel will save the state
> > > (associated with fd)
> > > kernel will check fd state at the time of mmap(fd, executable memory)
> > > and enforce SECBIT_EXEC_RESTRICT_FILE = 1
> >
> > The idea with AT_CHECK is that there is no kernel side effect, no extra
> > kernel state, and the semantic is the same as with execve(2).
> >
> > This also enables us to check file's executable permission and ignore
> > it, which is useful in a "permissive mode" when preparing for a
> > migration without breaking a system, or to do extra integrity checks.
> For preparing a migration (detect all violations), this is useful.
> But as a defense mechanism (SECBIT_EXEC_RESTRICT_FILE = 1) , this
> seems to be weak, at least for elf loading case.
We could add more restrictions, but that is outside the scope of this
patch series.
>
> > BTW, this use case would also be more complex with a new openat2(2) flag
> > like the original O_MAYEXEC.
> >
> > >
> > > Alternative solution 2:
> > > a new syscall to load the .so and enforce the AT_CHECK in kernel
> >
> > A new syscall would be overkill for this feature. Please see Linus's
> > comment.
> >
> maybe, I was thinking on how to prevent "/tmp/a.o" from getting mmap()
> to executable memory.
OK, this is another story.
>
> > >
> > > This also means, for the solution to be complete, we might want to
> > > block creation of executable anonymous memory (e.g. by seccomp, ),
> >
> > How seccomp could create anonymous memory in user space?
> > seccomp filters should be treated (and checked with AT_CHECK) as
> > executable code anyway.
> >
> > > unless the user space can harden the creation of executable anonymous
> > > memory in some way.
> >
> > User space is already in charge of mmapping its own memory. I don't see
> > what is missing.
> >
> > >
> > > For case 3>
> > > I think binfmt_elf.c in the kernel needs to check the ld.so to make
> > > sure it passes AT_CHECK, before loading it into memory.
> >
> > All ELF dependencies are opened and checked with open_exec(), which
> > perform the main executability checks (with the __FMODE_EXEC flag).
> > Did I miss something?
> >
> I mean the ld-linux-x86-64.so.2 which is loaded by binfmt in the kernel.
> The app can choose its own dynamic linker path during build, (maybe
> even statically link one ?) This is another reason that relying on a
> userspace only is not enough.
The kernel calls open_exec() on all dependencies, including
ld-linux-x86-64.so.2, so these files are checked for executability too.
>
> > However, we must be careful with programs using the (deprecated)
> > uselib(2). They should also check with AT_CHECK because this syscall
> > opens the shared library without __FMODE_EXEC (similar to a simple file
> > open). See
> > https://lore.kernel.org/all/CAHk-=wiUwRG7LuR=z5sbkFVGQh+7qVB6_1NM0Ny9SVNL1Un4Sw@mail.gmail.com/
> >
> > >
> > > For case 4>
> > > same as case 2.
> > >
> > > Consider those cases: I think:
> > > a> relying purely on userspace for enforcement does't seem to be
> > > effective, e.g. it is trivial to call open(), then mmap() it into
> > > executable memory.
> >
> > As Steve explained (and is also explained in the patches), it is trivial
> > if the attacker can already execute its own code, which is too late to
> > enforce any script execution control.
> >
> > > b> if both user space and kernel need to call AT_CHECK, the faccessat
> > > seems to be a better place for AT_CHECK, e.g. kernel can call
> > > do_faccessat(AT_CHECK) and userspace can call faccessat(). This will
> > > avoid complicating the execveat() code path.
> >
> > A previous version of this patches series already patched faccessat(2),
> > but this is not the right place. faccessat2(2) is dedicated to check
> > file permissions, not executability (e.g. with mount's noexec).
> >
> > >
> > > What do you think ?
> >
> > I think there are some misunderstandings. Please let me know if it's
> > clearer now.
> >
> I'm still not sure about the user case for dynamic linker (elf
> loading) case. Maybe this patch is more suitable for scripts?
It's suitable for both, but we could add more restriction on mmap
with an (existing) LSM. The kernel already checks for mount's noexec
when mapping a file, but not for the file permission, which is OK
because it could be bypassed by coping the content of the file and
mprotecting it anyway. For a consistent memory execution control, all
memory mapping need to be restricted, which is out of scope for this
patch series.
> A detailed user case will help demonstrate the use case for dynamic
> linker, e.g. what kind of app will benefit from
> SECBIT_EXEC_RESTRICT_FILE = 1, what kind of threat model are we
> dealing with , what kind of attack chain we blocked as a result.
I explained that in the patches and in the description of these new
securebits. Please point which part is not clear. The full threat
model is simple: the TCB includes the kernel and system's files, which
are integrity-protected, but we don't trust arbitrary data/scripts that
can be written to user-owned files or directly provided to script
interpreters. As for the ptrace restrictions, the dynamic linker
restrictions helps to avoid trivial bypasses (e.g. with LD_PRELOAD)
with consistent executability checks.
>
> > >
> > > Thanks
> > > -Jeff
> > >
> > > > With the information that a script interpreter is about to interpret a
> > > > script, an LSM security policy can adjust caller's access rights or log
> > > > execution request as for native script execution (e.g. role transition).
> > > > This is possible thanks to the call to security_bprm_creds_for_exec().
> > > >
> > > > Because LSMs may only change bprm's credentials, use of AT_CHECK with
> > > > current kernel code should not be a security issue (e.g. unexpected role
> > > > transition). LSMs willing to update the caller's credential could now
> > > > do so when bprm->is_check is set. Of course, such policy change should
> > > > be in line with the new user space code.
> > > >
> > > > Because AT_CHECK is dedicated to user space interpreters, it doesn't
> > > > make sense for the kernel to parse the checked files, look for
> > > > interpreters known to the kernel (e.g. ELF, shebang), and return ENOEXEC
> > > > if the format is unknown. Because of that, security_bprm_check() is
> > > > never called when AT_CHECK is used.
> > > >
> > > > It should be noted that script interpreters cannot directly use
> > > > execveat(2) (without this new AT_CHECK flag) because this could lead to
> > > > unexpected behaviors e.g., `python script.sh` could lead to Bash being
> > > > executed to interpret the script. Unlike the kernel, script
> > > > interpreters may just interpret the shebang as a simple comment, which
> > > > should not change for backward compatibility reasons.
> > > >
> > > > Because scripts or libraries files might not currently have the
> > > > executable permission set, or because we might want specific users to be
> > > > allowed to run arbitrary scripts, the following patch provides a dynamic
> > > > configuration mechanism with the SECBIT_SHOULD_EXEC_CHECK and
> > > > SECBIT_SHOULD_EXEC_RESTRICT securebits.
> > > >
> > > > This is a redesign of the CLIP OS 4's O_MAYEXEC:
> > > > https://github.com/clipos-archive/src_platform_clip-patches/blob/f5cb330d6b684752e403b4e41b39f7004d88e561/1901_open_mayexec.patch
> > > > This patch has been used for more than a decade with customized script
> > > > interpreters. Some examples can be found here:
> > > > https://github.com/clipos-archive/clipos4_portage-overlay/search?q=O_MAYEXEC
> > > >
> > > > Cc: Al Viro <viro@zeniv.linux.org.uk>
> > > > Cc: Christian Brauner <brauner@kernel.org>
> > > > Cc: Kees Cook <keescook@chromium.org>
> > > > Cc: Paul Moore <paul@paul-moore.com>
> > > > Link: https://docs.python.org/3/library/io.html#io.open_code [1]
> > > > Signed-off-by: Mickaël Salaün <mic@digikod.net>
> > > > Link: https://lore.kernel.org/r/20240704190137.696169-2-mic@digikod.net
> > > > ---
> > > >
> > > > New design since v18:
> > > > https://lore.kernel.org/r/20220104155024.48023-3-mic@digikod.net
> > > > ---
> > > > fs/exec.c | 5 +++--
> > > > include/linux/binfmts.h | 7 ++++++-
> > > > include/uapi/linux/fcntl.h | 30 ++++++++++++++++++++++++++++++
> > > > kernel/audit.h | 1 +
> > > > kernel/auditsc.c | 1 +
> > > > 5 files changed, 41 insertions(+), 3 deletions(-)
> > > >
> > > > diff --git a/fs/exec.c b/fs/exec.c
> > > > index 40073142288f..ea2a1867afdc 100644
> > > > --- a/fs/exec.c
> > > > +++ b/fs/exec.c
> > > > @@ -931,7 +931,7 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags)
> > > > .lookup_flags = LOOKUP_FOLLOW,
> > > > };
> > > >
> > > > - if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0)
> > > > + if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH | AT_CHECK)) != 0)
> > > > return ERR_PTR(-EINVAL);
> > > > if (flags & AT_SYMLINK_NOFOLLOW)
> > > > open_exec_flags.lookup_flags &= ~LOOKUP_FOLLOW;
> > > > @@ -1595,6 +1595,7 @@ static struct linux_binprm *alloc_bprm(int fd, struct filename *filename, int fl
> > > > bprm->filename = bprm->fdpath;
> > > > }
> > > > bprm->interp = bprm->filename;
> > > > + bprm->is_check = !!(flags & AT_CHECK);
> > > >
> > > > retval = bprm_mm_init(bprm);
> > > > if (!retval)
> > > > @@ -1885,7 +1886,7 @@ static int bprm_execve(struct linux_binprm *bprm)
> > > >
> > > > /* Set the unchanging part of bprm->cred */
> > > > retval = security_bprm_creds_for_exec(bprm);
> > > > - if (retval)
> > > > + if (retval || bprm->is_check)
> > > > goto out;
> > > >
> > > > retval = exec_binprm(bprm);
> > > > diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
> > > > index 70f97f685bff..8ff9c9e33aed 100644
> > > > --- a/include/linux/binfmts.h
> > > > +++ b/include/linux/binfmts.h
> > > > @@ -42,7 +42,12 @@ struct linux_binprm {
> > > > * Set when errors can no longer be returned to the
> > > > * original userspace.
> > > > */
> > > > - point_of_no_return:1;
> > > > + point_of_no_return:1,
> > > > + /*
> > > > + * Set by user space to check executability according to the
> > > > + * caller's environment.
> > > > + */
> > > > + is_check:1;
> > > > struct file *executable; /* Executable to pass to the interpreter */
> > > > struct file *interpreter;
> > > > struct file *file;
> > > > diff --git a/include/uapi/linux/fcntl.h b/include/uapi/linux/fcntl.h
> > > > index c0bcc185fa48..bcd05c59b7df 100644
> > > > --- a/include/uapi/linux/fcntl.h
> > > > +++ b/include/uapi/linux/fcntl.h
> > > > @@ -118,6 +118,36 @@
> > > > #define AT_HANDLE_FID AT_REMOVEDIR /* file handle is needed to
> > > > compare object identity and may not
> > > > be usable to open_by_handle_at(2) */
> > > > +
> > > > +/*
> > > > + * AT_CHECK only performs a check on a regular file and returns 0 if execution
> > > > + * of this file would be allowed, ignoring the file format and then the related
> > > > + * interpreter dependencies (e.g. ELF libraries, script's shebang). AT_CHECK
> > > > + * should only be used if SECBIT_SHOULD_EXEC_CHECK is set for the calling
> > > > + * thread. See securebits.h documentation.
> > > > + *
> > > > + * Programs should use this check to apply kernel-level checks against files
> > > > + * that are not directly executed by the kernel but directly passed to a user
> > > > + * space interpreter instead. All files that contain executable code, from the
> > > > + * point of view of the interpreter, should be checked. The main purpose of
> > > > + * this flag is to improve the security and consistency of an execution
> > > > + * environment to ensure that direct file execution (e.g. ./script.sh) and
> > > > + * indirect file execution (e.g. sh script.sh) lead to the same result. For
> > > > + * instance, this can be used to check if a file is trustworthy according to
> > > > + * the caller's environment.
> > > > + *
> > > > + * In a secure environment, libraries and any executable dependencies should
> > > > + * also be checked. For instance dynamic linking should make sure that all
> > > > + * libraries are allowed for execution to avoid trivial bypass (e.g. using
> > > > + * LD_PRELOAD). For such secure execution environment to make sense, only
> > > > + * trusted code should be executable, which also requires integrity guarantees.
> > > > + *
> > > > + * To avoid race conditions leading to time-of-check to time-of-use issues,
> > > > + * AT_CHECK should be used with AT_EMPTY_PATH to check against a file
> > > > + * descriptor instead of a path.
> > > > + */
> > > > +#define AT_CHECK 0x10000
> > > > +
> > > > #if defined(__KERNEL__)
> > > > #define AT_GETATTR_NOSEC 0x80000000
> > > > #endif
> > > > diff --git a/kernel/audit.h b/kernel/audit.h
> > > > index a60d2840559e..8ebdabd2ab81 100644
> > > > --- a/kernel/audit.h
> > > > +++ b/kernel/audit.h
> > > > @@ -197,6 +197,7 @@ struct audit_context {
> > > > struct open_how openat2;
> > > > struct {
> > > > int argc;
> > > > + bool is_check;
> > > > } execve;
> > > > struct {
> > > > char *name;
> > > > diff --git a/kernel/auditsc.c b/kernel/auditsc.c
> > > > index 6f0d6fb6523f..b6316e284342 100644
> > > > --- a/kernel/auditsc.c
> > > > +++ b/kernel/auditsc.c
> > > > @@ -2662,6 +2662,7 @@ void __audit_bprm(struct linux_binprm *bprm)
> > > >
> > > > context->type = AUDIT_EXECVE;
> > > > context->execve.argc = bprm->argc;
> > > > + context->execve.is_check = bprm->is_check;
> > > > }
> > > >
> > > >
> > > > --
> > > > 2.45.2
> > > >
> > >
>
^ permalink raw reply
* Re: [RFC PATCH v19 1/5] exec: Add a new AT_CHECK flag to execveat(2)
From: Mickaël Salaün @ 2024-07-18 12:23 UTC (permalink / raw)
To: Jeff Xu
Cc: Steve Dower, Al Viro, Christian Brauner, Kees Cook,
Linus Torvalds, Paul Moore, Theodore Ts'o, Alejandro Colomar,
Aleksa Sarai, Andrew Morton, Andy Lutomirski, Arnd Bergmann,
Casey Schaufler, Christian Heimes, Dmitry Vyukov, Eric Biggers,
Eric Chiang, Fan Wu, Florian Weimer, Geert Uytterhoeven,
James Morris, Jan Kara, Jann Horn, Jonathan Corbet,
Jordan R Abrahams, Lakshmi Ramasubramanian, Luca Boccassi,
Luis Chamberlain, Madhavan T . Venkataraman, Matt Bobrowski,
Matthew Garrett, Matthew Wilcox, Miklos Szeredi, Mimi Zohar,
Nicolas Bouchinet, Scott Shell, Shuah Khan, Stephen Rothwell,
Steve Grubb, Thibaut Sautereau, Vincent Strubel, Xiaoming Ni,
Yin Fengwei, kernel-hardening, linux-api, linux-fsdevel,
linux-integrity, linux-kernel, linux-security-module,
Elliott Hughes
In-Reply-To: <CALmYWFvxJSyi=BT5BKDiKCNanmbhLuZ6=iAMvv1ibnP24SC7fA@mail.gmail.com>
On Wed, Jul 17, 2024 at 06:51:11PM -0700, Jeff Xu wrote:
> On Wed, Jul 17, 2024 at 3:00 AM Mickaël Salaün <mic@digikod.net> wrote:
> >
> > On Wed, Jul 17, 2024 at 09:26:22AM +0100, Steve Dower wrote:
> > > On 17/07/2024 07:33, Jeff Xu wrote:
> > > > Consider those cases: I think:
> > > > a> relying purely on userspace for enforcement does't seem to be
> > > > effective, e.g. it is trivial to call open(), then mmap() it into
> > > > executable memory.
> > >
> > > If there's a way to do this without running executable code that had to pass
> > > a previous execveat() check, then yeah, it's not effective (e.g. a Python
> > > interpreter that *doesn't* enforce execveat() is a trivial way to do it).
> > >
> > > Once arbitrary code is running, all bets are off. So long as all arbitrary
> > > code is being checked itself, it's allowed to do things that would bypass
> > > later checks (and it's up to whoever audited it in the first place to
> > > prevent this by not giving it the special mark that allows it to pass the
> > > check).
> >
> We will want to define what is considered as "arbitrary code is running"
>
> Using an example of ROP, attackers change the return address in stack,
> e.g. direct the execution flow to a gauge to call "ld.so /tmp/a.out",
> do you consider "arbitrary code is running" when stack is overwritten
> ? or after execve() is called.
Yes, ROP is arbitrary code execution (which can be mitigated with CFI).
ROP could be enough to interpret custom commands and create a small
interpreter/VM.
> If it is later, this patch can prevent "ld.so /tmp/a.out".
>
> > Exactly. As explained in the patches, one crucial prerequisite is that
> > the executable code is trusted, and the system must provide integrity
> > guarantees. We cannot do anything without that. This patches series is
> > a building block to fix a blind spot on Linux systems to be able to
> > fully control executability.
>
> Even trusted executable can have a bug.
Definitely, but this patch series is dedicated to script execution
control.
>
> I'm thinking in the context of ChromeOS, where all its system services
> are from trusted partitions, and legit code won't load .so from a
> non-exec mount. But we want to sandbox those services, so even under
> some kind of ROP attack, the service still won't be able to load .so
> from /tmp. Of course, if an attacker can already write arbitrary
> length of data into the stack, it is probably already a game over.
>
OK, you want to tie executable file permission to mmap. That makes
sense if you have a consistent execution model. This can be enforced by
LSMs. Contrary to script interpretation which is a full user space
implementation (and then controlled by user space), mmap restrictions
should indeed be enforced by the kernel.
^ permalink raw reply
* Re: [RFC PATCH v19 1/5] exec: Add a new AT_CHECK flag to execveat(2)
From: James Bottomley @ 2024-07-18 13:03 UTC (permalink / raw)
To: Mickaël Salaün, Jeff Xu
Cc: Al Viro, Christian Brauner, Kees Cook, Linus Torvalds, Paul Moore,
Theodore Ts'o, Alejandro Colomar, Aleksa Sarai, Andrew Morton,
Andy Lutomirski, Arnd Bergmann, Casey Schaufler, Christian Heimes,
Dmitry Vyukov, Eric Biggers, Eric Chiang, Fan Wu, Florian Weimer,
Geert Uytterhoeven, James Morris, Jan Kara, Jann Horn,
Jonathan Corbet, Jordan R Abrahams, Lakshmi Ramasubramanian,
Luca Boccassi, Luis Chamberlain, Madhavan T . Venkataraman,
Matt Bobrowski, Matthew Garrett, Matthew Wilcox, Miklos Szeredi,
Mimi Zohar, Nicolas Bouchinet, Scott Shell, Shuah Khan,
Stephen Rothwell, Steve Dower, Steve Grubb, Thibaut Sautereau,
Vincent Strubel, Xiaoming Ni, Yin Fengwei, kernel-hardening,
linux-api, linux-fsdevel, linux-integrity, linux-kernel,
linux-security-module, Elliott Hughes
In-Reply-To: <20240718.kaePhei9Ahm9@digikod.net>
On Thu, 2024-07-18 at 14:24 +0200, Mickaël Salaün wrote:
> On Wed, Jul 17, 2024 at 07:08:17PM -0700, Jeff Xu wrote:
> > On Wed, Jul 17, 2024 at 3:01 AM Mickaël Salaün <mic@digikod.net>
> > wrote:
> > > On Tue, Jul 16, 2024 at 11:33:55PM -0700, Jeff Xu wrote:
[...]
> > > > I'm still thinking execveat(AT_CHECK) vs faccessat(AT_CHECK)
> > > > in different use cases:
> > > >
> > > > execveat clearly has less code change, but that also means: we
> > > > can't add logic specific to exec (i.e. logic that can't be
> > > > applied to config) for this part (from do_execveat_common to
> > > > security_bprm_creds_for_exec) in future. This would require
> > > > some agreement/sign-off, I'm not sure from whom.
> > >
> > > I'm not sure to follow. We could still add new flags, but for now
> > > I don't see use cases. This patch series is not meant to handle
> > > all possible "trust checks", only executable code, which makes
> > > sense for the kernel.
> > >
> > I guess the "configfile" discussion is where I get confused, at one
> > point, I think this would become a generic "trust checks" api for
> > everything related to "generating executable code", e.g.
> > javascript, java code, and more. We will want to clearly define the
> > scope of execveat(AT_CHECK)
>
> The line between data and code is blurry. For instance, a
> configuration file can impact the execution flow of a program. So,
> where to draw the line?
Having a way to have config files part of the trusted envelope, either
by signing or measurement would be really useful. The current standard
distro IMA deployment is signed executables, but not signed config
because it's hard to construct a policy that doesn't force the signing
of too many extraneous files (and files which might change often).
> It might makes sense to follow the kernel and interpreter semantic:
> if a file can be executed by the kernel (e.g. ELF binary, file
> containing a shebang, or just configured with binfmt_misc), then this
> should be considered as executable code. This applies to Bash,
> Python, Javascript, NodeJS, PE, PHP... However, we can also make a
> picture executable with binfmt_misc. So, again, where to draw the
> line?
Possibly by making open for config an indication executables can give?
I'm not advocating doing it in this patch, but if we had an open for
config indication, the LSMs could do much finer grained policy,
especially if they knew which executable was trying to open the config
file. It would allow things like an IMA policy saying if a signed
executable is opening a config file, then that file must also be
signed.
James
^ permalink raw reply
* Re: [RFC PATCH v19 0/5] Script execution control (was O_MAYEXEC)
From: Mickaël Salaün @ 2024-07-18 13:00 UTC (permalink / raw)
To: Boris Lukashev
Cc: James Bottomley, Roberto Sassu, Mimi Zohar, Al Viro,
Christian Brauner, Kees Cook, Linus Torvalds, Paul Moore,
Theodore Ts'o, Alejandro Colomar, Aleksa Sarai, Andrew Morton,
Andy Lutomirski, Arnd Bergmann, Casey Schaufler, Christian Heimes,
Dmitry Vyukov, Eric Biggers, Eric Chiang, Fan Wu, Florian Weimer,
Geert Uytterhoeven, James Morris, Jan Kara, Jann Horn, Jeff Xu,
Jonathan Corbet, Jordan R Abrahams, Lakshmi Ramasubramanian,
Luca Boccassi, Luis Chamberlain, Madhavan T . Venkataraman,
Matt Bobrowski, Matthew Garrett, Matthew Wilcox, Miklos Szeredi,
Nicolas Bouchinet, Scott Shell, Shuah Khan, Stephen Rothwell,
Steve Dower, Steve Grubb, Thibaut Sautereau, Vincent Strubel,
Xiaoming Ni, Yin Fengwei, kernel-hardening, linux-api,
linux-fsdevel, linux-integrity, linux-kernel,
linux-security-module, Elliott Hughes
In-Reply-To: <CAFUG7CfqAV0vzuFf_WL+wedeRzAfOyRGVWRVhfNBxS3FU78Tig@mail.gmail.com>
On Wed, Jul 17, 2024 at 01:59:22PM -0400, Boris Lukashev wrote:
> Apologies, sent from phone so plain-text wasn't flying.
> To elaborate a bit on the quick commentary there - i'm the happy
> camper behind most of the SSL shells, SSH stuff, AWS shells, and so on
> in Metasploit. So please take the following with a grain of
> tinfoil-hat salt as i'm well aware that there is no perfect defense
> against these things which covers all bases while permitting any level
> of sane operation in a general-purpose linux system (also work w/
> GrapheneOS which is a far more suitable context for this sort of
> thing). Having loosely followed the discussion thread, my offsec-brain
> $0.02 are:
>
> Shells are the provenance of the post-exploitation world - it's what
> we want to get as a result of the exploit succeeding. So i think we
> want to keep clear delineation between exploit and post-exp mitigation
> as they're actually separate concerns of the killchain.
Indeed. The goal of this patch series is to control executable code, so
mostly to make exploitation more difficult. When an attacker can execute
code (e.g. with ROP), execution control is already bypassed.
> 1. Command shells tend to differentiate from interpreted or binary
> execution environments in their use of POSIX file descriptor
> primitives such as pipes. How those are marshalled, chained, and
> maintained (in a loop or whatever, hiding args, etc) are the only real
> IOCs available at this tier for interdiction as observation of data
> flow through the pipes is too onerous and complex.
I agree. Only files can reliably be inspected.
> Target systems vary
> in the post-exp surfaces exposed (/dev/tcp for example) with the
> mechanics of that exposure necessitating adaptation of marshalling,
> chaining, and maintenance to fit the environment; but the basic
> premise of what forms a command shell cannot be mitigated without
> breaking POSIX mechanics themselves - offsec devs are no different
> from anyone else, we want our code to utilize architectural primitives
> instead of undefined behavior for longevity and ecosystem
> persistence/relevance.
> 2. The conversation about interpreted languages is probably a dead-end
> unless you want to neuter the interpreter - check out Spencer
> McIntyre's work re Python meterpreter or HDs/mine/etc on the PHP side.
> The stagers, loaded contexts, execution patterns, etc are all
> trivially modified to avoid detection (private versions not submitted
> for free ripping by lazy commercial entities to the FOSS ecosystem,
> yet). Dynamic code loading of interpreted languages is trivial and
> requires no syscalls, just text/serialized IL/etc. The complexity of
> loaded context available permits much more advanced functionality than
> we get in most basic command interpreter shells - <advanced evasions
> go here before doing something that'll get you caught> sort of thing.
Right, if attackers can bring their own code (or even do ROP), it
doesn't matter what it interprets, its arbitrary code execution.
> 3. Lastly, binary payloads such as Mettle have their own advantages re
> portability, skipping over libc, etc but need to be "harnessed-in"
> from say a command-injection exploit via memfd or similar. We haven't
> published our memfd stagers while the relevant sysctl gets adopted
> more widely, but we've had them for a long time (meaning real bad guys
> have as well) and have other ways to get binary content into
> executable memory or make memory containing it executable
> (to-the-gills Grsec/PaX systems notwithstanding). IMO, interdiction of
> the harnessed injection from a command context is the last time when
> anything of use can be done at this layer unless we're sure that we
> can trace all related and potentially async (not within the process
> tree anyway) syscalls emanating from what happens next. Subsequent
> actions are separate "remedial" workflows which is a wholly separate
> philosophical discussion about how to handle having been compromised
> already.
Indeed, there are some prerequisites for a secure system. In this case
we trust all the system-installed executable code. If attackers can
fill a memfd with arbitrary code, it means that they already have code
execution. This patch series will help mitigate some ways to get code
execution.
>
> Security is very much not binary and in that vein of logic i think
> that we should probably define our shades of gray as ranges of what we
> want to protect/how and at what operational cost to then permit
> "dial-in" knobs to actually garner adoption from a broad range of
> systems outside the "real hardened efforts." At some point this turns
> into "limit users to sftp or git shells" which is a perfectly valid
> approach when the context permits that level of draconian restriction
> but the architectural breakdown of "native command, interpreted
> context, fully binary" shell types is pretty universal with new ones
> being API access into runtimes of clouds (SSM/serial/etc) which have
> their own set of limitations at execution and interface layers.
> Organizing defensive functions to handle the primitives necessary for
> each of these shell classes would likely help stratify/simplify this
> conversation and allow for more granular tasking toward those specific
> objectives.
Thanks for the discussion. I agree, but the difficulty with this patch
series is that it brings a simple *building block*. Of course, this
will definitely not be enough to secure any systems, but it will fill a
gap in some secure systems, and it could also harden more generic
systems (e.g. restricted system services which should not need shell
access). I listed some examples with the new securebits proposal:
https://lore.kernel.org/all/20240710.eiKohpa4Phai@digikod.net/
>
> Thanks,
> -Boris
>
>
> On Tue, Jul 16, 2024 at 1:48 PM Mickaël Salaün <mic@digikod.net> wrote:
> >
> > (adding back other people in Cc)
> >
> > On Tue, Jul 16, 2024 at 01:29:43PM -0400, Boris Lukashev wrote:
> > > Wouldn't count those shell chickens - awk alone is enough and we can
> > > use ssh and openssl clients (all in metasploit public code). As one of
> > > the people who makes novel shell types, I can assure you that this
> > > effort is only going to slow skiddies and only until the rest of us
> > > publish mitigations for this mitigation :)
> >
> > Security is not binary. :)
> >
> > Not all Linux systems are equals. Some hardened systems need this kind
> > of feature and they can get guarantees because they fully control and
> > trust their executable binaries (e.g. CLIP OS, chromeOS) or they
> > properly sandbox them. See context in the cover letter.
> >
> > awk is a script interpreter that should be patched too, like other Linux
> > tools.
> >
> > >
> > > -Boris (RageLtMan)
> > >
> > > On July 16, 2024 12:12:49 PM EDT, James Bottomley <James.Bottomley@HansenPartnership.com> wrote:
> > > >On Tue, 2024-07-16 at 17:57 +0200, Roberto Sassu wrote:
> > > >> But the Clip OS 4 patch does not cover the redirection case:
> > > >>
> > > >> # ./bash < /root/test.sh
> > > >> Hello World
> > > >>
> > > >> Do you have a more recent patch for that?
> > > >
> > > >How far down the rabbit hole do you want to go? You can't forbid a
> > > >shell from executing commands from stdin because logging in then won't
> > > >work. It may be possible to allow from a tty backed file and not from
> > > >a file backed one, but you still have the problem of the attacker
> > > >manually typing in the script.
> > > >
> > > >The saving grace for this for shells is that they pretty much do
> > > >nothing on their own (unlike python) so you can still measure all the
> > > >executables they call out to, which provides reasonable safety.
> > > >
> > > >James
> > > >
^ permalink raw reply
* Re: [RFC PATCH v19 2/5] security: Add new SHOULD_EXEC_CHECK and SHOULD_EXEC_RESTRICT securebits
From: Roberto Sassu @ 2024-07-18 14:16 UTC (permalink / raw)
To: Mickaël Salaün, Kees Cook
Cc: Al Viro, Christian Brauner, Linus Torvalds, Paul Moore,
Theodore Ts'o, Alejandro Colomar, Aleksa Sarai, Andrew Morton,
Andy Lutomirski, Arnd Bergmann, Casey Schaufler, Christian Heimes,
Dmitry Vyukov, Eric Biggers, Eric Chiang, Fan Wu, Florian Weimer,
Geert Uytterhoeven, James Morris, Jan Kara, Jann Horn, Jeff Xu,
Jonathan Corbet, Jordan R Abrahams, Lakshmi Ramasubramanian,
Luca Boccassi, Luis Chamberlain, Madhavan T . Venkataraman,
Matt Bobrowski, Matthew Garrett, Matthew Wilcox, Miklos Szeredi,
Mimi Zohar, Nicolas Bouchinet, Scott Shell, Shuah Khan,
Stephen Rothwell, Steve Dower, Steve Grubb, Thibaut Sautereau,
Vincent Strubel, Xiaoming Ni, Yin Fengwei, kernel-hardening,
linux-api, linux-fsdevel, linux-integrity, linux-kernel,
linux-security-module
In-Reply-To: <20240706.eng1ieSh0wa5@digikod.net>
On Sat, 2024-07-06 at 16:56 +0200, Mickaël Salaün wrote:
> On Fri, Jul 05, 2024 at 02:44:03PM -0700, Kees Cook wrote:
> > On Fri, Jul 05, 2024 at 07:54:16PM +0200, Mickaël Salaün wrote:
> > > On Thu, Jul 04, 2024 at 05:18:04PM -0700, Kees Cook wrote:
> > > > On Thu, Jul 04, 2024 at 09:01:34PM +0200, Mickaël Salaün wrote:
> > > > > Such a secure environment can be achieved with an appropriate access
> > > > > control policy (e.g. mount's noexec option, file access rights, LSM
> > > > > configuration) and an enlighten ld.so checking that libraries are
> > > > > allowed for execution e.g., to protect against illegitimate use of
> > > > > LD_PRELOAD.
> > > > >
> > > > > Scripts may need some changes to deal with untrusted data (e.g. stdin,
> > > > > environment variables), but that is outside the scope of the kernel.
> > > >
> > > > If the threat model includes an attacker sitting at a shell prompt, we
> > > > need to be very careful about how process perform enforcement. E.g. even
> > > > on a locked down system, if an attacker has access to LD_PRELOAD or a
> > >
> > > LD_PRELOAD should be OK once ld.so will be patched to check the
> > > libraries. We can still imagine a debug library used to bypass security
> > > checks, but in this case the issue would be that this library is
> > > executable in the first place.
> >
> > Ah yes, that's fair: the shell would discover the malicious library
> > while using AT_CHECK during resolution of the LD_PRELOAD.
>
> That's the idea, but it would be checked by ld.so, not the shell.
>
> >
> > > > seccomp wrapper (which you both mention here), it would be possible to
> > > > run commands where the resulting process is tricked into thinking it
> > > > doesn't have the bits set.
> > >
> > > As explained in the UAPI comments, all parent processes need to be
> > > trusted. This meeans that their code is trusted, their seccomp filters
> > > are trusted, and that they are patched, if needed, to check file
> > > executability.
> >
> > But we have launchers that apply arbitrary seccomp policy, e.g. minijail
> > on Chrome OS, or even systemd on regular distros. In theory, this should
> > be handled via other ACLs.
>
> Processes running with untrusted seccomp filter should be considered
> untrusted. It would then make sense for these seccomp filters/programs
> to be considered executable code, and then for minijail and systemd to
> check them with AT_CHECK (according to the securebits policy).
>
> >
> > > > But this would be exactly true for calling execveat(): LD_PRELOAD or
> > > > seccomp policy could have it just return 0.
> > >
> > > If an attacker is allowed/able to load an arbitrary seccomp filter on a
> > > process, we cannot trust this process.
> > >
> > > >
> > > > While I like AT_CHECK, I do wonder if it's better to do the checks via
> > > > open(), as was originally designed with O_MAYEXEC. Because then
> > > > enforcement is gated by the kernel -- the process does not get a file
> > > > descriptor _at all_, no matter what LD_PRELOAD or seccomp tricks it into
> > > > doing.
> > >
> > > Being able to check a path name or a file descriptor (with the same
> > > syscall) is more flexible and cover more use cases.
> >
> > If flexibility costs us reliability, I think that flexibility is not
> > a benefit.
>
> Well, it's a matter of letting user space do what they think is best,
> and I think there are legitimate and safe uses of path names, even if I
> agree that this should not be used in most use cases. Would we want
> faccessat2(2) to only take file descriptor as argument and not file
> path? I don't think so but I'd defer to the VFS maintainers.
>
> Christian, Al, Linus?
>
> Steve, could you share a use case with file paths?
>
> >
> > > The execveat(2)
> > > interface, including current and future flags, is dedicated to file
> > > execution. I then think that using execveat(2) for this kind of check
> > > makes more sense, and will easily evolve with this syscall.
> >
> > Yeah, I do recognize that is feels much more natural, but I remain
> > unhappy about how difficult it will become to audit a system for safety
> > when the check is strictly per-process opt-in, and not enforced by the
> > kernel for a given process tree. But, I think this may have always been
> > a fiction in my mind. :)
>
> Hmm, I'm not sure to follow. Securebits are inherited, so process tree.
> And we need the parent processes to be trusted anyway.
>
> >
> > > > And this thinking also applies to faccessat() too: if a process can be
> > > > tricked into thinking the access check passed, it'll happily interpret
> > > > whatever. :( But not being able to open the fd _at all_ when O_MAYEXEC
> > > > is being checked seems substantially safer to me...
> > >
> > > If attackers can filter execveat(2), they can also filter open(2) and
> > > any other syscalls. In all cases, that would mean an issue in the
> > > security policy.
> >
> > Hm, as in, make a separate call to open(2) without O_MAYEXEC, and pass
> > that fd back to the filtered open(2) that did have O_MAYEXEC. Yes, true.
> >
> > I guess it does become morally equivalent.
> >
> > Okay. Well, let me ask about usability. Right now, a process will need
> > to do:
> >
> > - should I use AT_CHECK? (check secbit)
> > - if yes: perform execveat(AT_CHECK)
> >
> > Why not leave the secbit test up to the kernel, and then the program can
> > just unconditionally call execveat(AT_CHECK)?
>
> That was kind of the approach of the previous patch series and Linus
> wanted the new interface to follow the kernel semantic. Enforcing this
> kind of restriction will always be the duty of user space anyway, so I
> think it's simpler (i.e. no mix of policy definition, access check, and
> policy enforcement, but a standalone execveat feature), more flexible,
> and it fully delegates the policy enforcement to user space instead of
> trying to enforce some part in the kernel which would only give the
> illusion of security/policy enforcement.
A problem could be that from IMA perspective there is no indication on
whether the interpreter executed or not execveat(). Sure, we can detect
that the binary supports it, but if the enforcement was
enabled/disabled that it is not recorded.
Maybe, setting the process flags should be influenced by the kernel,
for example not allowing changes and enforcing when there is an IMA
policy loaded requiring to measure/appraise scripts.
Roberto
> >
> > Though perhaps the issue here is that an execveat() EINVAL doesn't
> > tell the program if AT_CHECK is unimplemented or if something else
> > went wrong, and the secbit prctl() will give the correct signal about
> > AT_CHECK availability?
>
> This kind of check could indeed help to identify the issue.
^ permalink raw reply
* Re: [RFC PATCH v19 1/5] exec: Add a new AT_CHECK flag to execveat(2)
From: enh @ 2024-07-18 14:46 UTC (permalink / raw)
To: Jeff Xu
Cc: Mickaël Salaün, Al Viro, Christian Brauner, Kees Cook,
Linus Torvalds, Paul Moore, Theodore Ts'o, Alejandro Colomar,
Aleksa Sarai, Andrew Morton, Andy Lutomirski, Arnd Bergmann,
Casey Schaufler, Christian Heimes, Dmitry Vyukov, Eric Biggers,
Eric Chiang, Fan Wu, Florian Weimer, Geert Uytterhoeven,
James Morris, Jan Kara, Jann Horn, Jonathan Corbet,
Jordan R Abrahams, Lakshmi Ramasubramanian, Luca Boccassi,
Luis Chamberlain, Madhavan T . Venkataraman, Matt Bobrowski,
Matthew Garrett, Matthew Wilcox, Miklos Szeredi, Mimi Zohar,
Nicolas Bouchinet, Scott Shell, Shuah Khan, Stephen Rothwell,
Steve Dower, Steve Grubb, Thibaut Sautereau, Vincent Strubel,
Xiaoming Ni, Yin Fengwei, kernel-hardening, linux-api,
linux-fsdevel, linux-integrity, linux-kernel,
linux-security-module
In-Reply-To: <CALmYWFt=yXpzhS=HS9FjwVMvx6U1MoR31vK79wxNLhmJm9bBoA@mail.gmail.com>
On Wed, Jul 17, 2024 at 10:08 PM Jeff Xu <jeffxu@google.com> wrote:
>
> On Wed, Jul 17, 2024 at 3:01 AM Mickaël Salaün <mic@digikod.net> wrote:
> >
> > On Tue, Jul 16, 2024 at 11:33:55PM -0700, Jeff Xu wrote:
> > > On Thu, Jul 4, 2024 at 12:02 PM Mickaël Salaün <mic@digikod.net> wrote:
> > > >
> > > > Add a new AT_CHECK flag to execveat(2) to check if a file would be
> > > > allowed for execution. The main use case is for script interpreters and
> > > > dynamic linkers to check execution permission according to the kernel's
> > > > security policy. Another use case is to add context to access logs e.g.,
> > > > which script (instead of interpreter) accessed a file. As any
> > > > executable code, scripts could also use this check [1].
> > > >
> > > > This is different than faccessat(2) which only checks file access
> > > > rights, but not the full context e.g. mount point's noexec, stack limit,
> > > > and all potential LSM extra checks (e.g. argv, envp, credentials).
> > > > Since the use of AT_CHECK follows the exact kernel semantic as for a
> > > > real execution, user space gets the same error codes.
> > > >
> > > So we concluded that execveat(AT_CHECK) will be used to check the
> > > exec, shared object, script and config file (such as seccomp config),
> >
> > "config file" that contains executable code.
> >
> Is seccomp config considered as "contains executable code", seccomp
> config is translated into bpf, so maybe yes ? but bpf is running in
> the kernel.
>
> > > I'm still thinking execveat(AT_CHECK) vs faccessat(AT_CHECK) in
> > > different use cases:
> > >
> > > execveat clearly has less code change, but that also means: we can't
> > > add logic specific to exec (i.e. logic that can't be applied to
> > > config) for this part (from do_execveat_common to
> > > security_bprm_creds_for_exec) in future. This would require some
> > > agreement/sign-off, I'm not sure from whom.
> >
> > I'm not sure to follow. We could still add new flags, but for now I
> > don't see use cases. This patch series is not meant to handle all
> > possible "trust checks", only executable code, which makes sense for the
> > kernel.
> >
> I guess the "configfile" discussion is where I get confused, at one
> point, I think this would become a generic "trust checks" api for
> everything related to "generating executable code", e.g. javascript,
> java code, and more.
> We will want to clearly define the scope of execveat(AT_CHECK)
>
> > If we want other checks, we'll need to clearly define their semantic and
> > align with the kernel. faccessat2(2) might be used to check other file
> > properties, but the executable property is not only defined by the file
> > attributes.
> >
> Agreed.
>
> > >
> > > --------------------------
> > > now looked at user cases (focus on elf for now)
> > >
> > > 1> ld.so /tmp/a.out, /tmp/a.out is on non-exec mount
> > > dynamic linker will first call execveat(fd, AT_CHECK) then execveat(fd)
> > >
> > > 2> execve(/usr/bin/some.out) and some.out has dependency on /tmp/a.so
> > > /usr/bin/some.out will pass AT_CHECK
> > >
> > > 3> execve(usr/bin/some.out) and some.out uses custom /tmp/ld.so
> > > /usr/bin/some.out will pass AT_CHECK, however, it uses a custom
> > > /tmp/ld.so (I assume this is possible for elf header will set the
> > > path for ld.so because kernel has no knowledge of that, and
> > > binfmt_elf.c allocate memory for ld.so during execveat call)
> > >
> > > 4> dlopen(/tmp/a.so)
> > > I assume dynamic linker will call execveat(AT_CHECK), before map a.so
> > > into memory.
> > >
> > > For case 1>
> > > Alternative solution: Because AT_CHECK is always called, I think we
> > > can avoid the first AT_CHECK call, and check during execveat(fd),
> >
> > There is no need to use AT_CHECK if we're going to call execveat(2) on
> > the same file descriptor. By design, AT_CHECK is implicit for any
> > execve(2).
> >
> Yes. I realized I was wrong to say that ld.so will call execve() for
> /tmp/a.out, there is no execve() call, otherwise it would have been
> blocked already today.
> The ld.so will mmap the /tmp/a.out directly. So case 1 is no
> different than case 2 and 4. ( the elf objects are mapped to memory
> by dynamic linker.)
> I'm not familiar with dynamic linker, Florian is on this thread, and
> can help to correct me if my guess is wrong.
for Android, this has been the nail in the coffin of previous attempts
to disallow running code from non-trusted filesystems --- instead of
execing /tmp/a.out, the attacker just execs the linker with /tmp/a.out
as an argument. people are doing this already in some cases, because
we already have ineffectual "barriers" in place. [the usual argument
for doing such things anyway is "it makes it harder to be doing this
by _accident_".]
the other workaround for the attacker is to copy and paste the entire
dynamic linker source and change the bits they don't like :-) (if
you're thinking "is that a thing?", yes, so much so that the idea has
been independently reinvented multiple times by several major legit
apps and by basically every piece of DRM middleware. which is why --
although i'm excited by mseal(2) -- i expect to face significant
challenges rolling it out in Android _especially_ in places like
"dynamic linker internal data structures" where i've wanted it for
years!)
this proposal feels like it _ought_ to let a defender tighten their
seccomp filter to require a "safe" fd if i'm using mmap() with an fd,
but in practice as long as JITs exist i can always just copy code into
a non-fd-backed mmap() region. and -- from the perspective of Android,
where all "apps" are code loaded into a Java runtime -- there's not
much getting away from JITs. (and last i looked, ART -- Android's Java
runtime -- uses memfd() for the JIT cache.)
> > > this means the kernel will enforce SECBIT_EXEC_RESTRICT_FILE = 1, the
> > > benefit is that there is no TOCTOU and save one round trip of syscall
> > > for a succesful execveat() case.
> >
> > As long as user space uses the same file descriptor, there is no TOCTOU.
> >
> > SECBIT_EXEC_RESTRICT_FILE only makes sense for user space: it defines
> > the user space security policy. The kernel already enforces the same
> > security policy for any execve(2), whatever are the calling process's
> > securebits.
> >
> > >
> > > For case 2>
> > > dynamic linker will call execve(AT_CHECK), then mmap(fd) into memory.
> > > However, the process can all open then mmap() directly, it seems
> > > minimal effort for an attacker to walk around such a defence from
> > > dynamic linker.
> >
> > Which process? What do you mean by "can all open then mmap() directly"?
> >
> > In this context the dynamic linker (like its parent processes) is
> > trusted (guaranteed by the system).
> >
> > For case 2, the dynamic linker must check with AT_CHECK all files that
> > will be mapped, which include /usr/bin/some.out and /tmp/a.so
> >
> My point is that the process can work around this by mmap() the file directly.
>
> > >
> > > Alternative solution:
> > > dynamic linker call AT_CHECK for each .so, kernel will save the state
> > > (associated with fd)
> > > kernel will check fd state at the time of mmap(fd, executable memory)
> > > and enforce SECBIT_EXEC_RESTRICT_FILE = 1
> >
> > The idea with AT_CHECK is that there is no kernel side effect, no extra
> > kernel state, and the semantic is the same as with execve(2).
> >
> > This also enables us to check file's executable permission and ignore
> > it, which is useful in a "permissive mode" when preparing for a
> > migration without breaking a system, or to do extra integrity checks.
> For preparing a migration (detect all violations), this is useful.
> But as a defense mechanism (SECBIT_EXEC_RESTRICT_FILE = 1) , this
> seems to be weak, at least for elf loading case.
>
> > BTW, this use case would also be more complex with a new openat2(2) flag
> > like the original O_MAYEXEC.
> >
> > >
> > > Alternative solution 2:
> > > a new syscall to load the .so and enforce the AT_CHECK in kernel
> >
> > A new syscall would be overkill for this feature. Please see Linus's
> > comment.
> >
> maybe, I was thinking on how to prevent "/tmp/a.o" from getting mmap()
> to executable memory.
>
> > >
> > > This also means, for the solution to be complete, we might want to
> > > block creation of executable anonymous memory (e.g. by seccomp, ),
> >
> > How seccomp could create anonymous memory in user space?
> > seccomp filters should be treated (and checked with AT_CHECK) as
> > executable code anyway.
> >
> > > unless the user space can harden the creation of executable anonymous
> > > memory in some way.
> >
> > User space is already in charge of mmapping its own memory. I don't see
> > what is missing.
> >
> > >
> > > For case 3>
> > > I think binfmt_elf.c in the kernel needs to check the ld.so to make
> > > sure it passes AT_CHECK, before loading it into memory.
> >
> > All ELF dependencies are opened and checked with open_exec(), which
> > perform the main executability checks (with the __FMODE_EXEC flag).
> > Did I miss something?
> >
> I mean the ld-linux-x86-64.so.2 which is loaded by binfmt in the kernel.
> The app can choose its own dynamic linker path during build, (maybe
> even statically link one ?) This is another reason that relying on a
> userspace only is not enough.
>
> > However, we must be careful with programs using the (deprecated)
> > uselib(2). They should also check with AT_CHECK because this syscall
> > opens the shared library without __FMODE_EXEC (similar to a simple file
> > open). See
> > https://lore.kernel.org/all/CAHk-=wiUwRG7LuR=z5sbkFVGQh+7qVB6_1NM0Ny9SVNL1Un4Sw@mail.gmail.com/
> >
> > >
> > > For case 4>
> > > same as case 2.
> > >
> > > Consider those cases: I think:
> > > a> relying purely on userspace for enforcement does't seem to be
> > > effective, e.g. it is trivial to call open(), then mmap() it into
> > > executable memory.
> >
> > As Steve explained (and is also explained in the patches), it is trivial
> > if the attacker can already execute its own code, which is too late to
> > enforce any script execution control.
> >
> > > b> if both user space and kernel need to call AT_CHECK, the faccessat
> > > seems to be a better place for AT_CHECK, e.g. kernel can call
> > > do_faccessat(AT_CHECK) and userspace can call faccessat(). This will
> > > avoid complicating the execveat() code path.
> >
> > A previous version of this patches series already patched faccessat(2),
> > but this is not the right place. faccessat2(2) is dedicated to check
> > file permissions, not executability (e.g. with mount's noexec).
> >
> > >
> > > What do you think ?
> >
> > I think there are some misunderstandings. Please let me know if it's
> > clearer now.
> >
> I'm still not sure about the user case for dynamic linker (elf
> loading) case. Maybe this patch is more suitable for scripts?
> A detailed user case will help demonstrate the use case for dynamic
> linker, e.g. what kind of app will benefit from
> SECBIT_EXEC_RESTRICT_FILE = 1, what kind of threat model are we
> dealing with , what kind of attack chain we blocked as a result.
>
> > >
> > > Thanks
> > > -Jeff
> > >
> > > > With the information that a script interpreter is about to interpret a
> > > > script, an LSM security policy can adjust caller's access rights or log
> > > > execution request as for native script execution (e.g. role transition).
> > > > This is possible thanks to the call to security_bprm_creds_for_exec().
> > > >
> > > > Because LSMs may only change bprm's credentials, use of AT_CHECK with
> > > > current kernel code should not be a security issue (e.g. unexpected role
> > > > transition). LSMs willing to update the caller's credential could now
> > > > do so when bprm->is_check is set. Of course, such policy change should
> > > > be in line with the new user space code.
> > > >
> > > > Because AT_CHECK is dedicated to user space interpreters, it doesn't
> > > > make sense for the kernel to parse the checked files, look for
> > > > interpreters known to the kernel (e.g. ELF, shebang), and return ENOEXEC
> > > > if the format is unknown. Because of that, security_bprm_check() is
> > > > never called when AT_CHECK is used.
> > > >
> > > > It should be noted that script interpreters cannot directly use
> > > > execveat(2) (without this new AT_CHECK flag) because this could lead to
> > > > unexpected behaviors e.g., `python script.sh` could lead to Bash being
> > > > executed to interpret the script. Unlike the kernel, script
> > > > interpreters may just interpret the shebang as a simple comment, which
> > > > should not change for backward compatibility reasons.
> > > >
> > > > Because scripts or libraries files might not currently have the
> > > > executable permission set, or because we might want specific users to be
> > > > allowed to run arbitrary scripts, the following patch provides a dynamic
> > > > configuration mechanism with the SECBIT_SHOULD_EXEC_CHECK and
> > > > SECBIT_SHOULD_EXEC_RESTRICT securebits.
> > > >
> > > > This is a redesign of the CLIP OS 4's O_MAYEXEC:
> > > > https://github.com/clipos-archive/src_platform_clip-patches/blob/f5cb330d6b684752e403b4e41b39f7004d88e561/1901_open_mayexec.patch
> > > > This patch has been used for more than a decade with customized script
> > > > interpreters. Some examples can be found here:
> > > > https://github.com/clipos-archive/clipos4_portage-overlay/search?q=O_MAYEXEC
> > > >
> > > > Cc: Al Viro <viro@zeniv.linux.org.uk>
> > > > Cc: Christian Brauner <brauner@kernel.org>
> > > > Cc: Kees Cook <keescook@chromium.org>
> > > > Cc: Paul Moore <paul@paul-moore.com>
> > > > Link: https://docs.python.org/3/library/io.html#io.open_code [1]
> > > > Signed-off-by: Mickaël Salaün <mic@digikod.net>
> > > > Link: https://lore.kernel.org/r/20240704190137.696169-2-mic@digikod.net
> > > > ---
> > > >
> > > > New design since v18:
> > > > https://lore.kernel.org/r/20220104155024.48023-3-mic@digikod.net
> > > > ---
> > > > fs/exec.c | 5 +++--
> > > > include/linux/binfmts.h | 7 ++++++-
> > > > include/uapi/linux/fcntl.h | 30 ++++++++++++++++++++++++++++++
> > > > kernel/audit.h | 1 +
> > > > kernel/auditsc.c | 1 +
> > > > 5 files changed, 41 insertions(+), 3 deletions(-)
> > > >
> > > > diff --git a/fs/exec.c b/fs/exec.c
> > > > index 40073142288f..ea2a1867afdc 100644
> > > > --- a/fs/exec.c
> > > > +++ b/fs/exec.c
> > > > @@ -931,7 +931,7 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags)
> > > > .lookup_flags = LOOKUP_FOLLOW,
> > > > };
> > > >
> > > > - if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0)
> > > > + if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH | AT_CHECK)) != 0)
> > > > return ERR_PTR(-EINVAL);
> > > > if (flags & AT_SYMLINK_NOFOLLOW)
> > > > open_exec_flags.lookup_flags &= ~LOOKUP_FOLLOW;
> > > > @@ -1595,6 +1595,7 @@ static struct linux_binprm *alloc_bprm(int fd, struct filename *filename, int fl
> > > > bprm->filename = bprm->fdpath;
> > > > }
> > > > bprm->interp = bprm->filename;
> > > > + bprm->is_check = !!(flags & AT_CHECK);
> > > >
> > > > retval = bprm_mm_init(bprm);
> > > > if (!retval)
> > > > @@ -1885,7 +1886,7 @@ static int bprm_execve(struct linux_binprm *bprm)
> > > >
> > > > /* Set the unchanging part of bprm->cred */
> > > > retval = security_bprm_creds_for_exec(bprm);
> > > > - if (retval)
> > > > + if (retval || bprm->is_check)
> > > > goto out;
> > > >
> > > > retval = exec_binprm(bprm);
> > > > diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
> > > > index 70f97f685bff..8ff9c9e33aed 100644
> > > > --- a/include/linux/binfmts.h
> > > > +++ b/include/linux/binfmts.h
> > > > @@ -42,7 +42,12 @@ struct linux_binprm {
> > > > * Set when errors can no longer be returned to the
> > > > * original userspace.
> > > > */
> > > > - point_of_no_return:1;
> > > > + point_of_no_return:1,
> > > > + /*
> > > > + * Set by user space to check executability according to the
> > > > + * caller's environment.
> > > > + */
> > > > + is_check:1;
> > > > struct file *executable; /* Executable to pass to the interpreter */
> > > > struct file *interpreter;
> > > > struct file *file;
> > > > diff --git a/include/uapi/linux/fcntl.h b/include/uapi/linux/fcntl.h
> > > > index c0bcc185fa48..bcd05c59b7df 100644
> > > > --- a/include/uapi/linux/fcntl.h
> > > > +++ b/include/uapi/linux/fcntl.h
> > > > @@ -118,6 +118,36 @@
> > > > #define AT_HANDLE_FID AT_REMOVEDIR /* file handle is needed to
> > > > compare object identity and may not
> > > > be usable to open_by_handle_at(2) */
> > > > +
> > > > +/*
> > > > + * AT_CHECK only performs a check on a regular file and returns 0 if execution
> > > > + * of this file would be allowed, ignoring the file format and then the related
> > > > + * interpreter dependencies (e.g. ELF libraries, script's shebang). AT_CHECK
> > > > + * should only be used if SECBIT_SHOULD_EXEC_CHECK is set for the calling
> > > > + * thread. See securebits.h documentation.
> > > > + *
> > > > + * Programs should use this check to apply kernel-level checks against files
> > > > + * that are not directly executed by the kernel but directly passed to a user
> > > > + * space interpreter instead. All files that contain executable code, from the
> > > > + * point of view of the interpreter, should be checked. The main purpose of
> > > > + * this flag is to improve the security and consistency of an execution
> > > > + * environment to ensure that direct file execution (e.g. ./script.sh) and
> > > > + * indirect file execution (e.g. sh script.sh) lead to the same result. For
> > > > + * instance, this can be used to check if a file is trustworthy according to
> > > > + * the caller's environment.
> > > > + *
> > > > + * In a secure environment, libraries and any executable dependencies should
> > > > + * also be checked. For instance dynamic linking should make sure that all
> > > > + * libraries are allowed for execution to avoid trivial bypass (e.g. using
> > > > + * LD_PRELOAD). For such secure execution environment to make sense, only
> > > > + * trusted code should be executable, which also requires integrity guarantees.
> > > > + *
> > > > + * To avoid race conditions leading to time-of-check to time-of-use issues,
> > > > + * AT_CHECK should be used with AT_EMPTY_PATH to check against a file
> > > > + * descriptor instead of a path.
> > > > + */
> > > > +#define AT_CHECK 0x10000
> > > > +
> > > > #if defined(__KERNEL__)
> > > > #define AT_GETATTR_NOSEC 0x80000000
> > > > #endif
> > > > diff --git a/kernel/audit.h b/kernel/audit.h
> > > > index a60d2840559e..8ebdabd2ab81 100644
> > > > --- a/kernel/audit.h
> > > > +++ b/kernel/audit.h
> > > > @@ -197,6 +197,7 @@ struct audit_context {
> > > > struct open_how openat2;
> > > > struct {
> > > > int argc;
> > > > + bool is_check;
> > > > } execve;
> > > > struct {
> > > > char *name;
> > > > diff --git a/kernel/auditsc.c b/kernel/auditsc.c
> > > > index 6f0d6fb6523f..b6316e284342 100644
> > > > --- a/kernel/auditsc.c
> > > > +++ b/kernel/auditsc.c
> > > > @@ -2662,6 +2662,7 @@ void __audit_bprm(struct linux_binprm *bprm)
> > > >
> > > > context->type = AUDIT_EXECVE;
> > > > context->execve.argc = bprm->argc;
> > > > + context->execve.is_check = bprm->is_check;
> > > > }
> > > >
> > > >
> > > > --
> > > > 2.45.2
> > > >
> > >
^ permalink raw reply
* Re: [RFC PATCH v19 1/5] exec: Add a new AT_CHECK flag to execveat(2)
From: Mickaël Salaün @ 2024-07-18 15:35 UTC (permalink / raw)
To: James Bottomley
Cc: Jeff Xu, Al Viro, Christian Brauner, Kees Cook, Linus Torvalds,
Paul Moore, Theodore Ts'o, Alejandro Colomar, Aleksa Sarai,
Andrew Morton, Andy Lutomirski, Arnd Bergmann, Casey Schaufler,
Christian Heimes, Dmitry Vyukov, Eric Biggers, Eric Chiang,
Fan Wu, Florian Weimer, Geert Uytterhoeven, James Morris,
Jan Kara, Jann Horn, Jonathan Corbet, Jordan R Abrahams,
Lakshmi Ramasubramanian, Luca Boccassi, Luis Chamberlain,
Madhavan T . Venkataraman, Matt Bobrowski, Matthew Garrett,
Matthew Wilcox, Miklos Szeredi, Mimi Zohar, Nicolas Bouchinet,
Scott Shell, Shuah Khan, Stephen Rothwell, Steve Dower,
Steve Grubb, Thibaut Sautereau, Vincent Strubel, Xiaoming Ni,
Yin Fengwei, kernel-hardening, linux-api, linux-fsdevel,
linux-integrity, linux-kernel, linux-security-module,
Elliott Hughes
In-Reply-To: <544d08f5b55a0fbb1dc883bce6cf94c78cf46e42.camel@HansenPartnership.com>
On Thu, Jul 18, 2024 at 09:03:36AM -0400, James Bottomley wrote:
> On Thu, 2024-07-18 at 14:24 +0200, Mickaël Salaün wrote:
> > On Wed, Jul 17, 2024 at 07:08:17PM -0700, Jeff Xu wrote:
> > > On Wed, Jul 17, 2024 at 3:01 AM Mickaël Salaün <mic@digikod.net>
> > > wrote:
> > > > On Tue, Jul 16, 2024 at 11:33:55PM -0700, Jeff Xu wrote:
> [...]
> > > > > I'm still thinking execveat(AT_CHECK) vs faccessat(AT_CHECK)
> > > > > in different use cases:
> > > > >
> > > > > execveat clearly has less code change, but that also means: we
> > > > > can't add logic specific to exec (i.e. logic that can't be
> > > > > applied to config) for this part (from do_execveat_common to
> > > > > security_bprm_creds_for_exec) in future. This would require
> > > > > some agreement/sign-off, I'm not sure from whom.
> > > >
> > > > I'm not sure to follow. We could still add new flags, but for now
> > > > I don't see use cases. This patch series is not meant to handle
> > > > all possible "trust checks", only executable code, which makes
> > > > sense for the kernel.
> > > >
> > > I guess the "configfile" discussion is where I get confused, at one
> > > point, I think this would become a generic "trust checks" api for
> > > everything related to "generating executable code", e.g.
> > > javascript, java code, and more. We will want to clearly define the
> > > scope of execveat(AT_CHECK)
> >
> > The line between data and code is blurry. For instance, a
> > configuration file can impact the execution flow of a program. So,
> > where to draw the line?
>
> Having a way to have config files part of the trusted envelope, either
> by signing or measurement would be really useful. The current standard
> distro IMA deployment is signed executables, but not signed config
> because it's hard to construct a policy that doesn't force the signing
> of too many extraneous files (and files which might change often).
>
> > It might makes sense to follow the kernel and interpreter semantic:
> > if a file can be executed by the kernel (e.g. ELF binary, file
> > containing a shebang, or just configured with binfmt_misc), then this
> > should be considered as executable code. This applies to Bash,
> > Python, Javascript, NodeJS, PE, PHP... However, we can also make a
> > picture executable with binfmt_misc. So, again, where to draw the
> > line?
>
> Possibly by making open for config an indication executables can give?
> I'm not advocating doing it in this patch, but if we had an open for
> config indication, the LSMs could do much finer grained policy,
> especially if they knew which executable was trying to open the config
> file. It would allow things like an IMA policy saying if a signed
> executable is opening a config file, then that file must also be
> signed.
Checking configuration could be a next step, but not with this patch
series. FYI, the previous version was a (too) generic syscall:
https://lore.kernel.org/all/20220104155024.48023-1-mic@digikod.net/
One of the main concern was alignment with kernel semantic. For now,
let's focus on script execution control.
>
> James
>
^ permalink raw reply
* Re: [RFC PATCH v19 1/5] exec: Add a new AT_CHECK flag to execveat(2)
From: Mickaël Salaün @ 2024-07-18 15:35 UTC (permalink / raw)
To: enh
Cc: Jeff Xu, Al Viro, Christian Brauner, Kees Cook, Linus Torvalds,
Paul Moore, Theodore Ts'o, Alejandro Colomar, Aleksa Sarai,
Andrew Morton, Andy Lutomirski, Arnd Bergmann, Casey Schaufler,
Christian Heimes, Dmitry Vyukov, Eric Biggers, Eric Chiang,
Fan Wu, Florian Weimer, Geert Uytterhoeven, James Morris,
Jan Kara, Jann Horn, Jonathan Corbet, Jordan R Abrahams,
Lakshmi Ramasubramanian, Luca Boccassi, Luis Chamberlain,
Madhavan T . Venkataraman, Matt Bobrowski, Matthew Garrett,
Matthew Wilcox, Miklos Szeredi, Mimi Zohar, Nicolas Bouchinet,
Scott Shell, Shuah Khan, Stephen Rothwell, Steve Dower,
Steve Grubb, Thibaut Sautereau, Vincent Strubel, Xiaoming Ni,
Yin Fengwei, kernel-hardening, linux-api, linux-fsdevel,
linux-integrity, linux-kernel, linux-security-module
In-Reply-To: <CAJgzZor6frRHx1oZt3wnrNc6-13og=BL3GSKDhDmGE5JRq_LmQ@mail.gmail.com>
On Thu, Jul 18, 2024 at 10:46:54AM -0400, enh wrote:
> On Wed, Jul 17, 2024 at 10:08 PM Jeff Xu <jeffxu@google.com> wrote:
> >
> > On Wed, Jul 17, 2024 at 3:01 AM Mickaël Salaün <mic@digikod.net> wrote:
> > >
> > > On Tue, Jul 16, 2024 at 11:33:55PM -0700, Jeff Xu wrote:
> > > > On Thu, Jul 4, 2024 at 12:02 PM Mickaël Salaün <mic@digikod.net> wrote:
> > > > >
> > > > > Add a new AT_CHECK flag to execveat(2) to check if a file would be
> > > > > allowed for execution. The main use case is for script interpreters and
> > > > > dynamic linkers to check execution permission according to the kernel's
> > > > > security policy. Another use case is to add context to access logs e.g.,
> > > > > which script (instead of interpreter) accessed a file. As any
> > > > > executable code, scripts could also use this check [1].
> > > > >
> > > > > This is different than faccessat(2) which only checks file access
> > > > > rights, but not the full context e.g. mount point's noexec, stack limit,
> > > > > and all potential LSM extra checks (e.g. argv, envp, credentials).
> > > > > Since the use of AT_CHECK follows the exact kernel semantic as for a
> > > > > real execution, user space gets the same error codes.
> > > > >
> > > > So we concluded that execveat(AT_CHECK) will be used to check the
> > > > exec, shared object, script and config file (such as seccomp config),
> > >
> > > "config file" that contains executable code.
> > >
> > Is seccomp config considered as "contains executable code", seccomp
> > config is translated into bpf, so maybe yes ? but bpf is running in
> > the kernel.
> >
> > > > I'm still thinking execveat(AT_CHECK) vs faccessat(AT_CHECK) in
> > > > different use cases:
> > > >
> > > > execveat clearly has less code change, but that also means: we can't
> > > > add logic specific to exec (i.e. logic that can't be applied to
> > > > config) for this part (from do_execveat_common to
> > > > security_bprm_creds_for_exec) in future. This would require some
> > > > agreement/sign-off, I'm not sure from whom.
> > >
> > > I'm not sure to follow. We could still add new flags, but for now I
> > > don't see use cases. This patch series is not meant to handle all
> > > possible "trust checks", only executable code, which makes sense for the
> > > kernel.
> > >
> > I guess the "configfile" discussion is where I get confused, at one
> > point, I think this would become a generic "trust checks" api for
> > everything related to "generating executable code", e.g. javascript,
> > java code, and more.
> > We will want to clearly define the scope of execveat(AT_CHECK)
> >
> > > If we want other checks, we'll need to clearly define their semantic and
> > > align with the kernel. faccessat2(2) might be used to check other file
> > > properties, but the executable property is not only defined by the file
> > > attributes.
> > >
> > Agreed.
> >
> > > >
> > > > --------------------------
> > > > now looked at user cases (focus on elf for now)
> > > >
> > > > 1> ld.so /tmp/a.out, /tmp/a.out is on non-exec mount
> > > > dynamic linker will first call execveat(fd, AT_CHECK) then execveat(fd)
> > > >
> > > > 2> execve(/usr/bin/some.out) and some.out has dependency on /tmp/a.so
> > > > /usr/bin/some.out will pass AT_CHECK
> > > >
> > > > 3> execve(usr/bin/some.out) and some.out uses custom /tmp/ld.so
> > > > /usr/bin/some.out will pass AT_CHECK, however, it uses a custom
> > > > /tmp/ld.so (I assume this is possible for elf header will set the
> > > > path for ld.so because kernel has no knowledge of that, and
> > > > binfmt_elf.c allocate memory for ld.so during execveat call)
> > > >
> > > > 4> dlopen(/tmp/a.so)
> > > > I assume dynamic linker will call execveat(AT_CHECK), before map a.so
> > > > into memory.
> > > >
> > > > For case 1>
> > > > Alternative solution: Because AT_CHECK is always called, I think we
> > > > can avoid the first AT_CHECK call, and check during execveat(fd),
> > >
> > > There is no need to use AT_CHECK if we're going to call execveat(2) on
> > > the same file descriptor. By design, AT_CHECK is implicit for any
> > > execve(2).
> > >
> > Yes. I realized I was wrong to say that ld.so will call execve() for
> > /tmp/a.out, there is no execve() call, otherwise it would have been
> > blocked already today.
> > The ld.so will mmap the /tmp/a.out directly. So case 1 is no
> > different than case 2 and 4. ( the elf objects are mapped to memory
> > by dynamic linker.)
> > I'm not familiar with dynamic linker, Florian is on this thread, and
> > can help to correct me if my guess is wrong.
>
> for Android, this has been the nail in the coffin of previous attempts
> to disallow running code from non-trusted filesystems --- instead of
> execing /tmp/a.out, the attacker just execs the linker with /tmp/a.out
> as an argument. people are doing this already in some cases, because
> we already have ineffectual "barriers" in place. [the usual argument
> for doing such things anyway is "it makes it harder to be doing this
> by _accident_".]
This AT_CHECK and related securebits should cover this case.
>
> the other workaround for the attacker is to copy and paste the entire
> dynamic linker source and change the bits they don't like :-) (if
> you're thinking "is that a thing?", yes, so much so that the idea has
> been independently reinvented multiple times by several major legit
> apps and by basically every piece of DRM middleware. which is why --
> although i'm excited by mseal(2) -- i expect to face significant
> challenges rolling it out in Android _especially_ in places like
> "dynamic linker internal data structures" where i've wanted it for
> years!)
>
> this proposal feels like it _ought_ to let a defender tighten their
> seccomp filter to require a "safe" fd if i'm using mmap() with an fd,
> but in practice as long as JITs exist i can always just copy code into
> a non-fd-backed mmap() region. and -- from the perspective of Android,
> where all "apps" are code loaded into a Java runtime -- there's not
> much getting away from JITs. (and last i looked, ART -- Android's Java
> runtime -- uses memfd() for the JIT cache.)
Using the feature brought by this patch series makes sense for trusted
executables willing to enforce a security policy. Untrusted ones should
be sandboxed, which is the case for Android apps.
>
> > > > this means the kernel will enforce SECBIT_EXEC_RESTRICT_FILE = 1, the
> > > > benefit is that there is no TOCTOU and save one round trip of syscall
> > > > for a succesful execveat() case.
> > >
> > > As long as user space uses the same file descriptor, there is no TOCTOU.
> > >
> > > SECBIT_EXEC_RESTRICT_FILE only makes sense for user space: it defines
> > > the user space security policy. The kernel already enforces the same
> > > security policy for any execve(2), whatever are the calling process's
> > > securebits.
> > >
> > > >
> > > > For case 2>
> > > > dynamic linker will call execve(AT_CHECK), then mmap(fd) into memory.
> > > > However, the process can all open then mmap() directly, it seems
> > > > minimal effort for an attacker to walk around such a defence from
> > > > dynamic linker.
> > >
> > > Which process? What do you mean by "can all open then mmap() directly"?
> > >
> > > In this context the dynamic linker (like its parent processes) is
> > > trusted (guaranteed by the system).
> > >
> > > For case 2, the dynamic linker must check with AT_CHECK all files that
> > > will be mapped, which include /usr/bin/some.out and /tmp/a.so
> > >
> > My point is that the process can work around this by mmap() the file directly.
> >
> > > >
> > > > Alternative solution:
> > > > dynamic linker call AT_CHECK for each .so, kernel will save the state
> > > > (associated with fd)
> > > > kernel will check fd state at the time of mmap(fd, executable memory)
> > > > and enforce SECBIT_EXEC_RESTRICT_FILE = 1
> > >
> > > The idea with AT_CHECK is that there is no kernel side effect, no extra
> > > kernel state, and the semantic is the same as with execve(2).
> > >
> > > This also enables us to check file's executable permission and ignore
> > > it, which is useful in a "permissive mode" when preparing for a
> > > migration without breaking a system, or to do extra integrity checks.
> > For preparing a migration (detect all violations), this is useful.
> > But as a defense mechanism (SECBIT_EXEC_RESTRICT_FILE = 1) , this
> > seems to be weak, at least for elf loading case.
> >
> > > BTW, this use case would also be more complex with a new openat2(2) flag
> > > like the original O_MAYEXEC.
> > >
> > > >
> > > > Alternative solution 2:
> > > > a new syscall to load the .so and enforce the AT_CHECK in kernel
> > >
> > > A new syscall would be overkill for this feature. Please see Linus's
> > > comment.
> > >
> > maybe, I was thinking on how to prevent "/tmp/a.o" from getting mmap()
> > to executable memory.
> >
> > > >
> > > > This also means, for the solution to be complete, we might want to
> > > > block creation of executable anonymous memory (e.g. by seccomp, ),
> > >
> > > How seccomp could create anonymous memory in user space?
> > > seccomp filters should be treated (and checked with AT_CHECK) as
> > > executable code anyway.
> > >
> > > > unless the user space can harden the creation of executable anonymous
> > > > memory in some way.
> > >
> > > User space is already in charge of mmapping its own memory. I don't see
> > > what is missing.
> > >
> > > >
> > > > For case 3>
> > > > I think binfmt_elf.c in the kernel needs to check the ld.so to make
> > > > sure it passes AT_CHECK, before loading it into memory.
> > >
> > > All ELF dependencies are opened and checked with open_exec(), which
> > > perform the main executability checks (with the __FMODE_EXEC flag).
> > > Did I miss something?
> > >
> > I mean the ld-linux-x86-64.so.2 which is loaded by binfmt in the kernel.
> > The app can choose its own dynamic linker path during build, (maybe
> > even statically link one ?) This is another reason that relying on a
> > userspace only is not enough.
> >
> > > However, we must be careful with programs using the (deprecated)
> > > uselib(2). They should also check with AT_CHECK because this syscall
> > > opens the shared library without __FMODE_EXEC (similar to a simple file
> > > open). See
> > > https://lore.kernel.org/all/CAHk-=wiUwRG7LuR=z5sbkFVGQh+7qVB6_1NM0Ny9SVNL1Un4Sw@mail.gmail.com/
> > >
> > > >
> > > > For case 4>
> > > > same as case 2.
> > > >
> > > > Consider those cases: I think:
> > > > a> relying purely on userspace for enforcement does't seem to be
> > > > effective, e.g. it is trivial to call open(), then mmap() it into
> > > > executable memory.
> > >
> > > As Steve explained (and is also explained in the patches), it is trivial
> > > if the attacker can already execute its own code, which is too late to
> > > enforce any script execution control.
> > >
> > > > b> if both user space and kernel need to call AT_CHECK, the faccessat
> > > > seems to be a better place for AT_CHECK, e.g. kernel can call
> > > > do_faccessat(AT_CHECK) and userspace can call faccessat(). This will
> > > > avoid complicating the execveat() code path.
> > >
> > > A previous version of this patches series already patched faccessat(2),
> > > but this is not the right place. faccessat2(2) is dedicated to check
> > > file permissions, not executability (e.g. with mount's noexec).
> > >
> > > >
> > > > What do you think ?
> > >
> > > I think there are some misunderstandings. Please let me know if it's
> > > clearer now.
> > >
> > I'm still not sure about the user case for dynamic linker (elf
> > loading) case. Maybe this patch is more suitable for scripts?
> > A detailed user case will help demonstrate the use case for dynamic
> > linker, e.g. what kind of app will benefit from
> > SECBIT_EXEC_RESTRICT_FILE = 1, what kind of threat model are we
> > dealing with , what kind of attack chain we blocked as a result.
> >
> > > >
> > > > Thanks
> > > > -Jeff
> > > >
> > > > > With the information that a script interpreter is about to interpret a
> > > > > script, an LSM security policy can adjust caller's access rights or log
> > > > > execution request as for native script execution (e.g. role transition).
> > > > > This is possible thanks to the call to security_bprm_creds_for_exec().
> > > > >
> > > > > Because LSMs may only change bprm's credentials, use of AT_CHECK with
> > > > > current kernel code should not be a security issue (e.g. unexpected role
> > > > > transition). LSMs willing to update the caller's credential could now
> > > > > do so when bprm->is_check is set. Of course, such policy change should
> > > > > be in line with the new user space code.
> > > > >
> > > > > Because AT_CHECK is dedicated to user space interpreters, it doesn't
> > > > > make sense for the kernel to parse the checked files, look for
> > > > > interpreters known to the kernel (e.g. ELF, shebang), and return ENOEXEC
> > > > > if the format is unknown. Because of that, security_bprm_check() is
> > > > > never called when AT_CHECK is used.
> > > > >
> > > > > It should be noted that script interpreters cannot directly use
> > > > > execveat(2) (without this new AT_CHECK flag) because this could lead to
> > > > > unexpected behaviors e.g., `python script.sh` could lead to Bash being
> > > > > executed to interpret the script. Unlike the kernel, script
> > > > > interpreters may just interpret the shebang as a simple comment, which
> > > > > should not change for backward compatibility reasons.
> > > > >
> > > > > Because scripts or libraries files might not currently have the
> > > > > executable permission set, or because we might want specific users to be
> > > > > allowed to run arbitrary scripts, the following patch provides a dynamic
> > > > > configuration mechanism with the SECBIT_SHOULD_EXEC_CHECK and
> > > > > SECBIT_SHOULD_EXEC_RESTRICT securebits.
> > > > >
> > > > > This is a redesign of the CLIP OS 4's O_MAYEXEC:
> > > > > https://github.com/clipos-archive/src_platform_clip-patches/blob/f5cb330d6b684752e403b4e41b39f7004d88e561/1901_open_mayexec.patch
> > > > > This patch has been used for more than a decade with customized script
> > > > > interpreters. Some examples can be found here:
> > > > > https://github.com/clipos-archive/clipos4_portage-overlay/search?q=O_MAYEXEC
> > > > >
> > > > > Cc: Al Viro <viro@zeniv.linux.org.uk>
> > > > > Cc: Christian Brauner <brauner@kernel.org>
> > > > > Cc: Kees Cook <keescook@chromium.org>
> > > > > Cc: Paul Moore <paul@paul-moore.com>
> > > > > Link: https://docs.python.org/3/library/io.html#io.open_code [1]
> > > > > Signed-off-by: Mickaël Salaün <mic@digikod.net>
> > > > > Link: https://lore.kernel.org/r/20240704190137.696169-2-mic@digikod.net
> > > > > ---
> > > > >
> > > > > New design since v18:
> > > > > https://lore.kernel.org/r/20220104155024.48023-3-mic@digikod.net
> > > > > ---
> > > > > fs/exec.c | 5 +++--
> > > > > include/linux/binfmts.h | 7 ++++++-
> > > > > include/uapi/linux/fcntl.h | 30 ++++++++++++++++++++++++++++++
> > > > > kernel/audit.h | 1 +
> > > > > kernel/auditsc.c | 1 +
> > > > > 5 files changed, 41 insertions(+), 3 deletions(-)
> > > > >
> > > > > diff --git a/fs/exec.c b/fs/exec.c
> > > > > index 40073142288f..ea2a1867afdc 100644
> > > > > --- a/fs/exec.c
> > > > > +++ b/fs/exec.c
> > > > > @@ -931,7 +931,7 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags)
> > > > > .lookup_flags = LOOKUP_FOLLOW,
> > > > > };
> > > > >
> > > > > - if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0)
> > > > > + if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH | AT_CHECK)) != 0)
> > > > > return ERR_PTR(-EINVAL);
> > > > > if (flags & AT_SYMLINK_NOFOLLOW)
> > > > > open_exec_flags.lookup_flags &= ~LOOKUP_FOLLOW;
> > > > > @@ -1595,6 +1595,7 @@ static struct linux_binprm *alloc_bprm(int fd, struct filename *filename, int fl
> > > > > bprm->filename = bprm->fdpath;
> > > > > }
> > > > > bprm->interp = bprm->filename;
> > > > > + bprm->is_check = !!(flags & AT_CHECK);
> > > > >
> > > > > retval = bprm_mm_init(bprm);
> > > > > if (!retval)
> > > > > @@ -1885,7 +1886,7 @@ static int bprm_execve(struct linux_binprm *bprm)
> > > > >
> > > > > /* Set the unchanging part of bprm->cred */
> > > > > retval = security_bprm_creds_for_exec(bprm);
> > > > > - if (retval)
> > > > > + if (retval || bprm->is_check)
> > > > > goto out;
> > > > >
> > > > > retval = exec_binprm(bprm);
> > > > > diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
> > > > > index 70f97f685bff..8ff9c9e33aed 100644
> > > > > --- a/include/linux/binfmts.h
> > > > > +++ b/include/linux/binfmts.h
> > > > > @@ -42,7 +42,12 @@ struct linux_binprm {
> > > > > * Set when errors can no longer be returned to the
> > > > > * original userspace.
> > > > > */
> > > > > - point_of_no_return:1;
> > > > > + point_of_no_return:1,
> > > > > + /*
> > > > > + * Set by user space to check executability according to the
> > > > > + * caller's environment.
> > > > > + */
> > > > > + is_check:1;
> > > > > struct file *executable; /* Executable to pass to the interpreter */
> > > > > struct file *interpreter;
> > > > > struct file *file;
> > > > > diff --git a/include/uapi/linux/fcntl.h b/include/uapi/linux/fcntl.h
> > > > > index c0bcc185fa48..bcd05c59b7df 100644
> > > > > --- a/include/uapi/linux/fcntl.h
> > > > > +++ b/include/uapi/linux/fcntl.h
> > > > > @@ -118,6 +118,36 @@
> > > > > #define AT_HANDLE_FID AT_REMOVEDIR /* file handle is needed to
> > > > > compare object identity and may not
> > > > > be usable to open_by_handle_at(2) */
> > > > > +
> > > > > +/*
> > > > > + * AT_CHECK only performs a check on a regular file and returns 0 if execution
> > > > > + * of this file would be allowed, ignoring the file format and then the related
> > > > > + * interpreter dependencies (e.g. ELF libraries, script's shebang). AT_CHECK
> > > > > + * should only be used if SECBIT_SHOULD_EXEC_CHECK is set for the calling
> > > > > + * thread. See securebits.h documentation.
> > > > > + *
> > > > > + * Programs should use this check to apply kernel-level checks against files
> > > > > + * that are not directly executed by the kernel but directly passed to a user
> > > > > + * space interpreter instead. All files that contain executable code, from the
> > > > > + * point of view of the interpreter, should be checked. The main purpose of
> > > > > + * this flag is to improve the security and consistency of an execution
> > > > > + * environment to ensure that direct file execution (e.g. ./script.sh) and
> > > > > + * indirect file execution (e.g. sh script.sh) lead to the same result. For
> > > > > + * instance, this can be used to check if a file is trustworthy according to
> > > > > + * the caller's environment.
> > > > > + *
> > > > > + * In a secure environment, libraries and any executable dependencies should
> > > > > + * also be checked. For instance dynamic linking should make sure that all
> > > > > + * libraries are allowed for execution to avoid trivial bypass (e.g. using
> > > > > + * LD_PRELOAD). For such secure execution environment to make sense, only
> > > > > + * trusted code should be executable, which also requires integrity guarantees.
> > > > > + *
> > > > > + * To avoid race conditions leading to time-of-check to time-of-use issues,
> > > > > + * AT_CHECK should be used with AT_EMPTY_PATH to check against a file
> > > > > + * descriptor instead of a path.
> > > > > + */
> > > > > +#define AT_CHECK 0x10000
> > > > > +
> > > > > #if defined(__KERNEL__)
> > > > > #define AT_GETATTR_NOSEC 0x80000000
> > > > > #endif
> > > > > diff --git a/kernel/audit.h b/kernel/audit.h
> > > > > index a60d2840559e..8ebdabd2ab81 100644
> > > > > --- a/kernel/audit.h
> > > > > +++ b/kernel/audit.h
> > > > > @@ -197,6 +197,7 @@ struct audit_context {
> > > > > struct open_how openat2;
> > > > > struct {
> > > > > int argc;
> > > > > + bool is_check;
> > > > > } execve;
> > > > > struct {
> > > > > char *name;
> > > > > diff --git a/kernel/auditsc.c b/kernel/auditsc.c
> > > > > index 6f0d6fb6523f..b6316e284342 100644
> > > > > --- a/kernel/auditsc.c
> > > > > +++ b/kernel/auditsc.c
> > > > > @@ -2662,6 +2662,7 @@ void __audit_bprm(struct linux_binprm *bprm)
> > > > >
> > > > > context->type = AUDIT_EXECVE;
> > > > > context->execve.argc = bprm->argc;
> > > > > + context->execve.is_check = bprm->is_check;
> > > > > }
> > > > >
> > > > >
> > > > > --
> > > > > 2.45.2
> > > > >
> > > >
>
^ permalink raw reply
* Re: [RFC PATCH v19 2/5] security: Add new SHOULD_EXEC_CHECK and SHOULD_EXEC_RESTRICT securebits
From: Mickaël Salaün @ 2024-07-18 16:20 UTC (permalink / raw)
To: Roberto Sassu
Cc: Kees Cook, Al Viro, Christian Brauner, Linus Torvalds, Paul Moore,
Theodore Ts'o, Alejandro Colomar, Aleksa Sarai, Andrew Morton,
Andy Lutomirski, Arnd Bergmann, Casey Schaufler, Christian Heimes,
Dmitry Vyukov, Eric Biggers, Eric Chiang, Fan Wu, Florian Weimer,
Geert Uytterhoeven, James Morris, Jan Kara, Jann Horn, Jeff Xu,
Jonathan Corbet, Jordan R Abrahams, Lakshmi Ramasubramanian,
Luca Boccassi, Luis Chamberlain, Madhavan T . Venkataraman,
Matt Bobrowski, Matthew Garrett, Matthew Wilcox, Miklos Szeredi,
Mimi Zohar, Nicolas Bouchinet, Scott Shell, Shuah Khan,
Stephen Rothwell, Steve Dower, Steve Grubb, Thibaut Sautereau,
Vincent Strubel, Xiaoming Ni, Yin Fengwei, kernel-hardening,
linux-api, linux-fsdevel, linux-integrity, linux-kernel,
linux-security-module, Elliott Hughes
In-Reply-To: <ae769bbfe51a2c1c270739a91defc0dfbd5b8b5a.camel@huaweicloud.com>
On Thu, Jul 18, 2024 at 04:16:45PM +0200, Roberto Sassu wrote:
> On Sat, 2024-07-06 at 16:56 +0200, Mickaël Salaün wrote:
> > On Fri, Jul 05, 2024 at 02:44:03PM -0700, Kees Cook wrote:
> > > On Fri, Jul 05, 2024 at 07:54:16PM +0200, Mickaël Salaün wrote:
> > > > On Thu, Jul 04, 2024 at 05:18:04PM -0700, Kees Cook wrote:
> > > > > On Thu, Jul 04, 2024 at 09:01:34PM +0200, Mickaël Salaün wrote:
> > > > > > Such a secure environment can be achieved with an appropriate access
> > > > > > control policy (e.g. mount's noexec option, file access rights, LSM
> > > > > > configuration) and an enlighten ld.so checking that libraries are
> > > > > > allowed for execution e.g., to protect against illegitimate use of
> > > > > > LD_PRELOAD.
> > > > > >
> > > > > > Scripts may need some changes to deal with untrusted data (e.g. stdin,
> > > > > > environment variables), but that is outside the scope of the kernel.
> > > > >
> > > > > If the threat model includes an attacker sitting at a shell prompt, we
> > > > > need to be very careful about how process perform enforcement. E.g. even
> > > > > on a locked down system, if an attacker has access to LD_PRELOAD or a
> > > >
> > > > LD_PRELOAD should be OK once ld.so will be patched to check the
> > > > libraries. We can still imagine a debug library used to bypass security
> > > > checks, but in this case the issue would be that this library is
> > > > executable in the first place.
> > >
> > > Ah yes, that's fair: the shell would discover the malicious library
> > > while using AT_CHECK during resolution of the LD_PRELOAD.
> >
> > That's the idea, but it would be checked by ld.so, not the shell.
> >
> > >
> > > > > seccomp wrapper (which you both mention here), it would be possible to
> > > > > run commands where the resulting process is tricked into thinking it
> > > > > doesn't have the bits set.
> > > >
> > > > As explained in the UAPI comments, all parent processes need to be
> > > > trusted. This meeans that their code is trusted, their seccomp filters
> > > > are trusted, and that they are patched, if needed, to check file
> > > > executability.
> > >
> > > But we have launchers that apply arbitrary seccomp policy, e.g. minijail
> > > on Chrome OS, or even systemd on regular distros. In theory, this should
> > > be handled via other ACLs.
> >
> > Processes running with untrusted seccomp filter should be considered
> > untrusted. It would then make sense for these seccomp filters/programs
> > to be considered executable code, and then for minijail and systemd to
> > check them with AT_CHECK (according to the securebits policy).
> >
> > >
> > > > > But this would be exactly true for calling execveat(): LD_PRELOAD or
> > > > > seccomp policy could have it just return 0.
> > > >
> > > > If an attacker is allowed/able to load an arbitrary seccomp filter on a
> > > > process, we cannot trust this process.
> > > >
> > > > >
> > > > > While I like AT_CHECK, I do wonder if it's better to do the checks via
> > > > > open(), as was originally designed with O_MAYEXEC. Because then
> > > > > enforcement is gated by the kernel -- the process does not get a file
> > > > > descriptor _at all_, no matter what LD_PRELOAD or seccomp tricks it into
> > > > > doing.
> > > >
> > > > Being able to check a path name or a file descriptor (with the same
> > > > syscall) is more flexible and cover more use cases.
> > >
> > > If flexibility costs us reliability, I think that flexibility is not
> > > a benefit.
> >
> > Well, it's a matter of letting user space do what they think is best,
> > and I think there are legitimate and safe uses of path names, even if I
> > agree that this should not be used in most use cases. Would we want
> > faccessat2(2) to only take file descriptor as argument and not file
> > path? I don't think so but I'd defer to the VFS maintainers.
> >
> > Christian, Al, Linus?
> >
> > Steve, could you share a use case with file paths?
> >
> > >
> > > > The execveat(2)
> > > > interface, including current and future flags, is dedicated to file
> > > > execution. I then think that using execveat(2) for this kind of check
> > > > makes more sense, and will easily evolve with this syscall.
> > >
> > > Yeah, I do recognize that is feels much more natural, but I remain
> > > unhappy about how difficult it will become to audit a system for safety
> > > when the check is strictly per-process opt-in, and not enforced by the
> > > kernel for a given process tree. But, I think this may have always been
> > > a fiction in my mind. :)
> >
> > Hmm, I'm not sure to follow. Securebits are inherited, so process tree.
> > And we need the parent processes to be trusted anyway.
> >
> > >
> > > > > And this thinking also applies to faccessat() too: if a process can be
> > > > > tricked into thinking the access check passed, it'll happily interpret
> > > > > whatever. :( But not being able to open the fd _at all_ when O_MAYEXEC
> > > > > is being checked seems substantially safer to me...
> > > >
> > > > If attackers can filter execveat(2), they can also filter open(2) and
> > > > any other syscalls. In all cases, that would mean an issue in the
> > > > security policy.
> > >
> > > Hm, as in, make a separate call to open(2) without O_MAYEXEC, and pass
> > > that fd back to the filtered open(2) that did have O_MAYEXEC. Yes, true.
> > >
> > > I guess it does become morally equivalent.
> > >
> > > Okay. Well, let me ask about usability. Right now, a process will need
> > > to do:
> > >
> > > - should I use AT_CHECK? (check secbit)
> > > - if yes: perform execveat(AT_CHECK)
> > >
> > > Why not leave the secbit test up to the kernel, and then the program can
> > > just unconditionally call execveat(AT_CHECK)?
> >
> > That was kind of the approach of the previous patch series and Linus
> > wanted the new interface to follow the kernel semantic. Enforcing this
> > kind of restriction will always be the duty of user space anyway, so I
> > think it's simpler (i.e. no mix of policy definition, access check, and
> > policy enforcement, but a standalone execveat feature), more flexible,
> > and it fully delegates the policy enforcement to user space instead of
> > trying to enforce some part in the kernel which would only give the
> > illusion of security/policy enforcement.
>
> A problem could be that from IMA perspective there is no indication on
> whether the interpreter executed or not execveat(). Sure, we can detect
> that the binary supports it, but if the enforcement was
> enabled/disabled that it is not recorded.
We should assume that if the interpreter call execveat+AT_CHECK, it will
enforce restrictions according to its securebits.
>
> Maybe, setting the process flags should be influenced by the kernel,
> for example not allowing changes and enforcing when there is an IMA
> policy loaded requiring to measure/appraise scripts.
LSMs can set the required securebits per task/interpreter according to
their policies.
>
> Roberto
>
> > >
> > > Though perhaps the issue here is that an execveat() EINVAL doesn't
> > > tell the program if AT_CHECK is unimplemented or if something else
> > > went wrong, and the secbit prctl() will give the correct signal about
> > > AT_CHECK availability?
> >
> > This kind of check could indeed help to identify the issue.
>
>
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox