mirror of
https://github.com/netblue30/firejail.git
synced 2026-05-15 06:06:02 -06:00
modif: do not follow symlinks to /dev/null on disable (#7129)
When trying to prevent a file or directory in the user home from being written to, it is not uncommon to replace it with a symlink to /dev/null. If this path is also blacklisted (such as by disable-common.inc), the symlink will be followed, resulting in /dev/null itself being blacklisted, which can cause issues with (unrelated) programs that have their output redirected to /dev/null (for example). To avoid disabling /dev/null, when applying commands from `disable_file()` (such as `blacklist` and `read-only`), if a file is a symlink to /dev/null, avoid following the symlink and perform the operation on the link itself instead. Using these commands with "/dev/null" directly as the argument (that is, without going through a symlink) should still work the same way. It has been confirmed to work on Linux 3.8[1], so it should work on at least 3.8 and later. Closes #5803. [1] https://github.com/netblue30/firejail/pull/7129#issuecomment-4233141574 Reported-by: @fgpietersz Suggested-by: @Changaco Tested-by: @Changaco Tested-by: @Zopolis4
This commit is contained in:
parent
a7a66c5e6e
commit
a4e6495fd1
6 changed files with 101 additions and 10 deletions
|
|
@ -60,11 +60,11 @@ static int disable_file(OPERATION op, const char *filename) {
|
|||
EUID_ASSERT();
|
||||
|
||||
// Resolve all symlinks
|
||||
char* fname = realpath(filename, NULL);
|
||||
if (fname == NULL && errno != EACCES) {
|
||||
char* rpath = realpath(filename, NULL);
|
||||
if (rpath == NULL && errno != EACCES) {
|
||||
return 1;
|
||||
}
|
||||
if (fname == NULL && errno == EACCES) {
|
||||
if (rpath == NULL && errno == EACCES) {
|
||||
// realpath and stat functions will fail on FUSE filesystems
|
||||
// they don't seem to like a uid of 0
|
||||
// force mounting
|
||||
|
|
@ -95,22 +95,38 @@ static int disable_file(OPERATION op, const char *filename) {
|
|||
}
|
||||
}
|
||||
|
||||
assert(fname);
|
||||
assert(rpath);
|
||||
// check for firejail executable
|
||||
// we might have a file found in ${PATH} pointing to /usr/bin/firejail
|
||||
// blacklisting it here will end up breaking situations like user clicks on a link in Thunderbird
|
||||
// and expects Firefox to open in the same sandbox
|
||||
if (strcmp(BINDIR "/firejail", fname) == 0) {
|
||||
free(fname);
|
||||
if (strcmp(BINDIR "/firejail", rpath) == 0) {
|
||||
free(rpath);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int follow_symlinks = 1;
|
||||
|
||||
// Users may try to disable writing to certain paths in the user home
|
||||
// by replacing them with symlinks to /dev/null, so avoid following
|
||||
// symlinks to /dev/null to prevent disabling /dev/null itself, as that
|
||||
// can lead to unexpected issues (see #5803).
|
||||
if (strcmp(rpath, "/dev/null") == 0 && is_link(filename))
|
||||
follow_symlinks = 0;
|
||||
|
||||
int extraflags = 0;
|
||||
const char *fname = rpath; // filename to be used for operations
|
||||
if (!follow_symlinks) {
|
||||
fname = filename;
|
||||
extraflags |= O_NOFOLLOW;
|
||||
}
|
||||
|
||||
// if the file is not present, do nothing
|
||||
int fd = open(fname, O_PATH|O_CLOEXEC);
|
||||
int fd = open(fname, O_PATH|O_CLOEXEC|extraflags);
|
||||
if (fd < 0) {
|
||||
if (arg_debug)
|
||||
printf("Warning (blacklisting): cannot open %s: %s\n", fname, strerror(errno));
|
||||
free(fname);
|
||||
free(rpath);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -118,7 +134,7 @@ static int disable_file(OPERATION op, const char *filename) {
|
|||
if (fstat(fd, &s) < 0) {
|
||||
if (arg_debug)
|
||||
printf("Warning (blacklisting): cannot stat %s: %s\n", fname, strerror(errno));
|
||||
free(fname);
|
||||
free(rpath);
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -214,7 +230,7 @@ static int disable_file(OPERATION op, const char *filename) {
|
|||
|
||||
out:
|
||||
close(fd);
|
||||
free(fname);
|
||||
free(rpath);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -266,6 +266,13 @@ Symbolic link handling: Blacklisting a path that is a symbolic link will also
|
|||
blacklist the path that it points to.
|
||||
For example, if ~/foo is blacklisted and it points to /bar, then /bar will also
|
||||
be blacklisted.
|
||||
Symlinks to /dev/null are not followed (only the symlink itself will be
|
||||
affected), as replacing certain paths with symlinks to /dev/null is not
|
||||
uncommon and restricting access to /dev/null can lead to unexpected issues (see
|
||||
.UR https://github.com/netblue30/firejail/issues/5803
|
||||
#5803
|
||||
.UE ).
|
||||
Using /dev/null directly (instead of through a symlink) should work normally.
|
||||
.br
|
||||
|
||||
.br
|
||||
|
|
|
|||
|
|
@ -247,6 +247,13 @@ Symbolic link handling: Blacklisting a path that is a symbolic link will also
|
|||
blacklist the path that it points to.
|
||||
For example, if ~/foo is blacklisted and it points to /bar, then /bar will also
|
||||
be blacklisted.
|
||||
Symlinks to /dev/null are not followed (only the symlink itself will be
|
||||
affected), as replacing certain paths with symlinks to /dev/null is not
|
||||
uncommon and restricting access to /dev/null can lead to unexpected issues (see
|
||||
.UR https://github.com/netblue30/firejail/issues/5803
|
||||
#5803
|
||||
.UE ).
|
||||
Using /dev/null directly (instead of through a symlink) should work normally.
|
||||
.br
|
||||
|
||||
.br
|
||||
|
|
|
|||
1
test/fs/devnull_symlink
Symbolic link
1
test/fs/devnull_symlink
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
/dev/null
|
||||
57
test/fs/disable-devnull-symlink.exp
Executable file
57
test/fs/disable-devnull-symlink.exp
Executable file
|
|
@ -0,0 +1,57 @@
|
|||
#!/usr/bin/expect -f
|
||||
# This file is part of Firejail project
|
||||
# Copyright (C) 2014-2026 Firejail Authors
|
||||
# License GPL v2
|
||||
|
||||
set timeout 2
|
||||
spawn $env(SHELL)
|
||||
match_max 100000
|
||||
set PWD $env(PWD)
|
||||
|
||||
send -- "ls -l /dev/null\r"
|
||||
expect {
|
||||
timeout {puts "TESTING ERROR 0\n";exit}
|
||||
"crw-rw-rw-" {puts "OK\n"}
|
||||
}
|
||||
after 100
|
||||
|
||||
# Test that disabling a symlink to /dev/null does not disable /dev/null itself
|
||||
# (see #5803).
|
||||
send -- "firejail --noprofile --blacklist=./devnull_symlink ls -l /dev/null\r"
|
||||
expect {
|
||||
timeout {puts "TESTING ERROR 1\n";exit}
|
||||
"crw-rw-rw-" {puts "OK\n"}
|
||||
"cr--r--r--" {puts "TESTING ERROR 2\n";exit}
|
||||
}
|
||||
after 100
|
||||
|
||||
send -- "firejail --noprofile --blacklist=./devnull_symlink touch /dev/null; echo ret \$?\r"
|
||||
expect {
|
||||
timeout {puts "TESTING ERROR 3\n";exit}
|
||||
-re {ret 0} {puts "OK\n"}
|
||||
-re {ret [1-9]} {puts "TESTING ERROR 4\n";exit}
|
||||
}
|
||||
after 100
|
||||
|
||||
# Test that /dev/null can still be disabled directly (without a symlink).
|
||||
send -- "firejail --noprofile --blacklist=/dev/null ls -l /dev/null\r"
|
||||
expect {
|
||||
timeout {puts "TESTING ERROR 5\n";exit}
|
||||
"\\-r--------" {puts "OK\n"}
|
||||
"crw-rw-rw-" {puts "TESTING ERROR 6\n";exit}
|
||||
}
|
||||
after 100
|
||||
|
||||
send -- "firejail --noprofile --blacklist=/dev/null touch /dev/null; echo ret \$?\r"
|
||||
expect {
|
||||
timeout {puts "TESTING ERROR 7\n";exit}
|
||||
-re {ret 0} {puts "TESTING ERROR 8\n";exit}
|
||||
-re {ret [1-9]} {puts "OK\n"}
|
||||
}
|
||||
after 100
|
||||
|
||||
send -- "exit\r"
|
||||
|
||||
after 100
|
||||
|
||||
puts "\nall done\n"
|
||||
|
|
@ -125,6 +125,9 @@ echo "TESTING: blacklist glob (test/fs/option_blacklist_glob.exp)"
|
|||
./option_blacklist_glob.exp
|
||||
rm -fr ~/_firejail_test_dir
|
||||
|
||||
echo "TESTING: disable symlink to /dev/null (test/fs/disable-devnull-symlink.exp)"
|
||||
./disable-devnull-symlink.exp
|
||||
|
||||
echo "TESTING: noblacklist blacklist noexec (test/fs/noblacklist-blacklist-noexec.exp)"
|
||||
./noblacklist-blacklist-noexec.exp
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue