aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--RELNOTES5
-rw-r--r--etc/firejail.config3
-rw-r--r--src/firejail/checkcfg.c22
-rw-r--r--src/firejail/firejail.h5
-rw-r--r--src/firejail/main.c32
-rw-r--r--src/firejail/profile.c29
-rw-r--r--src/firejail/sandbox.c5
-rw-r--r--src/firejail/sbox.c55
-rw-r--r--src/firejail/seccomp.c35
-rw-r--r--src/firejail/usage.c1
-rw-r--r--src/fsec-print/main.c8
-rw-r--r--src/fseccomp/main.c13
-rw-r--r--src/fseccomp/seccomp.c20
-rw-r--r--src/fseccomp/seccomp_file.c15
-rw-r--r--src/fseccomp/seccomp_secondary.c2
-rw-r--r--src/include/seccomp.h9
-rw-r--r--src/include/syscall.h1
-rw-r--r--src/lib/syscall.c31
-rw-r--r--src/man/firejail-profile.txt3
-rw-r--r--src/man/firejail.txt33
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 @@
1firejail (0.9.63) baseline; urgency=low 1firejail (0.9.63) baseline; urgency=low
2 * work in progress 2 * work in progress
3 * The blocking action of seccomp filters has been changed from
4 killing the process to returning EPERM to the caller. To get the
5 previous behaviour, use --seccomp-error-action=kill or
6 syscall:kill syntax when constructing filters, or override in
7 /etc/firejail/firejail.config file.
3 * DHCP client support 8 * DHCP client support
4 * SELinux labeling support 9 * SELinux labeling support
5 * 32-bit seccomp filter 10 * 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 @@
146 146
147# Xvfb command extra parameters. None by default; this is an example. 147# Xvfb command extra parameters. None by default; this is an example.
148# xvfb-extra-params -pixdepths 8 24 32 148# xvfb-extra-params -pixdepths 8 24 32
149
150# Seccomp error action, kill or errno (EPERM, ENOSYS etc)
151# 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 @@
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/ 19*/
20#include "firejail.h" 20#include "firejail.h"
21#include "../include/seccomp.h"
22#include "../include/syscall.h"
21#include <sys/stat.h> 23#include <sys/stat.h>
22#include <linux/loop.h> 24#include <linux/loop.h>
23 25
@@ -32,6 +34,7 @@ char *xvfb_screen = "800x600x24";
32char *xvfb_extra_params = ""; 34char *xvfb_extra_params = "";
33char *netfilter_default = NULL; 35char *netfilter_default = NULL;
34unsigned long join_timeout = 5000000; // microseconds 36unsigned long join_timeout = 5000000; // microseconds
37char *config_seccomp_error_action_str = "EPERM";
35 38
36int checkcfg(int val) { 39int checkcfg(int val) {
37 assert(val < CFG_MAX); 40 assert(val < CFG_MAX);
@@ -51,6 +54,7 @@ int checkcfg(int val) {
51 cfg_val[CFG_DISABLE_MNT] = 0; 54 cfg_val[CFG_DISABLE_MNT] = 0;
52 cfg_val[CFG_ARP_PROBES] = DEFAULT_ARP_PROBES; 55 cfg_val[CFG_ARP_PROBES] = DEFAULT_ARP_PROBES;
53 cfg_val[CFG_XPRA_ATTACH] = 0; 56 cfg_val[CFG_XPRA_ATTACH] = 0;
57 cfg_val[CFG_SECCOMP_ERROR_ACTION] = -1;
54 58
55 // open configuration file 59 // open configuration file
56 const char *fname = SYSCONFDIR "/firejail.config"; 60 const char *fname = SYSCONFDIR "/firejail.config";
@@ -219,6 +223,24 @@ int checkcfg(int val) {
219 else if (strncmp(ptr, "join-timeout ", 13) == 0) 223 else if (strncmp(ptr, "join-timeout ", 13) == 0)
220 join_timeout = strtoul(ptr + 13, NULL, 10) * 1000000; // seconds to microseconds 224 join_timeout = strtoul(ptr + 13, NULL, 10) * 1000000; // seconds to microseconds
221 225
226 // seccomp error action
227 else if (strncmp(ptr, "seccomp-error-action ", 21) == 0) {
228#ifdef HAVE_SECCOMP
229 if (strcmp(ptr + 21, "kill") == 0)
230 cfg_val[CFG_SECCOMP_ERROR_ACTION] = SECCOMP_RET_KILL;
231 else {
232 cfg_val[CFG_SECCOMP_ERROR_ACTION] = errno_find_name(ptr + 21);
233 if (cfg_val[CFG_SECCOMP_ERROR_ACTION] == -1)
234 errExit("seccomp-error-action: unknown errno");
235 }
236 config_seccomp_error_action_str = strdup(ptr + 21);
237 if (!config_seccomp_error_action_str)
238 errExit("strdup");
239#else
240 warning_feature_disabled("seccomp");
241#endif
242 }
243
222 else 244 else
223 goto errout; 245 goto errout;
224 246
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 {
186 char *seccomp_list_drop, *seccomp_list_drop32; // seccomp drop list 186 char *seccomp_list_drop, *seccomp_list_drop32; // seccomp drop list
187 char *seccomp_list_keep, *seccomp_list_keep32; // seccomp keep list 187 char *seccomp_list_keep, *seccomp_list_keep32; // seccomp keep list
188 char *protocol; // protocol list 188 char *protocol; // protocol list
189 char *seccomp_error_action; // error action: kill or errno
189 190
190 // rlimits 191 // rlimits
191 long long unsigned rlimit_cpu; 192 long long unsigned rlimit_cpu;
@@ -572,6 +573,7 @@ int seccomp_install_filters(void);
572int seccomp_load(const char *fname); 573int seccomp_load(const char *fname);
573int seccomp_filter_drop(bool native); 574int seccomp_filter_drop(bool native);
574int seccomp_filter_keep(bool native); 575int seccomp_filter_keep(bool native);
576int seccomp_filter_mdwx(bool native);
575void seccomp_print_filter(pid_t pid); 577void seccomp_print_filter(pid_t pid);
576 578
577// caps.c 579// caps.c
@@ -754,6 +756,7 @@ enum {
754 CFG_PRIVATE_CACHE, 756 CFG_PRIVATE_CACHE,
755 CFG_CGROUP, 757 CFG_CGROUP,
756 CFG_NAME_CHANGE, 758 CFG_NAME_CHANGE,
759 CFG_SECCOMP_ERROR_ACTION,
757 // CFG_FILE_COPY_LIMIT - file copy limit handled using setenv/getenv 760 // CFG_FILE_COPY_LIMIT - file copy limit handled using setenv/getenv
758 CFG_MAX // this should always be the last entry 761 CFG_MAX // this should always be the last entry
759}; 762};
@@ -764,6 +767,8 @@ extern char *xvfb_screen;
764extern char *xvfb_extra_params; 767extern char *xvfb_extra_params;
765extern char *netfilter_default; 768extern char *netfilter_default;
766extern unsigned long join_timeout; 769extern unsigned long join_timeout;
770extern char *config_seccomp_error_action_str;
771
767int checkcfg(int val); 772int checkcfg(int val);
768void print_compiletime_support(void); 773void print_compiletime_support(void);
769 774
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 @@
21#include "../include/pid.h" 21#include "../include/pid.h"
22#include "../include/firejail_user.h" 22#include "../include/firejail_user.h"
23#include "../include/syscall.h" 23#include "../include/syscall.h"
24#include "../include/seccomp.h"
24#define _GNU_SOURCE 25#define _GNU_SOURCE
25#include <sys/utsname.h> 26#include <sys/utsname.h>
26#include <sched.h> 27#include <sched.h>
@@ -76,6 +77,7 @@ int arg_seccomp = 0; // enable default seccomp filter
76int arg_seccomp32 = 0; // enable default seccomp filter for 32 bit arch 77int arg_seccomp32 = 0; // enable default seccomp filter for 32 bit arch
77int arg_seccomp_postexec = 0; // need postexec ld.preload library? 78int arg_seccomp_postexec = 0; // need postexec ld.preload library?
78int arg_seccomp_block_secondary = 0; // block any secondary architectures 79int arg_seccomp_block_secondary = 0; // block any secondary architectures
80int arg_seccomp_error_action = 0;
79 81
80int arg_caps_default_filter = 0; // enable default capabilities filter 82int arg_caps_default_filter = 0; // enable default capabilities filter
81int arg_caps_drop = 0; // drop list 83int arg_caps_drop = 0; // drop list
@@ -349,6 +351,9 @@ static void init_cfg(int argc, char **argv) {
349 sandbox_pid = getpid(); 351 sandbox_pid = getpid();
350 time_t t = time(NULL); 352 time_t t = time(NULL);
351 srand(t ^ sandbox_pid); 353 srand(t ^ sandbox_pid);
354
355 arg_seccomp_error_action = EPERM;
356 cfg.seccomp_error_action = "EPERM";
352} 357}
353 358
354static void check_network(Bridge *br) { 359static void check_network(Bridge *br) {
@@ -973,6 +978,13 @@ void filter_add_errno(int fd, int syscall, int arg, void *ptrarg, bool native) {
973 (void) ptrarg; 978 (void) ptrarg;
974 (void) native; 979 (void) native;
975} 980}
981void filter_add_blacklist_override(int fd, int syscall, int arg, void *ptrarg, bool native) {
982 (void) fd;
983 (void) syscall;
984 (void) arg;
985 (void) ptrarg;
986 (void) native;
987}
976 988
977#ifdef HAVE_SECCOMP 989#ifdef HAVE_SECCOMP
978static int check_postexec(const char *list) { 990static int check_postexec(const char *list) {
@@ -1398,6 +1410,26 @@ int main(int argc, char **argv, char **envp) {
1398 else 1410 else
1399 exit_err_feature("seccomp"); 1411 exit_err_feature("seccomp");
1400 } 1412 }
1413 else if (strncmp(argv[i], "--seccomp-error-action=", 23) == 0) {
1414 if (checkcfg(CFG_SECCOMP)) {
1415 int config_seccomp_error_action = checkcfg(CFG_SECCOMP_ERROR_ACTION);
1416 if (config_seccomp_error_action == -1) {
1417 if (strcmp(argv[i] + 23, "kill") == 0)
1418 arg_seccomp_error_action = SECCOMP_RET_KILL;
1419 else {
1420 arg_seccomp_error_action = errno_find_name(argv[i] + 23);
1421 if (arg_seccomp_error_action == -1)
1422 errExit("seccomp-error-action: unknown errno");
1423 }
1424 cfg.seccomp_error_action = strdup(argv[i] + 23);
1425 if (!cfg.seccomp_error_action)
1426 errExit("strdup");
1427 } else
1428 exit_err_feature("seccomp-error-action");
1429
1430 } else
1431 exit_err_feature("seccomp");
1432 }
1401#endif 1433#endif
1402 else if (strcmp(argv[i], "--caps") == 0) { 1434 else if (strcmp(argv[i], "--caps") == 0) {
1403 arg_caps_default_filter = 1; 1435 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 @@
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/ 19*/
20#include "firejail.h" 20#include "firejail.h"
21#include "../include/seccomp.h"
22#include "../include/syscall.h"
21#include <dirent.h> 23#include <dirent.h>
22#include <sys/stat.h> 24#include <sys/stat.h>
23extern char *xephyr_screen; 25extern char *xephyr_screen;
@@ -870,6 +872,33 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
870 return 0; 872 return 0;
871 } 873 }
872 874
875 // seccomp error action
876 if (strncmp(ptr, "seccomp-error-action ", 21) == 0) {
877#ifdef HAVE_SECCOMP
878 if (checkcfg(CFG_SECCOMP)) {
879 int config_seccomp_error_action = checkcfg(CFG_SECCOMP_ERROR_ACTION);
880 if (config_seccomp_error_action == -1) {
881 if (strcmp(ptr + 21, "kill") == 0)
882 arg_seccomp_error_action = SECCOMP_RET_KILL;
883 else {
884 arg_seccomp_error_action = errno_find_name(ptr + 21);
885 if (arg_seccomp_error_action == -1)
886 errExit("seccomp-error-action: unknown errno");
887 }
888 cfg.seccomp_error_action = strdup(ptr + 21);
889 if (!cfg.seccomp_error_action)
890 errExit("strdup");
891 } else {
892 arg_seccomp_error_action = config_seccomp_error_action;
893 cfg.seccomp_error_action = config_seccomp_error_action_str;
894 warning_feature_disabled("seccomp-error-action");
895 }
896 } else
897 warning_feature_disabled("seccomp");
898#endif
899 return 0;
900 }
901
873 // caps drop list 902 // caps drop list
874 if (strncmp(ptr, "caps.drop ", 10) == 0) { 903 if (strncmp(ptr, "caps.drop ", 10) == 0) {
875 arg_caps_drop = 1; 904 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 @@
19*/ 19*/
20 20
21#include "firejail.h" 21#include "firejail.h"
22#include "../include/seccomp.h"
22#include <sys/mount.h> 23#include <sys/mount.h>
23#include <sys/wait.h> 24#include <sys/wait.h>
24#include <sys/stat.h> 25#include <sys/stat.h>
@@ -1124,6 +1125,10 @@ int sandbox(void* sandbox_arg) {
1124 } 1125 }
1125 1126
1126 if (arg_memory_deny_write_execute) { 1127 if (arg_memory_deny_write_execute) {
1128 if (arg_seccomp_error_action != EPERM) {
1129 seccomp_filter_mdwx(true);
1130 seccomp_filter_mdwx(false);
1131 }
1127 if (arg_debug) 1132 if (arg_debug)
1128 printf("Install memory write&execute filter\n"); 1133 printf("Install memory write&execute filter\n");
1129 seccomp_load(RUN_SECCOMP_MDWX); // install filter 1134 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 @@
31#define O_PATH 010000000 31#define O_PATH 010000000
32#endif 32#endif
33 33
34static struct sock_filter filter[] = { 34int sbox_run(unsigned filtermask, int num, ...) {
35 va_list valist;
36 va_start(valist, num);
37
38 // build argument list
39 char **arg = malloc((num + 1) * sizeof(char *));
40 int i;
41 for (i = 0; i < num; i++)
42 arg[i] = va_arg(valist, char*);
43 arg[i] = NULL;
44 va_end(valist);
45
46 int status = sbox_run_v(filtermask, arg);
47
48 free(arg);
49
50 return status;
51}
52
53int sbox_run_v(unsigned filtermask, char * const arg[]) {
54 struct sock_filter filter[] = {
35 VALIDATE_ARCHITECTURE, 55 VALIDATE_ARCHITECTURE,
36 EXAMINE_SYSCALL, 56 EXAMINE_SYSCALL,
37 57
@@ -105,33 +125,13 @@ static struct sock_filter filter[] = {
105 BLACKLIST(SYS_syslog), // kernel printk control 125 BLACKLIST(SYS_syslog), // kernel printk control
106#endif 126#endif
107 RETURN_ALLOW 127 RETURN_ALLOW
108}; 128 };
109 129
110static struct sock_fprog prog = { 130 struct sock_fprog prog = {
111 .len = (unsigned short)(sizeof(filter) / sizeof(filter[0])), 131 .len = (unsigned short)(sizeof(filter) / sizeof(filter[0])),
112 .filter = filter, 132 .filter = filter,
113}; 133 };
114 134
115int sbox_run(unsigned filtermask, int num, ...) {
116 va_list valist;
117 va_start(valist, num);
118
119 // build argument list
120 char **arg = malloc((num + 1) * sizeof(char *));
121 int i;
122 for (i = 0; i < num; i++)
123 arg[i] = va_arg(valist, char*);
124 arg[i] = NULL;
125 va_end(valist);
126
127 int status = sbox_run_v(filtermask, arg);
128
129 free(arg);
130
131 return status;
132}
133
134int sbox_run_v(unsigned filtermask, char * const arg[]) {
135 EUID_ROOT(); 135 EUID_ROOT();
136 136
137 if (arg_debug) { 137 if (arg_debug) {
@@ -161,6 +161,9 @@ int sbox_run_v(unsigned filtermask, char * const arg[]) {
161 new_environment[env_index++] = "FIREJAIL_QUIET=yes"; 161 new_environment[env_index++] = "FIREJAIL_QUIET=yes";
162 if (arg_debug) // --debug is passed as an environment variable 162 if (arg_debug) // --debug is passed as an environment variable
163 new_environment[env_index++] = "FIREJAIL_DEBUG=yes"; 163 new_environment[env_index++] = "FIREJAIL_DEBUG=yes";
164 if (cfg.seccomp_error_action)
165 if (asprintf(&new_environment[env_index++], "FIREJAIL_SECCOMP_ERROR_ACTION=%s", cfg.seccomp_error_action) == -1)
166 errExit("asprintf");
164 167
165 if (filtermask & SBOX_STDIN_FROM_FILE) { 168 if (filtermask & SBOX_STDIN_FROM_FILE) {
166 int fd; 169 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) {
208 // - seccomp list 208 // - seccomp list
209 // - seccomp 209 // - seccomp
210 if (cfg.seccomp_list_drop == NULL) { 210 if (cfg.seccomp_list_drop == NULL) {
211 // default seccomp 211 // default seccomp if error action is not changed
212 if (cfg.seccomp_list == NULL) { 212 if (cfg.seccomp_list == NULL && cfg.seccomp_error_action) {
213 if (arg_seccomp_block_secondary) 213 if (arg_seccomp_block_secondary)
214 seccomp_filter_block_secondary(); 214 seccomp_filter_block_secondary();
215 else { 215 else {
@@ -243,6 +243,8 @@ int seccomp_filter_drop(bool native) {
243 list = cfg.seccomp_list32; 243 list = cfg.seccomp_list32;
244 } 244 }
245 245
246 if (list == NULL)
247 list = "";
246 // build the seccomp filter as a regular user 248 // build the seccomp filter as a regular user
247 int rv; 249 int rv;
248 if (arg_allow_debuggers) 250 if (arg_allow_debuggers)
@@ -365,6 +367,35 @@ int seccomp_filter_keep(bool native) {
365 return 0; 367 return 0;
366} 368}
367 369
370// create mdwx filter for non-default error action
371int seccomp_filter_mdwx(bool native) {
372 if (arg_debug)
373 printf("Build memory-deny-write-execute filter\n");
374
375 const char *command, *filter, *postexec_filter, *list;
376 if (native) {
377 command = "memory-deny-write-execute";
378 filter = RUN_SECCOMP_MDWX;
379 } else {
380 command = "memory-deny-write-execute.32";
381 filter = RUN_SECCOMP_MDWX_32;
382 }
383
384 // build the seccomp filter as a regular user
385 int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 3,
386 PATH_FSECCOMP, command, filter);
387
388 if (rv) {
389 fprintf(stderr, "Error: cannot build memory-deny-write-execute filter\n");
390 exit(rv);
391 }
392
393 if (arg_debug)
394 printf("Memory-deny-write-execute filter configured\n");
395
396 return 0;
397}
398
368void seccomp_print_filter(pid_t pid) { 399void seccomp_print_filter(pid_t pid) {
369 EUID_ASSERT(); 400 EUID_ASSERT();
370 401
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 =
209 " --seccomp.print=name|pid - print the seccomp filter for the sandbox\n" 209 " --seccomp.print=name|pid - print the seccomp filter for the sandbox\n"
210 "\tidentified by name or PID.\n" 210 "\tidentified by name or PID.\n"
211 " --seccomp.32[.drop,.keep][=syscall] - like above but for 32 bit architecture.\n" 211 " --seccomp.32[.drop,.keep][=syscall] - like above but for 32 bit architecture.\n"
212 " --seccomp-error-action=errno|kill - change error code or kill process.\n"
212#endif 213#endif
213 " --shell=none - run the program directly without a user shell.\n" 214 " --shell=none - run the program directly without a user shell.\n"
214 " --shell=program - set default user shell.\n" 215 " --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) {
33 (void) native; 33 (void) native;
34} 34}
35 35
36void filter_add_blacklist_override(int fd, int syscall, int arg, void *ptrarg, bool native) {
37 (void) fd;
38 (void) syscall;
39 (void) arg;
40 (void) ptrarg;
41 (void) native;
42}
43
36int main(int argc, char **argv) { 44int main(int argc, char **argv) {
37#if 0 45#if 0
38{ 46{
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 @@
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/ 19*/
20#include "fseccomp.h" 20#include "fseccomp.h"
21#include "../include/seccomp.h"
21int arg_quiet = 0; 22int arg_quiet = 0;
23int arg_seccomp_error_action = EPERM; // error action: errno or kill
22 24
23static void usage(void) { 25static void usage(void) {
24 printf("Usage:\n"); 26 printf("Usage:\n");
@@ -67,6 +69,17 @@ printf("\n");
67 if (quiet && strcmp(quiet, "yes") == 0) 69 if (quiet && strcmp(quiet, "yes") == 0)
68 arg_quiet = 1; 70 arg_quiet = 1;
69 71
72 char *error_action = getenv("FIREJAIL_SECCOMP_ERROR_ACTION");
73 if (error_action)
74 if (strcmp(error_action, "kill") == 0)
75 arg_seccomp_error_action = SECCOMP_RET_KILL;
76 else {
77 arg_seccomp_error_action = errno_find_name(error_action);
78 if (arg_seccomp_error_action == -1)
79 errExit("seccomp-error-action: unknown errno");
80 arg_seccomp_error_action |= SECCOMP_RET_ERRNO;
81 }
82
70 if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") ==0) { 83 if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") ==0) {
71 usage(); 84 usage();
72 return 0; 85 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) {
255 EXAMINE_ARGUMENT(2), 255 EXAMINE_ARGUMENT(2),
256 BPF_STMT(BPF_ALU+BPF_AND+BPF_K, PROT_WRITE|PROT_EXEC), 256 BPF_STMT(BPF_ALU+BPF_AND+BPF_K, PROT_WRITE|PROT_EXEC),
257 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, PROT_WRITE|PROT_EXEC, 0, 1), 257 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, PROT_WRITE|PROT_EXEC, 0, 1),
258 KILL_PROCESS, 258 KILL_OR_RETURN_ERRNO,
259 RETURN_ALLOW, 259 RETURN_ALLOW,
260#endif 260#endif
261 261
@@ -264,7 +264,7 @@ void memory_deny_write_execute(const char *fname) {
264 EXAMINE_ARGUMENT(2), 264 EXAMINE_ARGUMENT(2),
265 BPF_STMT(BPF_ALU+BPF_AND+BPF_K, PROT_EXEC), 265 BPF_STMT(BPF_ALU+BPF_AND+BPF_K, PROT_EXEC),
266 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, PROT_EXEC, 0, 1), 266 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, PROT_EXEC, 0, 1),
267 KILL_PROCESS, 267 KILL_OR_RETURN_ERRNO,
268 RETURN_ALLOW, 268 RETURN_ALLOW,
269 269
270 // same for pkey_mprotect(,,PROT_EXEC), where available 270 // same for pkey_mprotect(,,PROT_EXEC), where available
@@ -273,7 +273,7 @@ void memory_deny_write_execute(const char *fname) {
273 EXAMINE_ARGUMENT(2), 273 EXAMINE_ARGUMENT(2),
274 BPF_STMT(BPF_ALU+BPF_AND+BPF_K, PROT_EXEC), 274 BPF_STMT(BPF_ALU+BPF_AND+BPF_K, PROT_EXEC),
275 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, PROT_EXEC, 0, 1), 275 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, PROT_EXEC, 0, 1),
276 KILL_PROCESS, 276 KILL_OR_RETURN_ERRNO,
277 RETURN_ALLOW, 277 RETURN_ALLOW,
278#endif 278#endif
279 279
@@ -284,7 +284,7 @@ void memory_deny_write_execute(const char *fname) {
284 EXAMINE_ARGUMENT(2), 284 EXAMINE_ARGUMENT(2),
285 BPF_STMT(BPF_ALU+BPF_AND+BPF_K, SHM_EXEC), 285 BPF_STMT(BPF_ALU+BPF_AND+BPF_K, SHM_EXEC),
286 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SHM_EXEC, 0, 1), 286 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SHM_EXEC, 0, 1),
287 KILL_PROCESS, 287 KILL_OR_RETURN_ERRNO,
288 RETURN_ALLOW, 288 RETURN_ALLOW,
289#endif 289#endif
290#ifdef SYS_memfd_create 290#ifdef SYS_memfd_create
@@ -292,7 +292,7 @@ void memory_deny_write_execute(const char *fname) {
292 // arbitrary memory contents which can be later mapped 292 // arbitrary memory contents which can be later mapped
293 // as executable 293 // as executable
294 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SYS_memfd_create, 0, 1), 294 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SYS_memfd_create, 0, 1),
295 KILL_PROCESS, 295 KILL_OR_RETURN_ERRNO,
296 RETURN_ALLOW 296 RETURN_ALLOW
297#endif 297#endif
298 }; 298 };
@@ -327,7 +327,7 @@ void memory_deny_write_execute_32(const char *fname) {
327 EXAMINE_ARGUMENT(2), 327 EXAMINE_ARGUMENT(2),
328 BPF_STMT(BPF_ALU+BPF_AND+BPF_K, PROT_WRITE|PROT_EXEC), 328 BPF_STMT(BPF_ALU+BPF_AND+BPF_K, PROT_WRITE|PROT_EXEC),
329 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, PROT_WRITE|PROT_EXEC, 0, 1), 329 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, PROT_WRITE|PROT_EXEC, 0, 1),
330 KILL_PROCESS, 330 KILL_OR_RETURN_ERRNO,
331 RETURN_ALLOW, 331 RETURN_ALLOW,
332#endif 332#endif
333#ifdef mprotect_32 333#ifdef mprotect_32
@@ -336,7 +336,7 @@ void memory_deny_write_execute_32(const char *fname) {
336 EXAMINE_ARGUMENT(2), 336 EXAMINE_ARGUMENT(2),
337 BPF_STMT(BPF_ALU+BPF_AND+BPF_K, PROT_EXEC), 337 BPF_STMT(BPF_ALU+BPF_AND+BPF_K, PROT_EXEC),
338 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, PROT_EXEC, 0, 1), 338 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, PROT_EXEC, 0, 1),
339 KILL_PROCESS, 339 KILL_OR_RETURN_ERRNO,
340 RETURN_ALLOW, 340 RETURN_ALLOW,
341#endif 341#endif
342#ifdef pkey_mprotect_32 342#ifdef pkey_mprotect_32
@@ -345,7 +345,7 @@ void memory_deny_write_execute_32(const char *fname) {
345 EXAMINE_ARGUMENT(2), 345 EXAMINE_ARGUMENT(2),
346 BPF_STMT(BPF_ALU+BPF_AND+BPF_K, PROT_EXEC), 346 BPF_STMT(BPF_ALU+BPF_AND+BPF_K, PROT_EXEC),
347 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, PROT_EXEC, 0, 1), 347 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, PROT_EXEC, 0, 1),
348 KILL_PROCESS, 348 KILL_OR_RETURN_ERRNO,
349 RETURN_ALLOW, 349 RETURN_ALLOW,
350#endif 350#endif
351 351
@@ -355,7 +355,7 @@ void memory_deny_write_execute_32(const char *fname) {
355 EXAMINE_ARGUMENT(2), 355 EXAMINE_ARGUMENT(2),
356 BPF_STMT(BPF_ALU+BPF_AND+BPF_K, SHM_EXEC), 356 BPF_STMT(BPF_ALU+BPF_AND+BPF_K, SHM_EXEC),
357 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SHM_EXEC, 0, 1), 357 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SHM_EXEC, 0, 1),
358 KILL_PROCESS, 358 KILL_OR_RETURN_ERRNO,
359 RETURN_ALLOW, 359 RETURN_ALLOW,
360#endif 360#endif
361#ifdef memfd_create_32 361#ifdef memfd_create_32
@@ -363,7 +363,7 @@ void memory_deny_write_execute_32(const char *fname) {
363 // arbitrary memory contents which can be later mapped 363 // arbitrary memory contents which can be later mapped
364 // as executable 364 // as executable
365 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, memfd_create_32, 0, 1), 365 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, memfd_create_32, 0, 1),
366 KILL_PROCESS, 366 KILL_OR_RETURN_ERRNO,
367#endif 367#endif
368#endif 368#endif
369 RETURN_ALLOW 369 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
112 } 112 }
113} 113}
114 114
115void filter_add_blacklist_override(int fd, int syscall, int arg, void *ptrarg, bool native) {
116 (void) arg;
117 (void) ptrarg;
118 (void) native;
119
120 if (syscall >= 0) {
121 int saved_error_action = arg_seccomp_error_action;
122 arg_seccomp_error_action = SECCOMP_RET_KILL;
123 write_blacklist(fd, syscall);
124 arg_seccomp_error_action = saved_error_action;
125 }
126}
127
115// handle seccomp list exceptions (seccomp x,y,!z) 128// handle seccomp list exceptions (seccomp x,y,!z)
116void filter_add_blacklist_for_excluded(int fd, int syscall, int arg, void *ptrarg, bool native) { 129void filter_add_blacklist_for_excluded(int fd, int syscall, int arg, void *ptrarg, bool native) {
117 (void) arg; 130 (void) arg;
@@ -142,7 +155,7 @@ void filter_end_blacklist(int fd) {
142 155
143void filter_end_whitelist(int fd) { 156void filter_end_whitelist(int fd) {
144 struct sock_filter filter[] = { 157 struct sock_filter filter[] = {
145 KILL_PROCESS 158 KILL_OR_RETURN_ERRNO
146 }; 159 };
147 write_to_file(fd, filter, sizeof(filter)); 160 write_to_file(fd, filter, sizeof(filter));
148} 161}
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) {
142 // 5: if MSW(arg0) == 0, goto 7 (allow) else continue to 6 (kill) 142 // 5: if MSW(arg0) == 0, goto 7 (allow) else continue to 6 (kill)
143 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, jmp_from_to(5, 7), 0), 143 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, jmp_from_to(5, 7), 0),
144 // 6: 144 // 6:
145 KILL_PROCESS, 145 KILL_OR_RETURN_ERRNO,
146 // 7: 146 // 7:
147 RETURN_ALLOW 147 RETURN_ALLOW
148 }; 148 };
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 {
243#define HANDLE_X32_KILL \ 243#define HANDLE_X32_KILL \
244 BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, X32_SYSCALL_BIT, 1, 0), \ 244 BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, X32_SYSCALL_BIT, 1, 0), \
245 BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, 0, 1, 0), \ 245 BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, 0, 1, 0), \
246 KILL_PROCESS 246 BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL)
247#endif 247#endif
248 248
249#define EXAMINE_SYSCALL BPF_STMT(BPF_LD+BPF_W+BPF_ABS, \ 249#define EXAMINE_SYSCALL BPF_STMT(BPF_LD+BPF_W+BPF_ABS, \
@@ -258,7 +258,7 @@ struct seccomp_data {
258 258
259#define BLACKLIST(syscall_nr) \ 259#define BLACKLIST(syscall_nr) \
260 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, syscall_nr, 0, 1), \ 260 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, syscall_nr, 0, 1), \
261 BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL) 261 KILL_OR_RETURN_ERRNO
262 262
263#define WHITELIST(syscall_nr) \ 263#define WHITELIST(syscall_nr) \
264 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, syscall_nr, 0, 1), \ 264 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, syscall_nr, 0, 1), \
@@ -274,7 +274,8 @@ struct seccomp_data {
274#define RETURN_ERRNO(nr) \ 274#define RETURN_ERRNO(nr) \
275 BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ERRNO | nr) 275 BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ERRNO | nr)
276 276
277#define KILL_PROCESS \ 277extern int arg_seccomp_error_action; // error action: errno or kill
278 BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL) 278#define KILL_OR_RETURN_ERRNO \
279 BPF_STMT(BPF_RET+BPF_K, arg_seccomp_error_action)
279 280
280#endif 281#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;
27 27
28// seccomp_file.c or dummy versions in firejail/main.c and fsec-print/main.c 28// seccomp_file.c or dummy versions in firejail/main.c and fsec-print/main.c
29void filter_add_errno(int fd, int syscall, int arg, void *ptrarg, bool native); 29void filter_add_errno(int fd, int syscall, int arg, void *ptrarg, bool native);
30void filter_add_blacklist_override(int fd, int syscall, int arg, void *ptrarg, bool native);
30 31
31// errno.c 32// errno.c
32void errno_print(void); 33void 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 @@
20#define _GNU_SOURCE 20#define _GNU_SOURCE
21#include "../include/syscall.h" 21#include "../include/syscall.h"
22#include <assert.h> 22#include <assert.h>
23#include <limits.h>
23#include <stdbool.h> 24#include <stdbool.h>
24#include <stdio.h> 25#include <stdio.h>
25#include <string.h> 26#include <string.h>
26#include <sys/syscall.h> 27#include <sys/syscall.h>
27#include "../include/common.h" 28#include "../include/common.h"
29#include "../include/seccomp.h"
30
31#define SYSCALL_ERROR INT_MAX
32#define ERRNO_KILL -2
28 33
29typedef struct { 34typedef struct {
30 const char * const name; 35 const char * const name;
@@ -1430,7 +1435,7 @@ static const SyscallGroupList sysgroups[] = {
1430 } 1435 }
1431}; 1436};
1432 1437
1433// return -1 if error, or syscall number 1438// return SYSCALL_ERROR if error, or syscall number
1434static int syscall_find_name(const char *name) { 1439static int syscall_find_name(const char *name) {
1435 int i; 1440 int i;
1436 int elems = sizeof(syslist) / sizeof(syslist[0]); 1441 int elems = sizeof(syslist) / sizeof(syslist[0]);
@@ -1439,7 +1444,7 @@ static int syscall_find_name(const char *name) {
1439 return syslist[i].nr; 1444 return syslist[i].nr;
1440 } 1445 }
1441 1446
1442 return -1; 1447 return SYSCALL_ERROR;
1443} 1448}
1444 1449
1445static int syscall_find_name_32(const char *name) { 1450static int syscall_find_name_32(const char *name) {
@@ -1450,7 +1455,7 @@ static int syscall_find_name_32(const char *name) {
1450 return syslist32[i].nr; 1455 return syslist32[i].nr;
1451 } 1456 }
1452 1457
1453 return -1; 1458 return SYSCALL_ERROR;
1454} 1459}
1455 1460
1456const char *syscall_find_nr(int nr) { 1461const char *syscall_find_nr(int nr) {
@@ -1538,9 +1543,13 @@ static void syscall_process_name(const char *name, int *syscall_nr, int *error_n
1538 *syscall_nr = syscall_find_name_32(syscall_name); 1543 *syscall_nr = syscall_find_name_32(syscall_name);
1539 } 1544 }
1540 if (error_name) { 1545 if (error_name) {
1541 *error_nr = errno_find_name(error_name); 1546 if (strcmp(error_name, "kill") == 0)
1542 if (*error_nr == -1) 1547 *error_nr = ERRNO_KILL;
1543 *syscall_nr = -1; 1548 else {
1549 *error_nr = errno_find_name(error_name);
1550 if (*error_nr == -1)
1551 *syscall_nr = SYSCALL_ERROR;
1552 }
1544 } 1553 }
1545 1554
1546 free(str); 1555 free(str);
@@ -1589,15 +1598,15 @@ int syscall_check_list(const char *slist, filter_fn *callback, int fd, int arg,
1589 ptr++; 1598 ptr++;
1590 } 1599 }
1591 syscall_process_name(ptr, &syscall_nr, &error_nr, native); 1600 syscall_process_name(ptr, &syscall_nr, &error_nr, native);
1592 if (syscall_nr == -1) {;} 1601 if (syscall_nr != SYSCALL_ERROR && callback != NULL) {
1593 else if (callback != NULL) {
1594 if (negate) { 1602 if (negate) {
1595 syscall_nr = -syscall_nr; 1603 syscall_nr = -syscall_nr;
1596 } 1604 }
1597 if (error_nr != -1 && fd > 0) { 1605 if (error_nr >= 0 && fd > 0)
1598 filter_add_errno(fd, syscall_nr, error_nr, ptrarg, native); 1606 filter_add_errno(fd, syscall_nr, error_nr, ptrarg, native);
1599 } 1607 else if (error_nr == ERRNO_KILL && fd > 0)
1600 else if (error_nr != -1 && fd == 0) { 1608 filter_add_blacklist_override(fd, syscall_nr, 0, ptrarg, native);
1609 else if (error_nr >= 0 && fd == 0) {
1601 callback(fd, syscall_nr, error_nr, ptrarg, native); 1610 callback(fd, syscall_nr, error_nr, ptrarg, native);
1602 } 1611 }
1603 else { 1612 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.
411\fBseccomp.32.keep syscall,syscall,syscall 411\fBseccomp.32.keep syscall,syscall,syscall
412Enable seccomp filter and whitelist the system calls in the list for 32 bit system calls on a 64 bit architecture system. 412Enable seccomp filter and whitelist the system calls in the list for 32 bit system calls on a 64 bit architecture system.
413.TP 413.TP
414\fBseccomp-error-action kill | ERRNO
415Return a different error instead of EPERM to the process or kill it when an attempt is made to call a blocked system call.
416.TP
414\fBx11 417\fBx11
415Enable X11 sandboxing. 418Enable X11 sandboxing.
416.TP 419.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
814Install a seccomp filter to block attempts to create memory mappings 814Install a seccomp filter to block attempts to create memory mappings
815that are both writable and executable, to change mappings to be 815that are both writable and executable, to change mappings to be
816executable, or to create executable shared memory. The filter examines 816executable, or to create executable shared memory. The filter examines
817the arguments of mmap, mmap2, mprotect, pkey_mprotect, memfd_create and 817the arguments of mmap, mmap2, mprotect, pkey_mprotect, memfd_create
818shmat system calls and kills the process if necessary. 818and shmat system calls and returns error EPERM to the process (or
819kills it, see \-\-seccomp-error-action below) if necessary.
819.br 820.br
820 821
821.br 822.br
@@ -1865,8 +1866,12 @@ $ firejail \-\-seccomp=@clock,mkdir,unlinkat transmission-gtk
1865.br 1866.br
1866 1867
1867.br 1868.br
1868Instead of dropping the syscall, a specific error number can be returned 1869Instead of dropping the syscall by returning EPERM, another error
1869using \fBsyscall:errorno\fR syntax. 1870number can be returned using \fBsyscall:errno\fR syntax. This can be
1871also changed globally with \-\-seccomp-error-action or
1872in /etc/firejail/firejail.config file. The process can also be killed
1873by using \fBsyscall:kill\fR syntax.
1874
1870.br 1875.br
1871 1876
1872.br 1877.br
@@ -1932,8 +1937,11 @@ $ firejail \-\-seccomp.drop=utime,utimensat,utimes,@clock
1932.br 1937.br
1933 1938
1934.br 1939.br
1935Instead of dropping the syscall, a specific error number can be returned 1940Instead of dropping the syscall by returning EPERM, another error
1936using \fBsyscall:errorno\fR syntax. 1941number can be returned using \fBsyscall:errno\fR syntax. This can be
1942also changed globally with \-\-seccomp-error-action or
1943in /etc/firejail/firejail.config file. The process can also be killed
1944by using \fBsyscall:kill\fR syntax.
1937.br 1945.br
1938 1946
1939.br 1947.br
@@ -2135,6 +2143,19 @@ $ firejail --seccomp.print=browser
2135 0049: 06 00 01 00000000 ret KILL 2143 0049: 06 00 01 00000000 ret KILL
2136.br 2144.br
2137$ 2145$
2146
2147.TP
2148\fB\-\-seccomp-error-action= kill | ERRNO
2149By default, if a seccomp filter blocks a system call, the process gets
2150EPERM as the error. With \-\-seccomp-error-action=error, another error
2151number can be returned, for example ENOSYS or EACCES. The process can
2152also be killed (like in versions <0.9.63 of Firejail) by using
2153\-\-seccomp-error-action=kill syntax. Not killing the process weakens
2154Firejail slightly when trying to contain intrusion, but it may also
2155allow tighter filters if the only alternative is to allow a system
2156call.
2157.br
2158
2138.TP 2159.TP
2139\fB\-\-shell=none 2160\fB\-\-shell=none
2140Run the program directly, without a user shell. 2161Run the program directly, without a user shell.