From 53606495188a5cc16ea67e3b65561127a98925b3 Mon Sep 17 00:00:00 2001 From: Topi Miettinen Date: Sat, 29 Jul 2017 19:53:27 +0300 Subject: Memory-deny-write-execute feature Feature to block attempts to create writable and executable memory. --- src/firejail/firejail.h | 3 +++ src/firejail/main.c | 7 ++++++ src/firejail/preproc.c | 2 ++ src/firejail/profile.c | 11 +++++++++ src/firejail/sandbox.c | 5 +++++ src/fseccomp/fseccomp.h | 3 +++ src/fseccomp/main.c | 3 +++ src/fseccomp/seccomp.c | 54 +++++++++++++++++++++++++++++++++++++++++++++ src/fseccomp/seccomp_file.c | 2 +- src/man/firejail.txt | 6 +++++ 10 files changed, 95 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index 37e4aeb30..1a26396a7 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h @@ -54,10 +54,12 @@ #define RUN_SECCOMP_CFG "/run/firejail/mnt/seccomp" // configured filter #define RUN_SECCOMP_AMD64 "/run/firejail/mnt/seccomp.amd64" // amd64 filter installed on i386 architectures #define RUN_SECCOMP_I386 "/run/firejail/mnt/seccomp.i386" // i386 filter installed on amd64 architectures +#define RUN_SECCOMP_MDWX "/run/firejail/mnt/seccomp.mdwx" // filter for memory-deny-write-execute #define PATH_SECCOMP_DEFAULT (LIBDIR "/firejail/seccomp") // default filter built during make #define PATH_SECCOMP_DEFAULT_DEBUG (LIBDIR "/firejail/seccomp.debug") // default filter built during make #define PATH_SECCOMP_AMD64 (LIBDIR "/firejail/seccomp.amd64") // amd64 filter built during make #define PATH_SECCOMP_I386 (LIBDIR "/firejail/seccomp.i386") // i386 filter built during make +#define PATH_SECCOMP_MDWX (LIBDIR "/firejail/seccomp.mdwx") // filter for memory-deny-write-execute built during make #define RUN_DEV_DIR "/run/firejail/mnt/dev" @@ -355,6 +357,7 @@ extern int arg_allusers; // all user home directories visible extern int arg_machineid; // preserve /etc/machine-id extern int arg_disable_mnt; // disable /mnt and /media extern int arg_noprofile; // use default.profile if none other found/specified +extern int arg_memory_deny_write_execute; // block writable and executable memory extern int login_shell; extern int parent_to_child_fds[2]; diff --git a/src/firejail/main.c b/src/firejail/main.c index 4ed8e30c9..561a14f5a 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c @@ -111,6 +111,7 @@ int arg_allow_private_blacklist = 0; // blacklist things in private directorie int arg_writable_var_log = 0; // writable /var/log int arg_disable_mnt = 0; // disable /mnt and /media int arg_noprofile = 0; // use default.profile if none other found/specified +int arg_memory_deny_write_execute = 0; // block writable and executable memory int login_shell = 0; @@ -1145,6 +1146,12 @@ int main(int argc, char **argv) { else exit_err_feature("seccomp"); } + else if (strcmp(argv[i], "--memory-deny-write-execute") == 0) { + if (checkcfg(CFG_SECCOMP)) + arg_memory_deny_write_execute = 1; + else + exit_err_feature("seccomp"); + } #endif else if (strcmp(argv[i], "--caps") == 0) arg_caps_default_filter = 1; diff --git a/src/firejail/preproc.c b/src/firejail/preproc.c index ef93368bf..9c474415d 100644 --- a/src/firejail/preproc.c +++ b/src/firejail/preproc.c @@ -83,6 +83,8 @@ void preproc_mount_mnt_dir(void) { else copy_file(PATH_SECCOMP_DEFAULT, RUN_SECCOMP_CFG, getuid(), getgid(), 0644); // root needed + if (arg_memory_deny_write_execute) + copy_file(PATH_SECCOMP_MDWX, RUN_SECCOMP_MDWX, getuid(), getgid(), 0644); // root needed // as root, create an empty RUN_SECCOMP_PROTOCOL file create_empty_file_as_root(RUN_SECCOMP_PROTOCOL, 0644); if (set_perms(RUN_SECCOMP_PROTOCOL, getuid(), getgid(), 0644)) diff --git a/src/firejail/profile.c b/src/firejail/profile.c index f084edaad..6d5ee349c 100644 --- a/src/firejail/profile.c +++ b/src/firejail/profile.c @@ -595,6 +595,17 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { return 0; } + // memory deny write&execute + if (strncmp(ptr, "memory-deny-write-execute ", sizeof("memory-deny-write-execute ") - 1) == 0) { +#ifdef HAVE_SECCOMP + if (checkcfg(CFG_SECCOMP)) + arg_memory_deny_write_execute = 1; + else + warning_feature_disabled("seccomp"); +#endif + return 0; + } + // caps drop list if (strncmp(ptr, "caps.drop ", 10) == 0) { arg_caps_drop = 1; diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c index 4b30cb991..6c0fdebe3 100644 --- a/src/firejail/sandbox.c +++ b/src/firejail/sandbox.c @@ -1001,6 +1001,11 @@ int sandbox(void* sandbox_arg) { else seccomp_filter_drop(enforce_seccomp); } + if (arg_memory_deny_write_execute) { + if (arg_debug) + printf("Install memory write&execute filter\n"); + seccomp_load(RUN_SECCOMP_MDWX); // install filter + } #endif //**************************************** diff --git a/src/fseccomp/fseccomp.h b/src/fseccomp/fseccomp.h index 1e4881e9c..157b71011 100644 --- a/src/fseccomp/fseccomp.h +++ b/src/fseccomp/fseccomp.h @@ -48,6 +48,7 @@ void seccomp_secondary_64(const char *fname); void seccomp_secondary_32(const char *fname); // seccomp_file.c +void write_to_file(int fd, const void *data, int size); void filter_init(int fd); void filter_add_whitelist(int fd, int syscall, int arg); void filter_add_blacklist(int fd, int syscall, int arg); @@ -64,6 +65,8 @@ void seccomp_drop(const char *fname, char *list, int allow_debuggers); void seccomp_default_drop(const char *fname, char *list, int allow_debuggers); // whitelisted filter void seccomp_keep(const char *fname, char *list); +// block writable and executable memory +void memory_deny_write_execute(const char *fname); // seccomp_print void filter_print(const char *fname); diff --git a/src/fseccomp/main.c b/src/fseccomp/main.c index e322b5bbb..3d95d5bb2 100644 --- a/src/fseccomp/main.c +++ b/src/fseccomp/main.c @@ -35,6 +35,7 @@ static void usage(void) { printf("\tfseccomp default drop file list\n"); printf("\tfseccomp default drop file list allow-debuggers\n"); printf("\tfseccomp keep file list\n"); + printf("\tfseccomp memory-deny-write-execute file\n"); printf("\tfseccomp print file\n"); } @@ -87,6 +88,8 @@ printf("\n"); seccomp_default_drop(argv[3], argv[4], 1); else if (argc == 4 && strcmp(argv[1], "keep") == 0) seccomp_keep(argv[2], argv[3]); + else if (argc == 3 && strcmp(argv[1], "memory-deny-write-execute") == 0) + memory_deny_write_execute(argv[2]); else if (argc == 3 && strcmp(argv[1], "print") == 0) filter_print(argv[2]); else { diff --git a/src/fseccomp/seccomp.c b/src/fseccomp/seccomp.c index 4f8de8c5e..7d2ccbbce 100644 --- a/src/fseccomp/seccomp.c +++ b/src/fseccomp/seccomp.c @@ -19,7 +19,10 @@ */ #include "fseccomp.h" #include "../include/seccomp.h" +#include +#include #include +#include static void add_default_list(int fd, int allow_debuggers) { #ifdef SYS_mount @@ -428,3 +431,54 @@ void seccomp_keep(const char *fname, char *list) { // close file close(fd); } + +void memory_deny_write_execute(const char *fname) { + // open file + int fd = open(fname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (fd < 0) { + fprintf(stderr, "Error fseccomp: cannot open %s file\n", fname); + exit(1); + } + + filter_init(fd); + + // build filter + static const struct sock_filter filter[] = { +#ifndef __x86_64__ + // block old multiplexing mmap syscall for i386 + BLACKLIST(SYS_mmap), +#endif + // block mmap(,,x|PROT_WRITE|PROT_EXEC) so W&X memory can't be created +#ifndef __x86_64__ + // mmap2 is used for mmap on i386 these days + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SYS_mmap2, 0, 5), +#else + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SYS_mmap, 0, 5), +#endif + EXAMINE_ARGUMENT(2), + BPF_STMT(BPF_ALU+BPF_AND+BPF_K, PROT_WRITE|PROT_EXEC), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, PROT_WRITE|PROT_EXEC, 0, 1), + KILL_PROCESS, + RETURN_ALLOW, + // block mprotect(,,PROT_EXEC) so writable memory can't be turned into executable + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SYS_mprotect, 0, 5), + EXAMINE_ARGUMENT(2), + BPF_STMT(BPF_ALU+BPF_AND+BPF_K, PROT_EXEC), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, PROT_EXEC, 0, 1), + KILL_PROCESS, + RETURN_ALLOW, + // block shmat(,,x|SHM_EXEC) so W&X shared memory can't be created + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SYS_shmat, 0, 5), + EXAMINE_ARGUMENT(2), + BPF_STMT(BPF_ALU+BPF_AND+BPF_K, SHM_EXEC), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SHM_EXEC, 0, 1), + KILL_PROCESS, + RETURN_ALLOW + }; + write_to_file(fd, filter, sizeof(filter)); + + filter_end_blacklist(fd); + + // close file + close(fd); +} diff --git a/src/fseccomp/seccomp_file.c b/src/fseccomp/seccomp_file.c index c74de9faf..16ffd5302 100644 --- a/src/fseccomp/seccomp_file.c +++ b/src/fseccomp/seccomp_file.c @@ -21,7 +21,7 @@ #include "../include/seccomp.h" #include -static void write_to_file(int fd, void *data, int size) { +void write_to_file(int fd, const void *data, int size) { assert(data); assert(size); diff --git a/src/man/firejail.txt b/src/man/firejail.txt index 0ce72f845..3a5e8560c 100644 --- a/src/man/firejail.txt +++ b/src/man/firejail.txt @@ -739,6 +739,12 @@ Example: .br $ firejail \-\-machine-id +.TP +\fB\-\-memory-deny-write-execute +Install a seccomp filter to block attempts to create memory mappings +that are both writable and executable, to change mappings to be +executable or to create executable shared memory. + .TP \fB\-\-mtu=number Assign a MTU value to the last network interface defined by a \-\-net option. -- cgit v1.2.3-54-g00ecf