From 3f27e8483158e50050f839db343bda7a522f686d Mon Sep 17 00:00:00 2001 From: Topi Miettinen Date: Fri, 27 Mar 2020 14:22:20 +0200 Subject: Allow changing error action in seccomp filters Let user specify the action when seccomp filters trigger: - errno name like EPERM (default) or ENOSYS: return errno and let the process continue. - 'kill': kill the process as previous versions The default action is EPERM, but killing can still be specified with syscall:kill syntax or globally with seccomp-error-action=kill. The action can be also overridden /etc/firejail/firejail.config file. Not killing the process weakens Firejail slightly when trying to contain intrusion, but it may also allow tighter filters if the only alternative is to allow a system call. --- RELNOTES | 5 ++++ etc/firejail.config | 3 +++ src/firejail/checkcfg.c | 22 ++++++++++++++++ src/firejail/firejail.h | 5 ++++ src/firejail/main.c | 32 +++++++++++++++++++++++ src/firejail/profile.c | 29 +++++++++++++++++++++ src/firejail/sandbox.c | 5 ++++ src/firejail/sbox.c | 55 +++++++++++++++++++++------------------- src/firejail/seccomp.c | 35 +++++++++++++++++++++++-- src/firejail/usage.c | 1 + src/fsec-print/main.c | 8 ++++++ src/fseccomp/main.c | 13 ++++++++++ src/fseccomp/seccomp.c | 20 +++++++-------- src/fseccomp/seccomp_file.c | 15 ++++++++++- src/fseccomp/seccomp_secondary.c | 2 +- src/include/seccomp.h | 9 ++++--- src/include/syscall.h | 1 + src/lib/syscall.c | 31 ++++++++++++++-------- src/man/firejail-profile.txt | 3 +++ src/man/firejail.txt | 33 +++++++++++++++++++----- 20 files changed, 266 insertions(+), 61 deletions(-) diff --git a/RELNOTES b/RELNOTES index e60dc9d5a..0d1f435f9 100644 --- a/RELNOTES +++ b/RELNOTES @@ -1,5 +1,10 @@ firejail (0.9.63) baseline; urgency=low * work in progress + * The blocking action of seccomp filters has been changed from + killing the process to returning EPERM to the caller. To get the + previous behaviour, use --seccomp-error-action=kill or + syscall:kill syntax when constructing filters, or override in + /etc/firejail/firejail.config file. * DHCP client support * SELinux labeling support * 32-bit seccomp filter diff --git a/etc/firejail.config b/etc/firejail.config index 6fb7d829a..410bd0ccb 100644 --- a/etc/firejail.config +++ b/etc/firejail.config @@ -146,3 +146,6 @@ # Xvfb command extra parameters. None by default; this is an example. # xvfb-extra-params -pixdepths 8 24 32 + +# Seccomp error action, kill or errno (EPERM, ENOSYS etc) +# seccomp-error-action EPERM diff --git a/src/firejail/checkcfg.c b/src/firejail/checkcfg.c index fbe150b34..eb4841210 100644 --- a/src/firejail/checkcfg.c +++ b/src/firejail/checkcfg.c @@ -18,6 +18,8 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "firejail.h" +#include "../include/seccomp.h" +#include "../include/syscall.h" #include #include @@ -32,6 +34,7 @@ char *xvfb_screen = "800x600x24"; char *xvfb_extra_params = ""; char *netfilter_default = NULL; unsigned long join_timeout = 5000000; // microseconds +char *config_seccomp_error_action_str = "EPERM"; int checkcfg(int val) { assert(val < CFG_MAX); @@ -51,6 +54,7 @@ int checkcfg(int val) { cfg_val[CFG_DISABLE_MNT] = 0; cfg_val[CFG_ARP_PROBES] = DEFAULT_ARP_PROBES; cfg_val[CFG_XPRA_ATTACH] = 0; + cfg_val[CFG_SECCOMP_ERROR_ACTION] = -1; // open configuration file const char *fname = SYSCONFDIR "/firejail.config"; @@ -219,6 +223,24 @@ int checkcfg(int val) { else if (strncmp(ptr, "join-timeout ", 13) == 0) join_timeout = strtoul(ptr + 13, NULL, 10) * 1000000; // seconds to microseconds + // seccomp error action + else if (strncmp(ptr, "seccomp-error-action ", 21) == 0) { +#ifdef HAVE_SECCOMP + if (strcmp(ptr + 21, "kill") == 0) + cfg_val[CFG_SECCOMP_ERROR_ACTION] = SECCOMP_RET_KILL; + else { + cfg_val[CFG_SECCOMP_ERROR_ACTION] = errno_find_name(ptr + 21); + if (cfg_val[CFG_SECCOMP_ERROR_ACTION] == -1) + errExit("seccomp-error-action: unknown errno"); + } + config_seccomp_error_action_str = strdup(ptr + 21); + if (!config_seccomp_error_action_str) + errExit("strdup"); +#else + warning_feature_disabled("seccomp"); +#endif + } + else goto errout; diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index 1be2bc1da..1cb8b2d22 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h @@ -186,6 +186,7 @@ typedef struct config_t { char *seccomp_list_drop, *seccomp_list_drop32; // seccomp drop list char *seccomp_list_keep, *seccomp_list_keep32; // seccomp keep list char *protocol; // protocol list + char *seccomp_error_action; // error action: kill or errno // rlimits long long unsigned rlimit_cpu; @@ -572,6 +573,7 @@ int seccomp_install_filters(void); int seccomp_load(const char *fname); int seccomp_filter_drop(bool native); int seccomp_filter_keep(bool native); +int seccomp_filter_mdwx(bool native); void seccomp_print_filter(pid_t pid); // caps.c @@ -754,6 +756,7 @@ enum { CFG_PRIVATE_CACHE, CFG_CGROUP, CFG_NAME_CHANGE, + CFG_SECCOMP_ERROR_ACTION, // CFG_FILE_COPY_LIMIT - file copy limit handled using setenv/getenv CFG_MAX // this should always be the last entry }; @@ -764,6 +767,8 @@ extern char *xvfb_screen; extern char *xvfb_extra_params; extern char *netfilter_default; extern unsigned long join_timeout; +extern char *config_seccomp_error_action_str; + int checkcfg(int val); void print_compiletime_support(void); diff --git a/src/firejail/main.c b/src/firejail/main.c index 9bff960cb..d01725c95 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c @@ -21,6 +21,7 @@ #include "../include/pid.h" #include "../include/firejail_user.h" #include "../include/syscall.h" +#include "../include/seccomp.h" #define _GNU_SOURCE #include #include @@ -76,6 +77,7 @@ int arg_seccomp = 0; // enable default seccomp filter int arg_seccomp32 = 0; // enable default seccomp filter for 32 bit arch int arg_seccomp_postexec = 0; // need postexec ld.preload library? int arg_seccomp_block_secondary = 0; // block any secondary architectures +int arg_seccomp_error_action = 0; int arg_caps_default_filter = 0; // enable default capabilities filter int arg_caps_drop = 0; // drop list @@ -349,6 +351,9 @@ static void init_cfg(int argc, char **argv) { sandbox_pid = getpid(); time_t t = time(NULL); srand(t ^ sandbox_pid); + + arg_seccomp_error_action = EPERM; + cfg.seccomp_error_action = "EPERM"; } static void check_network(Bridge *br) { @@ -973,6 +978,13 @@ void filter_add_errno(int fd, int syscall, int arg, void *ptrarg, bool native) { (void) ptrarg; (void) native; } +void filter_add_blacklist_override(int fd, int syscall, int arg, void *ptrarg, bool native) { + (void) fd; + (void) syscall; + (void) arg; + (void) ptrarg; + (void) native; +} #ifdef HAVE_SECCOMP static int check_postexec(const char *list) { @@ -1398,6 +1410,26 @@ int main(int argc, char **argv, char **envp) { else exit_err_feature("seccomp"); } + else if (strncmp(argv[i], "--seccomp-error-action=", 23) == 0) { + if (checkcfg(CFG_SECCOMP)) { + int config_seccomp_error_action = checkcfg(CFG_SECCOMP_ERROR_ACTION); + if (config_seccomp_error_action == -1) { + if (strcmp(argv[i] + 23, "kill") == 0) + arg_seccomp_error_action = SECCOMP_RET_KILL; + else { + arg_seccomp_error_action = errno_find_name(argv[i] + 23); + if (arg_seccomp_error_action == -1) + errExit("seccomp-error-action: unknown errno"); + } + cfg.seccomp_error_action = strdup(argv[i] + 23); + if (!cfg.seccomp_error_action) + errExit("strdup"); + } else + exit_err_feature("seccomp-error-action"); + + } else + exit_err_feature("seccomp"); + } #endif else if (strcmp(argv[i], "--caps") == 0) { arg_caps_default_filter = 1; diff --git a/src/firejail/profile.c b/src/firejail/profile.c index 2200fec01..d709a7951 100644 --- a/src/firejail/profile.c +++ b/src/firejail/profile.c @@ -18,6 +18,8 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "firejail.h" +#include "../include/seccomp.h" +#include "../include/syscall.h" #include #include extern char *xephyr_screen; @@ -870,6 +872,33 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { return 0; } + // seccomp error action + if (strncmp(ptr, "seccomp-error-action ", 21) == 0) { +#ifdef HAVE_SECCOMP + if (checkcfg(CFG_SECCOMP)) { + int config_seccomp_error_action = checkcfg(CFG_SECCOMP_ERROR_ACTION); + if (config_seccomp_error_action == -1) { + if (strcmp(ptr + 21, "kill") == 0) + arg_seccomp_error_action = SECCOMP_RET_KILL; + else { + arg_seccomp_error_action = errno_find_name(ptr + 21); + if (arg_seccomp_error_action == -1) + errExit("seccomp-error-action: unknown errno"); + } + cfg.seccomp_error_action = strdup(ptr + 21); + if (!cfg.seccomp_error_action) + errExit("strdup"); + } else { + arg_seccomp_error_action = config_seccomp_error_action; + cfg.seccomp_error_action = config_seccomp_error_action_str; + warning_feature_disabled("seccomp-error-action"); + } + } 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 9abf94a7f..e20ec603c 100644 --- a/src/firejail/sandbox.c +++ b/src/firejail/sandbox.c @@ -19,6 +19,7 @@ */ #include "firejail.h" +#include "../include/seccomp.h" #include #include #include @@ -1124,6 +1125,10 @@ int sandbox(void* sandbox_arg) { } if (arg_memory_deny_write_execute) { + if (arg_seccomp_error_action != EPERM) { + seccomp_filter_mdwx(true); + seccomp_filter_mdwx(false); + } if (arg_debug) printf("Install memory write&execute filter\n"); seccomp_load(RUN_SECCOMP_MDWX); // install filter diff --git a/src/firejail/sbox.c b/src/firejail/sbox.c index 0c7b13f1c..e96b9cf79 100644 --- a/src/firejail/sbox.c +++ b/src/firejail/sbox.c @@ -31,7 +31,27 @@ #define O_PATH 010000000 #endif -static struct sock_filter filter[] = { +int sbox_run(unsigned filtermask, int num, ...) { + va_list valist; + va_start(valist, num); + + // build argument list + char **arg = malloc((num + 1) * sizeof(char *)); + int i; + for (i = 0; i < num; i++) + arg[i] = va_arg(valist, char*); + arg[i] = NULL; + va_end(valist); + + int status = sbox_run_v(filtermask, arg); + + free(arg); + + return status; +} + +int sbox_run_v(unsigned filtermask, char * const arg[]) { + struct sock_filter filter[] = { VALIDATE_ARCHITECTURE, EXAMINE_SYSCALL, @@ -105,33 +125,13 @@ static struct sock_filter filter[] = { BLACKLIST(SYS_syslog), // kernel printk control #endif RETURN_ALLOW -}; + }; -static struct sock_fprog prog = { - .len = (unsigned short)(sizeof(filter) / sizeof(filter[0])), - .filter = filter, -}; + struct sock_fprog prog = { + .len = (unsigned short)(sizeof(filter) / sizeof(filter[0])), + .filter = filter, + }; -int sbox_run(unsigned filtermask, int num, ...) { - va_list valist; - va_start(valist, num); - - // build argument list - char **arg = malloc((num + 1) * sizeof(char *)); - int i; - for (i = 0; i < num; i++) - arg[i] = va_arg(valist, char*); - arg[i] = NULL; - va_end(valist); - - int status = sbox_run_v(filtermask, arg); - - free(arg); - - return status; -} - -int sbox_run_v(unsigned filtermask, char * const arg[]) { EUID_ROOT(); if (arg_debug) { @@ -161,6 +161,9 @@ int sbox_run_v(unsigned filtermask, char * const arg[]) { new_environment[env_index++] = "FIREJAIL_QUIET=yes"; if (arg_debug) // --debug is passed as an environment variable new_environment[env_index++] = "FIREJAIL_DEBUG=yes"; + if (cfg.seccomp_error_action) + if (asprintf(&new_environment[env_index++], "FIREJAIL_SECCOMP_ERROR_ACTION=%s", cfg.seccomp_error_action) == -1) + errExit("asprintf"); if (filtermask & SBOX_STDIN_FROM_FILE) { int fd; diff --git a/src/firejail/seccomp.c b/src/firejail/seccomp.c index 612ece85d..b42a1eeb0 100644 --- a/src/firejail/seccomp.c +++ b/src/firejail/seccomp.c @@ -208,8 +208,8 @@ int seccomp_filter_drop(bool native) { // - seccomp list // - seccomp if (cfg.seccomp_list_drop == NULL) { - // default seccomp - if (cfg.seccomp_list == NULL) { + // default seccomp if error action is not changed + if (cfg.seccomp_list == NULL && cfg.seccomp_error_action) { if (arg_seccomp_block_secondary) seccomp_filter_block_secondary(); else { @@ -243,6 +243,8 @@ int seccomp_filter_drop(bool native) { list = cfg.seccomp_list32; } + if (list == NULL) + list = ""; // build the seccomp filter as a regular user int rv; if (arg_allow_debuggers) @@ -365,6 +367,35 @@ int seccomp_filter_keep(bool native) { return 0; } +// create mdwx filter for non-default error action +int seccomp_filter_mdwx(bool native) { + if (arg_debug) + printf("Build memory-deny-write-execute filter\n"); + + const char *command, *filter, *postexec_filter, *list; + if (native) { + command = "memory-deny-write-execute"; + filter = RUN_SECCOMP_MDWX; + } else { + command = "memory-deny-write-execute.32"; + filter = RUN_SECCOMP_MDWX_32; + } + + // build the seccomp filter as a regular user + int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 3, + PATH_FSECCOMP, command, filter); + + if (rv) { + fprintf(stderr, "Error: cannot build memory-deny-write-execute filter\n"); + exit(rv); + } + + if (arg_debug) + printf("Memory-deny-write-execute filter configured\n"); + + return 0; +} + void seccomp_print_filter(pid_t pid) { EUID_ASSERT(); diff --git a/src/firejail/usage.c b/src/firejail/usage.c index 77bfea8c6..81a1a6099 100644 --- a/src/firejail/usage.c +++ b/src/firejail/usage.c @@ -209,6 +209,7 @@ static char *usage_str = " --seccomp.print=name|pid - print the seccomp filter for the sandbox\n" "\tidentified by name or PID.\n" " --seccomp.32[.drop,.keep][=syscall] - like above but for 32 bit architecture.\n" + " --seccomp-error-action=errno|kill - change error code or kill process.\n" #endif " --shell=none - run the program directly without a user shell.\n" " --shell=program - set default user shell.\n" diff --git a/src/fsec-print/main.c b/src/fsec-print/main.c index 8b7c68434..ade45c881 100644 --- a/src/fsec-print/main.c +++ b/src/fsec-print/main.c @@ -33,6 +33,14 @@ void filter_add_errno(int fd, int syscall, int arg, void *ptrarg, bool native) { (void) native; } +void filter_add_blacklist_override(int fd, int syscall, int arg, void *ptrarg, bool native) { + (void) fd; + (void) syscall; + (void) arg; + (void) ptrarg; + (void) native; +} + int main(int argc, char **argv) { #if 0 { diff --git a/src/fseccomp/main.c b/src/fseccomp/main.c index b3161a6db..98e32cdf4 100644 --- a/src/fseccomp/main.c +++ b/src/fseccomp/main.c @@ -18,7 +18,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "fseccomp.h" +#include "../include/seccomp.h" int arg_quiet = 0; +int arg_seccomp_error_action = EPERM; // error action: errno or kill static void usage(void) { printf("Usage:\n"); @@ -67,6 +69,17 @@ printf("\n"); if (quiet && strcmp(quiet, "yes") == 0) arg_quiet = 1; + char *error_action = getenv("FIREJAIL_SECCOMP_ERROR_ACTION"); + if (error_action) + if (strcmp(error_action, "kill") == 0) + arg_seccomp_error_action = SECCOMP_RET_KILL; + else { + arg_seccomp_error_action = errno_find_name(error_action); + if (arg_seccomp_error_action == -1) + errExit("seccomp-error-action: unknown errno"); + arg_seccomp_error_action |= SECCOMP_RET_ERRNO; + } + if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") ==0) { usage(); return 0; diff --git a/src/fseccomp/seccomp.c b/src/fseccomp/seccomp.c index 0db7b5954..e808538b0 100644 --- a/src/fseccomp/seccomp.c +++ b/src/fseccomp/seccomp.c @@ -255,7 +255,7 @@ void memory_deny_write_execute(const char *fname) { 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, + KILL_OR_RETURN_ERRNO, RETURN_ALLOW, #endif @@ -264,7 +264,7 @@ void memory_deny_write_execute(const char *fname) { 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, + KILL_OR_RETURN_ERRNO, RETURN_ALLOW, // same for pkey_mprotect(,,PROT_EXEC), where available @@ -273,7 +273,7 @@ void memory_deny_write_execute(const char *fname) { 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, + KILL_OR_RETURN_ERRNO, RETURN_ALLOW, #endif @@ -284,7 +284,7 @@ void memory_deny_write_execute(const char *fname) { 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, + KILL_OR_RETURN_ERRNO, RETURN_ALLOW, #endif #ifdef SYS_memfd_create @@ -292,7 +292,7 @@ void memory_deny_write_execute(const char *fname) { // arbitrary memory contents which can be later mapped // as executable BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SYS_memfd_create, 0, 1), - KILL_PROCESS, + KILL_OR_RETURN_ERRNO, RETURN_ALLOW #endif }; @@ -327,7 +327,7 @@ void memory_deny_write_execute_32(const char *fname) { 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, + KILL_OR_RETURN_ERRNO, RETURN_ALLOW, #endif #ifdef mprotect_32 @@ -336,7 +336,7 @@ void memory_deny_write_execute_32(const char *fname) { 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, + KILL_OR_RETURN_ERRNO, RETURN_ALLOW, #endif #ifdef pkey_mprotect_32 @@ -345,7 +345,7 @@ void memory_deny_write_execute_32(const char *fname) { 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, + KILL_OR_RETURN_ERRNO, RETURN_ALLOW, #endif @@ -355,7 +355,7 @@ void memory_deny_write_execute_32(const char *fname) { 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, + KILL_OR_RETURN_ERRNO, RETURN_ALLOW, #endif #ifdef memfd_create_32 @@ -363,7 +363,7 @@ void memory_deny_write_execute_32(const char *fname) { // arbitrary memory contents which can be later mapped // as executable BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, memfd_create_32, 0, 1), - KILL_PROCESS, + KILL_OR_RETURN_ERRNO, #endif #endif RETURN_ALLOW diff --git a/src/fseccomp/seccomp_file.c b/src/fseccomp/seccomp_file.c index 872b41261..9e8ceb898 100644 --- a/src/fseccomp/seccomp_file.c +++ b/src/fseccomp/seccomp_file.c @@ -112,6 +112,19 @@ void filter_add_blacklist(int fd, int syscall, int arg, void *ptrarg, bool nativ } } +void filter_add_blacklist_override(int fd, int syscall, int arg, void *ptrarg, bool native) { + (void) arg; + (void) ptrarg; + (void) native; + + if (syscall >= 0) { + int saved_error_action = arg_seccomp_error_action; + arg_seccomp_error_action = SECCOMP_RET_KILL; + write_blacklist(fd, syscall); + arg_seccomp_error_action = saved_error_action; + } +} + // handle seccomp list exceptions (seccomp x,y,!z) void filter_add_blacklist_for_excluded(int fd, int syscall, int arg, void *ptrarg, bool native) { (void) arg; @@ -142,7 +155,7 @@ void filter_end_blacklist(int fd) { void filter_end_whitelist(int fd) { struct sock_filter filter[] = { - KILL_PROCESS + KILL_OR_RETURN_ERRNO }; write_to_file(fd, filter, sizeof(filter)); } diff --git a/src/fseccomp/seccomp_secondary.c b/src/fseccomp/seccomp_secondary.c index 9a00d1884..f024859d3 100644 --- a/src/fseccomp/seccomp_secondary.c +++ b/src/fseccomp/seccomp_secondary.c @@ -142,7 +142,7 @@ void seccomp_secondary_block(const char *fname) { // 5: if MSW(arg0) == 0, goto 7 (allow) else continue to 6 (kill) BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, jmp_from_to(5, 7), 0), // 6: - KILL_PROCESS, + KILL_OR_RETURN_ERRNO, // 7: RETURN_ALLOW }; diff --git a/src/include/seccomp.h b/src/include/seccomp.h index 80a83df34..50920ce3a 100644 --- a/src/include/seccomp.h +++ b/src/include/seccomp.h @@ -243,7 +243,7 @@ struct seccomp_data { #define HANDLE_X32_KILL \ BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, X32_SYSCALL_BIT, 1, 0), \ BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, 0, 1, 0), \ - KILL_PROCESS + BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL) #endif #define EXAMINE_SYSCALL BPF_STMT(BPF_LD+BPF_W+BPF_ABS, \ @@ -258,7 +258,7 @@ struct seccomp_data { #define BLACKLIST(syscall_nr) \ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, syscall_nr, 0, 1), \ - BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL) + KILL_OR_RETURN_ERRNO #define WHITELIST(syscall_nr) \ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, syscall_nr, 0, 1), \ @@ -274,7 +274,8 @@ struct seccomp_data { #define RETURN_ERRNO(nr) \ BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ERRNO | nr) -#define KILL_PROCESS \ - BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL) +extern int arg_seccomp_error_action; // error action: errno or kill +#define KILL_OR_RETURN_ERRNO \ + BPF_STMT(BPF_RET+BPF_K, arg_seccomp_error_action) #endif diff --git a/src/include/syscall.h b/src/include/syscall.h index 9841fc7ab..89b54170e 100644 --- a/src/include/syscall.h +++ b/src/include/syscall.h @@ -27,6 +27,7 @@ extern int arg_quiet; // seccomp_file.c or dummy versions in firejail/main.c and fsec-print/main.c void filter_add_errno(int fd, int syscall, int arg, void *ptrarg, bool native); +void filter_add_blacklist_override(int fd, int syscall, int arg, void *ptrarg, bool native); // errno.c void errno_print(void); diff --git a/src/lib/syscall.c b/src/lib/syscall.c index 1cf7f2d52..5accdcb65 100644 --- a/src/lib/syscall.c +++ b/src/lib/syscall.c @@ -20,11 +20,16 @@ #define _GNU_SOURCE #include "../include/syscall.h" #include +#include #include #include #include #include #include "../include/common.h" +#include "../include/seccomp.h" + +#define SYSCALL_ERROR INT_MAX +#define ERRNO_KILL -2 typedef struct { const char * const name; @@ -1430,7 +1435,7 @@ static const SyscallGroupList sysgroups[] = { } }; -// return -1 if error, or syscall number +// return SYSCALL_ERROR if error, or syscall number static int syscall_find_name(const char *name) { int i; int elems = sizeof(syslist) / sizeof(syslist[0]); @@ -1439,7 +1444,7 @@ static int syscall_find_name(const char *name) { return syslist[i].nr; } - return -1; + return SYSCALL_ERROR; } static int syscall_find_name_32(const char *name) { @@ -1450,7 +1455,7 @@ static int syscall_find_name_32(const char *name) { return syslist32[i].nr; } - return -1; + return SYSCALL_ERROR; } const char *syscall_find_nr(int nr) { @@ -1538,9 +1543,13 @@ static void syscall_process_name(const char *name, int *syscall_nr, int *error_n *syscall_nr = syscall_find_name_32(syscall_name); } if (error_name) { - *error_nr = errno_find_name(error_name); - if (*error_nr == -1) - *syscall_nr = -1; + if (strcmp(error_name, "kill") == 0) + *error_nr = ERRNO_KILL; + else { + *error_nr = errno_find_name(error_name); + if (*error_nr == -1) + *syscall_nr = SYSCALL_ERROR; + } } free(str); @@ -1589,15 +1598,15 @@ int syscall_check_list(const char *slist, filter_fn *callback, int fd, int arg, ptr++; } syscall_process_name(ptr, &syscall_nr, &error_nr, native); - if (syscall_nr == -1) {;} - else if (callback != NULL) { + if (syscall_nr != SYSCALL_ERROR && callback != NULL) { if (negate) { syscall_nr = -syscall_nr; } - if (error_nr != -1 && fd > 0) { + if (error_nr >= 0 && fd > 0) filter_add_errno(fd, syscall_nr, error_nr, ptrarg, native); - } - else if (error_nr != -1 && fd == 0) { + else if (error_nr == ERRNO_KILL && fd > 0) + filter_add_blacklist_override(fd, syscall_nr, 0, ptrarg, native); + else if (error_nr >= 0 && fd == 0) { callback(fd, syscall_nr, error_nr, ptrarg, native); } else { diff --git a/src/man/firejail-profile.txt b/src/man/firejail-profile.txt index 511194ff3..203d4543d 100644 --- a/src/man/firejail-profile.txt +++ b/src/man/firejail-profile.txt @@ -411,6 +411,9 @@ Enable seccomp filter and whitelist the system calls in the list. \fBseccomp.32.keep syscall,syscall,syscall Enable seccomp filter and whitelist the system calls in the list for 32 bit system calls on a 64 bit architecture system. .TP +\fBseccomp-error-action kill | ERRNO +Return a different error instead of EPERM to the process or kill it when an attempt is made to call a blocked system call. +.TP \fBx11 Enable X11 sandboxing. .TP diff --git a/src/man/firejail.txt b/src/man/firejail.txt index 1bed40015..02c1d27b2 100644 --- a/src/man/firejail.txt +++ b/src/man/firejail.txt @@ -814,8 +814,9 @@ $ firejail \-\-machine-id 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. The filter examines -the arguments of mmap, mmap2, mprotect, pkey_mprotect, memfd_create and -shmat system calls and kills the process if necessary. +the arguments of mmap, mmap2, mprotect, pkey_mprotect, memfd_create +and shmat system calls and returns error EPERM to the process (or +kills it, see \-\-seccomp-error-action below) if necessary. .br .br @@ -1865,8 +1866,12 @@ $ firejail \-\-seccomp=@clock,mkdir,unlinkat transmission-gtk .br .br -Instead of dropping the syscall, a specific error number can be returned -using \fBsyscall:errorno\fR syntax. +Instead of dropping the syscall by returning EPERM, another error +number can be returned using \fBsyscall:errno\fR syntax. This can be +also changed globally with \-\-seccomp-error-action or +in /etc/firejail/firejail.config file. The process can also be killed +by using \fBsyscall:kill\fR syntax. + .br .br @@ -1932,8 +1937,11 @@ $ firejail \-\-seccomp.drop=utime,utimensat,utimes,@clock .br .br -Instead of dropping the syscall, a specific error number can be returned -using \fBsyscall:errorno\fR syntax. +Instead of dropping the syscall by returning EPERM, another error +number can be returned using \fBsyscall:errno\fR syntax. This can be +also changed globally with \-\-seccomp-error-action or +in /etc/firejail/firejail.config file. The process can also be killed +by using \fBsyscall:kill\fR syntax. .br .br @@ -2135,6 +2143,19 @@ $ firejail --seccomp.print=browser 0049: 06 00 01 00000000 ret KILL .br $ + +.TP +\fB\-\-seccomp-error-action= kill | ERRNO +By default, if a seccomp filter blocks a system call, the process gets +EPERM as the error. With \-\-seccomp-error-action=error, another error +number can be returned, for example ENOSYS or EACCES. The process can +also be killed (like in versions <0.9.63 of Firejail) by using +\-\-seccomp-error-action=kill syntax. Not killing the process weakens +Firejail slightly when trying to contain intrusion, but it may also +allow tighter filters if the only alternative is to allow a system +call. +.br + .TP \fB\-\-shell=none Run the program directly, without a user shell. -- cgit v1.2.3-54-g00ecf