diff options
author | netblue30 <netblue30@protonmail.com> | 2021-12-28 18:48:13 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-12-28 18:48:13 +0000 |
commit | 92f438cf87234236939ed90db86162f4ae8bac76 (patch) | |
tree | 4f6f8fd560ca3c3ef978ce809d32213f1264a0c3 /src | |
parent | Implement a `whitelist-ro` command (diff) | |
parent | Merge pull request #4755 from kmk3/mpv-add-yt-dlp (diff) | |
download | firejail-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.c | 6 | ||||
-rw-r--r-- | src/firecfg/firecfg.config | 2 | ||||
-rw-r--r-- | src/firejail/firejail.h | 5 | ||||
-rw-r--r-- | src/firejail/fs_home.c | 13 | ||||
-rw-r--r-- | src/firejail/main.c | 182 | ||||
-rw-r--r-- | src/firejail/netfilter.c | 85 | ||||
-rw-r--r-- | src/firejail/profile.c | 41 | ||||
-rw-r--r-- | src/firejail/sandbox.c | 7 | ||||
-rw-r--r-- | src/firejail/usage.c | 1 | ||||
-rw-r--r-- | src/firejail/util.c | 49 | ||||
-rw-r--r-- | src/fnettrace/Makefile.in | 17 | ||||
-rw-r--r-- | src/fnettrace/fnettrace.h | 64 | ||||
-rw-r--r-- | src/fnettrace/main.c | 433 | ||||
-rw-r--r-- | src/man/firejail-profile.txt | 2 | ||||
-rw-r--r-- | src/man/firejail.txt | 28 | ||||
-rw-r--r-- | src/profstats/Makefile.in | 2 | ||||
-rw-r--r-- | src/profstats/main.c | 29 | ||||
-rw-r--r-- | src/tools/profcleaner.c | 75 | ||||
-rwxr-xr-x | src/tools/profcleaner.sh | 45 | ||||
-rw-r--r-- | src/zsh_completion/_firejail.in | 30 |
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) |
110 | bzflag | 110 | bzflag |
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) |
112 | cachy-browser | ||
112 | calibre | 113 | calibre |
113 | calligra | 114 | calligra |
114 | calligraauthor | 115 | calligraauthor |
@@ -157,6 +158,7 @@ com.github.bleakgrey.tootle | |||
157 | com.github.dahenson.agenda | 158 | com.github.dahenson.agenda |
158 | com.github.johnfactotum.Foliate | 159 | com.github.johnfactotum.Foliate |
159 | com.github.phase1geo.minder | 160 | com.github.phase1geo.minder |
161 | com.github.tchx84.Flatseal | ||
160 | com.gitlab.newsflash | 162 | com.gitlab.newsflash |
161 | conkeror | 163 | conkeror |
162 | conky | 164 | conky |
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)); | |||
506 | void fwarning(char* fmt, ...); | 506 | void fwarning(char* fmt, ...); |
507 | void fmessage(char* fmt, ...); | 507 | void fmessage(char* fmt, ...); |
508 | long long unsigned parse_arg_size(char *str); | 508 | long long unsigned parse_arg_size(char *str); |
509 | void drop_privs(int nogroups); | 509 | int check_can_drop_all_groups(); |
510 | void drop_privs(int force_nogroups); | ||
510 | int mkpath_as_root(const char* path); | 511 | int mkpath_as_root(const char* path); |
511 | void extract_command_name(int index, char **argv); | 512 | void extract_command_name(int index, char **argv); |
512 | void logsignal(int s); | 513 | void logsignal(int s); |
@@ -657,6 +658,8 @@ void set_cgroup(const char *fname, pid_t pid); | |||
657 | void check_output(int argc, char **argv); | 658 | void check_output(int argc, char **argv); |
658 | 659 | ||
659 | // netfilter.c | 660 | // netfilter.c |
661 | void netfilter_netlock(pid_t pid); | ||
662 | void netfilter_trace(pid_t pid); | ||
660 | void check_netfilter_file(const char *fname); | 663 | void check_netfilter_file(const char *fname); |
661 | void netfilter(const char *fname); | 664 | void netfilter(const char *fname); |
662 | void netfilter6(const char *fname); | 665 | void 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) { | |||
456 | void fs_check_private_cwd(const char *dir) { | 456 | void 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; |
470 | errout: | ||
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; | ||
3283 | printf("data #%s#\n", prf->data); | ||
3284 | if (prf->data) | ||
3285 | free(prf->data); | ||
3286 | printf("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 | ||
27 | void 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 | |||
90 | void 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 | |||
27 | void check_netfilter_file(const char *fname) { | 112 | void 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 | ||
107 | int 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 | |||
137 | out: | ||
138 | return can_drop_all_groups; | ||
139 | } | ||
140 | |||
106 | static int find_group(gid_t group, const gid_t *groups, int ngroups) { | 141 | static 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 |
219 | void drop_privs(int nogroups) { | 257 | void 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 | ||
2 | all: fnettrace | ||
3 | |||
4 | include ../common.mk | ||
5 | |||
6 | %.o : %.c $(H_FILE_LIST) | ||
7 | $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@ | ||
8 | |||
9 | fnettrace: $(OBJS) | ||
10 | $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) $(EXTRA_LDFLAGS) | ||
11 | |||
12 | .PHONY: clean | ||
13 | clean:; rm -fr *.o fnettrace *.gcov *.gcda *.gcno *.plist | ||
14 | |||
15 | .PHONY: distclean | ||
16 | distclean: 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 | |||
16 | void logprintf(char* fmt, ...); | ||
17 | |||
18 | static 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 | |||
27 | static 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 | |||
37 | static 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 | |||
46 | static 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 | |||
56 | static 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 | |||
4 | static int arg_netfilter = 0; | ||
5 | static char *arg_log = NULL; | ||
6 | |||
7 | typedef 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 | ||
20 | HList *htable[HMAX] = {NULL}; | ||
21 | static int htable_empty = 1; | ||
22 | |||
23 | static 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 | ||
63 | static 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 | |||
89 | static 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 | |||
139 | static 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 | |||
216 | static 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 | ||
223 | static 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 | |||
255 | static 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 | |||
270 | static 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 | |||
295 | static 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 | |||
371 | void 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 | |||
385 | static 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 | |||
394 | int 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. | |||
358 | Set working directory inside jail to the home directory, and failing that, the root directory. | 358 | Set 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 |
361 | Set working directory inside the jail. | 361 | Set 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 |
364 | Create a new /dev directory. Only disc, dri, dvb, hidraw, null, full, zero, tty, pts, ptmx, | 364 | Create 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 |
1258 | Enable a new network namespace and connect it | 1258 | Enable a new network namespace and connect it |
1259 | to this ethernet interface using the standard Linux macvlan|ipvaln | 1259 | to this ethernet interface using the standard Linux macvlan|ipvlan |
1260 | driver. Unless specified with option \-\-ip and \-\-defaultgw, an | 1260 | driver. Unless specified with option \-\-ip and \-\-defaultgw, an |
1261 | IP address and a default gateway will be assigned automatically | 1261 | IP address and a default gateway will be assigned automatically |
1262 | to the sandbox. The IP address is verified using ARP before | 1262 | to the sandbox. The IP address is verified using ARP before |
@@ -1479,6 +1479,29 @@ PID User RX(KB/s) TX(KB/s) Command | |||
1479 | 1294 netblue 53.355 1.473 firejail \-\-net=eth0 firefox | 1479 | 1294 netblue 53.355 1.473 firejail \-\-net=eth0 firefox |
1480 | .br | 1480 | .br |
1481 | 7383 netblue 9.045 0.112 firejail \-\-net=eth0 transmission | 1481 | 7383 netblue 9.045 0.112 firejail \-\-net=eth0 transmission |
1482 | .TP | ||
1483 | \fB\-\-nettrace=name|pid | ||
1484 | Monitor TCP and UDP traffic coming into the sandbox specified by name or pid. Only networked sandboxes | ||
1485 | created with \-\-net are supported. | ||
1486 | .br | ||
1487 | |||
1488 | .br | ||
1489 | $ firejail --nettrace=browser | ||
1490 | .br | ||
1491 | 9.9.9.9:53 => 192.168.1.60 UDP: 122 B/sec | ||
1492 | .br | ||
1493 | 72.21.91.29:80 => 192.168.1.60 TCP: 257 B/sec | ||
1494 | .br | ||
1495 | 80.92.126.65:123 => 192.168.1.60 UDP: 25 B/sec | ||
1496 | .br | ||
1497 | 69.30.241.50:443 => 192.168.1.60 TCP: 88 KB/sec | ||
1498 | .br | ||
1499 | 140.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 |
1865 | Set working directory inside jail to the home directory, and failing that, the root directory. | 1888 | Set working directory inside jail to the home directory, and failing that, the root directory. |
1866 | .br | ||
1867 | Does not impact working directory of profile include paths. | 1889 | Does 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 |
1886 | Set working directory inside the jail. | 1908 | Set working directory inside the jail. |
1887 | .br | 1909 | Full directory path is required. Symbolic links are not allowed. |
1888 | Does not impact working directory of profile include paths. | 1910 | Does 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 | ||
4 | include ../common.mk | 4 | include ../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 | ||
9 | profstats: $(OBJS) | 9 | profstats: $(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 | ||
102 | void process_file(const char *fname) { | 100 | static 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 | ||
209 | int main(int argc, char **argv) { | 220 | int 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 | |||
35 | int 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 | |||
21 | if [[ $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 | ||
30 | fi | ||
31 | |||
32 | if [[ $1 == --system ]]; then | ||
33 | profiles=(/etc/firejail/*.{inc,local,profile}) | ||
34 | elif [[ $1 == --user ]]; then | ||
35 | profiles=("$HOME"/.config/firejail/*.{inc,local,profile}) | ||
36 | else | ||
37 | profiles=("$@") | ||
38 | fi | ||
39 | |||
40 | sed -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]' |