bpf-restrict-fsaccess: enforce W^X on file mappings
Right now RestrictFileSystemAccess= checks that a file comes from a trusted dm-verity device, but it doesn't guard against mapping that file and making use of COW modifications. So a MAP_PRIVATE mapping of a trusted binary can be made writable, modified, and executed, while the vma still points to the trusted file and the pages are attacker-controlled anonymous copies. Enforce W^X: deny any mapping that is writable and executable at once, and refuse to make a private file mapping executable once it has been modified through copy-on-write. Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
This commit is contained in:
@@ -16,8 +16,11 @@
|
||||
* dm-verity signals signature validity
|
||||
* - bdev_free_security hook: removes devices from the map on teardown
|
||||
* - bprm_check_security: blocks execve() from untrusted sources
|
||||
* - mmap_file: blocks PROT_EXEC mmap from untrusted sources
|
||||
* - file_mprotect: blocks W->X transitions from untrusted sources
|
||||
* - mmap_file: blocks PROT_EXEC mmap from untrusted sources, and
|
||||
* any writable+executable (W^X-violating) mapping
|
||||
* - file_mprotect: blocks writable+executable mprotect, and making
|
||||
* executable a mapping from an untrusted source or
|
||||
* one that was modified through copy-on-write
|
||||
*
|
||||
* On kernels providing the bpf_real_data_inode() kfunc (v7.2+), files on
|
||||
* union filesystems (overlayfs) are resolved to the layer hosting their
|
||||
@@ -39,6 +42,7 @@
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
|
||||
#define PROT_WRITE 0x2
|
||||
#define PROT_EXEC 0x4
|
||||
#define VM_EXEC 0x00000004
|
||||
#define PTRACE_MODE_ATTACH 0x02
|
||||
@@ -218,6 +222,10 @@ SEC("lsm.s/mmap_file")
|
||||
int BPF_PROG(restrict_fsaccess_mmap_file, struct file *file, unsigned long reqprot,
|
||||
unsigned long prot, unsigned long flags)
|
||||
{
|
||||
/* W^X: a mapping may never be writable and executable at once. */
|
||||
if ((prot & PROT_EXEC) && (prot & PROT_WRITE))
|
||||
return -EPERM;
|
||||
|
||||
/* Only enforce on executable mappings */
|
||||
if (!(prot & PROT_EXEC))
|
||||
return 0;
|
||||
@@ -245,17 +253,27 @@ int BPF_PROG(restrict_fsaccess_file_mprotect, struct vm_area_struct *vma,
|
||||
{
|
||||
struct file *file;
|
||||
unsigned long vm_flags;
|
||||
void *anon_vma;
|
||||
|
||||
/* W^X: a mapping may never be writable and executable at once. */
|
||||
if ((prot & PROT_EXEC) && (prot & PROT_WRITE))
|
||||
return -EPERM;
|
||||
|
||||
/* Only enforce when adding PROT_EXEC */
|
||||
if (!(prot & PROT_EXEC))
|
||||
return 0;
|
||||
|
||||
/* If VM_EXEC is already set, the mapping is already executable — this
|
||||
* mprotect isn't granting new executable capability, allow */
|
||||
/* Already executable so this mprotect() grants no new capability. */
|
||||
BPF_CORE_READ_INTO(&vm_flags, vma, vm_flags);
|
||||
if (vm_flags & VM_EXEC)
|
||||
return 0;
|
||||
|
||||
/* A private file mapping with an anon_vma has been written to
|
||||
* (copy-on-write), so its pages may no longer match the trusted file. */
|
||||
BPF_CORE_READ_INTO(&anon_vma, vma, anon_vma);
|
||||
if (anon_vma)
|
||||
return -EPERM;
|
||||
|
||||
/* Anonymous executable mapping — no file backing, deny. Direct
|
||||
* dereference, see restrict_fsaccess_bprm_check(). vm_file is in the
|
||||
* verifier's BTF_TYPE_SAFE_TRUSTED_OR_NULL list, so this check is live
|
||||
|
||||
Reference in New Issue
Block a user