aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar smitsohu <smitsohu@gmail.com>2022-07-31 20:06:37 +0200
committerLibravatar GitHub <noreply@github.com>2022-07-31 20:06:37 +0200
commit06d3fd05814da2bbf1f9f30a722092a562cf16b2 (patch)
treefe19af2ec07ac3434bf93890bd719abafd3cd5c7
parentMerge pull request #5271 from smitsohu/nnp (diff)
parentintroduce new option restrict-namespaces (diff)
downloadfirejail-06d3fd05814da2bbf1f9f30a722092a562cf16b2.tar.gz
firejail-06d3fd05814da2bbf1f9f30a722092a562cf16b2.tar.zst
firejail-06d3fd05814da2bbf1f9f30a722092a562cf16b2.zip
Merge pull request #5259 from smitsohu/ns
introduce new option restrict-namespaces
-rw-r--r--contrib/vim/syntax/firejail.vim6
-rw-r--r--etc/profile-a-l/default.profile1
-rw-r--r--etc/profile-m-z/server.profile1
-rw-r--r--etc/templates/profile.template1
-rw-r--r--src/firejail/firejail.h2
-rw-r--r--src/firejail/main.c14
-rw-r--r--src/firejail/preproc.c10
-rw-r--r--src/firejail/profile.c27
-rw-r--r--src/firejail/sandbox.c10
-rw-r--r--src/firejail/seccomp.c31
-rw-r--r--src/firejail/usage.c3
-rw-r--r--src/fseccomp/fseccomp.h4
-rw-r--r--src/fseccomp/main.c6
-rw-r--r--src/fseccomp/namespaces.c197
-rw-r--r--src/fseccomp/protocol.c9
-rw-r--r--src/include/rundefs.h2
-rw-r--r--src/man/firejail-profile.txt6
-rw-r--r--src/man/firejail.txt24
-rw-r--r--src/zsh_completion/_firejail.in4
19 files changed, 350 insertions, 8 deletions
diff --git a/contrib/vim/syntax/firejail.vim b/contrib/vim/syntax/firejail.vim
index 51e9cfdad..9099a0808 100644
--- a/contrib/vim/syntax/firejail.vim
+++ b/contrib/vim/syntax/firejail.vim
@@ -17,6 +17,9 @@ syn match fjComment "#.*$" contains=fjTodo
17syn keyword fjCapability audit_control audit_read audit_write block_suspend chown dac_override dac_read_search fowner fsetid ipc_lock ipc_owner kill lease linux_immutable mac_admin mac_override mknod net_admin net_bind_service net_broadcast net_raw setgid setfcap setpcap setuid sys_admin sys_boot sys_chroot sys_module sys_nice sys_pacct sys_ptrace sys_rawio sys_resource sys_time sys_tty_config syslog wake_alarm nextgroup=fjCapabilityList contained 17syn keyword fjCapability audit_control audit_read audit_write block_suspend chown dac_override dac_read_search fowner fsetid ipc_lock ipc_owner kill lease linux_immutable mac_admin mac_override mknod net_admin net_bind_service net_broadcast net_raw setgid setfcap setpcap setuid sys_admin sys_boot sys_chroot sys_module sys_nice sys_pacct sys_ptrace sys_rawio sys_resource sys_time sys_tty_config syslog wake_alarm nextgroup=fjCapabilityList contained
18syn match fjCapabilityList /,/ nextgroup=fjCapability contained 18syn match fjCapabilityList /,/ nextgroup=fjCapability contained
19 19
20syn keyword fjNamespaces cgroup ipc net mnt pid time user uts nextgroup=fjNamespacesList contained
21syn match fjNamespacesList /,/ nextgroup=fjNamespaces contained
22
20syn keyword fjProtocol unix inet inet6 netlink packet nextgroup=fjProtocolList contained 23syn keyword fjProtocol unix inet inet6 netlink packet nextgroup=fjProtocolList contained
21syn match fjProtocolList /,/ nextgroup=fjProtocol contained 24syn match fjProtocolList /,/ nextgroup=fjProtocol contained
22 25
@@ -48,7 +51,7 @@ syn keyword fjFilter filter contained
48syn match fjVar /\v\$\{(CFG|DESKTOP|DOCUMENTS|DOWNLOADS|HOME|MUSIC|PATH|PICTURES|RUNUSER|VIDEOS)}/ 51syn match fjVar /\v\$\{(CFG|DESKTOP|DOCUMENTS|DOWNLOADS|HOME|MUSIC|PATH|PICTURES|RUNUSER|VIDEOS)}/
49 52
50" Commands grabbed from: src/firejail/profile.c 53" Commands grabbed from: src/firejail/profile.c
51" Generate list with: { rg -o 'strn?cmp\(ptr, "([^"]+) "' -r '$1' src/firejail/profile.c; echo private-lib; } | grep -vEx '(include|ignore|caps\.drop|caps\.keep|protocol|seccomp|seccomp\.drop|seccomp\.keep|env|rmenv|net|ip)' | sort -u | tr $'\n' '|' # private-lib is special-cased in the code and doesn't match the regex; grep-ed patterns are handled later with 'syn match nextgroup=' directives (except for include which is special-cased as a fjCommandNoCond keyword) 54" Generate list with: { rg -o 'strn?cmp\(ptr, "([^"]+) "' -r '$1' src/firejail/profile.c; echo private-lib; } | grep -vEx '(include|ignore|caps\.drop|caps\.keep|protocol|restrict-namespaces|seccomp|seccomp\.drop|seccomp\.keep|env|rmenv|net|ip)' | sort -u | tr $'\n' '|' # private-lib is special-cased in the code and doesn't match the regex; grep-ed patterns are handled later with 'syn match nextgroup=' directives (except for include which is special-cased as a fjCommandNoCond keyword)
52syn match fjCommand /\v(bind|blacklist|blacklist-nolog|cpu|defaultgw|dns|hostname|hosts-file|ip6|iprange|join-or-start|mac|mkdir|mkfile|mtu|name|netfilter|netfilter6|netmask|nice|noblacklist|noexec|nowhitelist|overlay-named|private|private-bin|private-cwd|private-etc|private-home|private-lib|private-opt|private-srv|read-only|read-write|rlimit-as|rlimit-cpu|rlimit-fsize|rlimit-nofile|rlimit-nproc|rlimit-sigpending|timeout|tmpfs|veth-name|whitelist|xephyr-screen) / skipwhite contained 55syn match fjCommand /\v(bind|blacklist|blacklist-nolog|cpu|defaultgw|dns|hostname|hosts-file|ip6|iprange|join-or-start|mac|mkdir|mkfile|mtu|name|netfilter|netfilter6|netmask|nice|noblacklist|noexec|nowhitelist|overlay-named|private|private-bin|private-cwd|private-etc|private-home|private-lib|private-opt|private-srv|read-only|read-write|rlimit-as|rlimit-cpu|rlimit-fsize|rlimit-nofile|rlimit-nproc|rlimit-sigpending|timeout|tmpfs|veth-name|whitelist|xephyr-screen) / skipwhite contained
53" Generate list with: rg -o 'strn?cmp\(ptr, "([^ "]*[^ ])"' -r '$1' src/firejail/profile.c | grep -vEx '(include|rlimit|quiet)' | sed -e 's/\./\\./' | sort -u | tr $'\n' '|' # include/rlimit are false positives, quiet is special-cased below 56" Generate list with: rg -o 'strn?cmp\(ptr, "([^ "]*[^ ])"' -r '$1' src/firejail/profile.c | grep -vEx '(include|rlimit|quiet)' | sed -e 's/\./\\./' | sort -u | tr $'\n' '|' # include/rlimit are false positives, quiet is special-cased below
54syn match fjCommand /\v(allow-debuggers|allusers|apparmor|caps|deterministic-exit-code|deterministic-shutdown|disable-mnt|ipc-namespace|keep-config-pulse|keep-dev-shm|keep-fd|keep-var-tmp|machine-id|memory-deny-write-execute|netfilter|no3d|noautopulse|nodbus|nodvd|nogroups|noinput|nonewprivs|noprinters|noroot|nosound|notv|nou2f|novideo|overlay|overlay-tmpfs|private|private-cache|private-cwd|private-dev|private-lib|private-tmp|seccomp|seccomp\.32|seccomp\.block-secondary|tracelog|writable-etc|writable-run-user|writable-var|writable-var-log|x11)$/ contained 57syn match fjCommand /\v(allow-debuggers|allusers|apparmor|caps|deterministic-exit-code|deterministic-shutdown|disable-mnt|ipc-namespace|keep-config-pulse|keep-dev-shm|keep-fd|keep-var-tmp|machine-id|memory-deny-write-execute|netfilter|no3d|noautopulse|nodbus|nodvd|nogroups|noinput|nonewprivs|noprinters|noroot|nosound|notv|nou2f|novideo|overlay|overlay-tmpfs|private|private-cache|private-cwd|private-dev|private-lib|private-tmp|seccomp|seccomp\.32|seccomp\.block-secondary|tracelog|writable-etc|writable-run-user|writable-var|writable-var-log|x11)$/ contained
@@ -56,6 +59,7 @@ syn match fjCommand /ignore / nextgroup=fjCommand,fjCommandNoCond skipwhite cont
56syn match fjCommand /caps\.drop / nextgroup=fjCapability,fjAll skipwhite contained 59syn match fjCommand /caps\.drop / nextgroup=fjCapability,fjAll skipwhite contained
57syn match fjCommand /caps\.keep / nextgroup=fjCapability skipwhite contained 60syn match fjCommand /caps\.keep / nextgroup=fjCapability skipwhite contained
58syn match fjCommand /protocol / nextgroup=fjProtocol skipwhite contained 61syn match fjCommand /protocol / nextgroup=fjProtocol skipwhite contained
62syn match fjCommand /restrict-namespaces / nextgroup=fjNamespaces skipwhite contained
59syn match fjCommand /\vseccomp(\.32)?(\.drop|\.keep)? / nextgroup=fjSyscall skipwhite contained 63syn match fjCommand /\vseccomp(\.32)?(\.drop|\.keep)? / nextgroup=fjSyscall skipwhite contained
60syn match fjCommand /x11 / nextgroup=fjX11Sandbox skipwhite contained 64syn match fjCommand /x11 / nextgroup=fjX11Sandbox skipwhite contained
61syn match fjCommand /env / nextgroup=fjEnvVar skipwhite contained 65syn match fjCommand /env / nextgroup=fjEnvVar skipwhite contained
diff --git a/etc/profile-a-l/default.profile b/etc/profile-a-l/default.profile
index dac842bb6..397a89bee 100644
--- a/etc/profile-a-l/default.profile
+++ b/etc/profile-a-l/default.profile
@@ -60,3 +60,4 @@ seccomp
60# deterministic-shutdown 60# deterministic-shutdown
61# memory-deny-write-execute 61# memory-deny-write-execute
62# read-only ${HOME} 62# read-only ${HOME}
63# restrict-namespaces
diff --git a/etc/profile-m-z/server.profile b/etc/profile-m-z/server.profile
index fd7ffb38d..8d8a1dac6 100644
--- a/etc/profile-m-z/server.profile
+++ b/etc/profile-m-z/server.profile
@@ -90,6 +90,7 @@ dbus-user none
90# deterministic-shutdown 90# deterministic-shutdown
91# memory-deny-write-execute 91# memory-deny-write-execute
92# read-only ${HOME} 92# read-only ${HOME}
93# restrict-namespaces
93# writable-run-user 94# writable-run-user
94# writable-var 95# writable-var
95# writable-var-log 96# writable-var-log
diff --git a/etc/templates/profile.template b/etc/templates/profile.template
index 28339765f..59083f660 100644
--- a/etc/templates/profile.template
+++ b/etc/templates/profile.template
@@ -228,3 +228,4 @@ include globals.local
228##noexec PATH 228##noexec PATH
229##read-only ${HOME} 229##read-only ${HOME}
230##read-write ${HOME} 230##read-write ${HOME}
231#restrict-namespaces
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h
index 96e3f735e..167b6a843 100644
--- a/src/firejail/firejail.h
+++ b/src/firejail/firejail.h
@@ -198,6 +198,7 @@ typedef struct config_t {
198 char *seccomp_list_drop, *seccomp_list_drop32; // seccomp drop list 198 char *seccomp_list_drop, *seccomp_list_drop32; // seccomp drop list
199 char *seccomp_list_keep, *seccomp_list_keep32; // seccomp keep list 199 char *seccomp_list_keep, *seccomp_list_keep32; // seccomp keep list
200 char *protocol; // protocol list 200 char *protocol; // protocol list
201 char *restrict_namespaces; // namespaces list
201 char *seccomp_error_action; // error action: kill, log or errno 202 char *seccomp_error_action; // error action: kill, log or errno
202 203
203 // rlimits 204 // rlimits
@@ -632,6 +633,7 @@ int seccomp_load(const char *fname);
632int seccomp_filter_drop(bool native); 633int seccomp_filter_drop(bool native);
633int seccomp_filter_keep(bool native); 634int seccomp_filter_keep(bool native);
634int seccomp_filter_mdwx(bool native); 635int seccomp_filter_mdwx(bool native);
636int seccomp_filter_namespaces(bool native, const char *list);
635void seccomp_print_filter(pid_t pid) __attribute__((noreturn)); 637void seccomp_print_filter(pid_t pid) __attribute__((noreturn));
636 638
637// caps.c 639// caps.c
diff --git a/src/firejail/main.c b/src/firejail/main.c
index e6c5b50b0..55f623138 100644
--- a/src/firejail/main.c
+++ b/src/firejail/main.c
@@ -1400,6 +1400,20 @@ int main(int argc, char **argv, char **envp) {
1400 else 1400 else
1401 exit_err_feature("seccomp"); 1401 exit_err_feature("seccomp");
1402 } 1402 }
1403 else if (strcmp(argv[i], "--restrict-namespaces") == 0) {
1404 if (checkcfg(CFG_SECCOMP))
1405 profile_list_augment(&cfg.restrict_namespaces, "cgroup,ipc,net,mnt,pid,time,user,uts");
1406 else
1407 exit_err_feature("seccomp");
1408 }
1409 else if (strncmp(argv[i], "--restrict-namespaces=", 22) == 0) {
1410 if (checkcfg(CFG_SECCOMP)) {
1411 const char *add = argv[i] + 22;
1412 profile_list_augment(&cfg.restrict_namespaces, add);
1413 }
1414 else
1415 exit_err_feature("seccomp");
1416 }
1403 else if (strncmp(argv[i], "--seccomp-error-action=", 23) == 0) { 1417 else if (strncmp(argv[i], "--seccomp-error-action=", 23) == 0) {
1404 if (checkcfg(CFG_SECCOMP)) { 1418 if (checkcfg(CFG_SECCOMP)) {
1405 int config_seccomp_error_action = checkcfg(CFG_SECCOMP_ERROR_ACTION); 1419 int config_seccomp_error_action = checkcfg(CFG_SECCOMP_ERROR_ACTION);
diff --git a/src/firejail/preproc.c b/src/firejail/preproc.c
index b25b79a9e..44f82681a 100644
--- a/src/firejail/preproc.c
+++ b/src/firejail/preproc.c
@@ -91,10 +91,18 @@ void preproc_mount_mnt_dir(void) {
91 copy_file(PATH_SECCOMP_MDWX, RUN_SECCOMP_MDWX, getuid(), getgid(), 0644); // root needed 91 copy_file(PATH_SECCOMP_MDWX, RUN_SECCOMP_MDWX, getuid(), getgid(), 0644); // root needed
92 copy_file(PATH_SECCOMP_MDWX_32, RUN_SECCOMP_MDWX_32, getuid(), getgid(), 0644); // root needed 92 copy_file(PATH_SECCOMP_MDWX_32, RUN_SECCOMP_MDWX_32, getuid(), getgid(), 0644); // root needed
93 } 93 }
94 // as root, create empty RUN_SECCOMP_PROTOCOL and RUN_SECCOMP_POSTEXEC files 94 // as root, create empty RUN_SECCOMP_PROTOCOL, RUN_SECCOMP_NS and RUN_SECCOMP_POSTEXEC files
95 create_empty_file_as_root(RUN_SECCOMP_PROTOCOL, 0644); 95 create_empty_file_as_root(RUN_SECCOMP_PROTOCOL, 0644);
96 if (set_perms(RUN_SECCOMP_PROTOCOL, getuid(), getgid(), 0644)) 96 if (set_perms(RUN_SECCOMP_PROTOCOL, getuid(), getgid(), 0644))
97 errExit("set_perms"); 97 errExit("set_perms");
98 if (cfg.restrict_namespaces) {
99 create_empty_file_as_root(RUN_SECCOMP_NS, 0644);
100 if (set_perms(RUN_SECCOMP_NS, getuid(), getgid(), 0644))
101 errExit("set_perms");
102 create_empty_file_as_root(RUN_SECCOMP_NS_32, 0644);
103 if (set_perms(RUN_SECCOMP_NS_32, getuid(), getgid(), 0644))
104 errExit("set_perms");
105 }
98 create_empty_file_as_root(RUN_SECCOMP_POSTEXEC, 0644); 106 create_empty_file_as_root(RUN_SECCOMP_POSTEXEC, 0644);
99 if (set_perms(RUN_SECCOMP_POSTEXEC, getuid(), getgid(), 0644)) 107 if (set_perms(RUN_SECCOMP_POSTEXEC, getuid(), getgid(), 0644))
100 errExit("set_perms"); 108 errExit("set_perms");
diff --git a/src/firejail/profile.c b/src/firejail/profile.c
index 1a83a0628..dc1aff49a 100644
--- a/src/firejail/profile.c
+++ b/src/firejail/profile.c
@@ -26,7 +26,8 @@
26 26
27extern char *xephyr_screen; 27extern char *xephyr_screen;
28 28
29#define MAX_READ 8192 // line buffer for profile files 29#define MAX_READ 8192 // line buffer for profile files
30#define MAX_LIST 16384 // size limit for argument lists
30 31
31// find and read the profile specified by name from dir directory 32// find and read the profile specified by name from dir directory
32// return 1 if a profile was found 33// return 1 if a profile was found
@@ -1042,6 +1043,24 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
1042 return 0; 1043 return 0;
1043 } 1044 }
1044 1045
1046 // restrict-namespaces
1047 if (strcmp(ptr, "restrict-namespaces") == 0) {
1048 if (checkcfg(CFG_SECCOMP))
1049 profile_list_augment(&cfg.restrict_namespaces, "cgroup,ipc,net,mnt,pid,time,user,uts");
1050 else
1051 warning_feature_disabled("seccomp");
1052 return 0;
1053 }
1054 if (strncmp(ptr, "restrict-namespaces ", 20) == 0) {
1055 if (checkcfg(CFG_SECCOMP)) {
1056 const char *add = ptr + 20;
1057 profile_list_augment(&cfg.restrict_namespaces, add);
1058 }
1059 else
1060 warning_feature_disabled("seccomp");
1061 return 0;
1062 }
1063
1045 // seccomp error action 1064 // seccomp error action
1046 if (strncmp(ptr, "seccomp-error-action ", 21) == 0) { 1065 if (strncmp(ptr, "seccomp-error-action ", 21) == 0) {
1047 if (checkcfg(CFG_SECCOMP)) { 1066 if (checkcfg(CFG_SECCOMP)) {
@@ -1959,4 +1978,10 @@ void profile_list_augment(char **list, const char *items)
1959 errExit("asprintf"); 1978 errExit("asprintf");
1960 free(*list); 1979 free(*list);
1961 *list = profile_list_compress(tmp); 1980 *list = profile_list_compress(tmp);
1981
1982 // lists should not grow indefinitely
1983 if (strlen(*list) > MAX_LIST) {
1984 fprintf(stderr, "Error: argument list is too long\n");
1985 exit(1);
1986 }
1962} 1987}
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c
index 864236824..b1b3407b4 100644
--- a/src/firejail/sandbox.c
+++ b/src/firejail/sandbox.c
@@ -1205,6 +1205,16 @@ int sandbox(void* sandbox_arg) {
1205 seccomp_load(RUN_SECCOMP_MDWX_32); 1205 seccomp_load(RUN_SECCOMP_MDWX_32);
1206 } 1206 }
1207 1207
1208 if (cfg.restrict_namespaces) {
1209 seccomp_filter_namespaces(true, cfg.restrict_namespaces);
1210 seccomp_filter_namespaces(false, cfg.restrict_namespaces);
1211
1212 if (arg_debug)
1213 printf("Install namespaces filter\n");
1214 seccomp_load(RUN_SECCOMP_NS); // install filter
1215 seccomp_load(RUN_SECCOMP_NS_32);
1216 }
1217
1208 // make seccomp filters read-only 1218 // make seccomp filters read-only
1209 fs_remount(RUN_SECCOMP_DIR, MOUNT_READONLY, 0); 1219 fs_remount(RUN_SECCOMP_DIR, MOUNT_READONLY, 0);
1210 seccomp_debug(); 1220 seccomp_debug();
diff --git a/src/firejail/seccomp.c b/src/firejail/seccomp.c
index b8b4ec0d6..84748da77 100644
--- a/src/firejail/seccomp.c
+++ b/src/firejail/seccomp.c
@@ -416,7 +416,7 @@ int seccomp_filter_mdwx(bool native) {
416 416
417 // build the seccomp filter as a regular user 417 // build the seccomp filter as a regular user
418 int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 3, 418 int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 3,
419 PATH_FSECCOMP, command, filter); 419 PATH_FSECCOMP, command, filter);
420 420
421 if (rv) { 421 if (rv) {
422 fprintf(stderr, "Error: cannot build memory-deny-write-execute filter\n"); 422 fprintf(stderr, "Error: cannot build memory-deny-write-execute filter\n");
@@ -429,6 +429,35 @@ int seccomp_filter_mdwx(bool native) {
429 return 0; 429 return 0;
430} 430}
431 431
432// create namespaces filter
433int seccomp_filter_namespaces(bool native, const char *list) {
434 if (arg_debug)
435 printf("Build restrict-namespaces filter\n");
436
437 const char *command, *filter;
438 if (native) {
439 command = "restrict-namespaces";
440 filter = RUN_SECCOMP_NS;
441 } else {
442 command = "restrict-namespaces.32";
443 filter = RUN_SECCOMP_NS_32;
444 }
445
446 // build the seccomp filter as a regular user
447 int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 4,
448 PATH_FSECCOMP, command, filter, list);
449
450 if (rv) {
451 fprintf(stderr, "Error: cannot build restrict-namespaces filter\n");
452 exit(rv);
453 }
454
455 if (arg_debug)
456 printf("restrict-namespaces filter configured\n");
457
458 return 0;
459}
460
432void seccomp_print_filter(pid_t pid) { 461void seccomp_print_filter(pid_t pid) {
433 EUID_ASSERT(); 462 EUID_ASSERT();
434 463
diff --git a/src/firejail/usage.c b/src/firejail/usage.c
index 14cd1f3a4..c3c17393c 100644
--- a/src/firejail/usage.c
+++ b/src/firejail/usage.c
@@ -214,6 +214,9 @@ static char *usage_str =
214 " --quiet - turn off Firejail's output.\n" 214 " --quiet - turn off Firejail's output.\n"
215 " --read-only=filename - set directory or file read-only.\n" 215 " --read-only=filename - set directory or file read-only.\n"
216 " --read-write=filename - set directory or file read-write.\n" 216 " --read-write=filename - set directory or file read-write.\n"
217 " --restrict-namespaces - seccomp filter that blocks attempts to create new namespaces.\n"
218 " --restrict-namespaces=namespace,namespace - seccomp filter that blocks attempts\n"
219 "\tto create specified namespaces.\n"
217 " --rlimit-as=number - set the maximum size of the process's virtual memory.\n" 220 " --rlimit-as=number - set the maximum size of the process's virtual memory.\n"
218 "\t(address space) in bytes.\n" 221 "\t(address space) in bytes.\n"
219 " --rlimit-cpu=number - set the maximum CPU time in seconds.\n" 222 " --rlimit-cpu=number - set the maximum CPU time in seconds.\n"
diff --git a/src/fseccomp/fseccomp.h b/src/fseccomp/fseccomp.h
index 65337da2a..5911b5156 100644
--- a/src/fseccomp/fseccomp.h
+++ b/src/fseccomp/fseccomp.h
@@ -61,6 +61,10 @@ void seccomp_keep(const char *fname1, const char *fname2, char *list, bool nativ
61void memory_deny_write_execute(const char *fname); 61void memory_deny_write_execute(const char *fname);
62void memory_deny_write_execute_32(const char *fname); 62void memory_deny_write_execute_32(const char *fname);
63 63
64// namespaces.c
65void deny_ns(const char *fname, const char *list);
66void deny_ns_32(const char *fname, const char *list);
67
64// seccomp_print 68// seccomp_print
65void filter_print(const char *fname); 69void filter_print(const char *fname);
66 70
diff --git a/src/fseccomp/main.c b/src/fseccomp/main.c
index 48665ab71..01d7dd8cf 100644
--- a/src/fseccomp/main.c
+++ b/src/fseccomp/main.c
@@ -48,6 +48,8 @@ static void usage(void) {
48 printf("\tfseccomp keep32 file1 file2 list\n"); 48 printf("\tfseccomp keep32 file1 file2 list\n");
49 printf("\tfseccomp memory-deny-write-execute file\n"); 49 printf("\tfseccomp memory-deny-write-execute file\n");
50 printf("\tfseccomp memory-deny-write-execute.32 file\n"); 50 printf("\tfseccomp memory-deny-write-execute.32 file\n");
51 printf("\tfseccomp restrict-namespaces file list\n");
52 printf("\tfseccomp restrict-namespaces.32 file list\n");
51} 53}
52 54
53int main(int argc, char **argv) { 55int main(int argc, char **argv) {
@@ -135,6 +137,10 @@ printf("\n");
135 memory_deny_write_execute(argv[2]); 137 memory_deny_write_execute(argv[2]);
136 else if (argc == 3 && strcmp(argv[1], "memory-deny-write-execute.32") == 0) 138 else if (argc == 3 && strcmp(argv[1], "memory-deny-write-execute.32") == 0)
137 memory_deny_write_execute_32(argv[2]); 139 memory_deny_write_execute_32(argv[2]);
140 else if (argc == 4 && strcmp(argv[1], "restrict-namespaces") == 0)
141 deny_ns(argv[2], argv[3]);
142 else if (argc == 4 && strcmp(argv[1], "restrict-namespaces.32") == 0)
143 deny_ns_32(argv[2], argv[3]);
138 else { 144 else {
139 fprintf(stderr, "Error fseccomp: invalid arguments\n"); 145 fprintf(stderr, "Error fseccomp: invalid arguments\n");
140 return 1; 146 return 1;
diff --git a/src/fseccomp/namespaces.c b/src/fseccomp/namespaces.c
new file mode 100644
index 000000000..3df23dcff
--- /dev/null
+++ b/src/fseccomp/namespaces.c
@@ -0,0 +1,197 @@
1/*
2 * Copyright (C) 2014-2022 Firejail Authors
3 *
4 * This file is part of firejail project
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20#define _GNU_SOURCE
21#include "fseccomp.h"
22#include "../include/seccomp.h"
23#include <sys/syscall.h>
24
25#include <sched.h>
26#ifndef CLONE_NEWCGROUP
27#define CLONE_NEWCGROUP 0x02000000
28#endif
29#ifndef CLONE_NEWTIME
30#define CLONE_NEWTIME 0x00000080
31#endif
32
33// 64-bit architectures
34#if INTPTR_MAX == INT64_MAX
35#if defined __x86_64__
36// i386 syscalls
37#define clone_32 120
38#define clone3_32 435
39#define unshare_32 310
40#define setns_32 346
41#else
42#warning 32 bit namespaces filter not implemented yet for your architecture
43#endif
44#endif
45
46
47static int build_ns_mask(const char *list) {
48 int mask = 0;
49
50 char *dup = strdup(list);
51 if (!dup)
52 errExit("strdup");
53
54 char *token = strtok(dup, ",");
55 while (token) {
56 if (strcmp(token, "cgroup") == 0)
57 mask |= CLONE_NEWCGROUP;
58 else if (strcmp(token, "ipc") == 0)
59 mask |= CLONE_NEWIPC;
60 else if (strcmp(token, "net") == 0)
61 mask |= CLONE_NEWNET;
62 else if (strcmp(token, "mnt") == 0)
63 mask |= CLONE_NEWNS;
64 else if (strcmp(token, "pid") == 0)
65 mask |= CLONE_NEWPID;
66 else if (strcmp(token, "time") == 0)
67 mask |= CLONE_NEWTIME;
68 else if (strcmp(token, "user") == 0)
69 mask |= CLONE_NEWUSER;
70 else if (strcmp(token, "uts") == 0)
71 mask |= CLONE_NEWUTS;
72 else {
73 fprintf(stderr, "Error fseccomp: %s is not a valid namespace\n", token);
74 exit(1);
75 }
76
77 token = strtok(NULL, ",");
78 }
79
80 free(dup);
81 return mask;
82}
83
84void deny_ns(const char *fname, const char *list) {
85 int mask = build_ns_mask(list);
86 // CLONE_NEWTIME means something different for clone
87 // create a second mask without it
88 int clone_mask = mask & ~CLONE_NEWTIME;
89
90 // open file
91 int fd = open(fname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
92 if (fd < 0) {
93 fprintf(stderr, "Error fseccomp: cannot open %s file\n", fname);
94 exit(1);
95 }
96
97 filter_init(fd, true);
98
99 // build filter
100 struct sock_filter filter[] = {
101#ifdef SYS_clone
102 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SYS_clone, 0, 4),
103 // s390 has first and second argument flipped
104#if defined __s390__
105 EXAMINE_ARGUMENT(1),
106#else
107 EXAMINE_ARGUMENT(0),
108#endif
109 BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, clone_mask, 0, 1),
110 KILL_OR_RETURN_ERRNO,
111 RETURN_ALLOW,
112#endif
113#ifdef SYS_clone3
114 // cannot inspect clone3 argument because
115 // seccomp does not dereference pointers
116 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SYS_clone3, 0, 1),
117 RETURN_ERRNO(ENOSYS), // hint to use clone instead
118#endif
119#ifdef SYS_unshare
120 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SYS_unshare, 0, 4),
121 EXAMINE_ARGUMENT(0),
122 BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, mask, 0, 1),
123 KILL_OR_RETURN_ERRNO,
124 RETURN_ALLOW,
125#endif
126#ifdef SYS_setns
127 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SYS_setns, 0, 4),
128 EXAMINE_ARGUMENT(1),
129 // always fail if argument is zero
130 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0, 1, 0),
131 BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, mask, 0, 1),
132 KILL_OR_RETURN_ERRNO,
133 RETURN_ALLOW
134#endif
135 };
136 write_to_file(fd, filter, sizeof(filter));
137
138 filter_end_blacklist(fd);
139
140 // close file
141 close(fd);
142}
143
144void deny_ns_32(const char *fname, const char *list) {
145 int mask = build_ns_mask(list);
146 // CLONE_NEWTIME means something different for clone
147 // create a second mask without it
148 int clone_mask = mask & ~CLONE_NEWTIME;
149
150 // open file
151 int fd = open(fname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
152 if (fd < 0) {
153 fprintf(stderr, "Error fseccomp: cannot open %s file\n", fname);
154 exit(1);
155 }
156
157 filter_init(fd, false);
158
159 // build filter
160 struct sock_filter filter[] = {
161#ifdef clone_32
162 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, clone_32, 0, 4),
163 EXAMINE_ARGUMENT(0),
164 BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, clone_mask, 0, 1),
165 KILL_OR_RETURN_ERRNO,
166 RETURN_ALLOW,
167#endif
168#ifdef clone3_32
169 // cannot inspect clone3 argument because
170 // seccomp does not dereference pointers
171 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, clone3_32, 0, 1),
172 RETURN_ERRNO(ENOSYS), // hint to use clone instead
173#endif
174#ifdef unshare_32
175 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, unshare_32, 0, 4),
176 EXAMINE_ARGUMENT(0),
177 BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, mask, 0, 1),
178 KILL_OR_RETURN_ERRNO,
179 RETURN_ALLOW,
180#endif
181#ifdef setns_32
182 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, setns_32, 0, 4),
183 EXAMINE_ARGUMENT(1),
184 // always fail if argument is zero
185 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0, 1, 0),
186 BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, mask, 0, 1),
187 KILL_OR_RETURN_ERRNO,
188 RETURN_ALLOW
189#endif
190 };
191 write_to_file(fd, filter, sizeof(filter));
192
193 filter_end_blacklist(fd);
194
195 // close file
196 close(fd);
197}
diff --git a/src/fseccomp/protocol.c b/src/fseccomp/protocol.c
index 25742c173..ea5cd5bd4 100644
--- a/src/fseccomp/protocol.c
+++ b/src/fseccomp/protocol.c
@@ -132,15 +132,18 @@ void protocol_build_filter(const char *prlist, const char *fname) {
132 EXAMINE_SYSCALL, // 1 132 EXAMINE_SYSCALL, // 1
133 // checking SYS_socket only: filtering SYS_socketcall not possible with seccomp 133 // checking SYS_socket only: filtering SYS_socketcall not possible with seccomp
134 ONLY(359), // 1 + 2 134 ONLY(359), // 1 + 2
135 BPF_JUMP(BPF_JMP+BPF_JA+BPF_K, (3 + 1 + 2), 0, 0), // 1 + 2 + 1 135 BPF_JUMP(BPF_JMP+BPF_JA+BPF_K, (3 + 1 + 3 + 2), 0, 0), // 1 + 2 + 1
136#else 136#else
137#warning 32 bit protocol filter not implemented yet for your architecture 137#warning 32 bit protocol filter not implemented yet for your architecture
138#endif 138#endif
139 VALIDATE_ARCHITECTURE, // 3 139 VALIDATE_ARCHITECTURE, // 3
140 EXAMINE_SYSCALL, // 3 + 1 140 EXAMINE_SYSCALL, // 3 + 1
141 ONLY(SYS_socket), // 3 + 1 + 2 141#if defined __x86_64__
142 HANDLE_X32, // 3 + 1 + 3
143#endif
144 ONLY(SYS_socket), // 3 + 1 (+ 3) + 2
142 145
143 EXAMINE_ARGUMENT(0) // 3 + 1 + 2 + 1 146 EXAMINE_ARGUMENT(0) // 3 + 1 (+ 3) + 2 + 1
144 }; 147 };
145 memcpy(ptr, &filter_start[0], sizeof(filter_start)); 148 memcpy(ptr, &filter_start[0], sizeof(filter_start));
146 ptr += sizeof(filter_start); 149 ptr += sizeof(filter_start);
diff --git a/src/include/rundefs.h b/src/include/rundefs.h
index 08042d2c4..079670f10 100644
--- a/src/include/rundefs.h
+++ b/src/include/rundefs.h
@@ -68,6 +68,8 @@
68#define RUN_SECCOMP_32 RUN_SECCOMP_DIR "/seccomp.32" // 32bit arch filter installed on 64bit architectures 68#define RUN_SECCOMP_32 RUN_SECCOMP_DIR "/seccomp.32" // 32bit arch filter installed on 64bit architectures
69#define RUN_SECCOMP_MDWX RUN_SECCOMP_DIR "/seccomp.mdwx" // filter for memory-deny-write-execute 69#define RUN_SECCOMP_MDWX RUN_SECCOMP_DIR "/seccomp.mdwx" // filter for memory-deny-write-execute
70#define RUN_SECCOMP_MDWX_32 RUN_SECCOMP_DIR "/seccomp.mdwx.32" 70#define RUN_SECCOMP_MDWX_32 RUN_SECCOMP_DIR "/seccomp.mdwx.32"
71#define RUN_SECCOMP_NS RUN_SECCOMP_DIR "/seccomp.namespaces"
72#define RUN_SECCOMP_NS_32 RUN_SECCOMP_DIR "/seccomp.namespaces.32"
71#define RUN_SECCOMP_BLOCK_SECONDARY RUN_SECCOMP_DIR "/seccomp.block_secondary" // secondary arch blocking filter 73#define RUN_SECCOMP_BLOCK_SECONDARY RUN_SECCOMP_DIR "/seccomp.block_secondary" // secondary arch blocking filter
72#define RUN_SECCOMP_POSTEXEC RUN_SECCOMP_DIR "/seccomp.postexec" // filter for post-exec library 74#define RUN_SECCOMP_POSTEXEC RUN_SECCOMP_DIR "/seccomp.postexec" // filter for post-exec library
73#define RUN_SECCOMP_POSTEXEC_32 RUN_SECCOMP_DIR "/seccomp.postexec32" // filter for post-exec library 75#define RUN_SECCOMP_POSTEXEC_32 RUN_SECCOMP_DIR "/seccomp.postexec32" // filter for post-exec library
diff --git a/src/man/firejail-profile.txt b/src/man/firejail-profile.txt
index 5c8b6031d..be1f55f0f 100644
--- a/src/man/firejail-profile.txt
+++ b/src/man/firejail-profile.txt
@@ -520,6 +520,12 @@ first argument to socket system call. Recognized values: \fBunix\fR,
520\fBinet\fR, \fBinet6\fR, \fBnetlink\fR, \fBpacket\fR, and \fBbluetooth\fR. 520\fBinet\fR, \fBinet6\fR, \fBnetlink\fR, \fBpacket\fR, and \fBbluetooth\fR.
521Multiple protocol commands are allowed and they accumulate. 521Multiple protocol commands are allowed and they accumulate.
522.TP 522.TP
523\fBrestrict-namespaces
524Install a seccomp filter that blocks attempts to create new cgroup, ipc, net, mount, pid, time, user or uts namespaces.
525.TP
526\fBrestrict-namespaces cgroup,ipc,net,mnt,pid,time,user,uts
527Install a seccomp filter that blocks attempts to create any of the specified namespaces.
528.TP
523\fBseccomp 529\fBseccomp
524Enable seccomp filter and blacklist the syscalls in the default list. See man 1 firejail for more details. 530Enable seccomp filter and blacklist the syscalls in the default list. See man 1 firejail for more details.
525.TP 531.TP
diff --git a/src/man/firejail.txt b/src/man/firejail.txt
index c2c0bc297..087d1c85a 100644
--- a/src/man/firejail.txt
+++ b/src/man/firejail.txt
@@ -693,6 +693,7 @@ Example:
693.br 693.br
694$ firejail \-\-net=eth0 \-\-defaultgw=10.10.20.1 firefox 694$ firejail \-\-net=eth0 \-\-defaultgw=10.10.20.1 firefox
695#endif 695#endif
696
696.TP 697.TP
697\fB\-\-deterministic-exit-code 698\fB\-\-deterministic-exit-code
698Always exit firejail with the first child's exit status. The default behavior is to use the exit status of the final child to exit, which can be nondeterministic. 699Always exit firejail with the first child's exit status. The default behavior is to use the exit status of the final child to exit, which can be nondeterministic.
@@ -2257,6 +2258,29 @@ $ firejail --read-only=~/test --read-write=~/test/a
2257 2258
2258 2259
2259.TP 2260.TP
2261\fB\-\-restrict-namespaces
2262Install a seccomp filter that blocks attempts to create new cgroup, ipc, net, mount, pid, time, user or uts namespaces.
2263.br
2264
2265.br
2266Example:
2267.br
2268$ firejail \-\-restrict-namespaces
2269
2270.TP
2271\fB\-\-restrict-namespaces=cgroup,ipc,net,mnt,pid,time,user,uts
2272Install a seccomp filter that blocks attempts to create any of the specified namespaces. The filter examines
2273the arguments of clone, unshare and setns system calls and returns error EPERM to the process
2274(or kills it or logs the attempt, see \-\-seccomp-error-action below) if necessary. Note that the filter is not
2275able to examine the arguments of clone3 system calls, and always responds to these calls with error ENOSYS.
2276.br
2277
2278.br
2279Example:
2280.br
2281$ firejail \-\-restrict-namespaces=user,net
2282
2283.TP
2260\fB\-\-rlimit-as=number 2284\fB\-\-rlimit-as=number
2261Set the maximum size of the process's virtual memory (address space) in bytes. 2285Set the maximum size of the process's virtual memory (address space) in bytes.
2262Use k(ilobyte), m(egabyte) or g(igabyte) for size suffix (base 1024). 2286Use k(ilobyte), m(egabyte) or g(igabyte) for size suffix (base 1024).
diff --git a/src/zsh_completion/_firejail.in b/src/zsh_completion/_firejail.in
index 8383d83d3..605000e31 100644
--- a/src/zsh_completion/_firejail.in
+++ b/src/zsh_completion/_firejail.in
@@ -103,7 +103,7 @@ _firejail_args=(
103 '--join-or-start=-[join the sandbox or start a new one name|pid]: :_all_firejails' 103 '--join-or-start=-[join the sandbox or start a new one name|pid]: :_all_firejails'
104 '--keep-config-pulse[disable automatic ~/.config/pulse init]' 104 '--keep-config-pulse[disable automatic ~/.config/pulse init]'
105 '--keep-dev-shm[/dev/shm directory is untouched (even with --private-dev)]' 105 '--keep-dev-shm[/dev/shm directory is untouched (even with --private-dev)]'
106 '--keep-fd[inherit open file descriptors to sandbox]' 106 '--keep-fd[inherit open file descriptors to sandbox]: :'
107 '--keep-var-tmp[/var/tmp directory is untouched]' 107 '--keep-var-tmp[/var/tmp directory is untouched]'
108 '--machine-id[spoof /etc/machine-id with a random id]' 108 '--machine-id[spoof /etc/machine-id with a random id]'
109 '--memory-deny-write-execute[seccomp filter to block attempts to create memory mappings that are both writable and executable]' 109 '--memory-deny-write-execute[seccomp filter to block attempts to create memory mappings that are both writable and executable]'
@@ -141,6 +141,8 @@ _firejail_args=(
141 "--quiet[turn off Firejail's output.]" 141 "--quiet[turn off Firejail's output.]"
142 '*--read-only=-[set directory or file read-only]: :_files' 142 '*--read-only=-[set directory or file read-only]: :_files'
143 '*--read-write=-[set directory or file read-write]: :_files' 143 '*--read-write=-[set directory or file read-write]: :_files'
144 '--restrict-namespaces[seccomp filter that blocks attempts to create new namespaces]'
145 '--restrict-namespaces=-[seccomp filter that blocks attempts to create specified namespaces]: :'
144 "--rlimit-as=-[set the maximum size of the process's virtual memory (address space) in bytes]: :" 146 "--rlimit-as=-[set the maximum size of the process's virtual memory (address space) in bytes]: :"
145 '--rlimit-cpu=-[set the maximum CPU time in seconds]: :' 147 '--rlimit-cpu=-[set the maximum CPU time in seconds]: :'
146 '--rlimit-fsize=-[set the maximum file size that can be created by a process]: :' 148 '--rlimit-fsize=-[set the maximum file size that can be created by a process]: :'