aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar netblue30 <netblue30@protonmail.com>2021-12-28 18:48:13 +0000
committerLibravatar GitHub <noreply@github.com>2021-12-28 18:48:13 +0000
commit92f438cf87234236939ed90db86162f4ae8bac76 (patch)
tree4f6f8fd560ca3c3ef978ce809d32213f1264a0c3 /src
parentImplement a `whitelist-ro` command (diff)
parentMerge pull request #4755 from kmk3/mpv-add-yt-dlp (diff)
downloadfirejail-92f438cf87234236939ed90db86162f4ae8bac76.tar.gz
firejail-92f438cf87234236939ed90db86162f4ae8bac76.tar.zst
firejail-92f438cf87234236939ed90db86162f4ae8bac76.zip
Merge branch 'master' into whitelist-ro
Diffstat (limited to 'src')
-rw-r--r--src/firecfg/desktop_files.c6
-rw-r--r--src/firecfg/firecfg.config2
-rw-r--r--src/firejail/firejail.h5
-rw-r--r--src/firejail/fs_home.c13
-rw-r--r--src/firejail/main.c182
-rw-r--r--src/firejail/netfilter.c85
-rw-r--r--src/firejail/profile.c41
-rw-r--r--src/firejail/sandbox.c7
-rw-r--r--src/firejail/usage.c1
-rw-r--r--src/firejail/util.c49
-rw-r--r--src/fnettrace/Makefile.in17
-rw-r--r--src/fnettrace/fnettrace.h64
-rw-r--r--src/fnettrace/main.c433
-rw-r--r--src/man/firejail-profile.txt2
-rw-r--r--src/man/firejail.txt28
-rw-r--r--src/profstats/Makefile.in2
-rw-r--r--src/profstats/main.c29
-rw-r--r--src/tools/profcleaner.c75
-rwxr-xr-xsrc/tools/profcleaner.sh45
-rw-r--r--src/zsh_completion/_firejail.in30
20 files changed, 807 insertions, 309 deletions
diff --git a/src/firecfg/desktop_files.c b/src/firecfg/desktop_files.c
index 06b0a117f..c1aaf740c 100644
--- a/src/firecfg/desktop_files.c
+++ b/src/firecfg/desktop_files.c
@@ -168,9 +168,9 @@ void fix_desktop_files(char *homedir) {
168 168
169 char *filename = entry->d_name; 169 char *filename = entry->d_name;
170 170
171 // skip links 171 // skip links - Discord on Arch #4235 seems to be a symlink to /opt directory
172 if (is_link(filename)) 172// if (is_link(filename))
173 continue; 173// continue;
174 174
175 // no profile in /etc/firejail, no desktop file fixing 175 // no profile in /etc/firejail, no desktop file fixing
176 if (!have_profile(filename, homedir)) 176 if (!have_profile(filename, homedir))
diff --git a/src/firecfg/firecfg.config b/src/firecfg/firecfg.config
index 117c6f6ae..4bfdb7e57 100644
--- a/src/firecfg/firecfg.config
+++ b/src/firecfg/firecfg.config
@@ -109,6 +109,7 @@ brave-browser-stable
109# bzcat - disable until we fix CLI archivers for makepkg on Arch (see discussion in #3095) 109# bzcat - disable until we fix CLI archivers for makepkg on Arch (see discussion in #3095)
110bzflag 110bzflag
111# bzip2 - disable until we fix CLI archivers for makepkg on Arch (see discussion in #3095) 111# bzip2 - disable until we fix CLI archivers for makepkg on Arch (see discussion in #3095)
112cachy-browser
112calibre 113calibre
113calligra 114calligra
114calligraauthor 115calligraauthor
@@ -157,6 +158,7 @@ com.github.bleakgrey.tootle
157com.github.dahenson.agenda 158com.github.dahenson.agenda
158com.github.johnfactotum.Foliate 159com.github.johnfactotum.Foliate
159com.github.phase1geo.minder 160com.github.phase1geo.minder
161com.github.tchx84.Flatseal
160com.gitlab.newsflash 162com.gitlab.newsflash
161conkeror 163conkeror
162conky 164conky
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h
index a7673ae20..bc4cfe3fc 100644
--- a/src/firejail/firejail.h
+++ b/src/firejail/firejail.h
@@ -506,7 +506,8 @@ void errLogExit(char* fmt, ...) __attribute__((noreturn));
506void fwarning(char* fmt, ...); 506void fwarning(char* fmt, ...);
507void fmessage(char* fmt, ...); 507void fmessage(char* fmt, ...);
508long long unsigned parse_arg_size(char *str); 508long long unsigned parse_arg_size(char *str);
509void drop_privs(int nogroups); 509int check_can_drop_all_groups();
510void drop_privs(int force_nogroups);
510int mkpath_as_root(const char* path); 511int mkpath_as_root(const char* path);
511void extract_command_name(int index, char **argv); 512void extract_command_name(int index, char **argv);
512void logsignal(int s); 513void logsignal(int s);
@@ -657,6 +658,8 @@ void set_cgroup(const char *fname, pid_t pid);
657void check_output(int argc, char **argv); 658void check_output(int argc, char **argv);
658 659
659// netfilter.c 660// netfilter.c
661void netfilter_netlock(pid_t pid);
662void netfilter_trace(pid_t pid);
660void check_netfilter_file(const char *fname); 663void check_netfilter_file(const char *fname);
661void netfilter(const char *fname); 664void netfilter(const char *fname);
662void netfilter6(const char *fname); 665void netfilter6(const char *fname);
diff --git a/src/firejail/fs_home.c b/src/firejail/fs_home.c
index 4558934da..b410ba68e 100644
--- a/src/firejail/fs_home.c
+++ b/src/firejail/fs_home.c
@@ -456,15 +456,20 @@ void fs_check_private_dir(void) {
456void fs_check_private_cwd(const char *dir) { 456void fs_check_private_cwd(const char *dir) {
457 EUID_ASSERT(); 457 EUID_ASSERT();
458 invalid_filename(dir, 0); // no globbing 458 invalid_filename(dir, 0); // no globbing
459 if (strcmp(dir, ".") == 0 || *dir != '/')
460 goto errout;
459 461
460 // Expand the working directory 462 // Expand the working directory
461 cfg.cwd = expand_macros(dir); 463 cfg.cwd = expand_macros(dir);
462 464
463 // realpath/is_dir not used because path may not exist outside of jail 465 // realpath/is_dir not used because path may not exist outside of jail
464 if (strstr(cfg.cwd, "..")) { 466 if (strstr(cfg.cwd, ".."))
465 fprintf(stderr, "Error: invalid private working directory\n"); 467 goto errout;
466 exit(1); 468
467 } 469 return;
470errout:
471 fprintf(stderr, "Error: invalid private working directory\n");
472 exit(1);
468} 473}
469 474
470//*********************************************************************************** 475//***********************************************************************************
diff --git a/src/firejail/main.c b/src/firejail/main.c
index 59e88bdc6..3b12f7ca1 100644
--- a/src/firejail/main.c
+++ b/src/firejail/main.c
@@ -408,6 +408,10 @@ static void run_cmd_and_exit(int i, int argc, char **argv) {
408 } 408 }
409#endif 409#endif
410#ifdef HAVE_NETWORK 410#ifdef HAVE_NETWORK
411 else if (strncmp(argv[i], "--nettrace=", 11) == 0) {
412 pid_t pid = require_pid(argv[i] + 11);
413 netfilter_trace(pid);
414 }
411 else if (strncmp(argv[i], "--bandwidth=", 12) == 0) { 415 else if (strncmp(argv[i], "--bandwidth=", 12) == 0) {
412 if (checkcfg(CFG_NETWORK)) { 416 if (checkcfg(CFG_NETWORK)) {
413 logargs(argc, argv); 417 logargs(argc, argv);
@@ -990,8 +994,10 @@ int main(int argc, char **argv, char **envp) {
990 int option_cgroup = 0; 994 int option_cgroup = 0;
991 int custom_profile = 0; // custom profile loaded 995 int custom_profile = 0; // custom profile loaded
992 int arg_caps_cmdline = 0; // caps requested on command line (used to break out of --chroot) 996 int arg_caps_cmdline = 0; // caps requested on command line (used to break out of --chroot)
997 int arg_netlock = 0;
993 char **ptr; 998 char **ptr;
994 999
1000
995 // sanitize the umask 1001 // sanitize the umask
996 orig_umask = umask(022); 1002 orig_umask = umask(022);
997 1003
@@ -1013,10 +1019,10 @@ int main(int argc, char **argv, char **envp) {
1013 1019
1014 // sanity check for arguments 1020 // sanity check for arguments
1015 for (i = 0; i < argc; i++) { 1021 for (i = 0; i < argc; i++) {
1016 if (*argv[i] == 0) { 1022// if (*argv[i] == 0) { // see #4395 - bug reported by Debian
1017 fprintf(stderr, "Error: too short arguments: argv[%d] is empty\n", i); 1023// fprintf(stderr, "Error: too short arguments: argv[%d] is empty\n", i);
1018 exit(1); 1024// exit(1);
1019 } 1025// }
1020 if (strlen(argv[i]) >= MAX_ARG_LEN) { 1026 if (strlen(argv[i]) >= MAX_ARG_LEN) {
1021 fprintf(stderr, "Error: too long arguments: argv[%d] len (%zu) >= MAX_ARG_LEN (%d)\n", i, strlen(argv[i]), MAX_ARG_LEN); 1027 fprintf(stderr, "Error: too long arguments: argv[%d] len (%zu) >= MAX_ARG_LEN (%d)\n", i, strlen(argv[i]), MAX_ARG_LEN);
1022 exit(1); 1028 exit(1);
@@ -1574,7 +1580,6 @@ int main(int argc, char **argv, char **envp) {
1574 profile_add(line); 1580 profile_add(line);
1575 } 1581 }
1576 1582
1577 // blacklist/deny
1578 else if (strncmp(argv[i], "--blacklist=", 12) == 0) { 1583 else if (strncmp(argv[i], "--blacklist=", 12) == 0) {
1579 char *line; 1584 char *line;
1580 if (asprintf(&line, "blacklist %s", argv[i] + 12) == -1) 1585 if (asprintf(&line, "blacklist %s", argv[i] + 12) == -1)
@@ -1583,14 +1588,6 @@ int main(int argc, char **argv, char **envp) {
1583 profile_check_line(line, 0, NULL); // will exit if something wrong 1588 profile_check_line(line, 0, NULL); // will exit if something wrong
1584 profile_add(line); 1589 profile_add(line);
1585 } 1590 }
1586 else if (strncmp(argv[i], "--deny=", 7) == 0) {
1587 char *line;
1588 if (asprintf(&line, "blacklist %s", argv[i] + 7) == -1)
1589 errExit("asprintf");
1590
1591 profile_check_line(line, 0, NULL); // will exit if something wrong
1592 profile_add(line);
1593 }
1594 else if (strncmp(argv[i], "--noblacklist=", 14) == 0) { 1591 else if (strncmp(argv[i], "--noblacklist=", 14) == 0) {
1595 char *line; 1592 char *line;
1596 if (asprintf(&line, "noblacklist %s", argv[i] + 14) == -1) 1593 if (asprintf(&line, "noblacklist %s", argv[i] + 14) == -1)
@@ -1599,16 +1596,6 @@ int main(int argc, char **argv, char **envp) {
1599 profile_check_line(line, 0, NULL); // will exit if something wrong 1596 profile_check_line(line, 0, NULL); // will exit if something wrong
1600 profile_add(line); 1597 profile_add(line);
1601 } 1598 }
1602 else if (strncmp(argv[i], "--nodeny=", 9) == 0) {
1603 char *line;
1604 if (asprintf(&line, "noblacklist %s", argv[i] + 9) == -1)
1605 errExit("asprintf");
1606
1607 profile_check_line(line, 0, NULL); // will exit if something wrong
1608 profile_add(line);
1609 }
1610
1611 // whitelist
1612 else if (strncmp(argv[i], "--whitelist=", 12) == 0) { 1599 else if (strncmp(argv[i], "--whitelist=", 12) == 0) {
1613 char *line; 1600 char *line;
1614 if (asprintf(&line, "whitelist %s", argv[i] + 12) == -1) 1601 if (asprintf(&line, "whitelist %s", argv[i] + 12) == -1)
@@ -1617,14 +1604,6 @@ int main(int argc, char **argv, char **envp) {
1617 profile_check_line(line, 0, NULL); // will exit if something wrong 1604 profile_check_line(line, 0, NULL); // will exit if something wrong
1618 profile_add(line); 1605 profile_add(line);
1619 } 1606 }
1620 else if (strncmp(argv[i], "--allow=", 8) == 0) {
1621 char *line;
1622 if (asprintf(&line, "whitelist %s", argv[i] + 8) == -1)
1623 errExit("asprintf");
1624
1625 profile_check_line(line, 0, NULL); // will exit if something wrong
1626 profile_add(line);
1627 }
1628 else if (strncmp(argv[i], "--nowhitelist=", 14) == 0) { 1607 else if (strncmp(argv[i], "--nowhitelist=", 14) == 0) {
1629 char *line; 1608 char *line;
1630 if (asprintf(&line, "nowhitelist %s", argv[i] + 14) == -1) 1609 if (asprintf(&line, "nowhitelist %s", argv[i] + 14) == -1)
@@ -1633,15 +1612,6 @@ int main(int argc, char **argv, char **envp) {
1633 profile_check_line(line, 0, NULL); // will exit if something wrong 1612 profile_check_line(line, 0, NULL); // will exit if something wrong
1634 profile_add(line); 1613 profile_add(line);
1635 } 1614 }
1636 else if (strncmp(argv[i], "--noallow=", 10) == 0) {
1637 char *line;
1638 if (asprintf(&line, "nowhitelist %s", argv[i] + 10) == -1)
1639 errExit("asprintf");
1640
1641 profile_check_line(line, 0, NULL); // will exit if something wrong
1642 profile_add(line);
1643 }
1644
1645 1615
1646 else if (strncmp(argv[i], "--mkdir=", 8) == 0) { 1616 else if (strncmp(argv[i], "--mkdir=", 8) == 0) {
1647 char *line; 1617 char *line;
@@ -2324,6 +2294,12 @@ int main(int argc, char **argv, char **envp) {
2324 //************************************* 2294 //*************************************
2325 // network 2295 // network
2326 //************************************* 2296 //*************************************
2297 else if (strcmp(argv[i], "--netlock") == 0)
2298 arg_netlock = 1;
2299 else if (strncmp(argv[i], "--netlock=", 10) == 0) {
2300 pid_t pid = require_pid(argv[i] + 10);
2301 netfilter_netlock(pid);
2302 }
2327 else if (strcmp(argv[i], "--net=none") == 0) { 2303 else if (strcmp(argv[i], "--net=none") == 0) {
2328 arg_nonetwork = 1; 2304 arg_nonetwork = 1;
2329 cfg.bridge0.configured = 0; 2305 cfg.bridge0.configured = 0;
@@ -2642,7 +2618,7 @@ int main(int argc, char **argv, char **envp) {
2642 else if (cfg.dns4 == NULL) 2618 else if (cfg.dns4 == NULL)
2643 cfg.dns4 = dns; 2619 cfg.dns4 = dns;
2644 else { 2620 else {
2645 fwarning("Warning: up to 4 DNS servers can be specified, %s ignored\n", dns); 2621 fwarning("up to 4 DNS servers can be specified, %s ignored\n", dns);
2646 free(dns); 2622 free(dns);
2647 } 2623 }
2648 } 2624 }
@@ -3155,62 +3131,64 @@ int main(int argc, char **argv, char **envp) {
3155 ptr += strlen(ptr); 3131 ptr += strlen(ptr);
3156 3132
3157 gid_t g; 3133 gid_t g;
3158 // add audio group 3134 if (!arg_nogroups || !check_can_drop_all_groups()) {
3159 if (!arg_nosound) { 3135 // add audio group
3160 g = get_group_id("audio"); 3136 if (!arg_nosound) {
3161 if (g) { 3137 g = get_group_id("audio");
3162 sprintf(ptr, "%d %d 1\n", g, g); 3138 if (g) {
3163 ptr += strlen(ptr); 3139 sprintf(ptr, "%d %d 1\n", g, g);
3140 ptr += strlen(ptr);
3141 }
3164 } 3142 }
3165 }
3166 3143
3167 // add video group 3144 // add video group
3168 if (!arg_novideo) { 3145 if (!arg_novideo) {
3169 g = get_group_id("video"); 3146 g = get_group_id("video");
3170 if (g) { 3147 if (g) {
3171 sprintf(ptr, "%d %d 1\n", g, g); 3148 sprintf(ptr, "%d %d 1\n", g, g);
3172 ptr += strlen(ptr); 3149 ptr += strlen(ptr);
3150 }
3173 } 3151 }
3174 }
3175 3152
3176 // add render group 3153 // add render group
3177 if (!arg_no3d) { 3154 if (!arg_no3d) {
3178 g = get_group_id("render"); 3155 g = get_group_id("render");
3179 if (g) { 3156 if (g) {
3180 sprintf(ptr, "%d %d 1\n", g, g); 3157 sprintf(ptr, "%d %d 1\n", g, g);
3181 ptr += strlen(ptr); 3158 ptr += strlen(ptr);
3159 }
3182 } 3160 }
3183 }
3184 3161
3185 // add lp group 3162 // add lp group
3186 if (!arg_noprinters) { 3163 if (!arg_noprinters) {
3187 g = get_group_id("lp"); 3164 g = get_group_id("lp");
3188 if (g) { 3165 if (g) {
3189 sprintf(ptr, "%d %d 1\n", g, g); 3166 sprintf(ptr, "%d %d 1\n", g, g);
3190 ptr += strlen(ptr); 3167 ptr += strlen(ptr);
3168 }
3191 } 3169 }
3192 }
3193 3170
3194 // add cdrom/optical groups 3171 // add cdrom/optical groups
3195 if (!arg_nodvd) { 3172 if (!arg_nodvd) {
3196 g = get_group_id("cdrom"); 3173 g = get_group_id("cdrom");
3197 if (g) { 3174 if (g) {
3198 sprintf(ptr, "%d %d 1\n", g, g); 3175 sprintf(ptr, "%d %d 1\n", g, g);
3199 ptr += strlen(ptr); 3176 ptr += strlen(ptr);
3200 } 3177 }
3201 g = get_group_id("optical"); 3178 g = get_group_id("optical");
3202 if (g) { 3179 if (g) {
3203 sprintf(ptr, "%d %d 1\n", g, g); 3180 sprintf(ptr, "%d %d 1\n", g, g);
3204 ptr += strlen(ptr); 3181 ptr += strlen(ptr);
3182 }
3205 } 3183 }
3206 }
3207 3184
3208 // add input group 3185 // add input group
3209 if (!arg_noinput) { 3186 if (!arg_noinput) {
3210 g = get_group_id("input"); 3187 g = get_group_id("input");
3211 if (g) { 3188 if (g) {
3212 sprintf(ptr, "%d %d 1\n", g, g); 3189 sprintf(ptr, "%d %d 1\n", g, g);
3213 ptr += strlen(ptr); 3190 ptr += strlen(ptr);
3191 }
3214 } 3192 }
3215 } 3193 }
3216 3194
@@ -3254,6 +3232,16 @@ int main(int argc, char **argv, char **envp) {
3254 } 3232 }
3255 EUID_USER(); 3233 EUID_USER();
3256 3234
3235 // lock netfilter firewall
3236 if (arg_netlock) {
3237 char *cmd;
3238 if (asprintf(&cmd, "firejail --netlock=%d&", getpid()) == -1)
3239 errExit("asprintf");
3240 int rv = system(cmd);
3241 (void) rv;
3242 free(cmd);
3243 }
3244
3257 int status = 0; 3245 int status = 0;
3258 //***************************** 3246 //*****************************
3259 // following code is signal-safe 3247 // following code is signal-safe
@@ -3271,26 +3259,6 @@ int main(int argc, char **argv, char **envp) {
3271 // end of signal-safe code 3259 // end of signal-safe code
3272 //***************************** 3260 //*****************************
3273 3261
3274#if 0
3275// at this point the sandbox was closed and we are on our way out
3276// it would make sense to move this before waitpid above to free some memory
3277// crash for now as of issue #3662 from dhcp code
3278 // free globals
3279 if (cfg.profile) {
3280 ProfileEntry *prf = cfg.profile;
3281 while (prf != NULL) {
3282 ProfileEntry *next = prf->next;
3283printf("data #%s#\n", prf->data);
3284 if (prf->data)
3285 free(prf->data);
3286printf("link #%s#\n", prf->link);
3287 if (prf->link)
3288 free(prf->link);
3289 free(prf);
3290 prf = next;
3291 }
3292 }
3293#endif
3294 3262
3295 3263
3296 if (WIFEXITED(status)){ 3264 if (WIFEXITED(status)){
diff --git a/src/firejail/netfilter.c b/src/firejail/netfilter.c
index fc79dddec..f412950f2 100644
--- a/src/firejail/netfilter.c
+++ b/src/firejail/netfilter.c
@@ -24,6 +24,91 @@
24#include <sys/wait.h> 24#include <sys/wait.h>
25#include <fcntl.h> 25#include <fcntl.h>
26 26
27void netfilter_netlock(pid_t pid) {
28 EUID_ASSERT();
29
30 // give the sandbox a chance to start up before entering the network namespace
31 sleep(1);
32 enter_network_namespace(pid);
33
34 char *flog;
35 if (asprintf(&flog, "/run/firejail/network/%d-netlock", getpid()) == -1)
36 errExit("asprintf");
37 FILE *fp = fopen(flog, "w");
38 if (!fp)
39 errExit("fopen");
40 fclose(fp);
41
42 // try to find a X terminal
43 char *terminal = NULL;
44 if (access("/usr/bin/lxterminal", X_OK) == 0)
45 terminal = "/usr/bin/lxterminal";
46 else if (access("/usr/bin/xterm", X_OK) == 0)
47 terminal = "/usr/bin/xterm";
48 else if (access("/usr/bin/xfce4-terminal", X_OK) == 0)
49 terminal = "/usr/bin/xfce4-terminal";
50 else if (access("/usr/bin/konsole", X_OK) == 0)
51 terminal = "/usr/bin/konsole";
52// problem: newer gnome-terminal versions don't support -e command line option???
53// else if (access("/usr/bin/gnome-terminal", X_OK) == 0)
54// terminal = "/usr/bin/gnome-terminal";
55
56 if (terminal) {
57 pid_t p = fork();
58 if (p == -1)
59 ; // run without terminal logger
60 else if (p == 0) { // child
61 drop_privs(0);
62
63 char *cmd;
64 if (asprintf(&cmd, "%s -e \"tail -f %s\"", terminal, flog) == -1)
65 errExit("asprintf");
66 int rv = system(cmd);
67 (void) rv;
68 exit(0);
69 }
70 }
71
72 char *cmd;
73 if (asprintf(&cmd, "%s/firejail/fnettrace --netfilter --log=%s", LIBDIR, flog) == -1)
74 errExit("asprintf");
75 free(flog);
76
77 //************************
78 // build command
79 //************************
80 char *arg[4];
81 arg[0] = "/bin/sh";
82 arg[1] = "-c";
83 arg[2] = cmd;
84 arg[3] = NULL;
85 clearenv();
86 sbox_exec_v(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, arg);
87 // it will never get here!!
88}
89
90void netfilter_trace(pid_t pid) {
91 EUID_ASSERT();
92
93 enter_network_namespace(pid);
94 char *cmd;
95 if (asprintf(&cmd, "%s/firejail/fnettrace", LIBDIR) == -1)
96 errExit("asprintf");
97
98 //************************
99 // build command
100 //************************
101 char *arg[4];
102 arg[0] = "/bin/sh";
103 arg[1] = "-c";
104 arg[2] = cmd;
105 arg[3] = NULL;
106
107 clearenv();
108 sbox_exec_v(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, arg);
109 // it will never get here!!
110}
111
27void check_netfilter_file(const char *fname) { 112void check_netfilter_file(const char *fname) {
28 EUID_ASSERT(); 113 EUID_ASSERT();
29 114
diff --git a/src/firejail/profile.c b/src/firejail/profile.c
index 7757c1814..92dbecac1 100644
--- a/src/firejail/profile.c
+++ b/src/firejail/profile.c
@@ -1106,7 +1106,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
1106 else if (cfg.dns4 == NULL) 1106 else if (cfg.dns4 == NULL)
1107 cfg.dns4 = dns; 1107 cfg.dns4 = dns;
1108 else { 1108 else {
1109 fwarning("Warning: up to 4 DNS servers can be specified, %s ignored\n", dns); 1109 fwarning("up to 4 DNS servers can be specified, %s ignored\n", dns);
1110 free(dns); 1110 free(dns);
1111 } 1111 }
1112 return 0; 1112 return 0;
@@ -1752,44 +1752,7 @@ void profile_read(const char *fname) {
1752 continue; 1752 continue;
1753 } 1753 }
1754 1754
1755 // translate allow/deny to whitelist/blacklist 1755 if (strncmp(ptr, "whitelist-ro ", 13) == 0) {
1756 if (strncmp(ptr, "allow ", 6) == 0) {
1757 char *tmp;
1758 if (asprintf(&tmp, "whitelist %s", ptr + 6) == -1)
1759 errExit("asprintf");
1760 free(ptr);
1761 ptr = tmp;
1762 }
1763 else if (strncmp(ptr, "deny ", 5) == 0) {
1764 char *tmp;
1765 if (asprintf(&tmp, "blacklist %s", ptr + 5) == -1)
1766 errExit("asprintf");
1767 free(ptr);
1768 ptr = tmp;
1769 }
1770 else if (strncmp(ptr, "deny-nolog ", 11) == 0) {
1771 char *tmp;
1772 if (asprintf(&tmp, "blacklist-nolog %s", ptr + 11) == -1)
1773 errExit("asprintf");
1774 free(ptr);
1775 ptr = tmp;
1776 }
1777 // translate noallow/nodeny to nowhitelist/noblacklist
1778 else if (strncmp(ptr, "noallow ", 8) == 0) {
1779 char *tmp;
1780 if (asprintf(&tmp, "nowhitelist %s", ptr + 8) == -1)
1781 errExit("asprintf");
1782 free(ptr);
1783 ptr = tmp;
1784 }
1785 else if (strncmp(ptr, "nodeny ", 7) == 0) {
1786 char *tmp;
1787 if (asprintf(&tmp, "noblacklist %s", ptr + 7) == -1)
1788 errExit("asprintf");
1789 free(ptr);
1790 ptr = tmp;
1791 }
1792 else if (strncmp(ptr, "whitelist-ro ", 13) == 0) {
1793 char *whitelist, *readonly; 1756 char *whitelist, *readonly;
1794 if (asprintf(&whitelist, "whitelist %s", ptr + 13) == -1) 1757 if (asprintf(&whitelist, "whitelist %s", ptr + 13) == -1)
1795 errExit("asprintf"); 1758 errExit("asprintf");
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c
index 3887b5701..53b1e6914 100644
--- a/src/firejail/sandbox.c
+++ b/src/firejail/sandbox.c
@@ -1058,6 +1058,11 @@ int sandbox(void* sandbox_arg) {
1058 EUID_USER(); 1058 EUID_USER();
1059 int cwd = 0; 1059 int cwd = 0;
1060 if (cfg.cwd) { 1060 if (cfg.cwd) {
1061 if (is_link(cfg.cwd)) {
1062 fprintf(stderr, "Error: unable to enter private working directory: %s\n", cfg.cwd);
1063 exit(1);
1064 }
1065
1061 if (chdir(cfg.cwd) == 0) 1066 if (chdir(cfg.cwd) == 0)
1062 cwd = 1; 1067 cwd = 1;
1063 else if (arg_private_cwd) { 1068 else if (arg_private_cwd) {
@@ -1225,7 +1230,7 @@ int sandbox(void* sandbox_arg) {
1225 //**************************************** 1230 //****************************************
1226 // drop privileges 1231 // drop privileges
1227 //**************************************** 1232 //****************************************
1228 drop_privs(arg_nogroups); 1233 drop_privs(0);
1229 1234
1230 // kill the sandbox in case the parent died 1235 // kill the sandbox in case the parent died
1231 prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); 1236 prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
diff --git a/src/firejail/usage.c b/src/firejail/usage.c
index 4a0f05528..b993cb80c 100644
--- a/src/firejail/usage.c
+++ b/src/firejail/usage.c
@@ -150,6 +150,7 @@ static char *usage_str =
150 "\tparent interfaces.\n" 150 "\tparent interfaces.\n"
151 " --netns=name - Run the program in a named, persistent network namespace.\n" 151 " --netns=name - Run the program in a named, persistent network namespace.\n"
152 " --netstats - monitor network statistics.\n" 152 " --netstats - monitor network statistics.\n"
153 " --nettrace - monitor TCP and UDP traffic coming into the sandbox.\n"
153#endif 154#endif
154 " --nice=value - set nice value.\n" 155 " --nice=value - set nice value.\n"
155 " --no3d - disable 3D hardware acceleration.\n" 156 " --no3d - disable 3D hardware acceleration.\n"
diff --git a/src/firejail/util.c b/src/firejail/util.c
index 97afe9649..c1c31b43c 100644
--- a/src/firejail/util.c
+++ b/src/firejail/util.c
@@ -103,6 +103,41 @@ void errLogExit(char* fmt, ...) {
103 exit(1); 103 exit(1);
104} 104}
105 105
106// Returns whether all supplementary groups can be safely dropped
107int check_can_drop_all_groups() {
108 static int can_drop_all_groups = -1;
109
110 // Avoid needlessly checking (and printing) things twice
111 if (can_drop_all_groups != -1)
112 goto out;
113
114 // nvidia cards require video group; ignore nogroups
115 if (access("/dev/nvidiactl", R_OK) == 0 && arg_no3d == 0) {
116 fwarning("NVIDIA card detected, nogroups command ignored\n");
117 can_drop_all_groups = 0;
118 goto out;
119 }
120
121 /* When we are not sure that the system has working seat-based ACLs
122 * (e.g.: probably yes on (e)udev + (e)logind, probably not on eudev +
123 * seatd), supplementary groups (e.g.: audio and input) might be needed
124 * to avoid breakage (e.g.: audio or gamepads not working). See #4600
125 * and #4603.
126 */
127 if (access("/run/systemd/seats/", F_OK) != 0) {
128 fwarning("logind not detected, nogroups command ignored\n");
129 can_drop_all_groups = 0;
130 goto out;
131 }
132
133 if (arg_debug)
134 fprintf(stderr, "nogroups command not ignored\n");
135 can_drop_all_groups = 1;
136
137out:
138 return can_drop_all_groups;
139}
140
106static int find_group(gid_t group, const gid_t *groups, int ngroups) { 141static int find_group(gid_t group, const gid_t *groups, int ngroups) {
107 int i; 142 int i;
108 for (i = 0; i < ngroups; i++) { 143 for (i = 0; i < ngroups; i++) {
@@ -141,6 +176,9 @@ static void clean_supplementary_groups(gid_t gid) {
141 if (rv == -1) 176 if (rv == -1)
142 goto clean_all; 177 goto clean_all;
143 178
179 if (arg_nogroups && check_can_drop_all_groups())
180 goto clean_all;
181
144 // clean supplementary group list 182 // clean supplementary group list
145 gid_t new_groups[MAX_GROUPS]; 183 gid_t new_groups[MAX_GROUPS];
146 int new_ngroups = 0; 184 int new_ngroups = 0;
@@ -215,21 +253,22 @@ clean_all:
215 253
216 254
217// drop privileges 255// drop privileges
218// - for root group or if nogroups is set, supplementary groups are not configured 256// - for root group or if force_nogroups is set, supplementary groups are not configured
219void drop_privs(int nogroups) { 257void drop_privs(int force_nogroups) {
220 gid_t gid = getgid(); 258 gid_t gid = getgid();
221 if (arg_debug) 259 if (arg_debug)
222 printf("Drop privileges: pid %d, uid %d, gid %d, nogroups %d\n", getpid(), getuid(), gid, nogroups); 260 printf("Drop privileges: pid %d, uid %d, gid %d, force_nogroups %d\n",
261 getpid(), getuid(), gid, force_nogroups);
223 262
224 // configure supplementary groups 263 // configure supplementary groups
225 EUID_ROOT(); 264 EUID_ROOT();
226 if (gid == 0 || nogroups) { 265 if (gid == 0 || force_nogroups) {
227 if (setgroups(0, NULL) < 0) 266 if (setgroups(0, NULL) < 0)
228 errExit("setgroups"); 267 errExit("setgroups");
229 if (arg_debug) 268 if (arg_debug)
230 printf("No supplementary groups\n"); 269 printf("No supplementary groups\n");
231 } 270 }
232 else if (arg_noroot) 271 else if (arg_noroot || arg_nogroups)
233 clean_supplementary_groups(gid); 272 clean_supplementary_groups(gid);
234 273
235 // set uid/gid 274 // set uid/gid
diff --git a/src/fnettrace/Makefile.in b/src/fnettrace/Makefile.in
new file mode 100644
index 000000000..755ddcc3a
--- /dev/null
+++ b/src/fnettrace/Makefile.in
@@ -0,0 +1,17 @@
1.PHONY: all
2all: fnettrace
3
4include ../common.mk
5
6%.o : %.c $(H_FILE_LIST)
7 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@
8
9fnettrace: $(OBJS)
10 $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) $(EXTRA_LDFLAGS)
11
12.PHONY: clean
13clean:; rm -fr *.o fnettrace *.gcov *.gcda *.gcno *.plist
14
15.PHONY: distclean
16distclean: clean
17 rm -fr Makefile
diff --git a/src/fnettrace/fnettrace.h b/src/fnettrace/fnettrace.h
new file mode 100644
index 000000000..9c34e17ca
--- /dev/null
+++ b/src/fnettrace/fnettrace.h
@@ -0,0 +1,64 @@
1#ifndef FNETTRACE_H
2#define FNETTRACE_H
3
4#include "../include/common.h"
5#include <unistd.h>
6#include <sys/stat.h>
7#include <sys/socket.h>
8#include <netinet/in.h>
9#include <time.h>
10#include <stdarg.h>
11
12//#define NETLOCK_INTERVAL 60
13#define NETLOCK_INTERVAL 60
14#define DISPLAY_INTERVAL 3
15
16void logprintf(char* fmt, ...);
17
18static inline void ansi_topleft(int tolog) {
19 char str[] = {0x1b, '[', '1', ';', '1', 'H', '\0'};
20 if (tolog)
21 logprintf("%s", str);
22 else
23 printf("%s", str);
24 fflush(0);
25}
26
27static inline void ansi_clrscr(int tolog) {
28 ansi_topleft(tolog);
29 char str[] = {0x1b, '[', '0', 'J', '\0'};
30 if (tolog)
31 logprintf("%s", str);
32 else
33 printf("%s", str);
34 fflush(0);
35}
36
37static inline void ansi_linestart(int tolog) {
38 char str[] = {0x1b, '[', '0', 'G', '\0'};
39 if (tolog)
40 logprintf("%s", str);
41 else
42 printf("%s", str);
43 fflush(0);
44}
45
46static inline void ansi_clrline(int tolog) {
47 ansi_linestart(tolog);
48 char str[] = {0x1b, '[', '0', 'K', '\0'};
49 if (tolog)
50 logprintf("%s", str);
51 else
52 printf("%s", str);
53 fflush(0);
54}
55
56static inline uint8_t hash(uint32_t ip) {
57 uint8_t *ptr = (uint8_t *) &ip;
58 // simple byte xor
59 return *ptr ^ *(ptr + 1) ^ *(ptr + 2) ^ *(ptr + 3);
60}
61
62
63
64#endif \ No newline at end of file
diff --git a/src/fnettrace/main.c b/src/fnettrace/main.c
new file mode 100644
index 000000000..f036d0c9e
--- /dev/null
+++ b/src/fnettrace/main.c
@@ -0,0 +1,433 @@
1#include "fnettrace.h"
2#define MAX_BUF_SIZE (64 * 1024)
3
4static int arg_netfilter = 0;
5static char *arg_log = NULL;
6
7typedef struct hlist_t {
8 struct hlist_t *next;
9 uint32_t ip_src;
10 uint32_t ip_dst;
11 uint16_t port_src;
12 uint64_t bytes;
13 int instance;
14#define MAX_TTL 20 // 20 * DISPLAY_INTERVAL = 1 minute
15 short ttl;
16 uint8_t protocol;
17} HList;
18
19#define HMAX 256
20HList *htable[HMAX] = {NULL};
21static int htable_empty = 1;
22
23static void hlist_add(uint32_t ip_src, uint32_t ip_dst, uint8_t protocol, uint16_t port_src, uint64_t bytes) {
24 uint8_t h = hash(ip_src);
25 htable_empty = 0;
26
27 // find
28 int instance = 0;
29 HList *ptr = htable[h];
30 while (ptr) {
31 if (ptr->ip_src == ip_src) {
32 instance++;
33 if (ptr->ip_dst == ip_dst && ptr->port_src == port_src && ptr->protocol == protocol) {
34 ptr->bytes += bytes;
35 ptr->ttl = MAX_TTL;
36 return;
37 }
38 }
39 ptr = ptr->next;
40 }
41
42 HList *hnew = malloc(sizeof(HList));
43 hnew->ip_src = ip_src;
44 hnew->ip_dst = ip_dst;
45 hnew->port_src = port_src;
46 hnew->protocol = protocol;
47 hnew->next = NULL;
48 hnew->bytes = bytes;
49 hnew->ttl = MAX_TTL;
50 hnew->instance = instance + 1;
51 if (htable[h] == NULL)
52 htable[h] = hnew;
53 else {
54 hnew->next = htable[h];
55 htable[h] = hnew;
56 }
57
58 ansi_clrline(1);
59 logprintf(" %u.%u.%u.%u\n", PRINT_IP(hnew->ip_src));
60}
61
62// remove entries with a ttl <= 0
63static void hlist_clean_ttl() {
64 if (htable_empty)
65 return;
66
67 int i;
68 for (i = 0; i < HMAX; i++) {
69 HList *ptr = htable[i];
70 HList *parent = NULL;
71 while (ptr) {
72 if (--ptr->ttl <= 0) {
73 HList *tmp = ptr;
74 ptr = ptr->next;
75 if (parent)
76 parent->next = ptr;
77 else
78 htable[i] = ptr;
79 free(tmp);
80 }
81 else {
82 parent = ptr;
83 ptr = ptr->next;
84 }
85 }
86 }
87}
88
89static void hlist_print() {
90 ansi_clrscr(0);
91 if (htable_empty)
92 return;
93 if (arg_netfilter)
94 printf("\n\n");
95 static int clear_cnt = 0;
96
97 int i;
98 int cnt = 0;
99 int cnt_printed = 0;
100 for (i = 0; i < HMAX; i++) {
101 HList *ptr = htable[i];
102 while (ptr) {
103 if (ptr->bytes) {
104 cnt_printed++;
105 char ip_src[30];
106 sprintf(ip_src, "%u.%u.%u.%u:%u", PRINT_IP(ptr->ip_src), ptr->port_src);
107 char ip_dst[30];
108 sprintf(ip_dst, "%u.%u.%u.%u", PRINT_IP(ptr->ip_dst));
109 printf("%-25s => %-25s\t%s:",
110 ip_src,
111 ip_dst,
112 (ptr->protocol == 6)? "TCP": "UDP");
113
114 if (ptr->bytes > (DISPLAY_INTERVAL * 1024 * 2)) // > 2 KB/second
115 printf(" %lu KB/sec\n",
116 ptr->bytes / (DISPLAY_INTERVAL * 1024));
117 else
118 printf(" %lu B/sec\n",
119 ptr->bytes / DISPLAY_INTERVAL);
120 ptr->bytes = 0;
121 }
122
123 ptr = ptr->next;
124 cnt++;
125 }
126 }
127
128 if (cnt_printed < 7) {
129 for (i = 0; i < 7 - cnt_printed; i++)
130 printf("\n");
131 }
132
133 if (!arg_netfilter) {
134 printf("(%d %s in the last one minute)\n", cnt, (cnt == 1)? "stream": "streams");
135 hlist_clean_ttl();
136 }
137}
138
139static void run_trace(void) {
140 logprintf("accumulating traffic for %d seconds...\n", NETLOCK_INTERVAL);
141
142 // trace only rx ipv4 tcp and upd
143 int s1 = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
144 int s2 = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
145 if (s1 < 0 || s2 < 0)
146 errExit("socket");
147
148 unsigned start = time(NULL);
149 unsigned last_print_traces = 0;
150 unsigned last_print_remaining = 0;
151 unsigned char buf[MAX_BUF_SIZE];
152 int progress_cnt = 0;
153 while (1) {
154 unsigned end = time(NULL);
155 if (arg_netfilter && end - start >= NETLOCK_INTERVAL) {
156 ansi_clrline(1);
157 break;
158 }
159 if (end % DISPLAY_INTERVAL == 1 && last_print_traces != end) { // first print after 1 second
160 hlist_print();
161 last_print_traces = end;
162 }
163 if (arg_netfilter && last_print_remaining != end) {
164 ansi_clrline(1);
165 int secs = NETLOCK_INTERVAL - (end - start);
166 logprintf("%d %s remaining ", secs, (secs == 1)? "second": "seconds");
167 last_print_remaining = end;
168 }
169
170 fd_set rfds;
171 FD_ZERO(&rfds);
172 FD_SET(s1, &rfds);
173 FD_SET(s2, &rfds);
174 int maxfd = (s1 > s2) ? s1 : s2;
175 maxfd++;
176 struct timeval tv;
177 tv.tv_sec = 1;
178 tv.tv_usec = 0;
179 int rv = select(maxfd, &rfds, NULL, NULL, &tv);
180 if (rv < 0)
181 errExit("select");
182 else if (rv == 0)
183 continue;
184
185
186
187 int sock = (FD_ISSET(s1, &rfds)) ? s1 : s2;
188
189 unsigned char buf[MAX_BUF_SIZE];
190 unsigned bytes = recvfrom(sock, buf, MAX_BUF_SIZE, 0, NULL, NULL);
191 if (bytes >= 20) { // size of IP header
192 // filter out loopback traffic
193 if (buf[12] != 127) {
194 uint32_t ip_src;
195 memcpy(&ip_src, buf + 12, 4);
196 ip_src = ntohl(ip_src);
197
198 uint32_t ip_dst;
199 memcpy(&ip_dst, buf + 16, 4);
200 ip_dst = ntohl(ip_dst);
201
202 uint8_t hlen = (buf[0] & 0x0f) * 4;
203 uint16_t port_src;
204 memcpy(&port_src, buf + hlen, 2);
205 port_src = ntohs(port_src);
206
207 hlist_add(ip_src, ip_dst, buf[9], port_src, (uint64_t) bytes);
208 }
209 }
210 }
211
212 close(s1);
213 close(s2);
214}
215
216static char *filter_start =
217"*filter\n"
218":INPUT DROP [0:0]\n"
219":FORWARD DROP [0:0]\n"
220":OUTPUT DROP [0:0]\n";
221
222// return 1 if error
223static int print_filter(FILE *fp) {
224 if (htable_empty)
225 return 1;
226 fprintf(fp, "%s\n", filter_start);
227 fprintf(fp, "-A INPUT -s 127.0.0.0/8 -j ACCEPT\n");
228 fprintf(fp, "-A OUTPUT -d 127.0.0.0/8 -j ACCEPT\n");
229 fprintf(fp, "\n");
230
231 int i;
232 for (i = 0; i < HMAX; i++) {
233 HList *ptr = htable[i];
234 while (ptr) {
235 if (ptr->instance == 1) {
236 char *protocol = (ptr->protocol == 6)? "tcp": "udp";
237 fprintf(fp, "-A INPUT -s %u.%u.%u.%u -sport %u -p %s -j ACCEPT\n",
238 PRINT_IP(ptr->ip_src),
239 ptr->port_src,
240 protocol);
241 fprintf(fp, "-A OUTPUT -d %u.%u.%u.%u -dport %u -p %s -j ACCEPT\n",
242 PRINT_IP(ptr->ip_src),
243 ptr->port_src,
244 protocol);
245 fprintf(fp, "\n");
246 }
247 ptr = ptr->next;
248 }
249 }
250 fprintf(fp, "COMMIT\n");
251
252 return 0;
253}
254
255static char *flush_rules[] = {
256 "-P INPUT ACCEPT",
257 "-P FORWARD ACCEPT",
258 "-P OUTPUT ACCEPT",
259 "-F",
260 "-X",
261 "-t nat -F",
262 "-t nat -X",
263 "-t mangle -F",
264 "-t mangle -X",
265 "iptables -t raw -F",
266 "-t raw -X",
267 NULL
268};
269
270static void flush_netfilter(void) {
271 // find iptables command
272 struct stat s;
273 char *iptables = NULL;
274 if (stat("/sbin/iptables", &s) == 0)
275 iptables = "/sbin/iptables";
276 else if (stat("/usr/sbin/iptables", &s) == 0)
277 iptables = "/usr/sbin/iptables";
278 if (iptables == NULL) {
279 fprintf(stderr, "Error: iptables command not found, netfilter not configured\n");
280 exit(1);
281 }
282
283 int i = 0;
284 while (flush_rules[i]) {
285 char *cmd;
286 if (asprintf(&cmd, "%s %s", iptables, flush_rules[i]) == -1)
287 errExit("asprintf");
288 int rv = system(cmd);
289 (void) rv;
290 free(cmd);
291 i++;
292 }
293}
294
295static void deploy_netfilter(void) {
296 int rv;
297 char *cmd;
298
299 // create temporary file
300 char fname[] = "/tmp/firejail-XXXXXX";
301 int fd = mkstemp(fname);
302 if (fd == -1) {
303 fprintf(stderr, "Error: cannot create temporary configuration file\n");
304 exit(1);
305 }
306
307 FILE* fp = fdopen(fd, "w");
308 if (!fp) {
309 rv = unlink(fname);
310 (void) rv;
311 fprintf(stderr, "Error: cannot create temporary configuration file\n");
312 exit(1);
313 }
314 print_filter(fp);
315 fclose(fp);
316
317 if (arg_log) {
318 logprintf("\n");
319 logprintf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
320 if (asprintf(&cmd, "cat %s >> %s", fname, arg_log) == -1)
321 errExit("asprintf");
322 rv = system(cmd);
323 (void) rv;
324 free(cmd);
325 logprintf("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
326 }
327
328 // find iptables command
329 struct stat s;
330 char *iptables = NULL;
331 char *iptables_restore = NULL;
332 if (stat("/sbin/iptables", &s) == 0) {
333 iptables = "/sbin/iptables";
334 iptables_restore = "/sbin/iptables-restore";
335 }
336 else if (stat("/usr/sbin/iptables", &s) == 0) {
337 iptables = "/usr/sbin/iptables";
338 iptables_restore = "/usr/sbin/iptables-restore";
339 }
340 if (iptables == NULL || iptables_restore == NULL) {
341 fprintf(stderr, "Error: iptables command not found, netfilter not configured\n");
342 rv = unlink(fname);
343 (void) rv;
344 exit(1);
345 }
346
347 // configuring
348 if (asprintf(&cmd, "%s %s", iptables_restore, fname) == -1)
349 errExit("asprintf");
350 rv = system(cmd);
351 if (rv)
352 fprintf(stdout, "Warning: possible netfilter problem!");
353 free(cmd);
354
355 sleep(1);
356 if (asprintf(&cmd, "%s %s", iptables_restore, fname) == -1)
357 errExit("asprintf");
358 rv = system(cmd);
359 free(cmd);
360
361 printf("Current firewall configuration:\n\n");
362 if (asprintf(&cmd, "%s -vL -n", iptables) == -1)
363 errExit("asprintf");
364 rv = system(cmd);
365
366 rv = unlink(fname);
367 (void) rv;
368 logprintf("\nfirewall deployed\n");
369}
370
371void logprintf(char* fmt, ...) {
372 if (!arg_log)
373 return;
374
375 FILE *fp = fopen(arg_log, "a");
376 if (fp) { // disregard if error
377 va_list args;
378 va_start(args,fmt);
379 vfprintf(fp, fmt, args);
380 va_end(args);
381 fclose(fp);
382 }
383}
384
385static void usage(void) {
386 printf("Usage: fnetlock [OPTIONS]\n");
387 printf("Options:\n");
388 printf(" --help, -? - this help screen\n");
389 printf(" --netfilter - build the firewall rules and commit them.\n");
390 printf(" --log=filename - logfile\n");
391 printf("\n");
392}
393
394int main(int argc, char **argv) {
395 int i;
396 printf("\n\n");
397
398 if (getuid() != 0) {
399 fprintf(stderr, "Error: you need to be root to run this program\n");
400 return 1;
401 }
402
403 for (i = 1; i < argc; i++) {
404 if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-?") == 0) {
405 usage();
406 return 0;
407 }
408 else if (strcmp(argv[i], "--netfilter") == 0)
409 arg_netfilter = 1;
410 else if (strncmp(argv[i], "--log=", 6) == 0)
411 arg_log = argv[i] + 6;
412 else {
413 fprintf(stderr, "Error: invalid argument\n");
414 return 1;
415 }
416 }
417
418 if (arg_netfilter) {
419 logprintf("starting network lockdown\n");
420 flush_netfilter();
421 }
422
423 ansi_clrscr(0);
424 run_trace();
425 if (arg_netfilter) {
426 deploy_netfilter();
427 sleep(3);
428 if (arg_log)
429 unlink(arg_log);
430 }
431
432 return 0;
433}
diff --git a/src/man/firejail-profile.txt b/src/man/firejail-profile.txt
index f6c905d59..9c251ec34 100644
--- a/src/man/firejail-profile.txt
+++ b/src/man/firejail-profile.txt
@@ -358,7 +358,7 @@ modifications are discarded when the sandbox is closed.
358Set working directory inside jail to the home directory, and failing that, the root directory. 358Set working directory inside jail to the home directory, and failing that, the root directory.
359.TP 359.TP
360\fBprivate-cwd directory 360\fBprivate-cwd directory
361Set working directory inside the jail. 361Set working directory inside the jail. Full directory path is required. Symbolic links are not allowed.
362.TP 362.TP
363\fBprivate-dev 363\fBprivate-dev
364Create a new /dev directory. Only disc, dri, dvb, hidraw, null, full, zero, tty, pts, ptmx, 364Create a new /dev directory. Only disc, dri, dvb, hidraw, null, full, zero, tty, pts, ptmx,
diff --git a/src/man/firejail.txt b/src/man/firejail.txt
index b5cb1e7c2..b366fed7c 100644
--- a/src/man/firejail.txt
+++ b/src/man/firejail.txt
@@ -1256,7 +1256,7 @@ $ firejail \-\-net=br0 \-\-net=br1
1256.TP 1256.TP
1257\fB\-\-net=ethernet_interface|wireless_interface 1257\fB\-\-net=ethernet_interface|wireless_interface
1258Enable a new network namespace and connect it 1258Enable a new network namespace and connect it
1259to this ethernet interface using the standard Linux macvlan|ipvaln 1259to this ethernet interface using the standard Linux macvlan|ipvlan
1260driver. Unless specified with option \-\-ip and \-\-defaultgw, an 1260driver. Unless specified with option \-\-ip and \-\-defaultgw, an
1261IP address and a default gateway will be assigned automatically 1261IP address and a default gateway will be assigned automatically
1262to the sandbox. The IP address is verified using ARP before 1262to the sandbox. The IP address is verified using ARP before
@@ -1479,6 +1479,29 @@ PID User RX(KB/s) TX(KB/s) Command
14791294 netblue 53.355 1.473 firejail \-\-net=eth0 firefox 14791294 netblue 53.355 1.473 firejail \-\-net=eth0 firefox
1480.br 1480.br
14817383 netblue 9.045 0.112 firejail \-\-net=eth0 transmission 14817383 netblue 9.045 0.112 firejail \-\-net=eth0 transmission
1482.TP
1483\fB\-\-nettrace=name|pid
1484Monitor TCP and UDP traffic coming into the sandbox specified by name or pid. Only networked sandboxes
1485created with \-\-net are supported.
1486.br
1487
1488.br
1489$ firejail --nettrace=browser
1490.br
14919.9.9.9:53 => 192.168.1.60 UDP: 122 B/sec
1492.br
149372.21.91.29:80 => 192.168.1.60 TCP: 257 B/sec
1494.br
149580.92.126.65:123 => 192.168.1.60 UDP: 25 B/sec
1496.br
149769.30.241.50:443 => 192.168.1.60 TCP: 88 KB/sec
1498.br
1499140.82.112.4:443 => 192.168.1.60 TCP: 1861 B/sec
1500.br
1501
1502.br
1503(14 streams in the last one minute)
1504
1482#endif 1505#endif
1483.TP 1506.TP
1484\fB\-\-nice=value 1507\fB\-\-nice=value
@@ -1863,7 +1886,6 @@ $ firejail \-\-private-cache openbox
1863.TP 1886.TP
1864\fB\-\-private-cwd 1887\fB\-\-private-cwd
1865Set working directory inside jail to the home directory, and failing that, the root directory. 1888Set working directory inside jail to the home directory, and failing that, the root directory.
1866.br
1867Does not impact working directory of profile include paths. 1889Does not impact working directory of profile include paths.
1868.br 1890.br
1869 1891
@@ -1884,7 +1906,7 @@ $ pwd
1884.TP 1906.TP
1885\fB\-\-private-cwd=directory 1907\fB\-\-private-cwd=directory
1886Set working directory inside the jail. 1908Set working directory inside the jail.
1887.br 1909Full directory path is required. Symbolic links are not allowed.
1888Does not impact working directory of profile include paths. 1910Does not impact working directory of profile include paths.
1889.br 1911.br
1890 1912
diff --git a/src/profstats/Makefile.in b/src/profstats/Makefile.in
index e025f5939..fa1b4f200 100644
--- a/src/profstats/Makefile.in
+++ b/src/profstats/Makefile.in
@@ -3,7 +3,7 @@ all: profstats
3 3
4include ../common.mk 4include ../common.mk
5 5
6%.o : %.c $(H_FILE_LIST) 6%.o : %.c $(H_FILE_LIST) ../include/common.h
7 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@ 7 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@
8 8
9profstats: $(OBJS) 9profstats: $(OBJS)
diff --git a/src/profstats/main.c b/src/profstats/main.c
index 9ddbb2633..bc5047bfe 100644
--- a/src/profstats/main.c
+++ b/src/profstats/main.c
@@ -10,17 +10,15 @@
10 * 10 *
11 * This program is distributed in the hope that it will be useful, 11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details. 14 * GNU General Public License for more details.
15 * 15 *
16 * You should have received a copy of the GNU General Public License along 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., 17 * with this program; if not, write to the Free Software Foundation, Inc.,
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 <stdio.h> 20
21#include <stdlib.h> 21#include "../include/common.h"
22#include <string.h>
23#include <assert.h>
24 22
25#define MAXBUF 2048 23#define MAXBUF 2048
26// stats 24// stats
@@ -99,8 +97,9 @@ static void usage(void) {
99 printf("\n"); 97 printf("\n");
100} 98}
101 99
102void process_file(const char *fname) { 100static void process_file(char *fname) {
103 assert(fname); 101 assert(fname);
102 char *tmpfname = NULL;
104 103
105 if (arg_debug) 104 if (arg_debug)
106 printf("processing #%s#\n", fname); 105 printf("processing #%s#\n", fname);
@@ -109,9 +108,19 @@ void process_file(const char *fname) {
109 108
110 FILE *fp = fopen(fname, "r"); 109 FILE *fp = fopen(fname, "r");
111 if (!fp) { 110 if (!fp) {
112 fprintf(stderr, "Warning: cannot open %s, while processing %s\n", fname, profile); 111 // the file was not found in the current directory
113 level--; 112 // look for it in /etc/firejail directory
114 return; 113 if (asprintf(&tmpfname, "%s/%s", SYSCONFDIR, fname) == -1)
114 errExit("asprintf");
115
116 fp = fopen(tmpfname, "r");
117 if (!fp) {
118 fprintf(stderr, "Warning: cannot open %s or %s, while processing %s\n", fname, tmpfname, profile);
119 free(tmpfname);
120 level--;
121 return;
122 }
123 fname = tmpfname;
115 } 124 }
116 125
117 int have_include_local = 0; 126 int have_include_local = 0;
@@ -204,6 +213,8 @@ void process_file(const char *fname) {
204 if (!have_include_local) 213 if (!have_include_local)
205 printf("No include .local found in %s\n", fname); 214 printf("No include .local found in %s\n", fname);
206 level--; 215 level--;
216 if (tmpfname)
217 free(tmpfname);
207} 218}
208 219
209int main(int argc, char **argv) { 220int main(int argc, char **argv) {
diff --git a/src/tools/profcleaner.c b/src/tools/profcleaner.c
deleted file mode 100644
index beff93199..000000000
--- a/src/tools/profcleaner.c
+++ /dev/null
@@ -1,75 +0,0 @@
1/*
2 * Copyright (C) 2014-2021 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
21//*************************************************************
22// Small utility program to convert profiles from blacklist/whitelist to deny/allow
23// Compile:
24// gcc -o profcleaner profcleaner.c
25// Usage:
26// profcleaner *.profile
27//*************************************************************
28
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <unistd.h>
33#define MAXBUF 4096
34
35int main(int argc, char **argv) {
36 printf("Usage: profcleaner files\n");
37 int i;
38
39 for (i = 1; i < argc; i++) {
40 FILE *fp = fopen(argv[i], "r");
41 if (!fp) {
42 fprintf(stderr, "Error: cannot open %s\n", argv[i]);
43 return 1;
44 }
45
46 FILE *fpout = fopen("profcleaner-tmp", "w");
47 if (!fpout) {
48 fprintf(stderr, "Error: cannot open output file\n");
49 return 1;
50 }
51
52 char buf[MAXBUF];
53 while (fgets(buf, MAXBUF, fp)) {
54 if (strncmp(buf, "blacklist-nolog", 15) == 0)
55 fprintf(fpout, "deny-nolog %s", buf + 15);
56 else if (strncmp(buf, "blacklist", 9) == 0)
57 fprintf(fpout, "deny %s", buf + 9);
58 else if (strncmp(buf, "noblacklist", 11) == 0)
59 fprintf(fpout, "nodeny %s", buf + 11);
60 else if (strncmp(buf, "whitelist", 9) == 0)
61 fprintf(fpout, "allow %s", buf + 9);
62 else if (strncmp(buf, "nowhitelist", 11) == 0)
63 fprintf(fpout, "noallow %s", buf + 11);
64 else
65 fprintf(fpout, "%s", buf);
66 }
67
68 fclose(fp);
69 fclose(fpout);
70 unlink(argv[i]);
71 rename("profcleaner-tmp", argv[i]);
72 }
73
74 return 0;
75}
diff --git a/src/tools/profcleaner.sh b/src/tools/profcleaner.sh
deleted file mode 100755
index 96402aed6..000000000
--- a/src/tools/profcleaner.sh
+++ /dev/null
@@ -1,45 +0,0 @@
1#!/bin/bash
2
3# Copyright (C) 2021 Firejail Authors
4#
5# This file is part of firejail project
6#
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation; either version 2 of the License, or
10# (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License along
18# with this program; if not, write to the Free Software Foundation, Inc.,
19# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21if [[ $1 == --help ]]; then
22 cat <<-EOM
23 USAGE:
24 profcleaner.sh --help Show this help message and exit
25 profcleaner.sh --system Clean all profiles in /etc/firejail
26 profcleaner.sh --user Clean all profiles in ~/.config/firejail
27 profcleaner.sh /path/to/profile1 /path/to/profile2 ...
28 EOM
29 exit 0
30fi
31
32if [[ $1 == --system ]]; then
33 profiles=(/etc/firejail/*.{inc,local,profile})
34elif [[ $1 == --user ]]; then
35 profiles=("$HOME"/.config/firejail/*.{inc,local,profile})
36else
37 profiles=("$@")
38fi
39
40sed -i -E \
41 -e "s/^(# |#)?(ignore )?blacklist/\1\2deny/" \
42 -e "s/^(# |#)?(ignore )?noblacklist/\1\2nodeny/" \
43 -e "s/^(# |#)?(ignore )?whitelist/\1\2allow/" \
44 -e "s/^(# |#)?(ignore )?nowhitelist/\1\2noallow/" \
45 "${profiles[@]}"
diff --git a/src/zsh_completion/_firejail.in b/src/zsh_completion/_firejail.in
index 6ce71aed8..8c1d758cc 100644
--- a/src/zsh_completion/_firejail.in
+++ b/src/zsh_completion/_firejail.in
@@ -48,8 +48,8 @@ _firejail_args=(
48 '*::arguments:_normal' 48 '*::arguments:_normal'
49 49
50 '--appimage[sandbox an AppImage application]' 50 '--appimage[sandbox an AppImage application]'
51 '--build[build a profile for the application and print it on stdout]' 51 '--build[build a whitelisted profile for the application and print it on stdout]'
52 '--build=-[build a profile for the application and save it]: :_files' 52 '--build=-[build a whitelisted profile for the application and save it]: :_files'
53 # Ignore that you can do -? too as it's the only short option 53 # Ignore that you can do -? too as it's the only short option
54 '--help[this help screen]' 54 '--help[this help screen]'
55 '--join=-[join the sandbox name|pid]: :_all_firejails' 55 '--join=-[join the sandbox name|pid]: :_all_firejails'
@@ -66,14 +66,14 @@ _firejail_args=(
66 '--ids-init[initialize IDS database]' 66 '--ids-init[initialize IDS database]'
67 67
68 '--debug[print sandbox debug messages]' 68 '--debug[print sandbox debug messages]'
69 '--debug-allow[debug file system access]' 69 '--debug-blacklists[debug blacklisting]'
70 '--debug-caps[print all recognized capabilities]' 70 '--debug-caps[print all recognized capabilities]'
71 '--debug-deny[debug file system access]'
72 '--debug-errnos[print all recognized error numbers]' 71 '--debug-errnos[print all recognized error numbers]'
73 '--debug-private-lib[debug for --private-lib option]' 72 '--debug-private-lib[debug for --private-lib option]'
74 '--debug-protocols[print all recognized protocols]' 73 '--debug-protocols[print all recognized protocols]'
75 '--debug-syscalls[print all recognized system calls]' 74 '--debug-syscalls[print all recognized system calls]'
76 '--debug-syscalls32[print all recognized 32 bit system calls]' 75 '--debug-syscalls32[print all recognized 32 bit system calls]'
76 '--debug-whitelists[debug whitelisting]'
77 77
78 '--caps.print=-[print the caps filter name|pid]:firejail:_all_firejails' 78 '--caps.print=-[print the caps filter name|pid]:firejail:_all_firejails'
79 '--cpu.print=-[print the cpus in use name|pid]: :_all_firejails' 79 '--cpu.print=-[print the cpus in use name|pid]: :_all_firejails'
@@ -86,13 +86,13 @@ _firejail_args=(
86 '--allusers[all user home directories are visible inside the sandbox]' 86 '--allusers[all user home directories are visible inside the sandbox]'
87 # Should be _files, a comma and files or files -/ 87 # Should be _files, a comma and files or files -/
88 '*--bind=-[mount-bind dirname1/filename1 on top of dirname2/filename2]: :(file1,file2 dir1,dir2)' 88 '*--bind=-[mount-bind dirname1/filename1 on top of dirname2/filename2]: :(file1,file2 dir1,dir2)'
89 '*--blacklist=-[blacklist directory or file]: :_files'
89 '--caps[enable default Linux capabilities filter]' 90 '--caps[enable default Linux capabilities filter]'
90 '--caps.drop=all[drop all capabilities]' 91 '--caps.drop=all[drop all capabilities]'
91 '*--caps.drop=-[drop capabilities: all|cap1,cap2,...]: :_caps' 92 '*--caps.drop=-[drop capabilities: all|cap1,cap2,...]: :_caps'
92 '*--caps.keep=-[keep capabilities: cap1,cap2,...]: :_caps' 93 '*--caps.keep=-[keep capabilities: cap1,cap2,...]: :_caps'
93 '--cgroup=-[place the sandbox in the specified control group]: :' 94 '--cgroup=-[place the sandbox in the specified control group]: :'
94 '--cpu=-[set cpu affinity]: :->cpus' 95 '--cpu=-[set cpu affinity]: :->cpus'
95 '*--deny=-[deny access to directory or file]: :_files'
96 "--deterministic-exit-code[always exit with first child's status code]" 96 "--deterministic-exit-code[always exit with first child's status code]"
97 '--deterministic-shutdown[terminate orphan processes]' 97 '--deterministic-shutdown[terminate orphan processes]'
98 '*--dns=-[set DNS server]: :' 98 '*--dns=-[set DNS server]: :'
@@ -116,7 +116,7 @@ _firejail_args=(
116 '--nice=-[set nice value]: :(1 10 15 20)' 116 '--nice=-[set nice value]: :(1 10 15 20)'
117 '--no3d[disable 3D hardware acceleration]' 117 '--no3d[disable 3D hardware acceleration]'
118 '--noautopulse[disable automatic ~/.config/pulse init]' 118 '--noautopulse[disable automatic ~/.config/pulse init]'
119 '--nodeny=-[disable deny command for file or directory]: :_files' 119 '--noblacklist=-[disable blacklist for file or directory]: :_files'
120 '--nodbus[disable D-Bus access]' 120 '--nodbus[disable D-Bus access]'
121 '--nodvd[disable DVD and audio CD devices]' 121 '--nodvd[disable DVD and audio CD devices]'
122 '*--noexec=-[remount the file or directory noexec nosuid and nodev]: :_files' 122 '*--noexec=-[remount the file or directory noexec nosuid and nodev]: :_files'
@@ -147,13 +147,13 @@ _firejail_args=(
147 '--rlimit-nproc=-[set the maximum number of processes that can be created for the real user ID of the calling process]: :' 147 '--rlimit-nproc=-[set the maximum number of processes that can be created for the real user ID of the calling process]: :'
148 '--rlimit-sigpending=-[set the maximum number of pending signals for a process]: :' 148 '--rlimit-sigpending=-[set the maximum number of pending signals for a process]: :'
149 '*--rmenv=-[remove environment variable in the new sandbox]: :_values environment-variables $(env | cut -d= -f1)' 149 '*--rmenv=-[remove environment variable in the new sandbox]: :_values environment-variables $(env | cut -d= -f1)'
150 '--seccomp[enable seccomp filter and drop the default syscalls]: :' 150 '--seccomp[enable seccomp filter and apply the default blacklist]: :'
151 '--seccomp=-[enable seccomp filter, drop the default syscall list and the syscalls specified by the command]: :->seccomp' 151 '--seccomp=-[enable seccomp filter, blacklist the default syscall list and the syscalls specified by the command]: :->seccomp'
152 '--seccomp.block-secondary[build only the native architecture filters]' 152 '--seccomp.block-secondary[build only the native architecture filters]'
153 '*--seccomp.drop=-[enable seccomp filter, and drop the syscalls specified by the command]: :->seccomp' 153 '*--seccomp.drop=-[enable seccomp filter, and blacklist the syscalls specified by the command]: :->seccomp'
154 '*--seccomp.keep=-[enable seccomp filter, and allow the syscalls specified by the command]: :->seccomp' 154 '*--seccomp.keep=-[enable seccomp filter, and whitelist the syscalls specified by the command]: :->seccomp'
155 '*--seccomp.32.drop=-[enable seccomp filter, and drop the 32 bit syscalls specified by the command]: :' 155 '*--seccomp.32.drop=-[enable seccomp filter, and blacklist the 32 bit syscalls specified by the command]: :'
156 '*--seccomp.32.keep=-[enable seccomp filter, and drop the 32 bit syscalls specified by the command]: :' 156 '*--seccomp.32.keep=-[enable seccomp filter, and whitelist the 32 bit syscalls specified by the command]: :'
157 # FIXME: Add errnos 157 # FIXME: Add errnos
158 '--seccomp-error-action=-[change error code, kill process or log the attempt]: :(kill log)' 158 '--seccomp-error-action=-[change error code, kill process or log the attempt]: :(kill log)'
159 '--shell=none[run the program directly without a user shell]' 159 '--shell=none[run the program directly without a user shell]'
@@ -161,7 +161,7 @@ _firejail_args=(
161 '--timeout=-[kill the sandbox automatically after the time has elapsed]: :' 161 '--timeout=-[kill the sandbox automatically after the time has elapsed]: :'
162 #'(--tracelog)--trace[trace open, access and connect system calls]' 162 #'(--tracelog)--trace[trace open, access and connect system calls]'
163 '(--tracelog)--trace=-[trace open, access and connect system calls]: :_files' 163 '(--tracelog)--trace=-[trace open, access and connect system calls]: :_files'
164 '(--trace)--tracelog[add a syslog message for every access to files or directories dropped by the security profile]' 164 '(--trace)--tracelog[add a syslog message for every access to files or directories blacklisted by the security profile]'
165 '(--private-etc)--writable-etc[/etc directory is mounted read-write]' 165 '(--private-etc)--writable-etc[/etc directory is mounted read-write]'
166 '--writable-run-user[allow access to /run/user/$UID/systemd and /run/user/$UID/gnupg]' 166 '--writable-run-user[allow access to /run/user/$UID/systemd and /run/user/$UID/gnupg]'
167 '--writable-var[/var directory is mounted read-write]' 167 '--writable-var[/var directory is mounted read-write]'
@@ -255,8 +255,8 @@ _firejail_args=(
255 '*--tmpfs=-[mount a tmpfs filesystem on directory dirname]: :_files -/' 255 '*--tmpfs=-[mount a tmpfs filesystem on directory dirname]: :_files -/'
256#endif 256#endif
257 257
258 '*--noallow=-[disable allow command for file or directory]: :_files' 258 '*--nowhitelist=-[disable whitelist for file or directory]: :_files'
259 '*--allow=-[allow file system access]: :_files' 259 '*--whitelist=-[whitelist directory or file]: :_files'
260 260
261#ifdef HAVE_X11 261#ifdef HAVE_X11
262 '--x11[enable X11 sandboxing. The software checks first if Xpra is installed, then it checks if Xephyr is installed. If all fails, it will attempt to use X11 security extension]' 262 '--x11[enable X11 sandboxing. The software checks first if Xpra is installed, then it checks if Xephyr is installed. If all fails, it will attempt to use X11 security extension]'