aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/fbuilder/build_fs.c4
-rw-r--r--src/fbuilder/build_home.c2
-rw-r--r--src/fbuilder/build_profile.c26
-rw-r--r--src/fcopy/Makefile.in4
-rw-r--r--src/fcopy/main.c33
-rw-r--r--src/firecfg/firecfg.config10
-rw-r--r--src/firejail/appimage.c18
-rw-r--r--src/firejail/checkcfg.c6
-rw-r--r--src/firejail/chroot.c16
-rw-r--r--src/firejail/dbus.c30
-rw-r--r--src/firejail/env.c158
-rw-r--r--src/firejail/firejail.h11
-rw-r--r--src/firejail/fs.c53
-rw-r--r--src/firejail/fs_lib.c88
-rw-r--r--src/firejail/fs_lib2.c10
-rw-r--r--src/firejail/fs_whitelist.c2
-rw-r--r--src/firejail/join.c12
-rw-r--r--src/firejail/main.c58
-rw-r--r--src/firejail/no_sandbox.c2
-rw-r--r--src/firejail/output.c7
-rw-r--r--src/firejail/paths.c10
-rw-r--r--src/firejail/profile.c10
-rw-r--r--src/firejail/pulseaudio.c5
-rw-r--r--src/firejail/run_symlink.c52
-rw-r--r--src/firejail/sandbox.c7
-rw-r--r--src/firejail/sbox.c4
-rw-r--r--src/firejail/seccomp.c46
-rw-r--r--src/firejail/util.c45
-rw-r--r--src/firejail/x11.c61
-rw-r--r--src/firemon/Makefile.in2
-rw-r--r--src/fldd/Makefile.in4
-rw-r--r--src/fldd/main.c6
-rw-r--r--src/fnet/Makefile.in4
-rw-r--r--src/fnet/main.c8
-rw-r--r--src/fnetfilter/Makefile.in4
-rw-r--r--src/fnetfilter/main.c8
-rw-r--r--src/fsec-optimize/Makefile.in4
-rw-r--r--src/fsec-optimize/fsec_optimize.h1
-rw-r--r--src/fsec-optimize/main.c23
-rw-r--r--src/fsec-optimize/optimizer.c6
-rw-r--r--src/fsec-print/Makefile.in4
-rw-r--r--src/fsec-print/fsec_print.h1
-rw-r--r--src/fsec-print/main.c5
-rw-r--r--src/fseccomp/Makefile.in4
-rw-r--r--src/fseccomp/fseccomp.h1
-rw-r--r--src/fseccomp/main.c8
-rw-r--r--src/fseccomp/seccomp_secondary.c2
-rw-r--r--src/include/common.h7
-rw-r--r--src/include/seccomp.h10
-rw-r--r--src/include/syscall_armeabi.h1
-rw-r--r--src/include/syscall_i386.h1
-rw-r--r--src/include/syscall_x86_64.h1
-rw-r--r--src/lib/common.c34
-rw-r--r--src/lib/syscall.c4
-rw-r--r--src/man/firecfg.txt2
-rw-r--r--src/man/firejail-profile.txt7
-rw-r--r--src/man/firejail.txt12
-rw-r--r--src/profstats/main.c24
58 files changed, 633 insertions, 355 deletions
diff --git a/src/fbuilder/build_fs.c b/src/fbuilder/build_fs.c
index 1b8231033..0bc4a0ee2 100644
--- a/src/fbuilder/build_fs.c
+++ b/src/fbuilder/build_fs.c
@@ -217,6 +217,10 @@ void build_share(const char *fname, FILE *fp) {
217//******************************************* 217//*******************************************
218static FileDB *tmp_out = NULL; 218static FileDB *tmp_out = NULL;
219static void tmp_callback(char *ptr) { 219static void tmp_callback(char *ptr) {
220 // skip strace file
221 if (strncmp(ptr, "/tmp/firejail-strace", 20) == 0)
222 return;
223
220 tmp_out = filedb_add(tmp_out, ptr); 224 tmp_out = filedb_add(tmp_out, ptr);
221} 225}
222 226
diff --git a/src/fbuilder/build_home.c b/src/fbuilder/build_home.c
index fca3396c4..c0f4a3407 100644
--- a/src/fbuilder/build_home.c
+++ b/src/fbuilder/build_home.c
@@ -24,7 +24,7 @@ static FileDB *db_skip = NULL;
24static FileDB *db_out = NULL; 24static FileDB *db_out = NULL;
25 25
26static void load_whitelist_common(void) { 26static void load_whitelist_common(void) {
27 FILE *fp = fopen("/etc/firejail/whitelist-common.inc", "r"); 27 FILE *fp = fopen(SYSCONFDIR "/whitelist-common.inc", "r");
28 if (!fp) { 28 if (!fp) {
29 fprintf(stderr, "Error: cannot open whitelist-common.inc\n"); 29 fprintf(stderr, "Error: cannot open whitelist-common.inc\n");
30 exit(1); 30 exit(1);
diff --git a/src/fbuilder/build_profile.c b/src/fbuilder/build_profile.c
index adc00e67b..09f41a838 100644
--- a/src/fbuilder/build_profile.c
+++ b/src/fbuilder/build_profile.c
@@ -80,10 +80,19 @@ void build_profile(int argc, char **argv, int index, FILE *fp) {
80 stroutput, 80 stroutput,
81 }; 81 };
82 82
83 // detect strace 83 // detect strace and check if Yama LSM allows us to use it
84 int have_strace = 0; 84 int have_strace = 0;
85 if (access("/usr/bin/strace", X_OK) == 0) 85 int have_yama_permission = 1;
86 if (access("/usr/bin/strace", X_OK) == 0) {
86 have_strace = 1; 87 have_strace = 1;
88 FILE *ps = fopen("/proc/sys/kernel/yama/ptrace_scope", "r");
89 if (ps) {
90 unsigned val;
91 if (fscanf(ps, "%u", &val) == 1)
92 have_yama_permission = (val < 2);
93 fclose(ps);
94 }
95 }
87 96
88 // calculate command length 97 // calculate command length
89 unsigned len = (int) sizeof(cmdlist) / sizeof(char*) + argc - index + 1; 98 unsigned len = (int) sizeof(cmdlist) / sizeof(char*) + argc - index + 1;
@@ -93,10 +102,11 @@ void build_profile(int argc, char **argv, int index, FILE *fp) {
93 cmd[0] = cmdlist[0]; // explicit assignment to clean scan-build error 102 cmd[0] = cmdlist[0]; // explicit assignment to clean scan-build error
94 103
95 // build command 104 // build command
105 // skip strace if not installed, or no permission to use it
106 int skip_strace = !(have_strace && have_yama_permission);
96 unsigned i = 0; 107 unsigned i = 0;
97 for (i = 0; i < (int) sizeof(cmdlist) / sizeof(char*); i++) { 108 for (i = 0; i < (int) sizeof(cmdlist) / sizeof(char*); i++) {
98 // skip strace if not installed 109 if (skip_strace && strcmp(cmdlist[i], "/usr/bin/strace") == 0)
99 if (have_strace == 0 && strcmp(cmdlist[i], "/usr/bin/strace") == 0)
100 break; 110 break;
101 cmd[i] = cmdlist[i]; 111 cmd[i] = cmdlist[i];
102 } 112 }
@@ -172,12 +182,14 @@ void build_profile(int argc, char **argv, int index, FILE *fp) {
172 fprintf(fp, "caps.drop all\n"); 182 fprintf(fp, "caps.drop all\n");
173 fprintf(fp, "nonewprivs\n"); 183 fprintf(fp, "nonewprivs\n");
174 fprintf(fp, "seccomp\n"); 184 fprintf(fp, "seccomp\n");
175 if (have_strace) 185 if (!have_strace) {
176 build_seccomp(strace_output, fp);
177 else {
178 fprintf(fp, "# If you install strace on your system, Firejail will also create a\n"); 186 fprintf(fp, "# If you install strace on your system, Firejail will also create a\n");
179 fprintf(fp, "# whitelisted seccomp filter.\n"); 187 fprintf(fp, "# whitelisted seccomp filter.\n");
180 } 188 }
189 else if (!have_yama_permission)
190 fprintf(fp, "# Yama security module prevents creation of a whitelisted seccomp filter\n");
191 else
192 build_seccomp(strace_output, fp);
181 fprintf(fp, "\n"); 193 fprintf(fp, "\n");
182 194
183 fprintf(fp, "### network\n"); 195 fprintf(fp, "### network\n");
diff --git a/src/fcopy/Makefile.in b/src/fcopy/Makefile.in
index 64e277e2d..85f84aa32 100644
--- a/src/fcopy/Makefile.in
+++ b/src/fcopy/Makefile.in
@@ -5,8 +5,8 @@ include ../common.mk
5%.o : %.c $(H_FILE_LIST) ../include/common.h ../include/syscall.h 5%.o : %.c $(H_FILE_LIST) ../include/common.h ../include/syscall.h
6 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@ 6 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@
7 7
8fcopy: $(OBJS) 8fcopy: $(OBJS) ../lib/common.o
9 $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) $(EXTRA_LDFLAGS) 9 $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/common.o $(LIBS) $(EXTRA_LDFLAGS)
10 10
11clean:; rm -fr *.o fcopy *.gcov *.gcda *.gcno *.plist 11clean:; rm -fr *.o fcopy *.gcov *.gcda *.gcno *.plist
12 12
diff --git a/src/fcopy/main.c b/src/fcopy/main.c
index 67237b4ea..e65501d6d 100644
--- a/src/fcopy/main.c
+++ b/src/fcopy/main.c
@@ -23,7 +23,6 @@
23#include <ftw.h> 23#include <ftw.h>
24#include <errno.h> 24#include <errno.h>
25#include <pwd.h> 25#include <pwd.h>
26#include <sys/prctl.h>
27 26
28#if HAVE_SELINUX 27#if HAVE_SELINUX
29#include <sys/stat.h> 28#include <sys/stat.h>
@@ -112,7 +111,7 @@ static void copy_file(const char *srcname, const char *destname, mode_t mode, ui
112 } 111 }
113 112
114 // open destination 113 // open destination
115 int dst = open(destname, O_CREAT|O_WRONLY|O_TRUNC, 0755); 114 int dst = open(destname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR);
116 if (dst < 0) { 115 if (dst < 0) {
117 if (!arg_quiet) 116 if (!arg_quiet)
118 fprintf(stderr, "Warning fcopy: cannot open %s, file not copied\n", destname); 117 fprintf(stderr, "Warning fcopy: cannot open %s, file not copied\n", destname);
@@ -133,7 +132,8 @@ static void copy_file(const char *srcname, const char *destname, mode_t mode, ui
133 done += rv; 132 done += rv;
134 } 133 }
135 } 134 }
136 fflush(0); 135 if (len < 0)
136 goto errexit;
137 137
138 if (fchown(dst, uid, gid) == -1) 138 if (fchown(dst, uid, gid) == -1)
139 goto errexit; 139 goto errexit;
@@ -180,7 +180,7 @@ void copy_link(const char *target, const char *linkpath, mode_t mode, uid_t uid,
180 180
181 // if the link is already there, don't create it 181 // if the link is already there, don't create it
182 struct stat s; 182 struct stat s;
183 if (stat(linkpath, &s) == 0) 183 if (lstat(linkpath, &s) == 0)
184 return; 184 return;
185 185
186 char *rp = realpath(target, NULL); 186 char *rp = realpath(target, NULL);
@@ -412,30 +412,21 @@ int main(int argc, char **argv) {
412 exit(1); 412 exit(1);
413 } 413 }
414 414
415#ifdef WARN_DUMPABLE 415 warn_dumpable();
416 if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 1 && getuid())
417 fprintf(stderr, "Error fcopy: I am dumpable\n");
418#endif
419
420 // trim trailing chars
421 if (src[strlen(src) - 1] == '/')
422 src[strlen(src) - 1] = '\0';
423 if (dest[strlen(dest) - 1] == '/')
424 dest[strlen(dest) - 1] = '\0';
425 416
426 // check the two files; remove ending / 417 // check the two files; remove ending /
427 int len = strlen(src); 418 size_t len = strlen(src);
428 if (src[len - 1] == '/') 419 while (len > 1 && src[len - 1] == '/')
429 src[len - 1] = '\0'; 420 src[--len] = '\0';
430 if (strcspn(src, "\\*&!?\"'<>%^(){}[];,") != (size_t)len) { 421 if (strcspn(src, "\\*&!?\"'<>%^(){}[];,") != len) {
431 fprintf(stderr, "Error fcopy: invalid source file name %s\n", src); 422 fprintf(stderr, "Error fcopy: invalid source file name %s\n", src);
432 exit(1); 423 exit(1);
433 } 424 }
434 425
435 len = strlen(dest); 426 len = strlen(dest);
436 if (dest[len - 1] == '/') 427 while (len > 1 && dest[len - 1] == '/')
437 dest[len - 1] = '\0'; 428 dest[--len] = '\0';
438 if (strcspn(dest, "\\*&!?\"'<>%^(){}[];,~") != (size_t)len) { 429 if (strcspn(dest, "\\*&!?\"'<>%^(){}[];,~") != len) {
439 fprintf(stderr, "Error fcopy: invalid dest file name %s\n", dest); 430 fprintf(stderr, "Error fcopy: invalid dest file name %s\n", dest);
440 exit(1); 431 exit(1);
441 } 432 }
diff --git a/src/firecfg/firecfg.config b/src/firecfg/firecfg.config
index 23b1e364a..d056d0654 100644
--- a/src/firecfg/firecfg.config
+++ b/src/firecfg/firecfg.config
@@ -69,6 +69,7 @@ autokey-gtk
69autokey-qt 69autokey-qt
70autokey-run 70autokey-run
71autokey-shell 71autokey-shell
72avidemux3_qt5
72aweather 73aweather
73baloo_file 74baloo_file
74baloo_filemetadata_temp_extractor 75baloo_filemetadata_temp_extractor
@@ -106,6 +107,7 @@ calligra
106calligraauthor 107calligraauthor
107calligraconverter 108calligraconverter
108calligraflow 109calligraflow
110calligragemini
109calligraplan 111calligraplan
110calligraplanwork 112calligraplanwork
111calligrasheets 113calligrasheets
@@ -149,6 +151,7 @@ conkeror
149conky 151conky
150conplay 152conplay
151corebird 153corebird
154coyim
152crawl 155crawl
153crawl-tiles 156crawl-tiles
154crow 157crow
@@ -172,6 +175,7 @@ dino-im
172discord 175discord
173discord-canary 176discord-canary
174display 177display
178display-im6.q16
175dnox 179dnox
176dnscrypt-proxy 180dnscrypt-proxy
177dnsmasq 181dnsmasq
@@ -390,6 +394,7 @@ kazam
390kcalc 394kcalc
391# kdeinit4 395# kdeinit4
392kdenlive 396kdenlive
397kdiff3
393keepass 398keepass
394keepass2 399keepass2
395keepassx 400keepassx
@@ -455,6 +460,7 @@ macrofusion
455magicor 460magicor
456# man 461# man
457manaplus 462manaplus
463marker
458masterpdfeditor 464masterpdfeditor
459masterpdfeditor4 465masterpdfeditor4
460masterpdfeditor5 466masterpdfeditor5
@@ -532,6 +538,7 @@ mypaint
532mypaint-ora-thumbnailer 538mypaint-ora-thumbnailer
533natron 539natron
534ncdu 540ncdu
541neomutt
535netactview 542netactview
536nethack 543nethack
537netsurf 544netsurf
@@ -621,6 +628,7 @@ qemu-launcher
621qgis 628qgis
622qlipper 629qlipper
623qmmp 630qmmp
631qnapi
624qpdfview 632qpdfview
625qt-faststart 633qt-faststart
626qtox 634qtox
@@ -662,6 +670,7 @@ secret-tool
662shellcheck 670shellcheck
663shortwave 671shortwave
664shotcut 672shotcut
673shotwell
665signal-cli 674signal-cli
666signal-desktop 675signal-desktop
667silentarmy 676silentarmy
@@ -771,6 +780,7 @@ tremulous
771trojita 780trojita
772truecraft 781truecraft
773tshark 782tshark
783tutanota-desktop
774tuxguitar 784tuxguitar
775tvbrowser 785tvbrowser
776twitch 786twitch
diff --git a/src/firejail/appimage.c b/src/firejail/appimage.c
index 6190b6f01..dd94b9921 100644
--- a/src/firejail/appimage.c
+++ b/src/firejail/appimage.c
@@ -131,14 +131,16 @@ void appimage_set(const char *appimage) {
131 errExit("Failed to obtain absolute path"); 131 errExit("Failed to obtain absolute path");
132 132
133 // set environment 133 // set environment
134 if (setenv("APPIMAGE", abspath, 1) < 0) 134 env_store_name_val("APPIMAGE", abspath, SETENV);
135 errExit("setenv"); 135
136 if (mntdir && setenv("APPDIR", mntdir, 1) < 0) 136 if (mntdir)
137 errExit("setenv"); 137 env_store_name_val("APPDIR", mntdir, SETENV);
138 if (size != 0 && setenv("ARGV0", appimage, 1) < 0) 138
139 errExit("setenv"); 139 if (size != 0)
140 if (cfg.cwd && setenv("OWD", cfg.cwd, 1) < 0) 140 env_store_name_val("ARGV0", appimage, SETENV);
141 errExit("setenv"); 141
142 if (cfg.cwd)
143 env_store_name_val("OWD", cfg.cwd, SETENV);
142 144
143 // build new command line 145 // build new command line
144 if (asprintf(&cfg.command_line, "%s/AppRun", mntdir) == -1) 146 if (asprintf(&cfg.command_line, "%s/AppRun", mntdir) == -1)
diff --git a/src/firejail/checkcfg.c b/src/firejail/checkcfg.c
index 085221464..fb2171a55 100644
--- a/src/firejail/checkcfg.c
+++ b/src/firejail/checkcfg.c
@@ -215,10 +215,8 @@ int checkcfg(int val) {
215 } 215 }
216 216
217 // file copy limit 217 // file copy limit
218 else if (strncmp(ptr, "file-copy-limit ", 16) == 0) { 218 else if (strncmp(ptr, "file-copy-limit ", 16) == 0)
219 if (setenv("FIREJAIL_FILE_COPY_LIMIT", ptr + 16, 1) == -1) 219 env_store_name_val("FIREJAIL_FILE_COPY_LIMIT", ptr + 16, SETENV);
220 errExit("setenv");
221 }
222 220
223 // timeout for join option 221 // timeout for join option
224 else if (strncmp(ptr, "join-timeout ", 13) == 0) 222 else if (strncmp(ptr, "join-timeout ", 13) == 0)
diff --git a/src/firejail/chroot.c b/src/firejail/chroot.c
index cfa32d1d3..9253490ca 100644
--- a/src/firejail/chroot.c
+++ b/src/firejail/chroot.c
@@ -173,9 +173,21 @@ void fs_chroot(const char *rootdir) {
173 173
174 // x11 174 // x11
175 // if users want this mount, they should set FIREJAIL_CHROOT_X11 175 // if users want this mount, they should set FIREJAIL_CHROOT_X11
176 if (getenv("FIREJAIL_X11") || getenv("FIREJAIL_CHROOT_X11")) { 176 if (env_get("FIREJAIL_X11") || env_get("FIREJAIL_CHROOT_X11")) {
177 if (arg_debug) 177 if (arg_debug)
178 printf("Mounting /tmp/.X11-unix on chroot /tmp/.X11-unix\n"); 178 printf("Mounting /tmp/.X11-unix on chroot /tmp/.X11-unix\n");
179 struct stat s1, s2;
180 if (stat("/tmp", &s1) || lstat("/tmp/.X11-unix", &s2))
181 errExit("mounting /tmp/.X11-unix");
182 if ((s1.st_mode & S_ISVTX) != S_ISVTX) {
183 fprintf(stderr, "Error: sticky bit not set on /tmp directory\n");
184 exit(1);
185 }
186 if (s2.st_uid != 0) {
187 fprintf(stderr, "Error: /tmp/.X11-unix not owned by root user\n");
188 exit(1);
189 }
190
179 check_subdir(parentfd, "tmp/.X11-unix", 0); 191 check_subdir(parentfd, "tmp/.X11-unix", 0);
180 fd = openat(parentfd, "tmp/.X11-unix", O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); 192 fd = openat(parentfd, "tmp/.X11-unix", O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
181 if (fd == -1) 193 if (fd == -1)
@@ -194,7 +206,7 @@ void fs_chroot(const char *rootdir) {
194 check_subdir(parentfd, "run", 1); 206 check_subdir(parentfd, "run", 1);
195 207
196 // pulseaudio; only support for default directory /run/user/$UID/pulse 208 // pulseaudio; only support for default directory /run/user/$UID/pulse
197 if (getenv("FIREJAIL_CHROOT_PULSE")) { 209 if (env_get("FIREJAIL_CHROOT_PULSE")) {
198 char *pulse; 210 char *pulse;
199 if (asprintf(&pulse, "%s/run/user/%d/pulse", cfg.chrootdir, getuid()) == -1) 211 if (asprintf(&pulse, "%s/run/user/%d/pulse", cfg.chrootdir, getuid()) == -1)
200 errExit("asprintf"); 212 errExit("asprintf");
diff --git a/src/firejail/dbus.c b/src/firejail/dbus.c
index 3cf75ed84..1d0f07089 100644
--- a/src/firejail/dbus.c
+++ b/src/firejail/dbus.c
@@ -329,7 +329,7 @@ void dbus_proxy_start(void) {
329 errExit("close"); 329 errExit("close");
330 330
331 if (arg_dbus_user == DBUS_POLICY_FILTER) { 331 if (arg_dbus_user == DBUS_POLICY_FILTER) {
332 char *user_env = getenv(DBUS_SESSION_BUS_ADDRESS_ENV); 332 const char *user_env = env_get(DBUS_SESSION_BUS_ADDRESS_ENV);
333 if (user_env == NULL) { 333 if (user_env == NULL) {
334 char *dbus_user_socket = find_user_socket(); 334 char *dbus_user_socket = find_user_socket();
335 write_arg(args_pipe[1], DBUS_SOCKET_PATH_PREFIX "%s", 335 write_arg(args_pipe[1], DBUS_SOCKET_PATH_PREFIX "%s",
@@ -350,7 +350,7 @@ void dbus_proxy_start(void) {
350 } 350 }
351 351
352 if (arg_dbus_system == DBUS_POLICY_FILTER) { 352 if (arg_dbus_system == DBUS_POLICY_FILTER) {
353 char *system_env = getenv(DBUS_SYSTEM_BUS_ADDRESS_ENV); 353 const char *system_env = env_get(DBUS_SYSTEM_BUS_ADDRESS_ENV);
354 if (system_env == NULL) { 354 if (system_env == NULL) {
355 write_arg(args_pipe[1], 355 write_arg(args_pipe[1],
356 DBUS_SOCKET_PATH_PREFIX DBUS_SYSTEM_SOCKET); 356 DBUS_SOCKET_PATH_PREFIX DBUS_SYSTEM_SOCKET);
@@ -435,8 +435,8 @@ static void socket_overlay(char *socket_path, char *proxy_path) {
435 close(fd); 435 close(fd);
436} 436}
437 437
438static char *get_socket_env(const char *name) { 438static const char *get_socket_env(const char *name) {
439 char *value = getenv(name); 439 const char *value = env_get(name);
440 if (value == NULL) 440 if (value == NULL)
441 return NULL; 441 return NULL;
442 if (strncmp(value, DBUS_SOCKET_PATH_PREFIX, 442 if (strncmp(value, DBUS_SOCKET_PATH_PREFIX,
@@ -446,21 +446,13 @@ static char *get_socket_env(const char *name) {
446} 446}
447 447
448void dbus_set_session_bus_env(void) { 448void dbus_set_session_bus_env(void) {
449 if (setenv(DBUS_SESSION_BUS_ADDRESS_ENV, 449 env_store_name_val(DBUS_SESSION_BUS_ADDRESS_ENV,
450 DBUS_SOCKET_PATH_PREFIX RUN_DBUS_USER_SOCKET, 1) == -1) { 450 DBUS_SOCKET_PATH_PREFIX RUN_DBUS_USER_SOCKET, SETENV);
451 fprintf(stderr, "Error: cannot modify " DBUS_SESSION_BUS_ADDRESS_ENV
452 " required by --dbus-user\n");
453 exit(1);
454 }
455} 451}
456 452
457void dbus_set_system_bus_env(void) { 453void dbus_set_system_bus_env(void) {
458 if (setenv(DBUS_SYSTEM_BUS_ADDRESS_ENV, 454 env_store_name_val(DBUS_SYSTEM_BUS_ADDRESS_ENV,
459 DBUS_SOCKET_PATH_PREFIX RUN_DBUS_SYSTEM_SOCKET, 1) == -1) { 455 DBUS_SOCKET_PATH_PREFIX RUN_DBUS_SYSTEM_SOCKET, SETENV);
460 fprintf(stderr, "Error: cannot modify " DBUS_SYSTEM_BUS_ADDRESS_ENV
461 " required by --dbus-system\n");
462 exit(1);
463 }
464} 456}
465 457
466static void disable_socket_dir(void) { 458static void disable_socket_dir(void) {
@@ -506,7 +498,7 @@ void dbus_apply_policy(void) {
506 errExit("asprintf"); 498 errExit("asprintf");
507 disable_file_or_dir(dbus_user_socket2); 499 disable_file_or_dir(dbus_user_socket2);
508 500
509 char *user_env = get_socket_env(DBUS_SESSION_BUS_ADDRESS_ENV); 501 const char *user_env = get_socket_env(DBUS_SESSION_BUS_ADDRESS_ENV);
510 if (user_env != NULL && strcmp(user_env, dbus_user_socket) != 0 && 502 if (user_env != NULL && strcmp(user_env, dbus_user_socket) != 0 &&
511 strcmp(user_env, dbus_user_socket2) != 0) 503 strcmp(user_env, dbus_user_socket2) != 0)
512 disable_file_or_dir(user_env); 504 disable_file_or_dir(user_env);
@@ -535,7 +527,7 @@ void dbus_apply_policy(void) {
535 527
536 disable_file_or_dir(DBUS_SYSTEM_SOCKET); 528 disable_file_or_dir(DBUS_SYSTEM_SOCKET);
537 529
538 char *system_env = get_socket_env(DBUS_SYSTEM_BUS_ADDRESS_ENV); 530 const char *system_env = get_socket_env(DBUS_SYSTEM_BUS_ADDRESS_ENV);
539 if (system_env != NULL && strcmp(system_env, DBUS_SYSTEM_SOCKET) != 0) 531 if (system_env != NULL && strcmp(system_env, DBUS_SYSTEM_SOCKET) != 0)
540 disable_file_or_dir(system_env); 532 disable_file_or_dir(system_env);
541 533
@@ -561,4 +553,4 @@ void dbus_apply_policy(void) {
561 553
562 fwarning("An abstract unix socket for session D-BUS might still be available. Use --net or remove unix from --protocol set.\n"); 554 fwarning("An abstract unix socket for session D-BUS might still be available. Use --net or remove unix from --protocol set.\n");
563} 555}
564#endif // HAVE_DBUSPROXY \ No newline at end of file 556#endif // HAVE_DBUSPROXY
diff --git a/src/firejail/env.c b/src/firejail/env.c
index d74cebb39..9ee6c6bfb 100644
--- a/src/firejail/env.c
+++ b/src/firejail/env.c
@@ -25,8 +25,8 @@
25 25
26typedef struct env_t { 26typedef struct env_t {
27 struct env_t *next; 27 struct env_t *next;
28 char *name; 28 const char *name;
29 char *value; 29 const char *value;
30 ENV_OP op; 30 ENV_OP op;
31} Env; 31} Env;
32static Env *envlist = NULL; 32static Env *envlist = NULL;
@@ -117,45 +117,35 @@ void env_ibus_load(void) {
117// default sandbox env variables 117// default sandbox env variables
118void env_defaults(void) { 118void env_defaults(void) {
119 // Qt fixes 119 // Qt fixes
120 if (setenv("QT_X11_NO_MITSHM", "1", 1) < 0) 120 env_store_name_val("QT_X11_NO_MITSHM", "1", SETENV);
121 errExit("setenv"); 121 env_store_name_val("QML_DISABLE_DISK_CACHE", "1", SETENV);
122 if (setenv("QML_DISABLE_DISK_CACHE", "1", 1) < 0) 122// env_store_name_val("QTWEBENGINE_DISABLE_SANDBOX", "1", SETENV);
123 errExit("setenv"); 123// env_store_name_val("MOZ_NO_REMOTE, "1", SETENV);
124// if (setenv("QTWEBENGINE_DISABLE_SANDBOX", "1", 1) < 0) 124 env_store_name_val("container", "firejail", SETENV); // LXC sets container=lxc,
125// errExit("setenv");
126// if (setenv("MOZ_NO_REMOTE, "1", 1) < 0)
127// errExit("setenv");
128 if (setenv("container", "firejail", 1) < 0) // LXC sets container=lxc,
129 errExit("setenv");
130 if (!cfg.shell) 125 if (!cfg.shell)
131 cfg.shell = guess_shell(); 126 cfg.shell = guess_shell();
132 if (cfg.shell && setenv("SHELL", cfg.shell, 1) < 0) 127 if (cfg.shell)
133 errExit("setenv"); 128 env_store_name_val("SHELL", cfg.shell, SETENV);
134 129
135 // spawn KIO slaves inside the sandbox 130 // spawn KIO slaves inside the sandbox
136 if (setenv("KDE_FORK_SLAVES", "1", 1) < 0) 131 env_store_name_val("KDE_FORK_SLAVES", "1", SETENV);
137 errExit("setenv");
138 132
139 // set prompt color to green 133 // set prompt color to green
140 int set_prompt = 0; 134 int set_prompt = 0;
141 if (checkcfg(CFG_FIREJAIL_PROMPT)) 135 if (checkcfg(CFG_FIREJAIL_PROMPT))
142 set_prompt = 1; 136 set_prompt = 1;
143 else { // check FIREJAIL_PROMPT="yes" environment variable 137 else { // check FIREJAIL_PROMPT="yes" environment variable
144 char *prompt = getenv("FIREJAIL_PROMPT"); 138 const char *prompt = env_get("FIREJAIL_PROMPT");
145 if (prompt && strcmp(prompt, "yes") == 0) 139 if (prompt && strcmp(prompt, "yes") == 0)
146 set_prompt = 1; 140 set_prompt = 1;
147 } 141 }
148 142
149 if (set_prompt) { 143 if (set_prompt)
150 //export PS1='\[\e[1;32m\][\u@\h \W]\$\[\e[0m\] ' 144 //export PS1='\[\e[1;32m\][\u@\h \W]\$\[\e[0m\] '
151 if (setenv("PROMPT_COMMAND", "export PS1=\"\\[\\e[1;32m\\][\\u@\\h \\W]\\$\\[\\e[0m\\] \"", 1) < 0) 145 env_store_name_val("PROMPT_COMMAND", "export PS1=\"\\[\\e[1;32m\\][\\u@\\h \\W]\\$\\[\\e[0m\\] \"", SETENV);
152 errExit("setenv"); 146 else
153 }
154 else {
155 // remove PROMPT_COMMAND 147 // remove PROMPT_COMMAND
156 if (setenv("PROMPT_COMMAND", ":", 1) < 0) // unsetenv() will not work here, bash still picks it up from somewhere 148 env_store_name_val("PROMPT_COMMAND", ":", SETENV); // unsetenv() will not work here, bash still picks it up from somewhere
157 errExit("setenv");
158 }
159 149
160 // set the window title 150 // set the window title
161 if (!arg_quiet && isatty(STDOUT_FILENO)) 151 if (!arg_quiet && isatty(STDOUT_FILENO))
@@ -163,14 +153,13 @@ void env_defaults(void) {
163 153
164 // pass --quiet as an environment variable, in case the command calls further firejailed commands 154 // pass --quiet as an environment variable, in case the command calls further firejailed commands
165 if (arg_quiet) 155 if (arg_quiet)
166 setenv("FIREJAIL_QUIET", "yes", 1); 156 env_store_name_val("FIREJAIL_QUIET", "yes", SETENV);
167 157
168 fflush(0); 158 fflush(0);
169} 159}
170 160
171// parse and store the environment setting 161// parse and store the environment setting
172void env_store(const char *str, ENV_OP op) { 162void env_store(const char *str, ENV_OP op) {
173 EUID_ASSERT();
174 assert(str); 163 assert(str);
175 164
176 // some basic checking 165 // some basic checking
@@ -181,8 +170,7 @@ void env_store(const char *str, ENV_OP op) {
181 if (!ptr) 170 if (!ptr)
182 goto errexit; 171 goto errexit;
183 ptr++; 172 ptr++;
184 if (*ptr == '\0') 173 op = SETENV;
185 goto errexit;
186 } 174 }
187 175
188 // build list entry 176 // build list entry
@@ -210,8 +198,40 @@ errexit:
210 exit(1); 198 exit(1);
211} 199}
212 200
201void env_store_name_val(const char *name, const char *val, ENV_OP op) {
202 assert(name);
203
204 // some basic checking
205 if (*name == '\0')
206 goto errexit;
207
208 // build list entry
209 Env *env = calloc(1, sizeof(Env));
210 if (!env)
211 errExit("calloc");
212
213 env->name = strdup(name);
214 if (env->name == NULL)
215 errExit("strdup");
216
217 if (op == SETENV) {
218 env->value = strdup(val);
219 if (env->value == NULL)
220 errExit("strdup");
221 }
222 env->op = op;
223
224 // add entry to the list
225 env_add(env);
226 return;
227
228errexit:
229 fprintf(stderr, "Error: invalid --env setting\n");
230 exit(1);
231}
232
213// set env variables in the new sandbox process 233// set env variables in the new sandbox process
214void env_apply(void) { 234void env_apply_all(void) {
215 Env *env = envlist; 235 Env *env = envlist;
216 236
217 while (env) { 237 while (env) {
@@ -225,3 +245,81 @@ void env_apply(void) {
225 env = env->next; 245 env = env->next;
226 } 246 }
227} 247}
248
249// get env variable
250const char *env_get(const char *name) {
251 Env *env = envlist;
252 const char *r = NULL;
253
254 while (env) {
255 if (strcmp(env->name, name) == 0) {
256 if (env->op == SETENV)
257 r = env->value;
258 else if (env->op == RMENV)
259 r = NULL;
260 }
261 env = env->next;
262 }
263 return r;
264}
265
266static const char * const env_whitelist[] = {
267 "LANG",
268 "LANGUAGE",
269 "LC_MESSAGES",
270 "PATH",
271 "DISPLAY" // required by X11
272};
273
274static const char * const env_whitelist_sbox[] = {
275 "FIREJAIL_DEBUG",
276 "FIREJAIL_FILE_COPY_LIMIT",
277 "FIREJAIL_PLUGIN",
278 "FIREJAIL_QUIET",
279 "FIREJAIL_SECCOMP_ERROR_ACTION",
280 "FIREJAIL_TEST_ARGUMENTS",
281 "FIREJAIL_TRACEFILE"
282};
283
284static void env_apply_list(const char * const *list, unsigned int num_items) {
285 Env *env = envlist;
286
287 while (env) {
288 if (env->op == SETENV) {
289 for (unsigned int i = 0; i < num_items; i++)
290 if (strcmp(env->name, list[i]) == 0) {
291 // sanity check for whitelisted environment variables
292 if (strlen(env->name) + strlen(env->value) >= MAX_ENV_LEN) {
293 fprintf(stderr, "Error: too long environment variable %s, please use --rmenv\n", env->name);
294 exit(1);
295 }
296
297 //fprintf(stderr, "whitelisted env var %s=%s\n", env->name, env->value);
298 if (setenv(env->name, env->value, 1) < 0)
299 errExit("setenv");
300 break;
301 }
302 } else if (env->op == RMENV)
303 unsetenv(env->name);
304
305 env = env->next;
306 }
307}
308
309// Filter env variables in main firejail process. All variables will
310// be reapplied for the sandboxed app by env_apply_all().
311void env_apply_whitelist(void) {
312 int r;
313
314 r = clearenv();
315 if (r != 0)
316 errExit("clearenv");
317
318 env_apply_list(env_whitelist, ARRAY_SIZE(env_whitelist));
319}
320
321// Filter env variables for a sbox app
322void env_apply_whitelist_sbox(void) {
323 env_apply_whitelist();
324 env_apply_list(env_whitelist_sbox, ARRAY_SIZE(env_whitelist_sbox));
325}
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h
index 80987e494..e352dadc4 100644
--- a/src/firejail/firejail.h
+++ b/src/firejail/firejail.h
@@ -81,6 +81,8 @@
81 (void) rv;\ 81 (void) rv;\
82 } while (0) 82 } while (0)
83 83
84#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
85
84// main.c 86// main.c
85typedef struct bridge_t { 87typedef struct bridge_t {
86 // on the host 88 // on the host
@@ -513,7 +515,6 @@ void check_private_dir(void);
513void update_map(char *mapping, char *map_file); 515void update_map(char *mapping, char *map_file);
514void wait_for_other(int fd); 516void wait_for_other(int fd);
515void notify_other(int fd); 517void notify_other(int fd);
516const char *gnu_basename(const char *path);
517uid_t pid_get_uid(pid_t pid); 518uid_t pid_get_uid(pid_t pid);
518uid_t get_group_id(const char *group); 519uid_t get_group_id(const char *group);
519int remove_overlay_directory(void); 520int remove_overlay_directory(void);
@@ -656,7 +657,7 @@ int check_kernel_procs(void);
656void run_no_sandbox(int argc, char **argv) __attribute__((noreturn)); 657void run_no_sandbox(int argc, char **argv) __attribute__((noreturn));
657 658
658#define MAX_ENVS 256 // some sane maximum number of environment variables 659#define MAX_ENVS 256 // some sane maximum number of environment variables
659#define MAX_ENV_LEN (PATH_MAX + 32) // FOOBAR=SOME_PATH 660#define MAX_ENV_LEN (PATH_MAX + 32) // FOOBAR=SOME_PATH, only applied to Firejail's own sandboxed apps
660// env.c 661// env.c
661typedef enum { 662typedef enum {
662 SETENV = 0, 663 SETENV = 0,
@@ -664,8 +665,12 @@ typedef enum {
664} ENV_OP; 665} ENV_OP;
665 666
666void env_store(const char *str, ENV_OP op); 667void env_store(const char *str, ENV_OP op);
667void env_apply(void); 668void env_store_name_val(const char *name, const char *val, ENV_OP op);
669void env_apply_all(void);
670void env_apply_whitelist(void);
671void env_apply_whitelist_sbox(void);
668void env_defaults(void); 672void env_defaults(void);
673const char *env_get(const char *name);
669void env_ibus_load(void); 674void env_ibus_load(void);
670 675
671// fs_whitelist.c 676// fs_whitelist.c
diff --git a/src/firejail/fs.c b/src/firejail/fs.c
index 0d4e496e8..ef1f87f0c 100644
--- a/src/firejail/fs.c
+++ b/src/firejail/fs.c
@@ -487,27 +487,26 @@ void fs_tmpfs(const char *dir, unsigned check_owner) {
487 close(fd); 487 close(fd);
488} 488}
489 489
490// remount path, but preserve existing mount flags; requires a resolved path 490// remount path, preserving other mount flags; requires a resolved path
491static void fs_remount_simple(const char *path, OPERATION op) { 491static void fs_remount_simple(const char *path, OPERATION op) {
492 assert(path); 492 assert(path);
493 493
494 // open path without following symbolic links 494 // open path without following symbolic links
495 int fd = safe_fd(path, O_PATH|O_NOFOLLOW|O_CLOEXEC); 495 int fd1 = safe_fd(path, O_PATH|O_NOFOLLOW|O_CLOEXEC);
496 if (fd == -1) 496 if (fd1 == -1)
497 goto out; 497 goto out;
498 // identify file owner 498 struct stat s1;
499 struct stat s; 499 if (fstat(fd1, &s1) == -1) {
500 if (fstat(fd, &s) == -1) {
501 // fstat can fail with EACCES if path is a FUSE mount, 500 // fstat can fail with EACCES if path is a FUSE mount,
502 // mounted without 'allow_root' or 'allow_other' 501 // mounted without 'allow_root' or 'allow_other'
503 if (errno != EACCES) 502 if (errno != EACCES)
504 errExit("fstat"); 503 errExit("fstat");
505 close(fd); 504 close(fd1);
506 goto out; 505 goto out;
507 } 506 }
508 // get mount flags 507 // get mount flags
509 struct statvfs buf; 508 struct statvfs buf;
510 if (fstatvfs(fd, &buf) == -1) 509 if (fstatvfs(fd1, &buf) == -1)
511 errExit("fstatvfs"); 510 errExit("fstatvfs");
512 unsigned long flags = buf.f_flag; 511 unsigned long flags = buf.f_flag;
513 512
@@ -515,13 +514,13 @@ static void fs_remount_simple(const char *path, OPERATION op) {
515 if (op == MOUNT_RDWR || op == MOUNT_RDWR_NOCHECK) { 514 if (op == MOUNT_RDWR || op == MOUNT_RDWR_NOCHECK) {
516 // nothing to do if there is no read-only flag 515 // nothing to do if there is no read-only flag
517 if ((flags & MS_RDONLY) == 0) { 516 if ((flags & MS_RDONLY) == 0) {
518 close(fd); 517 close(fd1);
519 return; 518 return;
520 } 519 }
521 // allow only user owned directories, except the user is root 520 // allow only user owned directories, except the user is root
522 if (op == MOUNT_RDWR && getuid() != 0 && s.st_uid != getuid()) { 521 if (op != MOUNT_RDWR_NOCHECK && getuid() != 0 && s1.st_uid != getuid()) {
523 fwarning("you are not allowed to change %s to read-write\n", path); 522 fwarning("you are not allowed to change %s to read-write\n", path);
524 close(fd); 523 close(fd1);
525 return; 524 return;
526 } 525 }
527 flags &= ~MS_RDONLY; 526 flags &= ~MS_RDONLY;
@@ -530,7 +529,7 @@ static void fs_remount_simple(const char *path, OPERATION op) {
530 else if (op == MOUNT_NOEXEC) { 529 else if (op == MOUNT_NOEXEC) {
531 // nothing to do if path is mounted noexec already 530 // nothing to do if path is mounted noexec already
532 if ((flags & (MS_NOEXEC|MS_NODEV|MS_NOSUID)) == (MS_NOEXEC|MS_NODEV|MS_NOSUID)) { 531 if ((flags & (MS_NOEXEC|MS_NODEV|MS_NOSUID)) == (MS_NOEXEC|MS_NODEV|MS_NOSUID)) {
533 close(fd); 532 close(fd1);
534 return; 533 return;
535 } 534 }
536 flags |= MS_NOEXEC|MS_NODEV|MS_NOSUID; 535 flags |= MS_NOEXEC|MS_NODEV|MS_NOSUID;
@@ -539,7 +538,7 @@ static void fs_remount_simple(const char *path, OPERATION op) {
539 else if (op == MOUNT_READONLY) { 538 else if (op == MOUNT_READONLY) {
540 // nothing to do if path is mounted read-only already 539 // nothing to do if path is mounted read-only already
541 if ((flags & MS_RDONLY) == MS_RDONLY) { 540 if ((flags & MS_RDONLY) == MS_RDONLY) {
542 close(fd); 541 close(fd1);
543 return; 542 return;
544 } 543 }
545 flags |= MS_RDONLY; 544 flags |= MS_RDONLY;
@@ -549,21 +548,26 @@ static void fs_remount_simple(const char *path, OPERATION op) {
549 548
550 if (arg_debug) 549 if (arg_debug)
551 printf("Mounting %s %s\n", opstr[op], path); 550 printf("Mounting %s %s\n", opstr[op], path);
552 // mount --bind /bin /bin 551 // mount --bind path path
553 char *proc; 552 char *proc;
554 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) 553 if (asprintf(&proc, "/proc/self/fd/%d", fd1) == -1)
555 errExit("asprintf"); 554 errExit("asprintf");
556 if (mount(proc, proc, NULL, MS_BIND|MS_REC, NULL) < 0) 555 if (mount(proc, proc, NULL, MS_BIND|MS_REC, NULL) < 0)
557 errExit("mount"); 556 errExit("mount");
558 free(proc); 557 free(proc);
559 close(fd);
560 558
561 // mount --bind -o remount,ro /bin 559 // mount --bind -o remount,ro path
562 // we need to open path again 560 // need to open path again without following symbolic links
563 fd = safe_fd(path, O_PATH|O_NOFOLLOW|O_CLOEXEC); 561 int fd2 = safe_fd(path, O_PATH|O_NOFOLLOW|O_CLOEXEC);
564 if (fd == -1) 562 if (fd2 == -1)
565 errExit("open"); 563 errExit("open");
566 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) 564 struct stat s2;
565 if (fstat(fd2, &s2) == -1)
566 errExit("fstat");
567 // device and inode number should be the same
568 if (s1.st_dev != s2.st_dev || s1.st_ino != s2.st_ino)
569 errLogExit("invalid %s mount", opstr[op]);
570 if (asprintf(&proc, "/proc/self/fd/%d", fd2) == -1)
567 errExit("asprintf"); 571 errExit("asprintf");
568 if (mount(NULL, proc, NULL, flags|MS_BIND|MS_REMOUNT, NULL) < 0) 572 if (mount(NULL, proc, NULL, flags|MS_BIND|MS_REMOUNT, NULL) < 0)
569 errExit("mount"); 573 errExit("mount");
@@ -579,7 +583,8 @@ static void fs_remount_simple(const char *path, OPERATION op) {
579 errLogExit("invalid %s mount", opstr[op]); 583 errLogExit("invalid %s mount", opstr[op]);
580 fs_logger2(opstr[op], path); 584 fs_logger2(opstr[op], path);
581 free(proc); 585 free(proc);
582 close(fd); 586 close(fd1);
587 close(fd2);
583 return; 588 return;
584 589
585out: 590out:
@@ -795,6 +800,8 @@ void disable_config(void) {
795 disable_file(BLACKLIST_FILE, RUN_FIREJAIL_PROFILE_DIR); 800 disable_file(BLACKLIST_FILE, RUN_FIREJAIL_PROFILE_DIR);
796 if (stat(RUN_FIREJAIL_X11_DIR, &s) == 0) 801 if (stat(RUN_FIREJAIL_X11_DIR, &s) == 0)
797 disable_file(BLACKLIST_FILE, RUN_FIREJAIL_X11_DIR); 802 disable_file(BLACKLIST_FILE, RUN_FIREJAIL_X11_DIR);
803 if (!arg_appimage && stat(RUN_FIREJAIL_APPIMAGE_DIR, &s) == 0)
804 disable_file(BLACKLIST_FILE, RUN_FIREJAIL_APPIMAGE_DIR);
798} 805}
799 806
800 807
@@ -1219,7 +1226,7 @@ void fs_private_tmp(void) {
1219 printf("Generate private-tmp whitelist commands\n"); 1226 printf("Generate private-tmp whitelist commands\n");
1220 1227
1221 // check XAUTHORITY file, KDE keeps it under /tmp 1228 // check XAUTHORITY file, KDE keeps it under /tmp
1222 char *xauth = getenv("XAUTHORITY"); 1229 const char *xauth = env_get("XAUTHORITY");
1223 if (xauth) { 1230 if (xauth) {
1224 char *rp = realpath(xauth, NULL); 1231 char *rp = realpath(xauth, NULL);
1225 if (rp && strncmp(rp, "/tmp/", 5) == 0) { 1232 if (rp && strncmp(rp, "/tmp/", 5) == 0) {
diff --git a/src/firejail/fs_lib.c b/src/firejail/fs_lib.c
index 5cfd33b42..b8c1b21b1 100644
--- a/src/firejail/fs_lib.c
+++ b/src/firejail/fs_lib.c
@@ -33,6 +33,52 @@ extern void fslib_install_system(void);
33static int lib_cnt = 0; 33static int lib_cnt = 0;
34static int dir_cnt = 0; 34static int dir_cnt = 0;
35 35
36char *find_in_path(const char *program) {
37 EUID_ASSERT();
38 if (arg_debug)
39 printf("Searching $PATH for %s\n", program);
40
41 char self[MAXBUF];
42 ssize_t len = readlink("/proc/self/exe", self, MAXBUF - 1);
43 if (len < 0)
44 errExit("readlink");
45 self[len] = '\0';
46
47 char *path = getenv("PATH");
48 if (!path)
49 return NULL;
50 char *dup = strdup(path);
51 if (!dup)
52 errExit("strdup");
53 char *tok = strtok(dup, ":");
54 while (tok) {
55 char *fname;
56 if (asprintf(&fname, "%s/%s", tok, program) == -1)
57 errExit("asprintf");
58
59 if (arg_debug)
60 printf("trying #%s#\n", fname);
61 struct stat s;
62 if (stat(fname, &s) == 0) {
63 // but skip links created by firecfg
64 char *rp = realpath(fname, NULL);
65 if (!rp)
66 errExit("realpath");
67 if (strcmp(self, rp) != 0) {
68 free(rp);
69 free(dup);
70 return fname;
71 }
72 free(rp);
73 }
74 free(fname);
75 tok = strtok(NULL, ":");
76 }
77
78 free(dup);
79 return NULL;
80}
81
36static void report_duplication(const char *full_path) { 82static void report_duplication(const char *full_path) {
37 char *fname = strrchr(full_path, '/'); 83 char *fname = strrchr(full_path, '/');
38 if (fname && *(++fname) != '\0') { 84 if (fname && *(++fname) != '\0') {
@@ -165,7 +211,7 @@ void fslib_copy_dir(const char *full_path) {
165 mkdir_attr(dest, 0755, 0, 0); 211 mkdir_attr(dest, 0755, 0, 0);
166 212
167 if (mount(full_path, dest, NULL, MS_BIND|MS_REC, NULL) < 0 || 213 if (mount(full_path, dest, NULL, MS_BIND|MS_REC, NULL) < 0 ||
168 mount(NULL, dest, NULL, MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NODEV|MS_REC, NULL) < 0) 214 mount(NULL, dest, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_NOSUID|MS_NODEV|MS_REC, NULL) < 0)
169 errExit("mount bind"); 215 errExit("mount bind");
170 fs_logger2("clone", full_path); 216 fs_logger2("clone", full_path);
171 fs_logger2("mount", full_path); 217 fs_logger2("mount", full_path);
@@ -336,11 +382,40 @@ void fs_private_lib(void) {
336 // start timetrace 382 // start timetrace
337 timetrace_start(); 383 timetrace_start();
338 384
385 // bring in firejail executable libraries in case we are redirected here by a firejail symlink from /usr/local/bin/firejail
386 if (arg_debug || arg_debug_private_lib)
387 printf("Installing Firejail libraries\n");
388 fslib_install_list(PATH_FIREJAIL);
389
390 // bring in firejail directory
391 fslib_install_list(LIBDIR "/firejail");
392
393 // bring in dhclient libraries
394 if (any_dhcp()) {
395 if (arg_debug || arg_debug_private_lib)
396 printf("Installing dhclient libraries\n");
397 fslib_install_list(RUN_MNT_DIR "/dhclient");
398 }
399 fmessage("Firejail libraries installed in %0.2f ms\n", timetrace_end());
400
401 timetrace_start();
402
339 // copy the libs in the new lib directory for the main exe 403 // copy the libs in the new lib directory for the main exe
340 if (cfg.original_program_index > 0) { 404 if (cfg.original_program_index > 0) {
341 if (arg_debug || arg_debug_private_lib) 405 if (arg_debug || arg_debug_private_lib)
342 printf("Installing sandboxed program libraries\n"); 406 printf("Installing sandboxed program libraries\n");
343 fslib_install_list(cfg.original_argv[cfg.original_program_index]); 407
408 if (strchr(cfg.original_argv[cfg.original_program_index], '/'))
409 fslib_install_list(cfg.original_argv[cfg.original_program_index]);
410 else { // search executable in $PATH
411 EUID_USER();
412 char *fname = find_in_path(cfg.original_argv[cfg.original_program_index]);
413 EUID_ROOT();
414 if (fname) {
415 fslib_install_list(fname);
416 free(fname);
417 }
418 }
344 } 419 }
345 420
346 // for the shell 421 // for the shell
@@ -369,18 +444,11 @@ void fs_private_lib(void) {
369 } 444 }
370 fmessage("Program libraries installed in %0.2f ms\n", timetrace_end()); 445 fmessage("Program libraries installed in %0.2f ms\n", timetrace_end());
371 446
372 // install the reset of the system libraries 447 // install the rest of the system libraries
373 if (arg_debug || arg_debug_private_lib) 448 if (arg_debug || arg_debug_private_lib)
374 printf("Installing system libraries\n"); 449 printf("Installing system libraries\n");
375 fslib_install_system(); 450 fslib_install_system();
376 451
377 // bring in firejail directory for --trace and seccomp post exec
378 // bring in firejail executable libraries in case we are redirected here by a firejail symlink from /usr/local/bin/firejail
379 fslib_install_list("/usr/bin/firejail,firejail"); // todo: use the installed path for the executable
380
381 // install libraries needed by fcopy
382 fslib_install_list(PATH_FCOPY);
383
384 fmessage("Installed %d %s and %d %s\n", lib_cnt, (lib_cnt == 1)? "library": "libraries", 452 fmessage("Installed %d %s and %d %s\n", lib_cnt, (lib_cnt == 1)? "library": "libraries",
385 dir_cnt, (dir_cnt == 1)? "directory": "directories"); 453 dir_cnt, (dir_cnt == 1)? "directory": "directories");
386 454
diff --git a/src/firejail/fs_lib2.c b/src/firejail/fs_lib2.c
index b2ae07f3e..95e10ee05 100644
--- a/src/firejail/fs_lib2.c
+++ b/src/firejail/fs_lib2.c
@@ -30,6 +30,7 @@ extern void fslib_copy_dir(const char *full_path);
30//*************************************************************** 30//***************************************************************
31// standard libc libraries based on Debian's libc6 package 31// standard libc libraries based on Debian's libc6 package
32// selinux seems to be linked in most command line utilities 32// selinux seems to be linked in most command line utilities
33// libpcre2 is a dependency of selinux
33// locale (/usr/lib/locale) - without it, the program will default to "C" locale 34// locale (/usr/lib/locale) - without it, the program will default to "C" locale
34typedef struct liblist_t { 35typedef struct liblist_t {
35 const char *name; 36 const char *name;
@@ -38,6 +39,7 @@ typedef struct liblist_t {
38 39
39static LibList libc_list[] = { 40static LibList libc_list[] = {
40 { "libselinux.so.", 0 }, 41 { "libselinux.so.", 0 },
42 { "libpcre2-8.so.", 0 },
41 { "libapparmor.so.", 0}, 43 { "libapparmor.so.", 0},
42 { "ld-linux-x86-64.so.", 0 }, 44 { "ld-linux-x86-64.so.", 0 },
43 { "libanl.so.", 0 }, 45 { "libanl.so.", 0 },
@@ -104,17 +106,15 @@ static void stdc(const char *dirname) {
104 106
105void fslib_install_stdc(void) { 107void fslib_install_stdc(void) {
106 // install standard C libraries 108 // install standard C libraries
109 timetrace_start();
107 struct stat s; 110 struct stat s;
108 char *stdclib = "/lib64"; // CentOS, Fedora, Arch
109
110 if (stat("/lib/x86_64-linux-gnu", &s) == 0) { // Debian & friends 111 if (stat("/lib/x86_64-linux-gnu", &s) == 0) { // Debian & friends
111 mkdir_attr(RUN_LIB_DIR "/x86_64-linux-gnu", 0755, 0, 0); 112 mkdir_attr(RUN_LIB_DIR "/x86_64-linux-gnu", 0755, 0, 0);
112 selinux_relabel_path(RUN_LIB_DIR "/x86_64-linux-gnu", "/lib/x86_64-linux-gnu"); 113 selinux_relabel_path(RUN_LIB_DIR "/x86_64-linux-gnu", "/lib/x86_64-linux-gnu");
113 stdclib = "/lib/x86_64-linux-gnu"; 114 stdc("/lib/x86_64-linux-gnu");
114 } 115 }
115 116
116 timetrace_start(); 117 stdc("/lib64"); // CentOS, Fedora, Arch, ld-linux.so in Debian & friends
117 stdc(stdclib);
118 118
119 // install locale 119 // install locale
120 if (stat("/usr/lib/locale", &s) == 0) 120 if (stat("/usr/lib/locale", &s) == 0)
diff --git a/src/firejail/fs_whitelist.c b/src/firejail/fs_whitelist.c
index 1d7552339..d60c57fec 100644
--- a/src/firejail/fs_whitelist.c
+++ b/src/firejail/fs_whitelist.c
@@ -778,7 +778,7 @@ void fs_whitelist(void) {
778 fs_logger("tmpfs /tmp"); 778 fs_logger("tmpfs /tmp");
779 779
780 // pam-tmpdir - issue #2685 780 // pam-tmpdir - issue #2685
781 char *env = getenv("TMP"); 781 const char *env = env_get("TMP");
782 if (env) { 782 if (env) {
783 char *pamtmpdir; 783 char *pamtmpdir;
784 if (asprintf(&pamtmpdir, "/tmp/user/%u", getuid()) == -1) 784 if (asprintf(&pamtmpdir, "/tmp/user/%u", getuid()) == -1)
diff --git a/src/firejail/join.c b/src/firejail/join.c
index d2f802add..bdd0f286c 100644
--- a/src/firejail/join.c
+++ b/src/firejail/join.c
@@ -296,7 +296,7 @@ static void extract_umask(pid_t pid) {
296 fprintf(stderr, "Error: cannot open umask file\n"); 296 fprintf(stderr, "Error: cannot open umask file\n");
297 exit(1); 297 exit(1);
298 } 298 }
299 if (fscanf(fp, "%o", &orig_umask) != 1) { 299 if (fscanf(fp, "%3o", &orig_umask) != 1) {
300 fprintf(stderr, "Error: cannot read umask\n"); 300 fprintf(stderr, "Error: cannot read umask\n");
301 exit(1); 301 exit(1);
302 } 302 }
@@ -335,7 +335,7 @@ bool is_ready_for_join(const pid_t pid) {
335 struct stat s; 335 struct stat s;
336 if (fstat(fd, &s) == -1) 336 if (fstat(fd, &s) == -1)
337 errExit("fstat"); 337 errExit("fstat");
338 if (!S_ISREG(s.st_mode) || s.st_uid != 0) { 338 if (!S_ISREG(s.st_mode) || s.st_uid != 0 || s.st_size != 1) {
339 close(fd); 339 close(fd);
340 return false; 340 return false;
341 } 341 }
@@ -411,7 +411,7 @@ void join(pid_t pid, int argc, char **argv, int index) {
411 extract_x11_display(parent); 411 extract_x11_display(parent);
412 412
413 int shfd = -1; 413 int shfd = -1;
414 if (!arg_shell_none) 414 if (!arg_shell_none && !arg_audit)
415 shfd = open_shell(); 415 shfd = open_shell();
416 416
417 EUID_ROOT(); 417 EUID_ROOT();
@@ -423,6 +423,7 @@ void join(pid_t pid, int argc, char **argv, int index) {
423 extract_cgroup(pid); 423 extract_cgroup(pid);
424 extract_nogroups(pid); 424 extract_nogroups(pid);
425 extract_user_namespace(pid); 425 extract_user_namespace(pid);
426 extract_umask(pid);
426#ifdef HAVE_APPARMOR 427#ifdef HAVE_APPARMOR
427 extract_apparmor(pid); 428 extract_apparmor(pid);
428#endif 429#endif
@@ -432,9 +433,6 @@ void join(pid_t pid, int argc, char **argv, int index) {
432 if (cfg.cgroup) // not available for uid 0 433 if (cfg.cgroup) // not available for uid 0
433 set_cgroup(cfg.cgroup); 434 set_cgroup(cfg.cgroup);
434 435
435 // set umask, also uid 0
436 extract_umask(pid);
437
438 // join namespaces 436 // join namespaces
439 if (arg_join_network) { 437 if (arg_join_network) {
440 if (join_namespace(pid, "net")) 438 if (join_namespace(pid, "net"))
@@ -563,7 +561,7 @@ void join(pid_t pid, int argc, char **argv, int index) {
563 char *display_str; 561 char *display_str;
564 if (asprintf(&display_str, ":%d", display) == -1) 562 if (asprintf(&display_str, ":%d", display) == -1)
565 errExit("asprintf"); 563 errExit("asprintf");
566 setenv("DISPLAY", display_str, 1); 564 env_store_name_val("DISPLAY", display_str, SETENV);
567 free(display_str); 565 free(display_str);
568 } 566 }
569 567
diff --git a/src/firejail/main.c b/src/firejail/main.c
index e5d8a4720..982a4c7a6 100644
--- a/src/firejail/main.c
+++ b/src/firejail/main.c
@@ -861,19 +861,20 @@ static void run_cmd_and_exit(int i, int argc, char **argv) {
861} 861}
862 862
863char *guess_shell(void) { 863char *guess_shell(void) {
864 char *shell = NULL; 864 const char *shell;
865 char *retval;
865 struct stat s; 866 struct stat s;
866 867
867 shell = getenv("SHELL"); 868 shell = env_get("SHELL");
868 if (shell) { 869 if (shell) {
869 invalid_filename(shell, 0); // no globbing 870 invalid_filename(shell, 0); // no globbing
870 if (!is_dir(shell) && strstr(shell, "..") == NULL && stat(shell, &s) == 0 && access(shell, X_OK) == 0 && 871 if (!is_dir(shell) && strstr(shell, "..") == NULL && stat(shell, &s) == 0 && access(shell, X_OK) == 0 &&
871 strcmp(shell, PATH_FIREJAIL) != 0) 872 strcmp(shell, PATH_FIREJAIL) != 0)
872 return shell; 873 goto found;
873 } 874 }
874 875
875 // shells in order of preference 876 // shells in order of preference
876 char *shells[] = {"/bin/bash", "/bin/csh", "/usr/bin/zsh", "/bin/sh", "/bin/ash", NULL }; 877 static const char * const shells[] = {"/bin/bash", "/bin/csh", "/usr/bin/zsh", "/bin/sh", "/bin/ash", NULL };
877 878
878 int i = 0; 879 int i = 0;
879 while (shells[i] != NULL) { 880 while (shells[i] != NULL) {
@@ -884,8 +885,11 @@ char *guess_shell(void) {
884 } 885 }
885 i++; 886 i++;
886 } 887 }
887 888 found:
888 return shell; 889 retval = strdup(shell);
890 if (!retval)
891 errExit("strdup");
892 return retval;
889} 893}
890 894
891// return argument index 895// return argument index
@@ -926,9 +930,13 @@ static void run_builder(int argc, char **argv) {
926 if (setresuid(-1, getuid(), getuid()) != 0) 930 if (setresuid(-1, getuid(), getuid()) != 0)
927 errExit("setresuid"); 931 errExit("setresuid");
928 932
933 assert(env_get("LD_PRELOAD") == NULL);
929 assert(getenv("LD_PRELOAD") == NULL); 934 assert(getenv("LD_PRELOAD") == NULL);
930 umask(orig_umask); 935 umask(orig_umask);
931 936
937 // restore some environment variables
938 env_apply_whitelist_sbox();
939
932 argv[0] = LIBDIR "/firejail/fbuilder"; 940 argv[0] = LIBDIR "/firejail/fbuilder";
933 execvp(argv[0], argv); 941 execvp(argv[0], argv);
934 942
@@ -994,6 +1002,16 @@ int main(int argc, char **argv, char **envp) {
994 exit(1); 1002 exit(1);
995 } 1003 }
996 1004
1005 // Stash environment variables
1006 for (i = 0, ptr = envp; ptr && *ptr && i < MAX_ENVS; i++, ptr++)
1007 env_store(*ptr, SETENV);
1008
1009 // sanity check for environment variables
1010 if (i >= MAX_ENVS) {
1011 fprintf(stderr, "Error: too many environment variables, please use --rmenv\n");
1012 exit(1);
1013 }
1014
997 // sanity check for arguments 1015 // sanity check for arguments
998 for (i = 0; i < argc; i++) { 1016 for (i = 0; i < argc; i++) {
999 if (*argv[i] == 0) { 1017 if (*argv[i] == 0) {
@@ -1005,29 +1023,19 @@ int main(int argc, char **argv, char **envp) {
1005 exit(1); 1023 exit(1);
1006 } 1024 }
1007 // Also remove requested environment variables 1025 // Also remove requested environment variables
1008 // entirely to avoid tripping the length check below
1009 if (strncmp(argv[i], "--rmenv=", 8) == 0) 1026 if (strncmp(argv[i], "--rmenv=", 8) == 0)
1010 unsetenv(argv[i] + 8); 1027 env_store(argv[i] + 8, RMENV);
1011 } 1028 }
1012 1029
1013 // sanity check for environment variables 1030 // Reapply a minimal set of environment variables
1014 for (i = 0, ptr = envp; ptr && *ptr && i < MAX_ENVS; i++, ptr++) { 1031 env_apply_whitelist();
1015 if (strlen(*ptr) >= MAX_ENV_LEN) {
1016 fprintf(stderr, "Error: too long environment variables, please use --rmenv\n");
1017 exit(1);
1018 }
1019 }
1020 if (i >= MAX_ENVS) {
1021 fprintf(stderr, "Error: too many environment variables, please use --rmenv\n");
1022 exit(1);
1023 }
1024 1032
1025 // check if the user is allowed to use firejail 1033 // check if the user is allowed to use firejail
1026 init_cfg(argc, argv); 1034 init_cfg(argc, argv);
1027 1035
1028 // get starting timestamp, process --quiet 1036 // get starting timestamp, process --quiet
1029 timetrace_start(); 1037 timetrace_start();
1030 char *env_quiet = getenv("FIREJAIL_QUIET"); 1038 const char *env_quiet = env_get("FIREJAIL_QUIET");
1031 if (check_arg(argc, argv, "--quiet", 1) || (env_quiet && strcmp(env_quiet, "yes") == 0)) 1039 if (check_arg(argc, argv, "--quiet", 1) || (env_quiet && strcmp(env_quiet, "yes") == 0))
1032 arg_quiet = 1; 1040 arg_quiet = 1;
1033 1041
@@ -1037,7 +1045,7 @@ int main(int argc, char **argv, char **envp) {
1037 1045
1038 // build /run/firejail directory structure 1046 // build /run/firejail directory structure
1039 preproc_build_firejail_dir(); 1047 preproc_build_firejail_dir();
1040 char *container_name = getenv("container"); 1048 const char *container_name = env_get("container");
1041 if (!container_name || strcmp(container_name, "firejail")) { 1049 if (!container_name || strcmp(container_name, "firejail")) {
1042 lockfd_directory = open(RUN_DIRECTORY_LOCK_FILE, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); 1050 lockfd_directory = open(RUN_DIRECTORY_LOCK_FILE, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
1043 if (lockfd_directory != -1) { 1051 if (lockfd_directory != -1) {
@@ -1170,6 +1178,9 @@ int main(int argc, char **argv, char **envp) {
1170 1178
1171 drop_privs(1); 1179 drop_privs(1);
1172 umask(orig_umask); 1180 umask(orig_umask);
1181
1182 // restore original environment variables
1183 env_apply_all();
1173 int rv = system(argv[2]); 1184 int rv = system(argv[2]);
1174 exit(rv); 1185 exit(rv);
1175 } 1186 }
@@ -1231,11 +1242,6 @@ int main(int argc, char **argv, char **envp) {
1231 } 1242 }
1232 EUID_ASSERT(); 1243 EUID_ASSERT();
1233 1244
1234#ifdef WARN_DUMPABLE
1235 if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 1 && getuid())
1236 fprintf(stderr, "Error: Firejail is dumpable\n");
1237#endif
1238
1239 // check for force-nonewprivs in /etc/firejail/firejail.config file 1245 // check for force-nonewprivs in /etc/firejail/firejail.config file
1240 if (checkcfg(CFG_FORCE_NONEWPRIVS)) 1246 if (checkcfg(CFG_FORCE_NONEWPRIVS))
1241 arg_nonewprivs = 1; 1247 arg_nonewprivs = 1;
diff --git a/src/firejail/no_sandbox.c b/src/firejail/no_sandbox.c
index 6c7803602..111d94333 100644
--- a/src/firejail/no_sandbox.c
+++ b/src/firejail/no_sandbox.c
@@ -41,7 +41,7 @@ int check_namespace_virt(void) {
41 EUID_ASSERT(); 41 EUID_ASSERT();
42 42
43 // check container environment variable 43 // check container environment variable
44 char *str = getenv("container"); 44 const char *str = env_get("container");
45 if (str && is_container(str)) 45 if (str && is_container(str))
46 return 1; 46 return 1;
47 47
diff --git a/src/firejail/output.c b/src/firejail/output.c
index 36cb905cb..1682ee025 100644
--- a/src/firejail/output.c
+++ b/src/firejail/output.c
@@ -95,6 +95,9 @@ void check_output(int argc, char **argv) {
95 close(pipefd[0]); 95 close(pipefd[0]);
96 } 96 }
97 97
98 // restore some environment variables
99 env_apply_whitelist_sbox();
100
98 char *args[3]; 101 char *args[3];
99 args[0] = LIBDIR "/firejail/ftee"; 102 args[0] = LIBDIR "/firejail/ftee";
100 args[1] = outfile; 103 args[1] = outfile;
@@ -137,6 +140,10 @@ void check_output(int argc, char **argv) {
137 } 140 }
138 args[j++] = argv[i]; 141 args[j++] = argv[i];
139 } 142 }
143
144 // restore original environment variables
145 env_apply_all();
146
140 execvp(args[0], args); 147 execvp(args[0], args);
141 148
142 perror("execvp"); 149 perror("execvp");
diff --git a/src/firejail/paths.c b/src/firejail/paths.c
index 5de704bef..981a6bc71 100644
--- a/src/firejail/paths.c
+++ b/src/firejail/paths.c
@@ -26,13 +26,13 @@ static unsigned int longest_path_elt = 0;
26 26
27static char *elt = NULL; // moved from inside init_paths in order to get rid of scan-build warning 27static char *elt = NULL; // moved from inside init_paths in order to get rid of scan-build warning
28static void init_paths(void) { 28static void init_paths(void) {
29 char *path = getenv("PATH"); 29 const char *env_path = env_get("PATH");
30 char *p; 30 char *p;
31 if (!path) { 31 if (!env_path) {
32 path = "/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin"; 32 env_path = "/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin";
33 setenv("PATH", path, 1); 33 env_store_name_val("PATH", env_path, SETENV);
34 } 34 }
35 path = strdup(path); 35 char *path = strdup(env_path);
36 if (!path) 36 if (!path)
37 errExit("strdup"); 37 errExit("strdup");
38 38
diff --git a/src/firejail/profile.c b/src/firejail/profile.c
index 1ee8cdfcb..3766ba8f0 100644
--- a/src/firejail/profile.c
+++ b/src/firejail/profile.c
@@ -158,7 +158,7 @@ static int check_nosound(void) {
158} 158}
159 159
160static int check_x11(void) { 160static int check_x11(void) {
161 return (arg_x11_block || arg_x11_xorg || getenv("FIREJAIL_X11")); 161 return (arg_x11_block || arg_x11_xorg || env_get("FIREJAIL_X11"));
162} 162}
163 163
164static int check_disable_u2f(void) { 164static int check_disable_u2f(void) {
@@ -1181,7 +1181,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
1181 if (strcmp(ptr, "x11 xephyr") == 0) { 1181 if (strcmp(ptr, "x11 xephyr") == 0) {
1182#ifdef HAVE_X11 1182#ifdef HAVE_X11
1183 if (checkcfg(CFG_X11)) { 1183 if (checkcfg(CFG_X11)) {
1184 char *x11env = getenv("FIREJAIL_X11"); 1184 const char *x11env = env_get("FIREJAIL_X11");
1185 if (x11env && strcmp(x11env, "yes") == 0) { 1185 if (x11env && strcmp(x11env, "yes") == 0) {
1186 return 0; 1186 return 0;
1187 } 1187 }
@@ -1210,7 +1210,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
1210 if (strcmp(ptr, "x11 xpra") == 0) { 1210 if (strcmp(ptr, "x11 xpra") == 0) {
1211#ifdef HAVE_X11 1211#ifdef HAVE_X11
1212 if (checkcfg(CFG_X11)) { 1212 if (checkcfg(CFG_X11)) {
1213 char *x11env = getenv("FIREJAIL_X11"); 1213 const char *x11env = env_get("FIREJAIL_X11");
1214 if (x11env && strcmp(x11env, "yes") == 0) { 1214 if (x11env && strcmp(x11env, "yes") == 0) {
1215 return 0; 1215 return 0;
1216 } 1216 }
@@ -1229,7 +1229,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
1229 if (strcmp(ptr, "x11 xvfb") == 0) { 1229 if (strcmp(ptr, "x11 xvfb") == 0) {
1230#ifdef HAVE_X11 1230#ifdef HAVE_X11
1231 if (checkcfg(CFG_X11)) { 1231 if (checkcfg(CFG_X11)) {
1232 char *x11env = getenv("FIREJAIL_X11"); 1232 const char *x11env = env_get("FIREJAIL_X11");
1233 if (x11env && strcmp(x11env, "yes") == 0) { 1233 if (x11env && strcmp(x11env, "yes") == 0) {
1234 return 0; 1234 return 0;
1235 } 1235 }
@@ -1248,7 +1248,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
1248 if (strcmp(ptr, "x11") == 0) { 1248 if (strcmp(ptr, "x11") == 0) {
1249#ifdef HAVE_X11 1249#ifdef HAVE_X11
1250 if (checkcfg(CFG_X11)) { 1250 if (checkcfg(CFG_X11)) {
1251 char *x11env = getenv("FIREJAIL_X11"); 1251 const char *x11env = env_get("FIREJAIL_X11");
1252 if (x11env && strcmp(x11env, "yes") == 0) { 1252 if (x11env && strcmp(x11env, "yes") == 0) {
1253 return 0; 1253 return 0;
1254 } 1254 }
diff --git a/src/firejail/pulseaudio.c b/src/firejail/pulseaudio.c
index a5c924a70..5df3d9cd3 100644
--- a/src/firejail/pulseaudio.c
+++ b/src/firejail/pulseaudio.c
@@ -42,7 +42,7 @@ void pulseaudio_disable(void) {
42 42
43 43
44 // blacklist pulseaudio socket in XDG_RUNTIME_DIR 44 // blacklist pulseaudio socket in XDG_RUNTIME_DIR
45 char *name = getenv("XDG_RUNTIME_DIR"); 45 const char *name = env_get("XDG_RUNTIME_DIR");
46 if (name) 46 if (name)
47 disable_file_path(name, "pulse/native"); 47 disable_file_path(name, "pulse/native");
48 48
@@ -76,7 +76,10 @@ void pulseaudio_disable(void) {
76} 76}
77 77
78static void pulseaudio_fallback(const char *path) { 78static void pulseaudio_fallback(const char *path) {
79 assert(path);
80
79 fmessage("Cannot mount tmpfs on %s/.config/pulse\n", cfg.homedir); 81 fmessage("Cannot mount tmpfs on %s/.config/pulse\n", cfg.homedir);
82 env_store_name_val("PULSE_CLIENTCONFIG", path, SETENV);
80 if (setenv("PULSE_CLIENTCONFIG", path, 1) < 0) 83 if (setenv("PULSE_CLIENTCONFIG", path, 1) < 0)
81 errExit("setenv"); 84 errExit("setenv");
82} 85}
diff --git a/src/firejail/run_symlink.c b/src/firejail/run_symlink.c
index ea3889024..5bf27fc6d 100644
--- a/src/firejail/run_symlink.c
+++ b/src/firejail/run_symlink.c
@@ -22,6 +22,8 @@
22#include <sys/stat.h> 22#include <sys/stat.h>
23#include <unistd.h> 23#include <unistd.h>
24 24
25extern char *find_in_path(const char *program);
26
25void run_symlink(int argc, char **argv, int run_as_is) { 27void run_symlink(int argc, char **argv, int run_as_is) {
26 EUID_ASSERT(); 28 EUID_ASSERT();
27 29
@@ -40,58 +42,25 @@ void run_symlink(int argc, char **argv, int run_as_is) {
40 errExit("setresuid"); 42 errExit("setresuid");
41 43
42 // find the real program by looking in PATH 44 // find the real program by looking in PATH
43 char *p = getenv("PATH"); 45 const char *path = env_get("PATH");
44 if (!p) { 46 if (!path) {
45 fprintf(stderr, "Error: PATH environment variable not set\n"); 47 fprintf(stderr, "Error: PATH environment variable not set\n");
46 exit(1); 48 exit(1);
47 } 49 }
48 50
49 char *path = strdup(p); 51 char *p = find_in_path(program);
50 if (!path) 52 if (!p) {
51 errExit("strdup");
52
53 char *selfpath = realpath("/proc/self/exe", NULL);
54 if (!selfpath)
55 errExit("realpath");
56
57 // look in path for our program
58 char *tok = strtok(path, ":");
59 int found = 0;
60 while (tok) {
61 char *name;
62 if (asprintf(&name, "%s/%s", tok, program) == -1)
63 errExit("asprintf");
64
65 struct stat s;
66 if (stat(name, &s) == 0) {
67 /* coverity[toctou] */
68 char* rp = realpath(name, NULL);
69 if (!rp)
70 errExit("realpath");
71
72 if (strcmp(selfpath, rp) != 0) {
73 program = strdup(name);
74 found = 1;
75 free(rp);
76 break;
77 }
78
79 free(rp);
80 }
81
82 free(name);
83 tok = strtok(NULL, ":");
84 }
85 if (!found) {
86 fprintf(stderr, "Error: cannot find the program in the path\n"); 53 fprintf(stderr, "Error: cannot find the program in the path\n");
87 exit(1); 54 exit(1);
88 } 55 }
89 56 program = p;
90 free(selfpath);
91 57
92 // restore original umask 58 // restore original umask
93 umask(orig_umask); 59 umask(orig_umask);
94 60
61 // restore original environment variables
62 env_apply_all();
63
95 // desktop integration is not supported for root user; instead, the original program is started 64 // desktop integration is not supported for root user; instead, the original program is started
96 if (getuid() == 0 || run_as_is) { 65 if (getuid() == 0 || run_as_is) {
97 argv[0] = program; 66 argv[0] = program;
@@ -108,6 +77,7 @@ void run_symlink(int argc, char **argv, int run_as_is) {
108 a[i + 2] = argv[i + 1]; 77 a[i + 2] = argv[i + 1];
109 } 78 }
110 a[i + 2] = NULL; 79 a[i + 2] = NULL;
80 assert(env_get("LD_PRELOAD") == NULL);
111 assert(getenv("LD_PRELOAD") == NULL); 81 assert(getenv("LD_PRELOAD") == NULL);
112 execvp(a[0], a); 82 execvp(a[0], a);
113 83
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c
index d811fe45a..1f94d86cd 100644
--- a/src/firejail/sandbox.c
+++ b/src/firejail/sandbox.c
@@ -268,8 +268,7 @@ static void sandbox_if_up(Bridge *br) {
268 268
269static void chk_chroot(void) { 269static void chk_chroot(void) {
270 // if we are starting firejail inside some other container technology, we don't care about this 270 // if we are starting firejail inside some other container technology, we don't care about this
271 char *mycont = getenv("container"); 271 if (env_get("container"))
272 if (mycont)
273 return; 272 return;
274 273
275 // check if this is a regular chroot 274 // check if this is a regular chroot
@@ -419,7 +418,7 @@ static int ok_to_run(const char *program) {
419 return 1; 418 return 1;
420 } 419 }
421 else { // search $PATH 420 else { // search $PATH
422 char *path1 = getenv("PATH"); 421 const char *path1 = env_get("PATH");
423 if (path1) { 422 if (path1) {
424 if (arg_debug) 423 if (arg_debug)
425 printf("Searching $PATH for %s\n", program); 424 printf("Searching $PATH for %s\n", program);
@@ -465,7 +464,7 @@ void start_application(int no_sandbox, int fd, char *set_sandbox_status) {
465 // set environment 464 // set environment
466 if (no_sandbox == 0) { 465 if (no_sandbox == 0) {
467 env_defaults(); 466 env_defaults();
468 env_apply(); 467 env_apply_all();
469 } 468 }
470 // restore original umask 469 // restore original umask
471 umask(orig_umask); 470 umask(orig_umask);
diff --git a/src/firejail/sbox.c b/src/firejail/sbox.c
index a2aaa86eb..baf99c5b9 100644
--- a/src/firejail/sbox.c
+++ b/src/firejail/sbox.c
@@ -36,7 +36,7 @@ static int __attribute__((noreturn)) sbox_do_exec_v(unsigned filtermask, char *
36 int env_index = 0; 36 int env_index = 0;
37 char *new_environment[256] = { NULL }; 37 char *new_environment[256] = { NULL };
38 // preserve firejail-specific env vars 38 // preserve firejail-specific env vars
39 char *cl = getenv("FIREJAIL_FILE_COPY_LIMIT"); 39 const char *cl = env_get("FIREJAIL_FILE_COPY_LIMIT");
40 if (cl) { 40 if (cl) {
41 if (asprintf(&new_environment[env_index++], "FIREJAIL_FILE_COPY_LIMIT=%s", cl) == -1) 41 if (asprintf(&new_environment[env_index++], "FIREJAIL_FILE_COPY_LIMIT=%s", cl) == -1)
42 errExit("asprintf"); 42 errExit("asprintf");
@@ -120,7 +120,7 @@ static int __attribute__((noreturn)) sbox_do_exec_v(unsigned filtermask, char *
120 // handle X32 ABI 120 // handle X32 ABI
121 BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, X32_SYSCALL_BIT, 1, 0), 121 BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, X32_SYSCALL_BIT, 1, 0),
122 BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, 0, 1, 0), 122 BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, 0, 1, 0),
123 RETURN_ERRNO(EPERM), 123 KILL_OR_RETURN_ERRNO,
124#endif 124#endif
125 125
126 // syscall list 126 // syscall list
diff --git a/src/firejail/seccomp.c b/src/firejail/seccomp.c
index e47e6c910..808dd4c37 100644
--- a/src/firejail/seccomp.c
+++ b/src/firejail/seccomp.c
@@ -208,7 +208,7 @@ int seccomp_filter_drop(bool native) {
208 // - seccomp 208 // - seccomp
209 if (cfg.seccomp_list_drop == NULL) { 209 if (cfg.seccomp_list_drop == NULL) {
210 // default seccomp if error action is not changed 210 // default seccomp if error action is not changed
211 if (cfg.seccomp_list == NULL && cfg.seccomp_error_action) { 211 if (cfg.seccomp_list == NULL && arg_seccomp_error_action == DEFAULT_SECCOMP_ERROR_ACTION) {
212 if (arg_seccomp_block_secondary) 212 if (arg_seccomp_block_secondary)
213 seccomp_filter_block_secondary(); 213 seccomp_filter_block_secondary();
214 else { 214 else {
@@ -221,11 +221,29 @@ int seccomp_filter_drop(bool native) {
221 } 221 }
222 // default seccomp filter with additional drop list 222 // default seccomp filter with additional drop list
223 else { // cfg.seccomp_list != NULL 223 else { // cfg.seccomp_list != NULL
224 if (arg_seccomp_block_secondary) 224 int rv;
225
226 if (arg_seccomp_block_secondary) {
227 if (arg_seccomp_error_action != DEFAULT_SECCOMP_ERROR_ACTION) {
228 if (arg_debug)
229 printf("Rebuild secondary block seccomp filter\n");
230 rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 4,
231 PATH_FSECCOMP, "secondary", "block", RUN_SECCOMP_BLOCK_SECONDARY);
232 if (rv)
233 exit(rv);
234 }
225 seccomp_filter_block_secondary(); 235 seccomp_filter_block_secondary();
226 else { 236 } else {
227#if defined(__x86_64__) 237#if defined(__x86_64__)
228#if defined(__LP64__) 238#if defined(__LP64__)
239 if (arg_seccomp_error_action != DEFAULT_SECCOMP_ERROR_ACTION) {
240 if (arg_debug)
241 printf("Rebuild 32 bit seccomp filter\n");
242 rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 4,
243 PATH_FSECCOMP, "secondary", "32", RUN_SECCOMP_32);
244 if (rv)
245 exit(rv);
246 }
229 seccomp_filter_32(); 247 seccomp_filter_32();
230#endif 248#endif
231#endif 249#endif
@@ -242,16 +260,22 @@ int seccomp_filter_drop(bool native) {
242 list = cfg.seccomp_list32; 260 list = cfg.seccomp_list32;
243 } 261 }
244 262
245 if (list == NULL)
246 list = "";
247 // build the seccomp filter as a regular user 263 // build the seccomp filter as a regular user
248 int rv; 264 if (list)
249 if (arg_allow_debuggers) 265 if (arg_allow_debuggers)
250 rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 7, 266 rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 7,
251 PATH_FSECCOMP, command, "drop", filter, postexec_filter, list, "allow-debuggers"); 267 PATH_FSECCOMP, command, "drop", filter, postexec_filter, list, "allow-debuggers");
268 else
269 rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 6,
270 PATH_FSECCOMP, command, "drop", filter, postexec_filter, list);
252 else 271 else
253 rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 6, 272 if (arg_allow_debuggers)
254 PATH_FSECCOMP, command, "drop", filter, postexec_filter, list); 273 rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 4,
274 PATH_FSECCOMP, command, filter, "allow-debuggers");
275 else
276 rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 3,
277 PATH_FSECCOMP, command, filter);
278
255 if (rv) 279 if (rv)
256 exit(rv); 280 exit(rv);
257 281
diff --git a/src/firejail/util.c b/src/firejail/util.c
index a3927cc88..911c8bd94 100644
--- a/src/firejail/util.c
+++ b/src/firejail/util.c
@@ -565,27 +565,18 @@ char *clean_pathname(const char *path) {
565 if (!rv) 565 if (!rv)
566 errExit("malloc"); 566 errExit("malloc");
567 567
568 if (len > 0) { 568 size_t i = 0;
569 size_t i = 0, j = 0, cnt = 0; 569 size_t j = 0;
570 for (; i < len; i++) { 570 while (path[i]) {
571 if (path[i] == '/') 571 while (path[i] == '/' && path[i+1] == '/')
572 cnt++; 572 i++;
573 else 573 rv[j++] = path[i++];
574 cnt = 0;
575
576 if (cnt < 2) {
577 rv[j] = path[i];
578 j++;
579 }
580 }
581 rv[j] = '\0';
582
583 // remove a trailing slash
584 if (j > 1 && rv[j - 1] == '/')
585 rv[j - 1] = '\0';
586 } 574 }
587 else 575 rv[j] = '\0';
588 *rv = '\0'; 576
577 // remove a trailing slash
578 if (j > 1 && rv[j - 1] == '/')
579 rv[j - 1] = '\0';
589 580
590 return rv; 581 return rv;
591} 582}
@@ -820,20 +811,6 @@ void notify_other(int fd) {
820 fclose(stream); 811 fclose(stream);
821} 812}
822 813
823
824
825
826// Equivalent to the GNU version of basename, which is incompatible with
827// the POSIX basename. A few lines of code saves any portability pain.
828// https://www.gnu.org/software/libc/manual/html_node/Finding-Tokens-in-a-String.html#index-basename
829const char *gnu_basename(const char *path) {
830 const char *last_slash = strrchr(path, '/');
831 if (!last_slash)
832 return path;
833 return last_slash+1;
834}
835
836
837uid_t pid_get_uid(pid_t pid) { 814uid_t pid_get_uid(pid_t pid) {
838 EUID_ASSERT(); 815 EUID_ASSERT();
839 uid_t rv = 0; 816 uid_t rv = 0;
diff --git a/src/firejail/x11.c b/src/firejail/x11.c
index 4872a5207..1121ec84e 100644
--- a/src/firejail/x11.c
+++ b/src/firejail/x11.c
@@ -41,7 +41,7 @@
41// Parse the DISPLAY environment variable and return a display number. 41// Parse the DISPLAY environment variable and return a display number.
42// Returns -1 if DISPLAY is not set, or is set to anything other than :ddd. 42// Returns -1 if DISPLAY is not set, or is set to anything other than :ddd.
43int x11_display(void) { 43int x11_display(void) {
44 const char *display_str = getenv("DISPLAY"); 44 const char *display_str = env_get("DISPLAY");
45 char *endp; 45 char *endp;
46 unsigned long display; 46 unsigned long display;
47 47
@@ -208,7 +208,7 @@ void x11_start_xvfb(int argc, char **argv) {
208 pid_t jail = 0; 208 pid_t jail = 0;
209 pid_t server = 0; 209 pid_t server = 0;
210 210
211 setenv("FIREJAIL_X11", "yes", 1); 211 env_store_name_val("FIREJAIL_X11", "yes", SETENV);
212 212
213 // never try to run X servers as root!!! 213 // never try to run X servers as root!!!
214 if (getuid() == 0) { 214 if (getuid() == 0) {
@@ -326,7 +326,11 @@ void x11_start_xvfb(int argc, char **argv) {
326 if (arg_debug) 326 if (arg_debug)
327 printf("Starting xvfb...\n"); 327 printf("Starting xvfb...\n");
328 328
329 // restore original environment variables
330 env_apply_all();
331
329 // running without privileges - see drop_privs call above 332 // running without privileges - see drop_privs call above
333 assert(env_get("LD_PRELOAD") == NULL);
330 assert(getenv("LD_PRELOAD") == NULL); 334 assert(getenv("LD_PRELOAD") == NULL);
331 execvp(server_argv[0], server_argv); 335 execvp(server_argv[0], server_argv);
332 perror("execvp"); 336 perror("execvp");
@@ -355,7 +359,7 @@ void x11_start_xvfb(int argc, char **argv) {
355 free(fname); 359 free(fname);
356 360
357 assert(display_str); 361 assert(display_str);
358 setenv("DISPLAY", display_str, 1); 362 env_store_name_val("DISPLAY", display_str, SETENV);
359 // run attach command 363 // run attach command
360 jail = fork(); 364 jail = fork();
361 if (jail < 0) 365 if (jail < 0)
@@ -363,7 +367,11 @@ void x11_start_xvfb(int argc, char **argv) {
363 if (jail == 0) { 367 if (jail == 0) {
364 fmessage("\n*** Attaching to Xvfb display %d ***\n\n", display); 368 fmessage("\n*** Attaching to Xvfb display %d ***\n\n", display);
365 369
370 // restore original environment variables
371 env_apply_all();
372
366 // running without privileges - see drop_privs call above 373 // running without privileges - see drop_privs call above
374 assert(env_get("LD_PRELOAD") == NULL);
367 assert(getenv("LD_PRELOAD") == NULL); 375 assert(getenv("LD_PRELOAD") == NULL);
368 execvp(jail_argv[0], jail_argv); 376 execvp(jail_argv[0], jail_argv);
369 perror("execvp"); 377 perror("execvp");
@@ -428,7 +436,7 @@ void x11_start_xephyr(int argc, char **argv) {
428 if (newscreen) 436 if (newscreen)
429 xephyr_screen = newscreen; 437 xephyr_screen = newscreen;
430 438
431 setenv("FIREJAIL_X11", "yes", 1); 439 env_store_name_val("FIREJAIL_X11", "yes", SETENV);
432 440
433 // unfortunately, xephyr does a number of weird things when started by root user!!! 441 // unfortunately, xephyr does a number of weird things when started by root user!!!
434 if (getuid() == 0) { 442 if (getuid() == 0) {
@@ -556,7 +564,11 @@ void x11_start_xephyr(int argc, char **argv) {
556 if (arg_debug) 564 if (arg_debug)
557 printf("Starting xephyr...\n"); 565 printf("Starting xephyr...\n");
558 566
567 // restore original environment variables
568 env_apply_all();
569
559 // running without privileges - see drop_privs call above 570 // running without privileges - see drop_privs call above
571 assert(env_get("LD_PRELOAD") == NULL);
560 assert(getenv("LD_PRELOAD") == NULL); 572 assert(getenv("LD_PRELOAD") == NULL);
561 execvp(server_argv[0], server_argv); 573 execvp(server_argv[0], server_argv);
562 perror("execvp"); 574 perror("execvp");
@@ -585,7 +597,7 @@ void x11_start_xephyr(int argc, char **argv) {
585 free(fname); 597 free(fname);
586 598
587 assert(display_str); 599 assert(display_str);
588 setenv("DISPLAY", display_str, 1); 600 env_store_name_val("DISPLAY", display_str, SETENV);
589 // run attach command 601 // run attach command
590 jail = fork(); 602 jail = fork();
591 if (jail < 0) 603 if (jail < 0)
@@ -594,8 +606,12 @@ void x11_start_xephyr(int argc, char **argv) {
594 if (!arg_quiet) 606 if (!arg_quiet)
595 printf("\n*** Attaching to Xephyr display %d ***\n\n", display); 607 printf("\n*** Attaching to Xephyr display %d ***\n\n", display);
596 608
609 // restore original environment variables
610 env_apply_all();
611
597 // running without privileges - see drop_privs call above 612 // running without privileges - see drop_privs call above
598 assert(getenv("LD_PRELOAD") == NULL); 613 assert(getenv("LD_PRELOAD") == NULL);
614 assert(env_get("LD_PRELOAD") == NULL);
599 execvp(jail_argv[0], jail_argv); 615 execvp(jail_argv[0], jail_argv);
600 perror("execvp"); 616 perror("execvp");
601 _exit(1); 617 _exit(1);
@@ -780,8 +796,12 @@ static void __attribute__((noreturn)) x11_start_xpra_old(int argc, char **argv,
780 dup2(fd_null,2); 796 dup2(fd_null,2);
781 } 797 }
782 798
799 // restore original environment variables
800 env_apply_all();
801
783 // running without privileges - see drop_privs call above 802 // running without privileges - see drop_privs call above
784 assert(getenv("LD_PRELOAD") == NULL); 803 assert(getenv("LD_PRELOAD") == NULL);
804 assert(env_get("LD_PRELOAD") == NULL);
785 execvp(server_argv[0], server_argv); 805 execvp(server_argv[0], server_argv);
786 perror("execvp"); 806 perror("execvp");
787 _exit(1); 807 _exit(1);
@@ -827,7 +847,11 @@ static void __attribute__((noreturn)) x11_start_xpra_old(int argc, char **argv,
827 847
828 fmessage("\n*** Attaching to xpra display %d ***\n\n", display); 848 fmessage("\n*** Attaching to xpra display %d ***\n\n", display);
829 849
850 // restore original environment variables
851 env_apply_all();
852
830 // running without privileges - see drop_privs call above 853 // running without privileges - see drop_privs call above
854 assert(env_get("LD_PRELOAD") == NULL);
831 assert(getenv("LD_PRELOAD") == NULL); 855 assert(getenv("LD_PRELOAD") == NULL);
832 execvp(attach_argv[0], attach_argv); 856 execvp(attach_argv[0], attach_argv);
833 perror("execvp"); 857 perror("execvp");
@@ -835,7 +859,7 @@ static void __attribute__((noreturn)) x11_start_xpra_old(int argc, char **argv,
835 } 859 }
836 860
837 assert(display_str); 861 assert(display_str);
838 setenv("DISPLAY", display_str, 1); 862 env_store_name_val("DISPLAY", display_str, SETENV);
839 863
840 // build jail command 864 // build jail command
841 char *firejail_argv[argc+2]; 865 char *firejail_argv[argc+2];
@@ -857,7 +881,12 @@ static void __attribute__((noreturn)) x11_start_xpra_old(int argc, char **argv,
857 errExit("fork"); 881 errExit("fork");
858 if (jail == 0) { 882 if (jail == 0) {
859 // running without privileges - see drop_privs call above 883 // running without privileges - see drop_privs call above
884 assert(env_get("LD_PRELOAD") == NULL);
860 assert(getenv("LD_PRELOAD") == NULL); 885 assert(getenv("LD_PRELOAD") == NULL);
886
887 // restore original environment variables
888 env_apply_all();
889
861 if (firejail_argv[0]) // shut up llvm scan-build 890 if (firejail_argv[0]) // shut up llvm scan-build
862 execvp(firejail_argv[0], firejail_argv); 891 execvp(firejail_argv[0], firejail_argv);
863 perror("execvp"); 892 perror("execvp");
@@ -883,7 +912,12 @@ static void __attribute__((noreturn)) x11_start_xpra_old(int argc, char **argv,
883 dup2(fd_null,1); 912 dup2(fd_null,1);
884 dup2(fd_null,2); 913 dup2(fd_null,2);
885 } 914 }
915
916 // restore original environment variables
917 env_apply_all();
918
886 // running without privileges - see drop_privs call above 919 // running without privileges - see drop_privs call above
920 assert(env_get("LD_PRELOAD") == NULL);
887 assert(getenv("LD_PRELOAD") == NULL); 921 assert(getenv("LD_PRELOAD") == NULL);
888 execvp(stop_argv[0], stop_argv); 922 execvp(stop_argv[0], stop_argv);
889 perror("execvp"); 923 perror("execvp");
@@ -1051,7 +1085,11 @@ static void __attribute__((noreturn)) x11_start_xpra_new(int argc, char **argv,
1051 dup2(fd_null,2); 1085 dup2(fd_null,2);
1052 } 1086 }
1053 1087
1088 // restore original environment variables
1089 env_apply_all();
1090
1054 // running without privileges - see drop_privs call above 1091 // running without privileges - see drop_privs call above
1092 assert(env_get("LD_PRELOAD") == NULL);
1055 assert(getenv("LD_PRELOAD") == NULL); 1093 assert(getenv("LD_PRELOAD") == NULL);
1056 execvp(server_argv[0], server_argv); 1094 execvp(server_argv[0], server_argv);
1057 perror("execvp"); 1095 perror("execvp");
@@ -1072,7 +1110,7 @@ static void __attribute__((noreturn)) x11_start_xpra_new(int argc, char **argv,
1072void x11_start_xpra(int argc, char **argv) { 1110void x11_start_xpra(int argc, char **argv) {
1073 EUID_ASSERT(); 1111 EUID_ASSERT();
1074 1112
1075 setenv("FIREJAIL_X11", "yes", 1); 1113 env_store_name_val("FIREJAIL_X11", "yes", SETENV);
1076 1114
1077 // unfortunately, xpra does a number of weird things when started by root user!!! 1115 // unfortunately, xpra does a number of weird things when started by root user!!!
1078 if (getuid() == 0) { 1116 if (getuid() == 0) {
@@ -1134,7 +1172,7 @@ void x11_xorg(void) {
1134#ifdef HAVE_X11 1172#ifdef HAVE_X11
1135 1173
1136 // get DISPLAY env 1174 // get DISPLAY env
1137 char *display = getenv("DISPLAY"); 1175 const char *display = env_get("DISPLAY");
1138 if (!display) { 1176 if (!display) {
1139 fputs("Error: --x11=xorg requires an 'outer' X11 server to use.\n", stderr); 1177 fputs("Error: --x11=xorg requires an 'outer' X11 server to use.\n", stderr);
1140 exit(1); 1178 exit(1);
@@ -1259,7 +1297,7 @@ void x11_xorg(void) {
1259 ASSERT_PERMS(dest, getuid(), getgid(), 0600); 1297 ASSERT_PERMS(dest, getuid(), getgid(), 0600);
1260 1298
1261 // blacklist user .Xauthority file if it is not masked already 1299 // blacklist user .Xauthority file if it is not masked already
1262 char *envar = getenv("XAUTHORITY"); 1300 const char *envar = env_get("XAUTHORITY");
1263 if (envar) { 1301 if (envar) {
1264 char *rp = realpath(envar, NULL); 1302 char *rp = realpath(envar, NULL);
1265 if (rp) { 1303 if (rp) {
@@ -1269,8 +1307,7 @@ void x11_xorg(void) {
1269 } 1307 }
1270 } 1308 }
1271 // set environment variable 1309 // set environment variable
1272 if (setenv("XAUTHORITY", dest, 1) < 0) 1310 env_store_name_val("XAUTHORITY", dest, SETENV);
1273 errExit("setenv");
1274 free(dest); 1311 free(dest);
1275 1312
1276 // mask RUN_XAUTHORITY_SEC_DIR 1313 // mask RUN_XAUTHORITY_SEC_DIR
@@ -1391,7 +1428,7 @@ void x11_block(void) {
1391 errExit("strdup"); 1428 errExit("strdup");
1392 profile_check_line(cmd, 0, NULL); 1429 profile_check_line(cmd, 0, NULL);
1393 profile_add(cmd); 1430 profile_add(cmd);
1394 char *xauthority = getenv("XAUTHORITY"); 1431 const char *xauthority = env_get("XAUTHORITY");
1395 if (xauthority) { 1432 if (xauthority) {
1396 char *line; 1433 char *line;
1397 if (asprintf(&line, "blacklist %s", xauthority) == -1) 1434 if (asprintf(&line, "blacklist %s", xauthority) == -1)
diff --git a/src/firemon/Makefile.in b/src/firemon/Makefile.in
index f2513213c..9ee798fe9 100644
--- a/src/firemon/Makefile.in
+++ b/src/firemon/Makefile.in
@@ -2,7 +2,7 @@ all: firemon
2 2
3include ../common.mk 3include ../common.mk
4 4
5%.o : %.c $(H_FILE_LIST) 5%.o : %.c $(H_FILE_LIST) ../include/common.h ../include/pid.h
6 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@ 6 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@
7 7
8firemon: $(OBJS) ../lib/common.o ../lib/pid.o 8firemon: $(OBJS) ../lib/common.o ../lib/pid.o
diff --git a/src/fldd/Makefile.in b/src/fldd/Makefile.in
index 53382c2df..37b139d38 100644
--- a/src/fldd/Makefile.in
+++ b/src/fldd/Makefile.in
@@ -5,8 +5,8 @@ include ../common.mk
5%.o : %.c $(H_FILE_LIST) ../include/common.h ../include/syscall.h ../include/ldd_utils.h 5%.o : %.c $(H_FILE_LIST) ../include/common.h ../include/syscall.h ../include/ldd_utils.h
6 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@ 6 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@
7 7
8fldd: $(OBJS) ../lib/ldd_utils.o 8fldd: $(OBJS) ../lib/common.o ../lib/ldd_utils.o
9 $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/ldd_utils.o $(LIBS) $(EXTRA_LDFLAGS) 9 $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/common.o ../lib/ldd_utils.o $(LIBS) $(EXTRA_LDFLAGS)
10 10
11clean:; rm -fr *.o fldd *.gcov *.gcda *.gcno *.plist 11clean:; rm -fr *.o fldd *.gcov *.gcda *.gcno *.plist
12 12
diff --git a/src/fldd/main.c b/src/fldd/main.c
index d68504f6b..55a0dfcce 100644
--- a/src/fldd/main.c
+++ b/src/fldd/main.c
@@ -24,7 +24,6 @@
24#include <fcntl.h> 24#include <fcntl.h>
25#include <sys/mman.h> 25#include <sys/mman.h>
26#include <sys/mount.h> 26#include <sys/mount.h>
27#include <sys/prctl.h>
28#include <sys/stat.h> 27#include <sys/stat.h>
29#include <sys/types.h> 28#include <sys/types.h>
30#include <unistd.h> 29#include <unistd.h>
@@ -303,10 +302,7 @@ printf("\n");
303 return 0; 302 return 0;
304 } 303 }
305 304
306#ifdef WARN_DUMPABLE 305 warn_dumpable();
307 if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 1 && getuid())
308 fprintf(stderr, "Error fldd: I am dumpable\n");
309#endif
310 306
311 // check program access 307 // check program access
312 if (access(argv[1], R_OK)) { 308 if (access(argv[1], R_OK)) {
diff --git a/src/fnet/Makefile.in b/src/fnet/Makefile.in
index 37566db72..bd5fe9e7a 100644
--- a/src/fnet/Makefile.in
+++ b/src/fnet/Makefile.in
@@ -5,8 +5,8 @@ include ../common.mk
5%.o : %.c $(H_FILE_LIST) ../include/common.h ../include/libnetlink.h 5%.o : %.c $(H_FILE_LIST) ../include/common.h ../include/libnetlink.h
6 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@ 6 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@
7 7
8fnet: $(OBJS) ../lib/libnetlink.o 8fnet: $(OBJS) ../lib/common.o ../lib/libnetlink.o
9 $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/libnetlink.o $(LIBS) $(EXTRA_LDFLAGS) 9 $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/common.o ../lib/libnetlink.o $(LIBS) $(EXTRA_LDFLAGS)
10 10
11clean:; rm -fr *.o fnet *.gcov *.gcda *.gcno *.plist 11clean:; rm -fr *.o fnet *.gcov *.gcda *.gcno *.plist
12 12
diff --git a/src/fnet/main.c b/src/fnet/main.c
index f6316a7fe..db090fb95 100644
--- a/src/fnet/main.c
+++ b/src/fnet/main.c
@@ -21,7 +21,6 @@
21#include <sys/types.h> 21#include <sys/types.h>
22#include <sys/stat.h> 22#include <sys/stat.h>
23#include <sys/utsname.h> 23#include <sys/utsname.h>
24#include <sys/prctl.h>
25 24
26int arg_quiet = 0; 25int arg_quiet = 0;
27 26
@@ -69,10 +68,9 @@ printf("\n");
69 usage(); 68 usage();
70 return 0; 69 return 0;
71 } 70 }
72#ifdef WARN_DUMPABLE 71
73 if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 1 && getuid()) 72 warn_dumpable();
74 fprintf(stderr, "Error fnet: I am dumpable\n"); 73
75#endif
76 char *quiet = getenv("FIREJAIL_QUIET"); 74 char *quiet = getenv("FIREJAIL_QUIET");
77 if (quiet && strcmp(quiet, "yes") == 0) 75 if (quiet && strcmp(quiet, "yes") == 0)
78 arg_quiet = 1; 76 arg_quiet = 1;
diff --git a/src/fnetfilter/Makefile.in b/src/fnetfilter/Makefile.in
index 055167192..6fe650a17 100644
--- a/src/fnetfilter/Makefile.in
+++ b/src/fnetfilter/Makefile.in
@@ -5,8 +5,8 @@ include ../common.mk
5%.o : %.c $(H_FILE_LIST) ../include/common.h ../include/syscall.h 5%.o : %.c $(H_FILE_LIST) ../include/common.h ../include/syscall.h
6 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@ 6 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@
7 7
8fnetfilter: $(OBJS) 8fnetfilter: $(OBJS) ../lib/common.o
9 $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) $(EXTRA_LDFLAGS) 9 $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/common.o $(LIBS) $(EXTRA_LDFLAGS)
10 10
11clean:; rm -fr *.o fnetfilter *.gcov *.gcda *.gcno *.plist 11clean:; rm -fr *.o fnetfilter *.gcov *.gcda *.gcno *.plist
12 12
diff --git a/src/fnetfilter/main.c b/src/fnetfilter/main.c
index 1ca35ab56..381d0d36e 100644
--- a/src/fnetfilter/main.c
+++ b/src/fnetfilter/main.c
@@ -18,7 +18,6 @@
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 "../include/common.h" 20#include "../include/common.h"
21#include <sys/prctl.h>
22 21
23#define MAXBUF 4098 22#define MAXBUF 4098
24#define MAXARGS 16 23#define MAXARGS 16
@@ -181,10 +180,9 @@ printf("\n");
181 usage(); 180 usage();
182 return 1; 181 return 1;
183 } 182 }
184#ifdef WARN_DUMPABLE 183
185 if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 1 && getuid()) 184 warn_dumpable();
186 fprintf(stderr, "Error fnetfilter: I am dumpable\n"); 185
187#endif
188 char *destfile = (argc == 3)? argv[2]: argv[1]; 186 char *destfile = (argc == 3)? argv[2]: argv[1];
189 char *command = (argc == 3)? argv[1]: NULL; 187 char *command = (argc == 3)? argv[1]: NULL;
190//printf("command %s\n", command); 188//printf("command %s\n", command);
diff --git a/src/fsec-optimize/Makefile.in b/src/fsec-optimize/Makefile.in
index 0387f7ec7..cc5ac7e35 100644
--- a/src/fsec-optimize/Makefile.in
+++ b/src/fsec-optimize/Makefile.in
@@ -5,8 +5,8 @@ include ../common.mk
5%.o : %.c $(H_FILE_LIST) ../include/common.h ../include/seccomp.h ../include/syscall.h 5%.o : %.c $(H_FILE_LIST) ../include/common.h ../include/seccomp.h ../include/syscall.h
6 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@ 6 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@
7 7
8fsec-optimize: $(OBJS) ../lib/libnetlink.o 8fsec-optimize: $(OBJS) ../lib/common.o ../lib/libnetlink.o
9 $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) $(EXTRA_LDFLAGS) 9 $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/common.o ../lib/errno.o $(LIBS) $(EXTRA_LDFLAGS)
10 10
11clean:; rm -fr *.o fsec-optimize *.gcov *.gcda *.gcno *.plist 11clean:; rm -fr *.o fsec-optimize *.gcov *.gcda *.gcno *.plist
12 12
diff --git a/src/fsec-optimize/fsec_optimize.h b/src/fsec-optimize/fsec_optimize.h
index 034fde2ac..211111641 100644
--- a/src/fsec-optimize/fsec_optimize.h
+++ b/src/fsec-optimize/fsec_optimize.h
@@ -22,7 +22,6 @@
22#include "../include/common.h" 22#include "../include/common.h"
23#include "../include/seccomp.h" 23#include "../include/seccomp.h"
24#include <sys/mman.h> 24#include <sys/mman.h>
25#include <sys/prctl.h>
26 25
27// optimize.c 26// optimize.c
28struct sock_filter *duplicate(struct sock_filter *filter, int entries); 27struct sock_filter *duplicate(struct sock_filter *filter, int entries);
diff --git a/src/fsec-optimize/main.c b/src/fsec-optimize/main.c
index fb13eeca8..c64587068 100644
--- a/src/fsec-optimize/main.c
+++ b/src/fsec-optimize/main.c
@@ -18,6 +18,9 @@
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/ 19*/
20#include "fsec_optimize.h" 20#include "fsec_optimize.h"
21#include "../include/syscall.h"
22
23int arg_seccomp_error_action = SECCOMP_RET_ERRNO | EPERM; // error action: errno, log or kill
21 24
22static void usage(void) { 25static void usage(void) {
23 printf("Usage:\n"); 26 printf("Usage:\n");
@@ -44,11 +47,21 @@ printf("\n");
44 return 0; 47 return 0;
45 } 48 }
46 49
47#ifdef WARN_DUMPABLE 50 warn_dumpable();
48 // check FIREJAIL_PLUGIN in order to not print a warning during make 51
49 if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 1 && getuid() && getenv("FIREJAIL_PLUGIN")) 52 char *error_action = getenv("FIREJAIL_SECCOMP_ERROR_ACTION");
50 fprintf(stderr, "Error fsec-optimize: I am dumpable\n"); 53 if (error_action) {
51#endif 54 if (strcmp(error_action, "kill") == 0)
55 arg_seccomp_error_action = SECCOMP_RET_KILL;
56 else if (strcmp(error_action, "log") == 0)
57 arg_seccomp_error_action = SECCOMP_RET_LOG;
58 else {
59 arg_seccomp_error_action = errno_find_name(error_action);
60 if (arg_seccomp_error_action == -1)
61 errExit("seccomp-error-action: unknown errno");
62 arg_seccomp_error_action |= SECCOMP_RET_ERRNO;
63 }
64 }
52 65
53 char *fname = argv[1]; 66 char *fname = argv[1];
54 67
diff --git a/src/fsec-optimize/optimizer.c b/src/fsec-optimize/optimizer.c
index 776beaa75..eb777f13b 100644
--- a/src/fsec-optimize/optimizer.c
+++ b/src/fsec-optimize/optimizer.c
@@ -33,7 +33,7 @@
33static inline int is_blacklist(struct sock_filter *bpf) { 33static inline int is_blacklist(struct sock_filter *bpf) {
34 if (bpf->code == BPF_JMP + BPF_JEQ + BPF_K && 34 if (bpf->code == BPF_JMP + BPF_JEQ + BPF_K &&
35 (bpf + 1)->code == BPF_RET + BPF_K && 35 (bpf + 1)->code == BPF_RET + BPF_K &&
36 (bpf + 1)->k == SECCOMP_RET_KILL ) 36 (bpf + 1)->k == (__u32)arg_seccomp_error_action)
37 return 1; 37 return 1;
38 return 0; 38 return 0;
39} 39}
@@ -89,9 +89,9 @@ static int optimize_blacklists(struct sock_filter *filter, int entries) {
89 } 89 }
90 } 90 }
91 91
92 // step 3: add the new ret KILL, and recalculate entries 92 // step 3: add the new ret KILL/LOG/ERRNO, and recalculate entries
93 filter_step2[j].code = BPF_RET + BPF_K; 93 filter_step2[j].code = BPF_RET + BPF_K;
94 filter_step2[j].k = SECCOMP_RET_KILL; 94 filter_step2[j].k = arg_seccomp_error_action;
95 entries = j + 1; 95 entries = j + 1;
96 96
97 // step 4: recalculate jumps 97 // step 4: recalculate jumps
diff --git a/src/fsec-print/Makefile.in b/src/fsec-print/Makefile.in
index a30ff4ba3..bf39a8c77 100644
--- a/src/fsec-print/Makefile.in
+++ b/src/fsec-print/Makefile.in
@@ -5,8 +5,8 @@ include ../common.mk
5%.o : %.c $(H_FILE_LIST) ../include/common.h ../include/seccomp.h ../include/syscall.h 5%.o : %.c $(H_FILE_LIST) ../include/common.h ../include/seccomp.h ../include/syscall.h
6 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@ 6 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@
7 7
8fsec-print: $(OBJS) ../lib/libnetlink.o ../lib/errno.o ../lib/syscall.o 8fsec-print: $(OBJS) ../lib/common.o ../lib/libnetlink.o ../lib/errno.o ../lib/syscall.o
9 $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/errno.o ../lib/syscall.o $(LIBS) $(EXTRA_LDFLAGS) 9 $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/common.o ../lib/errno.o ../lib/syscall.o $(LIBS) $(EXTRA_LDFLAGS)
10 10
11clean:; rm -fr *.o fsec-print *.gcov *.gcda *.gcno *.plist 11clean:; rm -fr *.o fsec-print *.gcov *.gcda *.gcno *.plist
12 12
diff --git a/src/fsec-print/fsec_print.h b/src/fsec-print/fsec_print.h
index 9d17e3f18..337199288 100644
--- a/src/fsec-print/fsec_print.h
+++ b/src/fsec-print/fsec_print.h
@@ -23,7 +23,6 @@
23#include "../include/seccomp.h" 23#include "../include/seccomp.h"
24#include "../include/syscall.h" 24#include "../include/syscall.h"
25#include <sys/mman.h> 25#include <sys/mman.h>
26#include <sys/prctl.h>
27 26
28// print.c 27// print.c
29void print(struct sock_filter *filter, int entries); 28void print(struct sock_filter *filter, int entries);
diff --git a/src/fsec-print/main.c b/src/fsec-print/main.c
index d1f056e47..ed030db21 100644
--- a/src/fsec-print/main.c
+++ b/src/fsec-print/main.c
@@ -61,10 +61,7 @@ printf("\n");
61 return 0; 61 return 0;
62 } 62 }
63 63
64#ifdef WARN_DUMPABLE 64 warn_dumpable();
65 if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 1 && getuid())
66 fprintf(stderr, "Error fsec-print: I am dumpable\n");
67#endif
68 65
69 char *fname = argv[1]; 66 char *fname = argv[1];
70 67
diff --git a/src/fseccomp/Makefile.in b/src/fseccomp/Makefile.in
index 8623db6f8..b776a73ce 100644
--- a/src/fseccomp/Makefile.in
+++ b/src/fseccomp/Makefile.in
@@ -5,8 +5,8 @@ include ../common.mk
5%.o : %.c $(H_FILE_LIST) ../include/common.h ../include/syscall.h 5%.o : %.c $(H_FILE_LIST) ../include/common.h ../include/syscall.h
6 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@ 6 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@
7 7
8fseccomp: $(OBJS) ../lib/errno.o ../lib/syscall.o 8fseccomp: $(OBJS) ../lib/common.o ../lib/errno.o ../lib/syscall.o
9 $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/errno.o ../lib/syscall.o $(LIBS) $(EXTRA_LDFLAGS) 9 $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/common.o ../lib/errno.o ../lib/syscall.o $(LIBS) $(EXTRA_LDFLAGS)
10 10
11clean:; rm -fr *.o fseccomp *.gcov *.gcda *.gcno *.plist 11clean:; rm -fr *.o fseccomp *.gcov *.gcda *.gcno *.plist
12 12
diff --git a/src/fseccomp/fseccomp.h b/src/fseccomp/fseccomp.h
index e40999938..e8dd083b6 100644
--- a/src/fseccomp/fseccomp.h
+++ b/src/fseccomp/fseccomp.h
@@ -23,7 +23,6 @@
23#include <stdlib.h> 23#include <stdlib.h>
24#include <string.h> 24#include <string.h>
25#include <assert.h> 25#include <assert.h>
26#include <sys/prctl.h>
27#include "../include/common.h" 26#include "../include/common.h"
28#include "../include/syscall.h" 27#include "../include/syscall.h"
29 28
diff --git a/src/fseccomp/main.c b/src/fseccomp/main.c
index f505ca0f3..f47efb5e8 100644
--- a/src/fseccomp/main.c
+++ b/src/fseccomp/main.c
@@ -20,7 +20,7 @@
20#include "fseccomp.h" 20#include "fseccomp.h"
21#include "../include/seccomp.h" 21#include "../include/seccomp.h"
22int arg_quiet = 0; 22int arg_quiet = 0;
23int arg_seccomp_error_action = EPERM; // error action: errno, log or kill 23int arg_seccomp_error_action = SECCOMP_RET_ERRNO | EPERM; // error action: errno, log or kill
24 24
25static void usage(void) { 25static void usage(void) {
26 printf("Usage:\n"); 26 printf("Usage:\n");
@@ -69,11 +69,7 @@ printf("\n");
69 return 0; 69 return 0;
70 } 70 }
71 71
72#ifdef WARN_DUMPABLE 72 warn_dumpable();
73 // check FIREJAIL_PLUGIN in order to not print a warning during make
74 if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 1 && getuid() && getenv("FIREJAIL_PLUGIN"))
75 fprintf(stderr, "Error fseccomp: I am dumpable\n");
76#endif
77 73
78 char *quiet = getenv("FIREJAIL_QUIET"); 74 char *quiet = getenv("FIREJAIL_QUIET");
79 if (quiet && strcmp(quiet, "yes") == 0) 75 if (quiet && strcmp(quiet, "yes") == 0)
diff --git a/src/fseccomp/seccomp_secondary.c b/src/fseccomp/seccomp_secondary.c
index f024859d3..b8e8d0a89 100644
--- a/src/fseccomp/seccomp_secondary.c
+++ b/src/fseccomp/seccomp_secondary.c
@@ -126,7 +126,7 @@ void seccomp_secondary_block(const char *fname) {
126 EXAMINE_SYSCALL, 126 EXAMINE_SYSCALL,
127#if defined(__x86_64__) 127#if defined(__x86_64__)
128 // block x32 128 // block x32
129 HANDLE_X32_KILL, 129 HANDLE_X32,
130#endif 130#endif
131 // block personality(2) where domain != PER_LINUX or 0xffffffff (query current personality) 131 // block personality(2) where domain != PER_LINUX or 0xffffffff (query current personality)
132 // 0: if personality(2), continue to 1, else goto 7 (allow) 132 // 0: if personality(2), continue to 1, else goto 7 (allow)
diff --git a/src/include/common.h b/src/include/common.h
index 5df51c5a9..5497929c7 100644
--- a/src/include/common.h
+++ b/src/include/common.h
@@ -38,11 +38,6 @@
38 38
39#define errExit(msg) do { char msgout[500]; snprintf(msgout, 500, "Error %s: %s:%d %s", msg, __FILE__, __LINE__, __FUNCTION__); perror(msgout); exit(1);} while (0) 39#define errExit(msg) do { char msgout[500]; snprintf(msgout, 500, "Error %s: %s:%d %s", msg, __FILE__, __LINE__, __FUNCTION__); perror(msgout); exit(1);} while (0)
40 40
41// check if processes run with dumpable flag set
42// currently we get "Error fseccomp: I am dumpable" every time we run a firejail build on Debian 8,
43// regardless what Debian version we run the build on
44//#define WARN_DUMPABLE
45
46// macro to print ip addresses in a printf statement 41// macro to print ip addresses in a printf statement
47#define PRINT_IP(A) \ 42#define PRINT_IP(A) \
48((int) (((A) >> 24) & 0xFF)), ((int) (((A) >> 16) & 0xFF)), ((int) (((A) >> 8) & 0xFF)), ((int) ( (A) & 0xFF)) 43((int) (((A) >> 24) & 0xFF)), ((int) (((A) >> 16) & 0xFF)), ((int) (((A) >> 8) & 0xFF)), ((int) ( (A) & 0xFF))
@@ -126,4 +121,6 @@ char *pid_proc_comm(const pid_t pid);
126char *pid_proc_cmdline(const pid_t pid); 121char *pid_proc_cmdline(const pid_t pid);
127int pid_proc_cmdline_x11_xpra_xephyr(const pid_t pid); 122int pid_proc_cmdline_x11_xpra_xephyr(const pid_t pid);
128int pid_hidepid(void); 123int pid_hidepid(void);
124void warn_dumpable(void);
125const char *gnu_basename(const char *path);
129#endif 126#endif
diff --git a/src/include/seccomp.h b/src/include/seccomp.h
index 90db16d39..b3b75c2d1 100644
--- a/src/include/seccomp.h
+++ b/src/include/seccomp.h
@@ -201,7 +201,7 @@
201#define VALIDATE_ARCHITECTURE_KILL \ 201#define VALIDATE_ARCHITECTURE_KILL \
202 BPF_STMT(BPF_LD+BPF_W+BPF_ABS, (offsetof(struct seccomp_data, arch))), \ 202 BPF_STMT(BPF_LD+BPF_W+BPF_ABS, (offsetof(struct seccomp_data, arch))), \
203 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ARCH_NR, 1, 0), \ 203 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ARCH_NR, 1, 0), \
204 BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL) 204 KILL_OR_RETURN_ERRNO
205 205
206#define VALIDATE_ARCHITECTURE_64 \ 206#define VALIDATE_ARCHITECTURE_64 \
207 BPF_STMT(BPF_LD+BPF_W+BPF_ABS, (offsetof(struct seccomp_data, arch))), \ 207 BPF_STMT(BPF_LD+BPF_W+BPF_ABS, (offsetof(struct seccomp_data, arch))), \
@@ -222,11 +222,7 @@
222#define HANDLE_X32 \ 222#define HANDLE_X32 \
223 BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, X32_SYSCALL_BIT, 1, 0), \ 223 BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, X32_SYSCALL_BIT, 1, 0), \
224 BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, 0, 1, 0), \ 224 BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, 0, 1, 0), \
225 RETURN_ERRNO(EPERM) 225 KILL_OR_RETURN_ERRNO
226#define HANDLE_X32_KILL \
227 BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, X32_SYSCALL_BIT, 1, 0), \
228 BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, 0, 1, 0), \
229 BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL)
230#endif 226#endif
231 227
232#define EXAMINE_SYSCALL BPF_STMT(BPF_LD+BPF_W+BPF_ABS, \ 228#define EXAMINE_SYSCALL BPF_STMT(BPF_LD+BPF_W+BPF_ABS, \
@@ -258,6 +254,8 @@
258 BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ERRNO | nr) 254 BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ERRNO | nr)
259 255
260extern int arg_seccomp_error_action; // error action: errno, log or kill 256extern int arg_seccomp_error_action; // error action: errno, log or kill
257#define DEFAULT_SECCOMP_ERROR_ACTION EPERM
258
261#define KILL_OR_RETURN_ERRNO \ 259#define KILL_OR_RETURN_ERRNO \
262 BPF_STMT(BPF_RET+BPF_K, arg_seccomp_error_action) 260 BPF_STMT(BPF_RET+BPF_K, arg_seccomp_error_action)
263 261
diff --git a/src/include/syscall_armeabi.h b/src/include/syscall_armeabi.h
index cbdc67f37..3b574f875 100644
--- a/src/include/syscall_armeabi.h
+++ b/src/include/syscall_armeabi.h
@@ -42,6 +42,7 @@
42{ "exit", 1 }, 42{ "exit", 1 },
43{ "exit_group", 248 }, 43{ "exit_group", 248 },
44{ "faccessat", 334 }, 44{ "faccessat", 334 },
45{ "faccessat2", 439 },
45{ "fallocate", 352 }, 46{ "fallocate", 352 },
46{ "fanotify_init", 367 }, 47{ "fanotify_init", 367 },
47{ "fanotify_mark", 368 }, 48{ "fanotify_mark", 368 },
diff --git a/src/include/syscall_i386.h b/src/include/syscall_i386.h
index 4795e5b2a..752e11f24 100644
--- a/src/include/syscall_i386.h
+++ b/src/include/syscall_i386.h
@@ -54,6 +54,7 @@
54{ "exit", 1 }, 54{ "exit", 1 },
55{ "exit_group", 252 }, 55{ "exit_group", 252 },
56{ "faccessat", 307 }, 56{ "faccessat", 307 },
57{ "faccessat2", 439 },
57{ "fadvise64", 250 }, 58{ "fadvise64", 250 },
58{ "fadvise64_64", 272 }, 59{ "fadvise64_64", 272 },
59{ "fallocate", 324 }, 60{ "fallocate", 324 },
diff --git a/src/include/syscall_x86_64.h b/src/include/syscall_x86_64.h
index 539e874be..97f2762b1 100644
--- a/src/include/syscall_x86_64.h
+++ b/src/include/syscall_x86_64.h
@@ -47,6 +47,7 @@
47{ "exit", 60 }, 47{ "exit", 60 },
48{ "exit_group", 231 }, 48{ "exit_group", 231 },
49{ "faccessat", 269 }, 49{ "faccessat", 269 },
50{ "faccessat2", 439 },
50{ "fadvise64", 221 }, 51{ "fadvise64", 221 },
51{ "fallocate", 285 }, 52{ "fallocate", 285 },
52{ "fanotify_init", 300 }, 53{ "fanotify_init", 300 },
diff --git a/src/lib/common.c b/src/lib/common.c
index 823442835..ace5cb87e 100644
--- a/src/lib/common.c
+++ b/src/lib/common.c
@@ -267,7 +267,6 @@ int pid_proc_cmdline_x11_xpra_xephyr(const pid_t pid) {
267} 267}
268 268
269// return 1 if /proc is mounted hidepid, or if /proc/mouns access is denied 269// return 1 if /proc is mounted hidepid, or if /proc/mouns access is denied
270#define BUFLEN 4096
271int pid_hidepid(void) { 270int pid_hidepid(void) {
272 FILE *fp = fopen("/proc/mounts", "r"); 271 FILE *fp = fopen("/proc/mounts", "r");
273 if (!fp) 272 if (!fp)
@@ -288,6 +287,39 @@ int pid_hidepid(void) {
288 return 0; 287 return 0;
289} 288}
290 289
290// print error if unprivileged users can trace the process
291void warn_dumpable(void) {
292 if (getuid() != 0 && prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 1 && getenv("FIREJAIL_PLUGIN")) {
293 fprintf(stderr, "Error: dumpable process\n");
294
295 // best effort to provide detailed debug information
296 // cannot use process name, it is just a file descriptor number
297 char path[BUFLEN];
298 ssize_t len = readlink("/proc/self/exe", path, BUFLEN - 1);
299 if (len < 0)
300 return;
301 path[len] = '\0';
302 // path can refer to a sandbox mount namespace, use basename only
303 const char *base = gnu_basename(path);
304
305 struct stat s;
306 if (stat("/proc/self/exe", &s) == 0 && s.st_uid != 0)
307 fprintf(stderr, "Change owner of %s executable to root\n", base);
308 else if (access("/proc/self/exe", R_OK) == 0)
309 fprintf(stderr, "Remove read permission on %s executable\n", base);
310 }
311}
312
313// Equivalent to the GNU version of basename, which is incompatible with
314// the POSIX basename. A few lines of code saves any portability pain.
315// https://www.gnu.org/software/libc/manual/html_node/Finding-Tokens-in-a-String.html#index-basename
316const char *gnu_basename(const char *path) {
317 const char *last_slash = strrchr(path, '/');
318 if (!last_slash)
319 return path;
320 return last_slash+1;
321}
322
291//************************** 323//**************************
292// time trace based on getticks function 324// time trace based on getticks function
293//************************** 325//**************************
diff --git a/src/lib/syscall.c b/src/lib/syscall.c
index 4903971ad..758f1ce0b 100644
--- a/src/lib/syscall.c
+++ b/src/lib/syscall.c
@@ -336,6 +336,7 @@ static const SyscallGroupList sysgroups[] = {
336#endif 336#endif
337 }, 337 },
338 { .name = "@default-keep", .list = 338 { .name = "@default-keep", .list =
339 "execveat," // commonly used by fexecve
339 "execve," 340 "execve,"
340 "prctl" 341 "prctl"
341 }, 342 },
@@ -358,6 +359,9 @@ static const SyscallGroupList sysgroups[] = {
358#ifdef SYS_faccessat 359#ifdef SYS_faccessat
359 "faccessat," 360 "faccessat,"
360#endif 361#endif
362#ifdef SYS_faccessat2
363 "faccessat2,"
364#endif
361#ifdef SYS_fallocate 365#ifdef SYS_fallocate
362 "fallocate," 366 "fallocate,"
363#endif 367#endif
diff --git a/src/man/firecfg.txt b/src/man/firecfg.txt
index f3123356a..2c02aee47 100644
--- a/src/man/firecfg.txt
+++ b/src/man/firecfg.txt
@@ -61,7 +61,7 @@ $ sudo firecfg --add-users dustin lucas mike eleven
61 61
62.TP 62.TP
63\fB\-\-bindir=directory 63\fB\-\-bindir=directory
64Create and search symbolic links in directory instead of the default location /user/local/bin. 64Create and search symbolic links in directory instead of the default location /usr/local/bin.
65Directory should precede /usr/bin and /bin in the PATH environment variable. 65Directory should precede /usr/bin and /bin in the PATH environment variable.
66 66
67.TP 67.TP
diff --git a/src/man/firejail-profile.txt b/src/man/firejail-profile.txt
index 9524254c1..5e77b5f70 100644
--- a/src/man/firejail-profile.txt
+++ b/src/man/firejail-profile.txt
@@ -266,7 +266,7 @@ Mount new /root and /home/user directories in temporary
266filesystems. All modifications are discarded when the sandbox is 266filesystems. All modifications are discarded when the sandbox is
267closed. 267closed.
268.TP 268.TP
269\fBprivate directory 269\fBprivate=directory
270Use directory as user home. 270Use directory as user home.
271.TP 271.TP
272\fBprivate-bin file,file 272\fBprivate-bin file,file
@@ -862,6 +862,11 @@ the parent interface specified by --net is not configured. An IP address and
862a default gateway address also have to be added. 862a default gateway address also have to be added.
863 863
864.TP 864.TP
865\fBnetns namespace
866Run the program in a named, persistent network namespace. These can
867be created and configured using "ip netns".
868
869.TP
865\fBveth-name name 870\fBveth-name name
866Use this name for the interface connected to the bridge for --net=bridge_interface commands, 871Use this name for the interface connected to the bridge for --net=bridge_interface commands,
867instead of the default one. 872instead of the default one.
diff --git a/src/man/firejail.txt b/src/man/firejail.txt
index 347e2b31b..e85a02ee8 100644
--- a/src/man/firejail.txt
+++ b/src/man/firejail.txt
@@ -693,6 +693,10 @@ Example:
693$ firejail \-\-net=eth0 \-\-defaultgw=10.10.20.1 firefox 693$ firejail \-\-net=eth0 \-\-defaultgw=10.10.20.1 firefox
694#endif 694#endif
695.TP 695.TP
696\fB\-\-deterministic-exit-code
697Always exit firejail with the first child's exit status. The default behavior is to use the exit status of the final child to exit, which can be nondeterministic.
698.br
699.TP
696\fB\-\-disable-mnt 700\fB\-\-disable-mnt
697Blacklist /mnt, /media, /run/mount and /run/media access. 701Blacklist /mnt, /media, /run/mount and /run/media access.
698.br 702.br
@@ -703,10 +707,6 @@ Example:
703$ firejail \-\-disable-mnt firefox 707$ firejail \-\-disable-mnt firefox
704 708
705.TP 709.TP
706\fB\-\-deterministic-exit-code
707Always exit firejail with the first child's exit status. The default behavior is to use the exit status of the final child to exit, which can be nondeterministic.
708
709.TP
710\fB\-\-dns=address 710\fB\-\-dns=address
711Set a DNS server for the sandbox. Up to three DNS servers can be defined. 711Set a DNS server for the sandbox. Up to three DNS servers can be defined.
712Use this option if you don't trust the DNS setup on your network. 712Use this option if you don't trust the DNS setup on your network.
@@ -1317,7 +1317,7 @@ $ firejail --netfilter=/etc/firejail/webserver.net --net=eth0 \\
1317.br 1317.br
1318 1318
1319.br 1319.br
1320.B nolocal.net 1320.B nolocal.net/nolocal6.net
1321is a desktop client firewall that disable access to local network. Example: 1321is a desktop client firewall that disable access to local network. Example:
1322.br 1322.br
1323 1323
@@ -2273,7 +2273,7 @@ rm: cannot remove `testfile': Operation not permitted
2273.TP 2273.TP
2274\fB\-\-seccomp.keep=syscall,@group,!syscall2 2274\fB\-\-seccomp.keep=syscall,@group,!syscall2
2275Enable seccomp filter, blacklist all syscall not listed and "syscall2". 2275Enable seccomp filter, blacklist all syscall not listed and "syscall2".
2276The system calls needed by Firejail (group @default-keep: prctl, execve) 2276The system calls needed by Firejail (group @default-keep: prctl, execve, execveat)
2277are handled with the preload library. On a 64 bit architecture, an 2277are handled with the preload library. On a 64 bit architecture, an
2278additional filter for 32 bit system calls can be installed with 2278additional filter for 32 bit system calls can be installed with
2279\-\-seccomp.32.keep. 2279\-\-seccomp.32.keep.
diff --git a/src/profstats/main.c b/src/profstats/main.c
index 4c1221464..68f62831b 100644
--- a/src/profstats/main.c
+++ b/src/profstats/main.c
@@ -30,6 +30,8 @@ static int cnt_seccomp = 0;
30static int cnt_caps = 0; 30static int cnt_caps = 0;
31static int cnt_dbus_system_none = 0; 31static int cnt_dbus_system_none = 0;
32static int cnt_dbus_user_none = 0; 32static int cnt_dbus_user_none = 0;
33static int cnt_dbus_system_filter = 0;
34static int cnt_dbus_user_filter = 0;
33static int cnt_dotlocal = 0; 35static int cnt_dotlocal = 0;
34static int cnt_globalsdotlocal = 0; 36static int cnt_globalsdotlocal = 0;
35static int cnt_netnone = 0; 37static int cnt_netnone = 0;
@@ -107,6 +109,7 @@ void process_file(const char *fname) {
107 return; 109 return;
108 } 110 }
109 111
112 int have_include_local = 0;
110 char buf[MAXBUF]; 113 char buf[MAXBUF];
111 while (fgets(buf, MAXBUF, fp)) { 114 while (fgets(buf, MAXBUF, fp)) {
112 char *ptr = strchr(buf, '\n'); 115 char *ptr = strchr(buf, '\n');
@@ -152,11 +155,16 @@ void process_file(const char *fname) {
152 cnt_privateetc++; 155 cnt_privateetc++;
153 else if (strncmp(ptr, "dbus-system none", 16) == 0) 156 else if (strncmp(ptr, "dbus-system none", 16) == 0)
154 cnt_dbus_system_none++; 157 cnt_dbus_system_none++;
158 else if (strncmp(ptr, "dbus-system", 11) == 0)
159 cnt_dbus_system_filter++;
155 else if (strncmp(ptr, "dbus-user none", 14) == 0) 160 else if (strncmp(ptr, "dbus-user none", 14) == 0)
156 cnt_dbus_user_none++; 161 cnt_dbus_user_none++;
162 else if (strncmp(ptr, "dbus-user", 9) == 0)
163 cnt_dbus_user_filter++;
157 else if (strncmp(ptr, "include ", 8) == 0) { 164 else if (strncmp(ptr, "include ", 8) == 0) {
158 // not processing .local files 165 // not processing .local files
159 if (strstr(ptr, ".local")) { 166 if (strstr(ptr, ".local")) {
167 have_include_local = 1;
160//printf("dotlocal %d, level %d - #%s#, redirect #%s#\n", cnt_dotlocal, level, fname, buf + 8); 168//printf("dotlocal %d, level %d - #%s#, redirect #%s#\n", cnt_dotlocal, level, fname, buf + 8);
161 if (strstr(ptr, "globals.local")) 169 if (strstr(ptr, "globals.local"))
162 cnt_globalsdotlocal++; 170 cnt_globalsdotlocal++;
@@ -174,6 +182,8 @@ void process_file(const char *fname) {
174 } 182 }
175 183
176 fclose(fp); 184 fclose(fp);
185 if (!have_include_local)
186 printf("No include .local found in %s\n", fname);
177 level--; 187 level--;
178} 188}
179 189
@@ -257,7 +267,9 @@ int main(int argc, char **argv) {
257 int whitelistrunuser = cnt_whitelistrunuser; 267 int whitelistrunuser = cnt_whitelistrunuser;
258 int whitelistusrshare = cnt_whitelistusrshare; 268 int whitelistusrshare = cnt_whitelistusrshare;
259 int dbussystemnone = cnt_dbus_system_none; 269 int dbussystemnone = cnt_dbus_system_none;
270 int dbussystemfilter = cnt_dbus_system_filter;
260 int dbususernone = cnt_dbus_user_none; 271 int dbususernone = cnt_dbus_user_none;
272 int dbususerfilter = cnt_dbus_user_filter;
261 int ssh = cnt_ssh; 273 int ssh = cnt_ssh;
262 int mdwx = cnt_mdwx; 274 int mdwx = cnt_mdwx;
263 275
@@ -278,6 +290,16 @@ int main(int argc, char **argv) {
278 cnt_globalsdotlocal = globalsdotlocal + 1; 290 cnt_globalsdotlocal = globalsdotlocal + 1;
279 if (cnt_whitelistrunuser > (whitelistrunuser + 1)) 291 if (cnt_whitelistrunuser > (whitelistrunuser + 1))
280 cnt_whitelistrunuser = whitelistrunuser + 1; 292 cnt_whitelistrunuser = whitelistrunuser + 1;
293 if (cnt_seccomp > (seccomp + 1))
294 cnt_seccomp = seccomp + 1;
295 if (cnt_dbus_user_none > (dbususernone + 1))
296 cnt_dbus_user_none = dbususernone + 1;
297 if (cnt_dbus_user_filter > (dbususerfilter + 1))
298 cnt_dbus_user_filter = dbususerfilter + 1;
299 if (cnt_dbus_system_none > (dbussystemnone + 1))
300 cnt_dbus_system_none = dbussystemnone + 1;
301 if (cnt_dbus_system_filter > (dbussystemfilter + 1))
302 cnt_dbus_system_filter = dbussystemfilter + 1;
281 303
282 if (arg_dbus_system_none && dbussystemnone == cnt_dbus_system_none) 304 if (arg_dbus_system_none && dbussystemnone == cnt_dbus_system_none)
283 printf("No dbus-system none found in %s\n", argv[i]); 305 printf("No dbus-system none found in %s\n", argv[i]);
@@ -337,7 +359,9 @@ int main(int argc, char **argv) {
337 printf(" whitelist usr/share\t\t%d (include whitelist-usr-share-common.inc\n", cnt_whitelistusrshare); 359 printf(" whitelist usr/share\t\t%d (include whitelist-usr-share-common.inc\n", cnt_whitelistusrshare);
338 printf(" net none\t\t\t%d\n", cnt_netnone); 360 printf(" net none\t\t\t%d\n", cnt_netnone);
339 printf(" dbus-user none \t\t%d\n", cnt_dbus_user_none); 361 printf(" dbus-user none \t\t%d\n", cnt_dbus_user_none);
362 printf(" dbus-user filter \t\t%d\n", cnt_dbus_user_filter);
340 printf(" dbus-system none \t\t%d\n", cnt_dbus_system_none); 363 printf(" dbus-system none \t\t%d\n", cnt_dbus_system_none);
364 printf(" dbus-system filter \t\t%d\n", cnt_dbus_system_filter);
341 printf("\n"); 365 printf("\n");
342 return 0; 366 return 0;
343} 367}