Re: [RFC PATCH v19 1/5] exec: Add a new AT_CHECK flag to execveat(2)
From: Jeff Xu <hidden>
Date: 2024-08-09 16:16:30
Also in:
linux-fsdevel, linux-integrity, linux-security-module, lkml
On Fri, Aug 9, 2024 at 1:45 AM Mickaël Salaün [off-list ref] wrote:
On Mon, Aug 05, 2024 at 11:35:09AM -0700, Jeff Xu wrote:quoted
On Tue, Jul 23, 2024 at 6:15 AM Mickaël Salaün [off-list ref] wrote:quoted
On Fri, Jul 19, 2024 at 08:27:18AM -0700, Jeff Xu wrote:quoted
On Fri, Jul 19, 2024 at 8:04 AM Mickaël Salaün [off-list ref] wrote:quoted
On Fri, Jul 19, 2024 at 07:16:55AM -0700, Jeff Xu wrote:quoted
On Fri, Jul 19, 2024 at 1:45 AM Mickaël Salaün [off-list ref] wrote:quoted
On Thu, Jul 18, 2024 at 06:29:54PM -0700, Jeff Xu wrote:quoted
On Thu, Jul 18, 2024 at 5:24 AM Mickaël Salaün [off-list ref] wrote:quoted
On Wed, Jul 17, 2024 at 07:08:17PM -0700, Jeff Xu wrote:quoted
On Wed, Jul 17, 2024 at 3:01 AM Mickaël Salaün [off-list ref] wrote:quoted
On Tue, Jul 16, 2024 at 11:33:55PM -0700, Jeff Xu wrote:quoted
On Thu, Jul 4, 2024 at 12:02 PM Mickaël Salaün [off-list ref] wrote:quoted
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),quoted
quoted
quoted
quoted
quoted
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.This might not be entirely true. iiuc, kernel calls open_exec for open_exec for interpreter, but not all its dependency (e.g. libc.so.6)Correct, the dynamic linker is in charge of that, which is why it must be enlighten with execveat+AT_CHECK and securebits checks.quoted
load_elf_binary() { interpreter = open_exec(elf_interpreter); } libc.so.6 is opened and mapped by dynamic linker. so the call sequence is: execve(a.out) - open exec(a.out) - security_bprm_creds(a.out) - open the exec(ld.so) - call open_exec() for interruptor (ld.so) - call execveat(AT_CHECK, ld.so) <-- do we want ld.so going through the same check and code path as libc.so below ?open_exec() checks are enough. LSMs can use this information (open + __FMODE_EXEC) if needed. execveat+AT_CHECK is only a user space request.Then the ld.so doesn't go through the same security_bprm_creds() check as other .so.Indeed, but...My point is: we will want all the .so going through the same code path, so security_ functions are called consistently across all the objects, And in the future, if we want to develop additional LSM functionality based on AT_CHECK, it will be applied to all objects.I'll extend the doc to encourage LSMs to check for __FMODE_EXEC, which already is the common security check for all executable dependencies. As extra information, they can get explicit requests by looking at execveat+AT_CHECK call.I agree that security_file_open + __FMODE_EXEC for checking all the .so (e.g for executable memfd) is a better option than checking at security_bprm_creds_for_exec. But then maybe execveat( AT_CHECK) can return after calling alloc_bprm ? See below call graph: do_execveat_common (AT_CHECK) -> alloc_bprm ->->do_open_execat ->->-> do_filp_open (__FMODE_EXEC) ->->->->->->> security_file_open -> bprm_execve ->-> prepare_exec_creds ->->-> prepare_creds ->->->-> security_prepare_creds ->-> security_bprm_creds_for_exec What is the consideration to mark the end at security_bprm_creds_for_exec ? i.e. including brpm_execve, prepare_creds, security_prepare_creds, security_bprm_creds_for_exec.This enables LSMs to know/log an explicit execution request, including context with argv and envp.quoted
Since dynamic linker doesn't load ld.so (it is by kernel), ld.so won't go through those security_prepare_creds and security_bprm_creds_for_exec checks like other .so do.Yes, but this is not an issue nor an explicit request. ld.so is only one case of this patch series.quoted
quoted
quoted
Another thing to consider is: we are asking userspace to make additional syscall before loading the file into memory/get executed, there is a possibility for future expansion of the mechanism, without asking user space to add another syscall again.AT_CHECK is defined with a specific semantic. Other mechanisms (e.g. LSM policies) could enforce other restrictions following the same semantic. We need to keep in mind backward compatibility.quoted
I m still not convinced yet that execveat(AT_CHECK) fits more than faccessat(AT_CHECK)faccessat2(2) is dedicated to file permission/attribute check. execveat(2) is dedicated to execution, which is a superset of file permission for executability, plus other checks (e.g. noexec).That sounds reasonable, but if execveat(AT_CHECK) changes behavior of execveat(), someone might argue that faccessat2(EXEC_CHECK) can be made for the executability.AT_CHECK, as any other syscall flags, changes the behavior of execveat, but the overall semantic is clearly defined. Again, faccessat2 is only dedicated to file attributes/permissions, not file executability.quoted
I think the decision might depend on what this PATCH intended to check, i.e. where we draw the line.The goal is clearly defined in the cover letter and patches: makes it possible to control (or log) script execution.quoted
do_open_execat() seems to cover lots of checks for executability, if we are ok with the thing that do_open_execat() checks, then faccessat(AT_CHECK) calling do_open_execat() is an option, it won't have those "unrelated" calls in execve path, e.g. bprm_stack_limits, copy argc/env .I don't thing there is any unrelated calls in execve path, quite the contrary, it follows the same semantic as for a full execution, and that's another argument to use the execveat interface. Otherwise, we couldn't argue that `./script.sh` can be the same as `sh script.sh`
It is a good point from the "scrip.sh/exec" perspective that we want it to go through the same execve path. The reasoning is not obvious from the ".so" which doesn't go through stack/env check. Since execveat(AT_CHECK) wants to cover both cases, it is fine.
The only difference is that user space is in charge of parsing and interpreting the file's content.quoted
However, you mentioned superset of file permission for executability, can you elaborate on that ? Is there something not included in do_open_execat() but still necessary for execveat(AT_CHECK)? maybe security_bprm_creds_for_exec? (this goes back to my question above)As explained above, the goal is to have the same semantic as a full execveat call, taking into account all the checks (e.g. stack limit, argv/envp...).
I'm fine with this, thanks for taking time to explain the design. Regarding the future LSM based on this patch series: For .so, security_file_open is recommended for LSM. For scripts/exec (that needs a full exec code path), security_file_open and security_bprm_creds_for_exec can both be used. Thanks Best regards, -Jeff