aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar netblue30 <netblue30@protonmail.com>2021-10-09 13:16:31 +0000
committerLibravatar GitHub <noreply@github.com>2021-10-09 13:16:31 +0000
commit44c15b67d48f2e5d2deff3f88cbf1129979065f7 (patch)
tree093e526ad450867c3cdf0b19a48a35553a91d342 /src
parentfirejail.h: add missing linux/limits.h include (diff)
parentMerge pull request #4579 from dm9pZCAq/master (diff)
downloadfirejail-44c15b67d48f2e5d2deff3f88cbf1129979065f7.tar.gz
firejail-44c15b67d48f2e5d2deff3f88cbf1129979065f7.tar.zst
firejail-44c15b67d48f2e5d2deff3f88cbf1129979065f7.zip
Merge branch 'master' into fix-include-limits-h
Diffstat (limited to 'src')
-rw-r--r--src/fbuilder/build_fs.c8
-rw-r--r--src/fbuilder/build_home.c4
-rw-r--r--src/fbuilder/build_profile.c2
-rw-r--r--src/fcopy/main.c3
-rw-r--r--src/firecfg/firecfg.config1
-rw-r--r--src/firejail/checkcfg.c2
-rw-r--r--src/firejail/env.c1
-rw-r--r--src/firejail/firejail.h6
-rw-r--r--src/firejail/fs.c50
-rw-r--r--src/firejail/fs_dev.c2
-rw-r--r--src/firejail/fs_home.c11
-rw-r--r--src/firejail/fs_trace.c31
-rw-r--r--src/firejail/fs_whitelist.c59
-rw-r--r--src/firejail/main.c2
-rw-r--r--src/firejail/mountinfo.c71
-rw-r--r--src/firejail/profile.c15
-rw-r--r--src/firejail/sandbox.c13
-rw-r--r--src/firejail/selinux.c21
-rw-r--r--src/firejail/util.c14
-rw-r--r--src/man/firejail-profile.txt2
20 files changed, 181 insertions, 137 deletions
diff --git a/src/fbuilder/build_fs.c b/src/fbuilder/build_fs.c
index 019c3ac5a..8700e0ba1 100644
--- a/src/fbuilder/build_fs.c
+++ b/src/fbuilder/build_fs.c
@@ -182,12 +182,12 @@ static void var_callback(char *ptr) {
182void build_var(const char *fname, FILE *fp) { 182void build_var(const char *fname, FILE *fp) {
183 assert(fname); 183 assert(fname);
184 184
185 var_skip = filedb_load_whitelist(var_skip, "whitelist-var-common.inc", "allow /var/"); 185 var_skip = filedb_load_whitelist(var_skip, "whitelist-var-common.inc", "whitelist /var/");
186 process_files(fname, "/var", var_callback); 186 process_files(fname, "/var", var_callback);
187 187
188 // always whitelist /var 188 // always whitelist /var
189 if (var_out) 189 if (var_out)
190 filedb_print(var_out, "allow /var/", fp); 190 filedb_print(var_out, "whitelist /var/", fp);
191 fprintf(fp, "include whitelist-var-common.inc\n"); 191 fprintf(fp, "include whitelist-var-common.inc\n");
192} 192}
193 193
@@ -222,12 +222,12 @@ static void share_callback(char *ptr) {
222void build_share(const char *fname, FILE *fp) { 222void build_share(const char *fname, FILE *fp) {
223 assert(fname); 223 assert(fname);
224 224
225 share_skip = filedb_load_whitelist(share_skip, "whitelist-usr-share-common.inc", "allow /usr/share/"); 225 share_skip = filedb_load_whitelist(share_skip, "whitelist-usr-share-common.inc", "whitelist /usr/share/");
226 process_files(fname, "/usr/share", share_callback); 226 process_files(fname, "/usr/share", share_callback);
227 227
228 // always whitelist /usr/share 228 // always whitelist /usr/share
229 if (share_out) 229 if (share_out)
230 filedb_print(share_out, "allow /usr/share/", fp); 230 filedb_print(share_out, "whitelist /usr/share/", fp);
231 fprintf(fp, "include whitelist-usr-share-common.inc\n"); 231 fprintf(fp, "include whitelist-usr-share-common.inc\n");
232} 232}
233 233
diff --git a/src/fbuilder/build_home.c b/src/fbuilder/build_home.c
index c85474779..0fe0ffef6 100644
--- a/src/fbuilder/build_home.c
+++ b/src/fbuilder/build_home.c
@@ -140,7 +140,7 @@ void build_home(const char *fname, FILE *fp) {
140 assert(fname); 140 assert(fname);
141 141
142 // load whitelist common 142 // load whitelist common
143 db_skip = filedb_load_whitelist(db_skip, "whitelist-common.inc", "allow ${HOME}/"); 143 db_skip = filedb_load_whitelist(db_skip, "whitelist-common.inc", "whitelist ${HOME}/");
144 144
145 // find user home directory 145 // find user home directory
146 struct passwd *pw = getpwuid(getuid()); 146 struct passwd *pw = getpwuid(getuid());
@@ -168,7 +168,7 @@ void build_home(const char *fname, FILE *fp) {
168 168
169 // print the out list if any 169 // print the out list if any
170 if (db_out) { 170 if (db_out) {
171 filedb_print(db_out, "allow ${HOME}/", fp); 171 filedb_print(db_out, "whitelist ${HOME}/", fp);
172 fprintf(fp, "include whitelist-common.inc\n"); 172 fprintf(fp, "include whitelist-common.inc\n");
173 } 173 }
174 else 174 else
diff --git a/src/fbuilder/build_profile.c b/src/fbuilder/build_profile.c
index 0b9a99739..c945d7253 100644
--- a/src/fbuilder/build_profile.c
+++ b/src/fbuilder/build_profile.c
@@ -92,7 +92,7 @@ void build_profile(int argc, char **argv, int index, FILE *fp) {
92 92
93 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { 93 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
94 if (fp == stdout) 94 if (fp == stdout)
95 printf("--- Built profile beings after this line ---\n"); 95 printf("--- Built profile begins after this line ---\n");
96 fprintf(fp, "# Save this file as \"application.profile\" (change \"application\" with the\n"); 96 fprintf(fp, "# Save this file as \"application.profile\" (change \"application\" with the\n");
97 fprintf(fp, "# program name) in ~/.config/firejail directory. Firejail will find it\n"); 97 fprintf(fp, "# program name) in ~/.config/firejail directory. Firejail will find it\n");
98 fprintf(fp, "# automatically every time you sandbox your application.\n#\n"); 98 fprintf(fp, "# automatically every time you sandbox your application.\n#\n");
diff --git a/src/fcopy/main.c b/src/fcopy/main.c
index 31810de9a..f279af89f 100644
--- a/src/fcopy/main.c
+++ b/src/fcopy/main.c
@@ -88,7 +88,8 @@ static void selinux_relabel_path(const char *path, const char *inside_path) {
88 if (arg_debug) 88 if (arg_debug)
89 printf("Relabeling %s as %s (%s)\n", path, inside_path, fcon); 89 printf("Relabeling %s as %s (%s)\n", path, inside_path, fcon);
90 90
91 setfilecon_raw(procfs_path, fcon); 91 if (setfilecon_raw(procfs_path, fcon) != 0 && arg_debug)
92 printf("Cannot relabel %s: %s\n", path, strerror(errno));
92 } 93 }
93 freecon(fcon); 94 freecon(fcon);
94 close: 95 close:
diff --git a/src/firecfg/firecfg.config b/src/firecfg/firecfg.config
index a544e25f2..aad22ec7a 100644
--- a/src/firecfg/firecfg.config
+++ b/src/firecfg/firecfg.config
@@ -151,6 +151,7 @@ clocks
151cmus 151cmus
152code 152code
153code-oss 153code-oss
154codium
154cola 155cola
155colorful 156colorful
156com.github.bleakgrey.tootle 157com.github.bleakgrey.tootle
diff --git a/src/firejail/checkcfg.c b/src/firejail/checkcfg.c
index 06e6f0ccb..e5d837bbb 100644
--- a/src/firejail/checkcfg.c
+++ b/src/firejail/checkcfg.c
@@ -58,6 +58,7 @@ int checkcfg(int val) {
58 cfg_val[CFG_XPRA_ATTACH] = 0; 58 cfg_val[CFG_XPRA_ATTACH] = 0;
59 cfg_val[CFG_SECCOMP_ERROR_ACTION] = -1; 59 cfg_val[CFG_SECCOMP_ERROR_ACTION] = -1;
60 cfg_val[CFG_BROWSER_ALLOW_DRM] = 0; 60 cfg_val[CFG_BROWSER_ALLOW_DRM] = 0;
61 cfg_val[CFG_ALLOW_TRAY] = 0;
61 62
62 // open configuration file 63 // open configuration file
63 const char *fname = SYSCONFDIR "/firejail.config"; 64 const char *fname = SYSCONFDIR "/firejail.config";
@@ -122,6 +123,7 @@ int checkcfg(int val) {
122 PARSE_YESNO(CFG_XPRA_ATTACH, "xpra-attach") 123 PARSE_YESNO(CFG_XPRA_ATTACH, "xpra-attach")
123 PARSE_YESNO(CFG_BROWSER_DISABLE_U2F, "browser-disable-u2f") 124 PARSE_YESNO(CFG_BROWSER_DISABLE_U2F, "browser-disable-u2f")
124 PARSE_YESNO(CFG_BROWSER_ALLOW_DRM, "browser-allow-drm") 125 PARSE_YESNO(CFG_BROWSER_ALLOW_DRM, "browser-allow-drm")
126 PARSE_YESNO(CFG_ALLOW_TRAY, "allow-tray")
125#undef PARSE_YESNO 127#undef PARSE_YESNO
126 128
127 // netfilter 129 // netfilter
diff --git a/src/firejail/env.c b/src/firejail/env.c
index ad16de037..4c0d729a1 100644
--- a/src/firejail/env.c
+++ b/src/firejail/env.c
@@ -22,6 +22,7 @@
22#include <sys/stat.h> 22#include <sys/stat.h>
23#include <unistd.h> 23#include <unistd.h>
24#include <dirent.h> 24#include <dirent.h>
25#include <limits.h>
25 26
26typedef struct env_t { 27typedef struct env_t {
27 struct env_t *next; 28 struct env_t *next;
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h
index f554a3204..bf51a4c93 100644
--- a/src/firejail/firejail.h
+++ b/src/firejail/firejail.h
@@ -564,7 +564,7 @@ typedef struct {
564 564
565// mountinfo.c 565// mountinfo.c
566MountData *get_last_mount(void); 566MountData *get_last_mount(void);
567int get_mount_id(const char *path); 567int get_mount_id(int fd);
568char **build_mount_array(const int mount_id, const char *path); 568char **build_mount_array(const int mount_id, const char *path);
569 569
570// fs_var.c 570// fs_var.c
@@ -622,7 +622,8 @@ void caps_print_filter(pid_t pid) __attribute__((noreturn));
622void caps_drop_dac_override(void); 622void caps_drop_dac_override(void);
623 623
624// fs_trace.c 624// fs_trace.c
625void fs_trace_preload(void); 625void fs_trace_touch_preload(void);
626void fs_trace_touch_or_store_preload(void);
626void fs_tracefile(void); 627void fs_tracefile(void);
627void fs_trace(void); 628void fs_trace(void);
628 629
@@ -802,6 +803,7 @@ enum {
802 CFG_NAME_CHANGE, 803 CFG_NAME_CHANGE,
803 CFG_SECCOMP_ERROR_ACTION, 804 CFG_SECCOMP_ERROR_ACTION,
804 // CFG_FILE_COPY_LIMIT - file copy limit handled using setenv/getenv 805 // CFG_FILE_COPY_LIMIT - file copy limit handled using setenv/getenv
806 CFG_ALLOW_TRAY,
805 CFG_MAX // this should always be the last entry 807 CFG_MAX // this should always be the last entry
806}; 808};
807extern char *xephyr_screen; 809extern char *xephyr_screen;
diff --git a/src/firejail/fs.c b/src/firejail/fs.c
index 7e0a6e347..1a9a8df0d 100644
--- a/src/firejail/fs.c
+++ b/src/firejail/fs.c
@@ -199,8 +199,6 @@ static void disable_file(OPERATION op, const char *filename) {
199 } 199 }
200 200
201 fs_tmpfs(fname, uid); 201 fs_tmpfs(fname, uid);
202 EUID_USER(); // fs_tmpfs returns with EUID 0
203
204 selinux_relabel_path(fname, fname); 202 selinux_relabel_path(fname, fname);
205 } 203 }
206 else 204 else
@@ -281,6 +279,8 @@ static void globbing(OPERATION op, const char *pattern, const char *noblacklist[
281 279
282// blacklist files or directories by mounting empty files on top of them 280// blacklist files or directories by mounting empty files on top of them
283void fs_blacklist(void) { 281void fs_blacklist(void) {
282 EUID_ASSERT();
283
284 ProfileEntry *entry = cfg.profile; 284 ProfileEntry *entry = cfg.profile;
285 if (!entry) 285 if (!entry)
286 return; 286 return;
@@ -292,7 +292,6 @@ void fs_blacklist(void) {
292 if (noblacklist == NULL) 292 if (noblacklist == NULL)
293 errExit("failed allocating memory for noblacklist entries"); 293 errExit("failed allocating memory for noblacklist entries");
294 294
295 EUID_USER();
296 while (entry) { 295 while (entry) {
297 OPERATION op = OPERATION_MAX; 296 OPERATION op = OPERATION_MAX;
298 char *ptr; 297 char *ptr;
@@ -468,8 +467,6 @@ void fs_blacklist(void) {
468 for (i = 0; i < noblacklist_c; i++) 467 for (i = 0; i < noblacklist_c; i++)
469 free(noblacklist[i]); 468 free(noblacklist[i]);
470 free(noblacklist); 469 free(noblacklist);
471
472 EUID_ROOT();
473} 470}
474 471
475//*********************************************** 472//***********************************************
@@ -478,7 +475,7 @@ void fs_blacklist(void) {
478 475
479// mount a writable tmpfs on directory; requires a resolved path 476// mount a writable tmpfs on directory; requires a resolved path
480void fs_tmpfs(const char *dir, unsigned check_owner) { 477void fs_tmpfs(const char *dir, unsigned check_owner) {
481 EUID_USER(); 478 EUID_ASSERT();
482 assert(dir); 479 assert(dir);
483 if (arg_debug) 480 if (arg_debug)
484 printf("Mounting tmpfs on %s, check owner: %s\n", dir, (check_owner)? "yes": "no"); 481 printf("Mounting tmpfs on %s, check owner: %s\n", dir, (check_owner)? "yes": "no");
@@ -503,12 +500,13 @@ void fs_tmpfs(const char *dir, unsigned check_owner) {
503 errExit("fstatvfs"); 500 errExit("fstatvfs");
504 unsigned long flags = buf.f_flag & ~(MS_RDONLY|MS_BIND|MS_REMOUNT); 501 unsigned long flags = buf.f_flag & ~(MS_RDONLY|MS_BIND|MS_REMOUNT);
505 // mount via the symbolic link in /proc/self/fd 502 // mount via the symbolic link in /proc/self/fd
506 EUID_ROOT();
507 char *proc; 503 char *proc;
508 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) 504 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
509 errExit("asprintf"); 505 errExit("asprintf");
506 EUID_ROOT();
510 if (mount("tmpfs", proc, "tmpfs", flags|MS_NOSUID|MS_NODEV, options) < 0) 507 if (mount("tmpfs", proc, "tmpfs", flags|MS_NOSUID|MS_NODEV, options) < 0)
511 errExit("mounting tmpfs"); 508 errExit("mounting tmpfs");
509 EUID_USER();
512 // check the last mount operation 510 // check the last mount operation
513 MountData *mdata = get_last_mount(); 511 MountData *mdata = get_last_mount();
514 if (strcmp(mdata->fstype, "tmpfs") != 0 || strcmp(mdata->dir, dir) != 0) 512 if (strcmp(mdata->fstype, "tmpfs") != 0 || strcmp(mdata->dir, dir) != 0)
@@ -634,34 +632,30 @@ out:
634} 632}
635 633
636// remount recursively; requires a resolved path 634// remount recursively; requires a resolved path
637static void fs_remount_rec(const char *dir, OPERATION op) { 635static void fs_remount_rec(const char *path, OPERATION op) {
638 EUID_ASSERT(); 636 EUID_ASSERT();
639 assert(dir); 637 assert(op < OPERATION_MAX);
638 assert(path);
640 639
641 struct stat s; 640 // no need to search /proc/self/mountinfo for submounts if not a directory
642 if (stat(dir, &s) != 0) 641 int fd = open(path, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
643 return; 642 if (fd < 0) {
644 if (!S_ISDIR(s.st_mode)) { 643 fs_remount_simple(path, op);
645 // no need to search in /proc/self/mountinfo for submounts if not a directory
646 fs_remount_simple(dir, op);
647 return; 644 return;
648 } 645 }
649 // get mount point of the directory 646
650 int mountid = get_mount_id(dir); 647 // get mount id of the directory
651 if (mountid == -1) 648 int mountid = get_mount_id(fd);
652 return; 649 close(fd);
653 if (mountid == -2) { 650 if (mountid < 0) {
654 // falling back to a simple remount on old kernels 651 // falling back to a simple remount
655 static int mount_warning = 0; 652 fwarning("%s %s not applied recursively\n", opstr[op], path);
656 if (!mount_warning) { 653 fs_remount_simple(path, op);
657 fwarning("read-only, read-write and noexec options are not applied recursively\n");
658 mount_warning = 1;
659 }
660 fs_remount_simple(dir, op);
661 return; 654 return;
662 } 655 }
656
663 // build array with all mount points that need to get remounted 657 // build array with all mount points that need to get remounted
664 char **arr = build_mount_array(mountid, dir); 658 char **arr = build_mount_array(mountid, path);
665 assert(arr); 659 assert(arr);
666 // remount 660 // remount
667 char **tmp = arr; 661 char **tmp = arr;
diff --git a/src/firejail/fs_dev.c b/src/firejail/fs_dev.c
index d8bb1aded..694d0a379 100644
--- a/src/firejail/fs_dev.c
+++ b/src/firejail/fs_dev.c
@@ -329,8 +329,10 @@ void fs_dev_disable_sound(void) {
329 } 329 }
330 330
331 // disable all jack sockets in /dev/shm 331 // disable all jack sockets in /dev/shm
332 EUID_USER();
332 glob_t globbuf; 333 glob_t globbuf;
333 int globerr = glob("/dev/shm/jack*", GLOB_NOSORT, NULL, &globbuf); 334 int globerr = glob("/dev/shm/jack*", GLOB_NOSORT, NULL, &globbuf);
335 EUID_ROOT();
334 if (globerr) 336 if (globerr)
335 return; 337 return;
336 338
diff --git a/src/firejail/fs_home.c b/src/firejail/fs_home.c
index 45889b27f..8d8530d81 100644
--- a/src/firejail/fs_home.c
+++ b/src/firejail/fs_home.c
@@ -394,14 +394,16 @@ void fs_private(void) {
394 } 394 }
395 if (chown(homedir, u, g) < 0) 395 if (chown(homedir, u, g) < 0)
396 errExit("chown"); 396 errExit("chown");
397
398 fs_logger2("mkdir", homedir); 397 fs_logger2("mkdir", homedir);
399 fs_logger2("tmpfs", homedir); 398 fs_logger2("tmpfs", homedir);
400 } 399 }
401 else 400 else {
402 // mask user home directory 401 // mask user home directory
403 // the directory should be owned by the current user 402 // the directory should be owned by the current user
403 EUID_USER();
404 fs_tmpfs(homedir, 1); 404 fs_tmpfs(homedir, 1);
405 EUID_ROOT();
406 }
405 407
406 selinux_relabel_path(homedir, homedir); 408 selinux_relabel_path(homedir, homedir);
407 } 409 }
@@ -563,12 +565,13 @@ void fs_private_home_list(void) {
563 int xflag = store_xauthority(); 565 int xflag = store_xauthority();
564 int aflag = store_asoundrc(); 566 int aflag = store_asoundrc();
565 567
566 // create /run/firejail/mnt/home directory
567 EUID_ROOT(); 568 EUID_ROOT();
569 // create /run/firejail/mnt/home directory
568 mkdir_attr(RUN_HOME_DIR, 0755, uid, gid); 570 mkdir_attr(RUN_HOME_DIR, 0755, uid, gid);
569 selinux_relabel_path(RUN_HOME_DIR, homedir); 571 selinux_relabel_path(RUN_HOME_DIR, homedir);
570 572
571 fs_logger_print(); // save the current log 573 // save the current log
574 fs_logger_print();
572 EUID_USER(); 575 EUID_USER();
573 576
574 // copy the list of files in the new home directory 577 // copy the list of files in the new home directory
diff --git a/src/firejail/fs_trace.c b/src/firejail/fs_trace.c
index 9463fbcd0..28852a689 100644
--- a/src/firejail/fs_trace.c
+++ b/src/firejail/fs_trace.c
@@ -25,19 +25,26 @@
25#include <fcntl.h> 25#include <fcntl.h>
26#include <pwd.h> 26#include <pwd.h>
27 27
28void fs_trace_preload(void) { 28// create an empty /etc/ld.so.preload
29void fs_trace_touch_preload(void) {
30 create_empty_file_as_root("/etc/ld.so.preload", S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
31}
32
33void fs_trace_touch_or_store_preload(void) {
29 struct stat s; 34 struct stat s;
30 35
31 // create an empty /etc/ld.so.preload 36 if (stat("/etc/ld.so.preload", &s) != 0) {
32 if (stat("/etc/ld.so.preload", &s)) { 37 fs_trace_touch_preload();
33 if (arg_debug) 38 return;
34 printf("Creating an empty /etc/ld.so.preload file\n"); 39 }
35 FILE *fp = fopen("/etc/ld.so.preload", "wxe"); 40
36 if (!fp) 41 if (s.st_size == 0)
37 errExit("fopen"); 42 return;
38 SET_PERMS_STREAM(fp, 0, 0, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH); 43
39 fclose(fp); 44 // create a copy of /etc/ld.so.preload
40 fs_logger("touch /etc/ld.so.preload"); 45 if (copy_file("/etc/ld.so.preload", RUN_LDPRELOAD_FILE, 0, 0, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) {
46 fprintf(stderr, "Error: cannot copy /etc/ld.so.preload file\n");
47 exit(1);
41 } 48 }
42} 49}
43 50
@@ -82,7 +89,7 @@ void fs_trace(void) {
82 if (arg_debug) 89 if (arg_debug)
83 printf("Create the new ld.so.preload file\n"); 90 printf("Create the new ld.so.preload file\n");
84 91
85 FILE *fp = fopen(RUN_LDPRELOAD_FILE, "we"); 92 FILE *fp = fopen(RUN_LDPRELOAD_FILE, "ae");
86 if (!fp) 93 if (!fp)
87 errExit("fopen"); 94 errExit("fopen");
88 const char *prefix = RUN_FIREJAIL_LIB_DIR; 95 const char *prefix = RUN_FIREJAIL_LIB_DIR;
diff --git a/src/firejail/fs_whitelist.c b/src/firejail/fs_whitelist.c
index 943f275de..7afebed1f 100644
--- a/src/firejail/fs_whitelist.c
+++ b/src/firejail/fs_whitelist.c
@@ -105,6 +105,7 @@ static int whitelist_mkpath(const char* path, mode_t mode) {
105} 105}
106 106
107static void whitelist_file(int dirfd, const char *relpath, const char *path) { 107static void whitelist_file(int dirfd, const char *relpath, const char *path) {
108 EUID_ASSERT();
108 assert(relpath && path); 109 assert(relpath && path);
109 110
110 // open mount source, using a file descriptor that refers to the 111 // open mount source, using a file descriptor that refers to the
@@ -130,12 +131,9 @@ static void whitelist_file(int dirfd, const char *relpath, const char *path) {
130 } 131 }
131 132
132 // create mount target as root, except if inside home or run/user/$UID directory 133 // create mount target as root, except if inside home or run/user/$UID directory
133 int userprivs = 0; 134 if ((strncmp(path, cfg.homedir, homedir_len) != 0 || path[homedir_len] != '/') &&
134 if ((strncmp(path, cfg.homedir, homedir_len) == 0 && path[homedir_len] == '/') || 135 (strncmp(path, runuser, runuser_len) != 0 || path[runuser_len] != '/'))
135 (strncmp(path, runuser, runuser_len) == 0 && path[runuser_len] == '/')) { 136 EUID_ROOT();
136 EUID_USER();
137 userprivs = 1;
138 }
139 137
140 // create path of the mount target 138 // create path of the mount target
141 int fd2 = whitelist_mkpath(path, 0755); 139 int fd2 = whitelist_mkpath(path, 0755);
@@ -146,8 +144,7 @@ static void whitelist_file(int dirfd, const char *relpath, const char *path) {
146 if (arg_debug || arg_debug_whitelists) 144 if (arg_debug || arg_debug_whitelists)
147 printf("Debug %d: skip whitelist %s\n", __LINE__, path); 145 printf("Debug %d: skip whitelist %s\n", __LINE__, path);
148 close(fd); 146 close(fd);
149 if (userprivs) 147 EUID_USER();
150 EUID_ROOT();
151 return; 148 return;
152 } 149 }
153 150
@@ -166,8 +163,7 @@ static void whitelist_file(int dirfd, const char *relpath, const char *path) {
166 } 163 }
167 close(fd); 164 close(fd);
168 close(fd2); 165 close(fd2);
169 if (userprivs) 166 EUID_USER();
170 EUID_ROOT();
171 return; 167 return;
172 } 168 }
173 fd3 = openat(fd2, file, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); 169 fd3 = openat(fd2, file, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
@@ -184,19 +180,17 @@ static void whitelist_file(int dirfd, const char *relpath, const char *path) {
184 } 180 }
185 close(fd); 181 close(fd);
186 close(fd2); 182 close(fd2);
187 if (userprivs) 183 EUID_USER();
188 EUID_ROOT();
189 return; 184 return;
190 } 185 }
191
192 close(fd2); 186 close(fd2);
193 if (userprivs)
194 EUID_ROOT();
195 187
196 if (arg_debug || arg_debug_whitelists) 188 if (arg_debug || arg_debug_whitelists)
197 printf("Whitelisting %s\n", path); 189 printf("Whitelisting %s\n", path);
190 EUID_ROOT();
198 if (bind_mount_by_fd(fd, fd3)) 191 if (bind_mount_by_fd(fd, fd3))
199 errExit("mount bind"); 192 errExit("mount bind");
193 EUID_USER();
200 // check the last mount operation 194 // check the last mount operation
201 MountData *mptr = get_last_mount(); // will do exit(1) if the mount cannot be found 195 MountData *mptr = get_last_mount(); // will do exit(1) if the mount cannot be found
202#ifdef TEST_MOUNTINFO 196#ifdef TEST_MOUNTINFO
@@ -219,22 +213,19 @@ static void whitelist_file(int dirfd, const char *relpath, const char *path) {
219} 213}
220 214
221static void whitelist_symlink(const char *link, const char *target) { 215static void whitelist_symlink(const char *link, const char *target) {
216 EUID_ASSERT();
222 assert(link && target); 217 assert(link && target);
223 218
224 // create files as root, except if inside home or run/user/$UID directory 219 // create files as root, except if inside home or run/user/$UID directory
225 int userprivs = 0; 220 if ((strncmp(link, cfg.homedir, homedir_len) != 0 || link[homedir_len] != '/') &&
226 if ((strncmp(link, cfg.homedir, homedir_len) == 0 && link[homedir_len] == '/') || 221 (strncmp(link, runuser, runuser_len) != 0 || link[runuser_len] != '/'))
227 (strncmp(link, runuser, runuser_len) == 0 && link[runuser_len] == '/')) { 222 EUID_ROOT();
228 EUID_USER();
229 userprivs = 1;
230 }
231 223
232 int fd = whitelist_mkpath(link, 0755); 224 int fd = whitelist_mkpath(link, 0755);
233 if (fd == -1) { 225 if (fd == -1) {
234 if (arg_debug || arg_debug_whitelists) 226 if (arg_debug || arg_debug_whitelists)
235 printf("Debug %d: cannot create symbolic link %s\n", __LINE__, link); 227 printf("Debug %d: cannot create symbolic link %s\n", __LINE__, link);
236 if (userprivs) 228 EUID_USER();
237 EUID_ROOT();
238 return; 229 return;
239 } 230 }
240 231
@@ -252,8 +243,7 @@ static void whitelist_symlink(const char *link, const char *target) {
252 printf("Created symbolic link %s -> %s\n", link, target); 243 printf("Created symbolic link %s -> %s\n", link, target);
253 244
254 close(fd); 245 close(fd);
255 if (userprivs) 246 EUID_USER();
256 EUID_ROOT();
257} 247}
258 248
259static void globbing(const char *pattern) { 249static void globbing(const char *pattern) {
@@ -330,10 +320,11 @@ static void tmpfs_topdirs(const TopDir *topdirs) {
330 // init tmpfs 320 // init tmpfs
331 if (strcmp(topdirs[i].path, "/run") == 0) { 321 if (strcmp(topdirs[i].path, "/run") == 0) {
332 // restore /run/firejail directory 322 // restore /run/firejail directory
333 if (mkdir(RUN_FIREJAIL_DIR, 0755) == -1) 323 EUID_ROOT();
334 errExit("mkdir"); 324 mkdir_attr(RUN_FIREJAIL_DIR, 0755, 0, 0);
335 if (bind_mount_fd_to_path(fd, RUN_FIREJAIL_DIR)) 325 if (bind_mount_fd_to_path(fd, RUN_FIREJAIL_DIR))
336 errExit("mount bind"); 326 errExit("mount bind");
327 EUID_USER();
337 close(fd); 328 close(fd);
338 fs_logger2("whitelist", RUN_FIREJAIL_DIR); 329 fs_logger2("whitelist", RUN_FIREJAIL_DIR);
339 330
@@ -351,12 +342,14 @@ static void tmpfs_topdirs(const TopDir *topdirs) {
351 errExit("asprintf"); 342 errExit("asprintf");
352 if (strcmp(env, pamtmpdir) == 0) { 343 if (strcmp(env, pamtmpdir) == 0) {
353 // create empty user-owned /tmp/user/$UID directory 344 // create empty user-owned /tmp/user/$UID directory
345 EUID_ROOT();
354 mkdir_attr("/tmp/user", 0711, 0, 0); 346 mkdir_attr("/tmp/user", 0711, 0, 0);
355 selinux_relabel_path("/tmp/user", "/tmp/user"); 347 selinux_relabel_path("/tmp/user", "/tmp/user");
356 fs_logger("mkdir /tmp/user"); 348 fs_logger("mkdir /tmp/user");
357 mkdir_attr(pamtmpdir, 0700, getuid(), 0); 349 mkdir_attr(pamtmpdir, 0700, getuid(), 0);
358 selinux_relabel_path(pamtmpdir, pamtmpdir); 350 selinux_relabel_path(pamtmpdir, pamtmpdir);
359 fs_logger2("mkdir", pamtmpdir); 351 fs_logger2("mkdir", pamtmpdir);
352 EUID_USER();
360 } 353 }
361 free(pamtmpdir); 354 free(pamtmpdir);
362 } 355 }
@@ -374,11 +367,8 @@ static void tmpfs_topdirs(const TopDir *topdirs) {
374 } 367 }
375 368
376 // user home directory 369 // user home directory
377 if (tmpfs_home) { 370 if (tmpfs_home)
378 EUID_USER();
379 fs_private(); // checks owner if outside /home 371 fs_private(); // checks owner if outside /home
380 EUID_ROOT();
381 }
382 372
383 // /run/user/$UID directory 373 // /run/user/$UID directory
384 if (tmpfs_runuser) { 374 if (tmpfs_runuser) {
@@ -402,6 +392,7 @@ static int reject_topdir(const char *dir) {
402// keep track of whitelist top level directories by adding them to an array 392// keep track of whitelist top level directories by adding them to an array
403// open each directory 393// open each directory
404static TopDir *add_topdir(const char *dir, TopDir *topdirs, const char *path) { 394static TopDir *add_topdir(const char *dir, TopDir *topdirs, const char *path) {
395 EUID_ASSERT();
405 assert(dir && path); 396 assert(dir && path);
406 397
407 // /proc and /sys are not allowed 398 // /proc and /sys are not allowed
@@ -516,6 +507,8 @@ static char *extract_topdir(const char *path) {
516} 507}
517 508
518void fs_whitelist(void) { 509void fs_whitelist(void) {
510 EUID_ASSERT();
511
519 ProfileEntry *entry = cfg.profile; 512 ProfileEntry *entry = cfg.profile;
520 if (!entry) 513 if (!entry)
521 return; 514 return;
@@ -536,7 +529,6 @@ void fs_whitelist(void) {
536 errExit("calloc"); 529 errExit("calloc");
537 530
538 // verify whitelist files, extract symbolic links, etc. 531 // verify whitelist files, extract symbolic links, etc.
539 EUID_USER();
540 while (entry) { 532 while (entry) {
541 int nowhitelist_flag = 0; 533 int nowhitelist_flag = 0;
542 534
@@ -630,7 +622,7 @@ void fs_whitelist(void) {
630 if (!fname) { 622 if (!fname) {
631 if (arg_debug || arg_debug_whitelists) { 623 if (arg_debug || arg_debug_whitelists) {
632 printf("Removed path: %s\n", entry->data); 624 printf("Removed path: %s\n", entry->data);
633 printf("\texpanded: %s\n", new_name); 625 printf("\tnew_name: %s\n", new_name);
634 printf("\trealpath: (null)\n"); 626 printf("\trealpath: (null)\n");
635 printf("\t%s\n", strerror(errno)); 627 printf("\t%s\n", strerror(errno));
636 } 628 }
@@ -712,7 +704,6 @@ void fs_whitelist(void) {
712 free(nowhitelist); 704 free(nowhitelist);
713 705
714 // mount tmpfs on all top level directories 706 // mount tmpfs on all top level directories
715 EUID_ROOT();
716 tmpfs_topdirs(topdirs); 707 tmpfs_topdirs(topdirs);
717 708
718 // go through profile rules again, and interpret whitelist commands 709 // go through profile rules again, and interpret whitelist commands
diff --git a/src/firejail/main.c b/src/firejail/main.c
index a99249be9..cc5186204 100644
--- a/src/firejail/main.c
+++ b/src/firejail/main.c
@@ -32,6 +32,8 @@
32#include <dirent.h> 32#include <dirent.h>
33#include <pwd.h> 33#include <pwd.h>
34#include <errno.h> 34#include <errno.h>
35
36#include <limits.h>
35#include <sys/file.h> 37#include <sys/file.h>
36#include <sys/prctl.h> 38#include <sys/prctl.h>
37#include <signal.h> 39#include <signal.h>
diff --git a/src/firejail/mountinfo.c b/src/firejail/mountinfo.c
index 64a94bd84..304f80eee 100644
--- a/src/firejail/mountinfo.c
+++ b/src/firejail/mountinfo.c
@@ -19,6 +19,7 @@
19*/ 19*/
20 20
21#include "firejail.h" 21#include "firejail.h"
22#include <errno.h>
22 23
23#include <fcntl.h> 24#include <fcntl.h>
24#ifndef O_PATH 25#ifndef O_PATH
@@ -151,53 +152,71 @@ MountData *get_last_mount(void) {
151 return &mdata; 152 return &mdata;
152} 153}
153 154
154// Extract the mount id from /proc/self/fdinfo and return it. 155// Returns mount id, or -1 if fd refers to a procfs or sysfs file
155int get_mount_id(const char *path) { 156static int get_mount_id_from_handle(int fd) {
156 EUID_ASSERT(); 157 EUID_ASSERT();
157 assert(path);
158 158
159 int fd = open(path, O_PATH|O_CLOEXEC); 159 char *proc;
160 if (fd == -1) 160 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
161 return -1; 161 errExit("asprintf");
162 struct file_handle *fh = malloc(sizeof *fh);
163 if (!fh)
164 errExit("malloc");
165 fh->handle_bytes = 0;
166
167 int rv = -1;
168 int tmp;
169 if (name_to_handle_at(-1, proc, fh, &tmp, AT_SYMLINK_FOLLOW) != -1) {
170 fprintf(stderr, "Error: unexpected result from name_to_handle_at\n");
171 exit(1);
172 }
173 if (errno == EOVERFLOW && fh->handle_bytes)
174 rv = tmp;
175
176 free(proc);
177 free(fh);
178 return rv;
179}
180
181// Returns mount id, or -1 on kernels < 3.15
182static int get_mount_id_from_fdinfo(int fd) {
183 EUID_ASSERT();
184 int rv = -1;
162 185
163 char *fdinfo; 186 char *proc;
164 if (asprintf(&fdinfo, "/proc/self/fdinfo/%d", fd) == -1) 187 if (asprintf(&proc, "/proc/self/fdinfo/%d", fd) == -1)
165 errExit("asprintf"); 188 errExit("asprintf");
166 EUID_ROOT(); 189 EUID_ROOT();
167 FILE *fp = fopen(fdinfo, "re"); 190 FILE *fp = fopen(proc, "re");
168 EUID_USER(); 191 EUID_USER();
169 free(fdinfo);
170 if (!fp) 192 if (!fp)
171 goto errexit; 193 goto errexit;
172 194
173 // read the file
174 char buf[MAX_BUF]; 195 char buf[MAX_BUF];
175 if (fgets(buf, MAX_BUF, fp) == NULL) 196 while (fgets(buf, MAX_BUF, fp)) {
176 goto errexit;
177 do {
178 if (strncmp(buf, "mnt_id:", 7) == 0) { 197 if (strncmp(buf, "mnt_id:", 7) == 0) {
179 char *ptr = buf + 7; 198 if (sscanf(buf + 7, "%d", &rv) != 1)
180 while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t')) {
181 ptr++;
182 }
183 if (*ptr == '\0')
184 goto errexit; 199 goto errexit;
185 fclose(fp); 200 break;
186 close(fd);
187 return atoi(ptr);
188 } 201 }
189 } while (fgets(buf, MAX_BUF, fp)); 202 }
190 203
191 // fallback, kernels older than 3.15 don't expose the mount id in this place 204 free(proc);
192 fclose(fp); 205 fclose(fp);
193 close(fd); 206 return rv;
194 return -2;
195 207
196errexit: 208errexit:
197 fprintf(stderr, "Error: cannot read proc file\n"); 209 fprintf(stderr, "Error: cannot read proc file\n");
198 exit(1); 210 exit(1);
199} 211}
200 212
213int get_mount_id(int fd) {
214 int rv = get_mount_id_from_fdinfo(fd);
215 if (rv < 0)
216 rv = get_mount_id_from_handle(fd);
217 return rv;
218}
219
201// Check /proc/self/mountinfo if path contains any mounts points. 220// Check /proc/self/mountinfo if path contains any mounts points.
202// Returns an array that can be iterated over for recursive remounting. 221// Returns an array that can be iterated over for recursive remounting.
203char **build_mount_array(const int mount_id, const char *path) { 222char **build_mount_array(const int mount_id, const char *path) {
diff --git a/src/firejail/profile.c b/src/firejail/profile.c
index 059100fcb..5390249ea 100644
--- a/src/firejail/profile.c
+++ b/src/firejail/profile.c
@@ -175,6 +175,10 @@ static int check_allow_drm(void) {
175 return checkcfg(CFG_BROWSER_ALLOW_DRM) != 0; 175 return checkcfg(CFG_BROWSER_ALLOW_DRM) != 0;
176} 176}
177 177
178static int check_allow_tray(void) {
179 return checkcfg(CFG_ALLOW_TRAY) != 0;
180}
181
178Cond conditionals[] = { 182Cond conditionals[] = {
179 {"HAS_APPIMAGE", check_appimage}, 183 {"HAS_APPIMAGE", check_appimage},
180 {"HAS_NET", check_netoptions}, 184 {"HAS_NET", check_netoptions},
@@ -184,6 +188,7 @@ Cond conditionals[] = {
184 {"HAS_X11", check_x11}, 188 {"HAS_X11", check_x11},
185 {"BROWSER_DISABLE_U2F", check_disable_u2f}, 189 {"BROWSER_DISABLE_U2F", check_disable_u2f},
186 {"BROWSER_ALLOW_DRM", check_allow_drm}, 190 {"BROWSER_ALLOW_DRM", check_allow_drm},
191 {"ALLOW_TRAY", check_allow_tray},
187 { NULL, NULL } 192 { NULL, NULL }
188}; 193};
189 194
@@ -630,7 +635,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
630#endif 635#endif
631 return 0; 636 return 0;
632 } 637 }
633 else if (strncmp(ptr, "netns ", 6) == 0) { 638 else if (strncmp(ptr, "netns ", 6) == 0) {
634#ifdef HAVE_NETWORK 639#ifdef HAVE_NETWORK
635 if (checkcfg(CFG_NETWORK)) { 640 if (checkcfg(CFG_NETWORK)) {
636 arg_netns = ptr + 6; 641 arg_netns = ptr + 6;
@@ -981,10 +986,10 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
981 warning_feature_disabled("seccomp"); 986 warning_feature_disabled("seccomp");
982 return 0; 987 return 0;
983 } 988 }
984 if (strncmp(ptr, "seccomp.32.drop ", 13) == 0) { 989 if (strncmp(ptr, "seccomp.32.drop ", 16) == 0) {
985 if (checkcfg(CFG_SECCOMP)) { 990 if (checkcfg(CFG_SECCOMP)) {
986 arg_seccomp32 = 1; 991 arg_seccomp32 = 1;
987 cfg.seccomp_list_drop32 = seccomp_check_list(ptr + 13); 992 cfg.seccomp_list_drop32 = seccomp_check_list(ptr + 16);
988 } 993 }
989 else 994 else
990 warning_feature_disabled("seccomp"); 995 warning_feature_disabled("seccomp");
@@ -1001,10 +1006,10 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
1001 warning_feature_disabled("seccomp"); 1006 warning_feature_disabled("seccomp");
1002 return 0; 1007 return 0;
1003 } 1008 }
1004 if (strncmp(ptr, "seccomp.32.keep ", 13) == 0) { 1009 if (strncmp(ptr, "seccomp.32.keep ", 16) == 0) {
1005 if (checkcfg(CFG_SECCOMP)) { 1010 if (checkcfg(CFG_SECCOMP)) {
1006 arg_seccomp32 = 1; 1011 arg_seccomp32 = 1;
1007 cfg.seccomp_list_keep32 = seccomp_check_list(ptr + 13); 1012 cfg.seccomp_list_keep32 = seccomp_check_list(ptr + 16);
1008 } 1013 }
1009 else 1014 else
1010 warning_feature_disabled("seccomp"); 1015 warning_feature_disabled("seccomp");
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c
index 995827fb7..b776a0cc5 100644
--- a/src/firejail/sandbox.c
+++ b/src/firejail/sandbox.c
@@ -798,7 +798,7 @@ int sandbox(void* sandbox_arg) {
798 798
799 // trace pre-install 799 // trace pre-install
800 if (need_preload) 800 if (need_preload)
801 fs_trace_preload(); 801 fs_trace_touch_or_store_preload();
802 802
803 // store hosts file 803 // store hosts file
804 if (cfg.hosts_file) 804 if (cfg.hosts_file)
@@ -814,8 +814,11 @@ int sandbox(void* sandbox_arg) {
814 //**************************** 814 //****************************
815 // trace pre-install, this time inside chroot 815 // trace pre-install, this time inside chroot
816 //**************************** 816 //****************************
817 if (need_preload) 817 if (need_preload) {
818 fs_trace_preload(); 818 int rv = unlink(RUN_LDPRELOAD_FILE);
819 (void) rv;
820 fs_trace_touch_or_store_preload();
821 }
819 } 822 }
820 else 823 else
821#endif 824#endif
@@ -992,7 +995,7 @@ int sandbox(void* sandbox_arg) {
992 995
993 // create /etc/ld.so.preload file again 996 // create /etc/ld.so.preload file again
994 if (need_preload) 997 if (need_preload)
995 fs_trace_preload(); 998 fs_trace_touch_preload();
996 999
997 // openSUSE configuration is split between /etc and /usr/etc 1000 // openSUSE configuration is split between /etc and /usr/etc
998 // process private-etc a second time 1001 // process private-etc a second time
@@ -1004,10 +1007,12 @@ int sandbox(void* sandbox_arg) {
1004 // apply the profile file 1007 // apply the profile file
1005 //**************************** 1008 //****************************
1006 // apply all whitelist commands ... 1009 // apply all whitelist commands ...
1010 EUID_USER();
1007 fs_whitelist(); 1011 fs_whitelist();
1008 1012
1009 // ... followed by blacklist commands 1013 // ... followed by blacklist commands
1010 fs_blacklist(); // mkdir and mkfile are processed all over again 1014 fs_blacklist(); // mkdir and mkfile are processed all over again
1015 EUID_ROOT();
1011 1016
1012 //**************************** 1017 //****************************
1013 // nosound/no3d/notv/novideo and fix for pulseaudio 7.0 1018 // nosound/no3d/notv/novideo and fix for pulseaudio 7.0
diff --git a/src/firejail/selinux.c b/src/firejail/selinux.c
index 6969e7a3d..fa59882ed 100644
--- a/src/firejail/selinux.c
+++ b/src/firejail/selinux.c
@@ -21,6 +21,7 @@
21#include "firejail.h" 21#include "firejail.h"
22#include <sys/types.h> 22#include <sys/types.h>
23#include <sys/stat.h> 23#include <sys/stat.h>
24#include <errno.h>
24 25
25#include <fcntl.h> 26#include <fcntl.h>
26#ifndef O_PATH 27#ifndef O_PATH
@@ -57,7 +58,17 @@ void selinux_relabel_path(const char *path, const char *inside_path)
57 58
58 /* Open the file as O_PATH, to pin it while we determine and adjust the label 59 /* Open the file as O_PATH, to pin it while we determine and adjust the label
59 * Defeat symlink races by not allowing symbolic links */ 60 * Defeat symlink races by not allowing symbolic links */
61 int called_as_root = 0;
62 if (geteuid() == 0)
63 called_as_root = 1;
64 if (called_as_root)
65 EUID_USER();
66
60 fd = safer_openat(-1, path, O_NOFOLLOW|O_CLOEXEC|O_PATH); 67 fd = safer_openat(-1, path, O_NOFOLLOW|O_CLOEXEC|O_PATH);
68
69 if (called_as_root)
70 EUID_ROOT();
71
61 if (fd < 0) 72 if (fd < 0)
62 return; 73 return;
63 if (fstat(fd, &st) < 0) 74 if (fstat(fd, &st) < 0)
@@ -68,8 +79,16 @@ void selinux_relabel_path(const char *path, const char *inside_path)
68 if (arg_debug) 79 if (arg_debug)
69 printf("Relabeling %s as %s (%s)\n", path, inside_path, fcon); 80 printf("Relabeling %s as %s (%s)\n", path, inside_path, fcon);
70 81
71 setfilecon_raw(procfs_path, fcon); 82 if (!called_as_root)
83 EUID_ROOT();
84
85 if (setfilecon_raw(procfs_path, fcon) != 0 && arg_debug)
86 printf("Cannot relabel %s: %s\n", path, strerror(errno));
87
88 if (!called_as_root)
89 EUID_USER();
72 } 90 }
91
73 freecon(fcon); 92 freecon(fcon);
74 close: 93 close:
75 close(fd); 94 close(fd);
diff --git a/src/firejail/util.c b/src/firejail/util.c
index 094a68c60..f0df45eb2 100644
--- a/src/firejail/util.c
+++ b/src/firejail/util.c
@@ -459,31 +459,21 @@ int is_dir(const char *fname) {
459 if (*fname == '\0') 459 if (*fname == '\0')
460 return 0; 460 return 0;
461 461
462 int called_as_root = 0;
463 if (geteuid() == 0)
464 called_as_root = 1;
465
466 if (called_as_root)
467 EUID_USER();
468
469 // if fname doesn't end in '/', add one 462 // if fname doesn't end in '/', add one
470 int rv; 463 int rv;
471 struct stat s; 464 struct stat s;
472 if (fname[strlen(fname) - 1] == '/') 465 if (fname[strlen(fname) - 1] == '/')
473 rv = stat(fname, &s); 466 rv = stat_as_user(fname, &s);
474 else { 467 else {
475 char *tmp; 468 char *tmp;
476 if (asprintf(&tmp, "%s/", fname) == -1) { 469 if (asprintf(&tmp, "%s/", fname) == -1) {
477 fprintf(stderr, "Error: cannot allocate memory, %s:%d\n", __FILE__, __LINE__); 470 fprintf(stderr, "Error: cannot allocate memory, %s:%d\n", __FILE__, __LINE__);
478 errExit("asprintf"); 471 errExit("asprintf");
479 } 472 }
480 rv = stat(tmp, &s); 473 rv = stat_as_user(tmp, &s);
481 free(tmp); 474 free(tmp);
482 } 475 }
483 476
484 if (called_as_root)
485 EUID_ROOT();
486
487 if (rv == -1) 477 if (rv == -1)
488 return 0; 478 return 0;
489 479
diff --git a/src/man/firejail-profile.txt b/src/man/firejail-profile.txt
index a76fd3765..a1eccaa5e 100644
--- a/src/man/firejail-profile.txt
+++ b/src/man/firejail-profile.txt
@@ -174,7 +174,7 @@ Example: "?HAS_APPIMAGE: whitelist ${HOME}/special/appimage/dir"
174 174
175This example will load the whitelist profile line only if the \-\-appimage option has been specified on the command line. 175This example will load the whitelist profile line only if the \-\-appimage option has been specified on the command line.
176 176
177Currently the only conditionals supported this way are HAS_APPIMAGE, HAS_NET, HAS_NODBUS, HAS_NOSOUND, HAS_PRIVATE and HAS_X11. The conditionals BROWSER_DISABLE_U2F and BROWSER_ALLOW_DRM 177Currently the only conditionals supported this way are HAS_APPIMAGE, HAS_NET, HAS_NODBUS, HAS_NOSOUND, HAS_PRIVATE and HAS_X11. The conditionals ALLOW_TRAY, BROWSER_DISABLE_U2F and BROWSER_ALLOW_DRM
178can be enabled or disabled globally in Firejail's configuration file. 178can be enabled or disabled globally in Firejail's configuration file.
179 179
180The profile line may be any profile line that you would normally use in a profile \fBexcept\fR for "quiet" and "include" lines. 180The profile line may be any profile line that you would normally use in a profile \fBexcept\fR for "quiet" and "include" lines.