mirror of
https://github.com/netblue30/firejail.git
synced 2026-05-15 14:16:14 -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();
|
EUID_ASSERT();
|
||||||
|
|
||||||
// Resolve all symlinks
|
// Resolve all symlinks
|
||||||
char* fname = realpath(filename, NULL);
|
char* rpath = realpath(filename, NULL);
|
||||||
if (fname == NULL && errno != EACCES) {
|
if (rpath == NULL && errno != EACCES) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (fname == NULL && errno == EACCES) {
|
if (rpath == NULL && errno == EACCES) {
|
||||||
// realpath and stat functions will fail on FUSE filesystems
|
// realpath and stat functions will fail on FUSE filesystems
|
||||||
// they don't seem to like a uid of 0
|
// they don't seem to like a uid of 0
|
||||||
// force mounting
|
// force mounting
|
||||||
|
|
@ -95,22 +95,38 @@ static int disable_file(OPERATION op, const char *filename) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(fname);
|
assert(rpath);
|
||||||
// check for firejail executable
|
// check for firejail executable
|
||||||
// we might have a file found in ${PATH} pointing to /usr/bin/firejail
|
// 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
|
// 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
|
// and expects Firefox to open in the same sandbox
|
||||||
if (strcmp(BINDIR "/firejail", fname) == 0) {
|
if (strcmp(BINDIR "/firejail", rpath) == 0) {
|
||||||
free(fname);
|
free(rpath);
|
||||||
return 1;
|
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
|
// 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 (fd < 0) {
|
||||||
if (arg_debug)
|
if (arg_debug)
|
||||||
printf("Warning (blacklisting): cannot open %s: %s\n", fname, strerror(errno));
|
printf("Warning (blacklisting): cannot open %s: %s\n", fname, strerror(errno));
|
||||||
free(fname);
|
free(rpath);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -118,7 +134,7 @@ static int disable_file(OPERATION op, const char *filename) {
|
||||||
if (fstat(fd, &s) < 0) {
|
if (fstat(fd, &s) < 0) {
|
||||||
if (arg_debug)
|
if (arg_debug)
|
||||||
printf("Warning (blacklisting): cannot stat %s: %s\n", fname, strerror(errno));
|
printf("Warning (blacklisting): cannot stat %s: %s\n", fname, strerror(errno));
|
||||||
free(fname);
|
free(rpath);
|
||||||
close(fd);
|
close(fd);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
@ -214,7 +230,7 @@ static int disable_file(OPERATION op, const char *filename) {
|
||||||
|
|
||||||
out:
|
out:
|
||||||
close(fd);
|
close(fd);
|
||||||
free(fname);
|
free(rpath);
|
||||||
return retval;
|
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.
|
blacklist the path that it points to.
|
||||||
For example, if ~/foo is blacklisted and it points to /bar, then /bar will also
|
For example, if ~/foo is blacklisted and it points to /bar, then /bar will also
|
||||||
be blacklisted.
|
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
|
||||||
|
|
||||||
.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.
|
blacklist the path that it points to.
|
||||||
For example, if ~/foo is blacklisted and it points to /bar, then /bar will also
|
For example, if ~/foo is blacklisted and it points to /bar, then /bar will also
|
||||||
be blacklisted.
|
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
|
||||||
|
|
||||||
.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
|
./option_blacklist_glob.exp
|
||||||
rm -fr ~/_firejail_test_dir
|
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)"
|
echo "TESTING: noblacklist blacklist noexec (test/fs/noblacklist-blacklist-noexec.exp)"
|
||||||
./noblacklist-blacklist-noexec.exp
|
./noblacklist-blacklist-noexec.exp
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue