aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/firejail/firejail.h1
-rw-r--r--src/firejail/fs.c88
-rw-r--r--src/firejail/fs_whitelist.c1
-rw-r--r--src/firejail/join.c22
-rw-r--r--src/firejail/main.c48
-rw-r--r--src/firejail/mountinfo.c3
-rw-r--r--src/firejail/sandbox.c53
-rw-r--r--src/firejail/shutdown.c26
-rw-r--r--src/firejail/util.c43
9 files changed, 181 insertions, 104 deletions
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h
index a3d3dfcd4..03ad25f75 100644
--- a/src/firejail/firejail.h
+++ b/src/firejail/firejail.h
@@ -551,6 +551,7 @@ void disable_file_or_dir(const char *fname);
551void disable_file_path(const char *path, const char *file); 551void disable_file_path(const char *path, const char *file);
552int safe_fd(const char *path, int flags); 552int safe_fd(const char *path, int flags);
553int invalid_sandbox(const pid_t pid); 553int invalid_sandbox(const pid_t pid);
554int has_handler(pid_t pid, int signal);
554void enter_network_namespace(pid_t pid); 555void enter_network_namespace(pid_t pid);
555 556
556// Get info regarding the last kernel mount operation from /proc/self/mountinfo 557// Get info regarding the last kernel mount operation from /proc/self/mountinfo
diff --git a/src/firejail/fs.c b/src/firejail/fs.c
index 5edcdd58f..184875f58 100644
--- a/src/firejail/fs.c
+++ b/src/firejail/fs.c
@@ -30,11 +30,12 @@
30#include <fcntl.h> 30#include <fcntl.h>
31 31
32#define MAX_BUF 4096 32#define MAX_BUF 4096
33#define EMPTY_STRING ("")
33// check noblacklist statements not matched by a proper blacklist in disable-*.inc files 34// check noblacklist statements not matched by a proper blacklist in disable-*.inc files
34//#define TEST_NO_BLACKLIST_MATCHING 35//#define TEST_NO_BLACKLIST_MATCHING
35 36
36 37
37static int mount_warning = 0; // remember if warning was printed already 38static int mount_warning = 0;
38static void fs_rdwr(const char *dir); 39static void fs_rdwr(const char *dir);
39static void fs_rdwr_rec(const char *dir); 40static void fs_rdwr_rec(const char *dir);
40 41
@@ -467,12 +468,11 @@ void fs_tmpfs(const char *dir, unsigned check_owner) {
467 char *options; 468 char *options;
468 if (asprintf(&options, "mode=%o,uid=%u,gid=%u", s.st_mode & 07777, s.st_uid, s.st_gid) == -1) 469 if (asprintf(&options, "mode=%o,uid=%u,gid=%u", s.st_mode & 07777, s.st_uid, s.st_gid) == -1)
469 errExit("asprintf"); 470 errExit("asprintf");
470 // preserve some mount flags 471 // preserve mount flags, but remove read-only flag
471 struct statvfs buf; 472 struct statvfs buf;
472 if (fstatvfs(fd, &buf) == -1) 473 if (fstatvfs(fd, &buf) == -1)
473 errExit("fstatvfs"); 474 errExit("fstatvfs");
474 unsigned long flags = buf.f_flag & // remove read-only flag 475 unsigned long flags = buf.f_flag & ~(MS_RDONLY|MS_BIND);
475 (MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_MANDLOCK|MS_STRICTATIME|MS_NODIRATIME|MS_RELATIME|MS_NOATIME);
476 // mount via the symbolic link in /proc/self/fd 476 // mount via the symbolic link in /proc/self/fd
477 char *proc; 477 char *proc;
478 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) 478 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
@@ -1215,9 +1215,8 @@ void fs_overlayfs(void) {
1215void fs_check_chroot_dir(const char *rootdir) { 1215void fs_check_chroot_dir(const char *rootdir) {
1216 EUID_ASSERT(); 1216 EUID_ASSERT();
1217 assert(rootdir); 1217 assert(rootdir);
1218 char *dir = EMPTY_STRING;
1218 struct stat s; 1219 struct stat s;
1219 int fd = -1;
1220 int parentfd = -1;
1221 1220
1222 char *overlay; 1221 char *overlay;
1223 if (asprintf(&overlay, "%s/.firejail", cfg.homedir) == -1) 1222 if (asprintf(&overlay, "%s/.firejail", cfg.homedir) == -1)
@@ -1229,7 +1228,7 @@ void fs_check_chroot_dir(const char *rootdir) {
1229 free(overlay); 1228 free(overlay);
1230 1229
1231 // fails if there is any symlink or if rootdir is not a directory 1230 // fails if there is any symlink or if rootdir is not a directory
1232 parentfd = safe_fd(rootdir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); 1231 int parentfd = safe_fd(rootdir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
1233 if (parentfd == -1) { 1232 if (parentfd == -1) {
1234 fprintf(stderr, "Error: invalid chroot directory %s\n", rootdir); 1233 fprintf(stderr, "Error: invalid chroot directory %s\n", rootdir);
1235 exit(1); 1234 exit(1);
@@ -1248,78 +1247,58 @@ void fs_check_chroot_dir(const char *rootdir) {
1248 } 1247 }
1249 1248
1250 // check /dev 1249 // check /dev
1251 char *dir = "dev"; 1250 dir = "dev";
1252 fd = openat(parentfd, dir, O_PATH|O_CLOEXEC); 1251 int fd = openat(parentfd, dir, O_PATH|O_CLOEXEC);
1253 if (fd == -1) { 1252 if (fd == -1)
1254 if (errno == ENOENT) 1253 goto error1;
1255 goto error1;
1256 else
1257 goto error2;
1258 }
1259 if (fstat(fd, &s) == -1) 1254 if (fstat(fd, &s) == -1)
1260 errExit("fstat"); 1255 errExit("fstat");
1261 if (!S_ISDIR(s.st_mode) || s.st_uid != 0) 1256 if (!S_ISDIR(s.st_mode) || s.st_uid != 0)
1262 goto error3; 1257 goto error2;
1263 close(fd); 1258 close(fd);
1264 1259
1265 // check /var/tmp 1260 // check /var/tmp
1266 dir = "var/tmp"; 1261 dir = "var/tmp";
1267 fd = openat(parentfd, dir, O_PATH|O_CLOEXEC); 1262 fd = openat(parentfd, dir, O_PATH|O_CLOEXEC);
1268 if (fd == -1) { 1263 if (fd == -1)
1269 if (errno == ENOENT) 1264 goto error1;
1270 goto error1;
1271 else
1272 goto error2;
1273 }
1274 if (fstat(fd, &s) == -1) 1265 if (fstat(fd, &s) == -1)
1275 errExit("fstat"); 1266 errExit("fstat");
1276 if (!S_ISDIR(s.st_mode) || s.st_uid != 0) 1267 if (!S_ISDIR(s.st_mode) || s.st_uid != 0)
1277 goto error3; 1268 goto error2;
1278 close(fd); 1269 close(fd);
1279 1270
1280 // check /proc 1271 // check /proc
1281 dir = "proc"; 1272 dir = "proc";
1282 fd = openat(parentfd, dir, O_PATH|O_CLOEXEC); 1273 fd = openat(parentfd, dir, O_PATH|O_CLOEXEC);
1283 if (fd == -1) { 1274 if (fd == -1)
1284 if (errno == ENOENT) 1275 goto error1;
1285 goto error1;
1286 else
1287 goto error2;
1288 }
1289 if (fstat(fd, &s) == -1) 1276 if (fstat(fd, &s) == -1)
1290 errExit("fstat"); 1277 errExit("fstat");
1291 if (!S_ISDIR(s.st_mode) || s.st_uid != 0) 1278 if (!S_ISDIR(s.st_mode) || s.st_uid != 0)
1292 goto error3; 1279 goto error2;
1293 close(fd); 1280 close(fd);
1294 1281
1295 // check /tmp 1282 // check /tmp
1296 dir = "tmp"; 1283 dir = "tmp";
1297 fd = openat(parentfd, dir, O_PATH|O_CLOEXEC); 1284 fd = openat(parentfd, dir, O_PATH|O_CLOEXEC);
1298 if (fd == -1) { 1285 if (fd == -1)
1299 if (errno == ENOENT) 1286 goto error1;
1300 goto error1;
1301 else
1302 goto error2;
1303 }
1304 if (fstat(fd, &s) == -1) 1287 if (fstat(fd, &s) == -1)
1305 errExit("fstat"); 1288 errExit("fstat");
1306 if (!S_ISDIR(s.st_mode) || s.st_uid != 0) 1289 if (!S_ISDIR(s.st_mode) || s.st_uid != 0)
1307 goto error3; 1290 goto error2;
1308 close(fd); 1291 close(fd);
1309 1292
1310 // check /etc 1293 // check /etc
1311 dir = "etc"; 1294 dir = "etc";
1312 fd = openat(parentfd, dir, O_PATH|O_CLOEXEC); 1295 fd = openat(parentfd, dir, O_PATH|O_CLOEXEC);
1313 if (fd == -1) { 1296 if (fd == -1)
1314 if (errno == ENOENT) 1297 goto error1;
1315 goto error1;
1316 else
1317 goto error2;
1318 }
1319 if (fstat(fd, &s) == -1) 1298 if (fstat(fd, &s) == -1)
1320 errExit("fstat"); 1299 errExit("fstat");
1321 if (!S_ISDIR(s.st_mode) || s.st_uid != 0) 1300 if (!S_ISDIR(s.st_mode) || s.st_uid != 0)
1322 goto error3; 1301 goto error2;
1323 if (((S_IWGRP|S_IWOTH) & s.st_mode) != 0) { 1302 if (((S_IWGRP|S_IWOTH) & s.st_mode) != 0) {
1324 fprintf(stderr, "Error: only root user should be given write permission on chroot /etc\n"); 1303 fprintf(stderr, "Error: only root user should be given write permission on chroot /etc\n");
1325 exit(1); 1304 exit(1);
@@ -1358,16 +1337,12 @@ void fs_check_chroot_dir(const char *rootdir) {
1358 if (getenv("FIREJAIL_X11")) { 1337 if (getenv("FIREJAIL_X11")) {
1359 dir = "tmp/.X11-unix"; 1338 dir = "tmp/.X11-unix";
1360 fd = openat(parentfd, dir, O_PATH|O_CLOEXEC); 1339 fd = openat(parentfd, dir, O_PATH|O_CLOEXEC);
1361 if (fd == -1) { 1340 if (fd == -1)
1362 if (errno == ENOENT) 1341 goto error1;
1363 goto error1;
1364 else
1365 goto error2;
1366 }
1367 if (fstat(fd, &s) == -1) 1342 if (fstat(fd, &s) == -1)
1368 errExit("fstat"); 1343 errExit("fstat");
1369 if (!S_ISDIR(s.st_mode) || s.st_uid != 0) 1344 if (!S_ISDIR(s.st_mode) || s.st_uid != 0)
1370 goto error3; 1345 goto error2;
1371 close(fd); 1346 close(fd);
1372 } 1347 }
1373 1348
@@ -1375,13 +1350,14 @@ void fs_check_chroot_dir(const char *rootdir) {
1375 return; 1350 return;
1376 1351
1377error1: 1352error1:
1378 fprintf(stderr, "Error: cannot find /%s in chroot directory\n", dir); 1353 if (errno == ENOENT)
1354 fprintf(stderr, "Error: cannot find /%s in chroot directory\n", dir);
1355 else {
1356 perror("open");
1357 fprintf(stderr, "Error: cannot open /%s in chroot directory\n", dir);
1358 }
1379 exit(1); 1359 exit(1);
1380error2: 1360error2:
1381 perror("open");
1382 fprintf(stderr, "Error: cannot open /%s in chroot directory\n", dir);
1383 exit(1);
1384error3:
1385 fprintf(stderr, "Error: chroot /%s should be a directory owned by root\n", dir); 1361 fprintf(stderr, "Error: chroot /%s should be a directory owned by root\n", dir);
1386 exit(1); 1362 exit(1);
1387} 1363}
diff --git a/src/firejail/fs_whitelist.c b/src/firejail/fs_whitelist.c
index 829636b9c..74b9449be 100644
--- a/src/firejail/fs_whitelist.c
+++ b/src/firejail/fs_whitelist.c
@@ -119,7 +119,6 @@ static int mkpath(const char* path, mode_t mode) {
119static void whitelist_path(ProfileEntry *entry) { 119static void whitelist_path(ProfileEntry *entry) {
120 assert(entry); 120 assert(entry);
121 const char *path = entry->data + 10; 121 const char *path = entry->data + 10;
122 assert(path);
123 const char *fname; 122 const char *fname;
124 char *wfile = NULL; 123 char *wfile = NULL;
125 124
diff --git a/src/firejail/join.c b/src/firejail/join.c
index 4259644f7..60980fb2e 100644
--- a/src/firejail/join.c
+++ b/src/firejail/join.c
@@ -31,7 +31,6 @@
31 31
32static int apply_caps = 0; 32static int apply_caps = 0;
33static uint64_t caps = 0; 33static uint64_t caps = 0;
34static int apply_seccomp = 0;
35static unsigned display = 0; 34static unsigned display = 0;
36#define BUFLEN 4096 35#define BUFLEN 4096
37 36
@@ -41,7 +40,15 @@ static void signal_handler(int sig){
41 exit(sig); 40 exit(sig);
42} 41}
43 42
43static void install_handler(void) {
44 struct sigaction sga;
44 45
46 // handle SIGTERM
47 sigemptyset(&sga.sa_mask);
48 sga.sa_handler = signal_handler;
49 sga.sa_flags = 0;
50 sigaction(SIGTERM, &sga, NULL);
51}
45 52
46static void extract_x11_display(pid_t pid) { 53static void extract_x11_display(pid_t pid) {
47 char *fname; 54 char *fname;
@@ -313,7 +320,7 @@ void join(pid_t pid, int argc, char **argv, int index) {
313 EUID_ROOT(); 320 EUID_ROOT();
314 // in user mode set caps seccomp, cpu, cgroup, etc 321 // in user mode set caps seccomp, cpu, cgroup, etc
315 if (getuid() != 0) { 322 if (getuid() != 0) {
316 extract_nonewprivs(pid); // redundant on Linux >= 4.10; duplicated in function extract_caps_seccomp 323 extract_nonewprivs(pid); // redundant on Linux >= 4.10; duplicated in function extract_caps
317 extract_caps(pid); 324 extract_caps(pid);
318 extract_cpu(pid); 325 extract_cpu(pid);
319 extract_cgroup(pid); 326 extract_cgroup(pid);
@@ -465,8 +472,19 @@ void join(pid_t pid, int argc, char **argv, int index) {
465 } 472 }
466 473
467 int status = 0; 474 int status = 0;
475 //*****************************
476 // following code is signal-safe
477
478 install_handler();
479
468 // wait for the child to finish 480 // wait for the child to finish
469 waitpid(child, &status, 0); 481 waitpid(child, &status, 0);
482
483 // restore default signal action
484 signal(SIGTERM, SIG_DFL);
485
486 // end of signal-safe code
487 //*****************************
470 flush_stdin(); 488 flush_stdin();
471 489
472 if (WIFEXITED(status)) { 490 if (WIFEXITED(status)) {
diff --git a/src/firejail/main.c b/src/firejail/main.c
index faa4972e2..61f507f36 100644
--- a/src/firejail/main.c
+++ b/src/firejail/main.c
@@ -157,12 +157,35 @@ static void myexit(int rv) {
157 exit(rv); 157 exit(rv);
158} 158}
159 159
160static void my_handler(int s){ 160static void my_handler(int s) {
161 EUID_ROOT();
162 fmessage("\nParent received signal %d, shutting down the child process...\n", s); 161 fmessage("\nParent received signal %d, shutting down the child process...\n", s);
163 logsignal(s); 162 logsignal(s);
164 kill(child, SIGTERM); 163 if (waitpid(child, NULL, WNOHANG) == 0) {
165 myexit(1); 164 if (has_handler(child, s)) // signals are not delivered if there is no handler yet
165 kill(child, s);
166 else
167 kill(child, SIGKILL);
168 waitpid(child, NULL, 0);
169 }
170 myexit(s);
171}
172
173static void install_handler(void) {
174 struct sigaction sga;
175
176 // block SIGTERM while handling SIGINT
177 sigemptyset(&sga.sa_mask);
178 sigaddset(&sga.sa_mask, SIGTERM);
179 sga.sa_handler = my_handler;
180 sga.sa_flags = 0;
181 sigaction(SIGINT, &sga, NULL);
182
183 // block SIGINT while handling SIGTERM
184 sigemptyset(&sga.sa_mask);
185 sigaddset(&sga.sa_mask, SIGINT);
186 sga.sa_handler = my_handler;
187 sga.sa_flags = 0;
188 sigaction(SIGTERM, &sga, NULL);
166} 189}
167 190
168// return 1 if error, 0 if a valid pid was found 191// return 1 if error, 0 if a valid pid was found
@@ -2601,16 +2624,25 @@ int main(int argc, char **argv) {
2601 flock(lockfd_network, LOCK_UN); 2624 flock(lockfd_network, LOCK_UN);
2602 close(lockfd_network); 2625 close(lockfd_network);
2603 } 2626 }
2627 EUID_USER();
2628
2629 int status = 0;
2630 //*****************************
2631 // following code is signal-safe
2604 2632
2605 // handle CTRL-C in parent 2633 // handle CTRL-C in parent
2606 signal (SIGINT, my_handler); 2634 install_handler();
2607 signal (SIGTERM, my_handler);
2608 2635
2609 // wait for the child to finish 2636 // wait for the child to finish
2610 EUID_USER();
2611 int status = 0;
2612 waitpid(child, &status, 0); 2637 waitpid(child, &status, 0);
2613 2638
2639 // restore default signal actions
2640 signal(SIGTERM, SIG_DFL);
2641 signal(SIGINT, SIG_DFL);
2642
2643 // end of signal-safe code
2644 //*****************************
2645
2614 // free globals 2646 // free globals
2615 if (cfg.profile) { 2647 if (cfg.profile) {
2616 ProfileEntry *prf = cfg.profile; 2648 ProfileEntry *prf = cfg.profile;
diff --git a/src/firejail/mountinfo.c b/src/firejail/mountinfo.c
index c89845ace..02c28cc5e 100644
--- a/src/firejail/mountinfo.c
+++ b/src/firejail/mountinfo.c
@@ -199,8 +199,7 @@ int get_mount_id(const char *path) {
199 return -2; 199 return -2;
200} 200}
201 201
202// Check /proc/self/mountinfo if path has any submounts (or if path would have submounts 202// Check /proc/self/mountinfo if path contains any mounts points.
203// if it was made a mount point).
204// Returns an array that can be iterated over for recursive remounting. 203// Returns an array that can be iterated over for recursive remounting.
205char **build_mount_array(const int mount_id, const char *path) { 204char **build_mount_array(const int mount_id, const char *path) {
206 assert(path); 205 assert(path);
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c
index 475779f47..735bab684 100644
--- a/src/firejail/sandbox.c
+++ b/src/firejail/sandbox.c
@@ -53,6 +53,7 @@ static int force_nonewprivs = 0;
53 53
54static int monitored_pid = 0; 54static int monitored_pid = 0;
55static void sandbox_handler(int sig){ 55static void sandbox_handler(int sig){
56 usleep(10000); // don't race to print a message
56 fmessage("\nChild received signal %d, shutting down the sandbox...\n", sig); 57 fmessage("\nChild received signal %d, shutting down the sandbox...\n", sig);
57 58
58 // broadcast sigterm to all processes in the group 59 // broadcast sigterm to all processes in the group
@@ -81,10 +82,8 @@ static void sandbox_handler(int sig){
81 monsec--; 82 monsec--;
82 } 83 }
83 free(monfile); 84 free(monfile);
84
85 } 85 }
86 86
87
88 // broadcast a SIGKILL 87 // broadcast a SIGKILL
89 kill(-1, SIGKILL); 88 kill(-1, SIGKILL);
90 flush_stdin(); 89 flush_stdin();
@@ -92,6 +91,23 @@ static void sandbox_handler(int sig){
92 exit(sig); 91 exit(sig);
93} 92}
94 93
94static void install_handler(void) {
95 struct sigaction sga;
96
97 // block SIGTERM while handling SIGINT
98 sigemptyset(&sga.sa_mask);
99 sigaddset(&sga.sa_mask, SIGTERM);
100 sga.sa_handler = sandbox_handler;
101 sga.sa_flags = 0;
102 sigaction(SIGINT, &sga, NULL);
103
104 // block SIGINT while handling SIGTERM
105 sigemptyset(&sga.sa_mask);
106 sigaddset(&sga.sa_mask, SIGINT);
107 sga.sa_handler = sandbox_handler;
108 sga.sa_flags = 0;
109 sigaction(SIGTERM, &sga, NULL);
110}
95 111
96static void set_caps(void) { 112static void set_caps(void) {
97 if (arg_caps_drop_all) 113 if (arg_caps_drop_all)
@@ -123,7 +139,6 @@ static void save_nogroups(void) {
123 fprintf(stderr, "Error: cannot save nogroups state\n"); 139 fprintf(stderr, "Error: cannot save nogroups state\n");
124 exit(1); 140 exit(1);
125 } 141 }
126
127} 142}
128 143
129static void save_nonewprivs(void) { 144static void save_nonewprivs(void) {
@@ -235,9 +250,17 @@ static void chk_chroot(void) {
235} 250}
236 251
237static int monitor_application(pid_t app_pid) { 252static int monitor_application(pid_t app_pid) {
253 EUID_ASSERT();
238 monitored_pid = app_pid; 254 monitored_pid = app_pid;
239 signal (SIGTERM, sandbox_handler); 255
240 EUID_USER(); 256 // block signals and install handler
257 sigset_t oldmask, newmask;
258 sigemptyset(&oldmask);
259 sigemptyset(&newmask);
260 sigaddset(&newmask, SIGTERM);
261 sigaddset(&newmask, SIGINT);
262 sigprocmask(SIG_BLOCK, &newmask, &oldmask);
263 install_handler();
241 264
242 // handle --timeout 265 // handle --timeout
243 int options = 0;; 266 int options = 0;;
@@ -260,16 +283,25 @@ static int monitor_application(pid_t app_pid) {
260 283
261 pid_t rv; 284 pid_t rv;
262 do { 285 do {
286 // handle signals asynchronously
287 sigprocmask(SIG_SETMASK, &oldmask, NULL);
288
263 rv = waitpid(-1, &status, options); 289 rv = waitpid(-1, &status, options);
264 if (rv == -1) 290
291 // block signals again
292 sigprocmask(SIG_BLOCK, &newmask, NULL);
293
294 if (rv == -1) { // we can get here if we have processes joining the sandbox (ECHILD)
295 sleep(1);
265 break; 296 break;
297 }
266 298
267 // handle --timeout 299 // handle --timeout
268 if (options) { 300 if (options) {
269 if (--timeout == 0) { 301 if (--timeout == 0) {
270 kill(-1, SIGTERM); 302 kill(-1, SIGTERM);
271 flush_stdin();
272 sleep(1); 303 sleep(1);
304 flush_stdin();
273 _exit(1); 305 _exit(1);
274 } 306 }
275 else 307 else
@@ -279,11 +311,6 @@ static int monitor_application(pid_t app_pid) {
279 while(rv != monitored_pid); 311 while(rv != monitored_pid);
280 if (arg_debug) 312 if (arg_debug)
281 printf("Sandbox monitor: waitpid %d retval %d status %d\n", monitored_pid, rv, status); 313 printf("Sandbox monitor: waitpid %d retval %d status %d\n", monitored_pid, rv, status);
282 if (rv == -1) { // we can get here if we have processes joining the sandbox (ECHILD)
283 if (arg_debug)
284 perror("waitpid");
285 sleep(1);
286 }
287 314
288 DIR *dir; 315 DIR *dir;
289 if (!(dir = opendir("/proc"))) { 316 if (!(dir = opendir("/proc"))) {
@@ -1151,7 +1178,7 @@ int sandbox(void* sandbox_arg) {
1151 // drop privileges, fork the application and monitor it 1178 // drop privileges, fork the application and monitor it
1152 //**************************************** 1179 //****************************************
1153 drop_privs(arg_nogroups); 1180 drop_privs(arg_nogroups);
1154 prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); // kill the child in case the parent died 1181 prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); // kill the sandbox in case the parent died
1155 pid_t app_pid = fork(); 1182 pid_t app_pid = fork();
1156 if (app_pid == -1) 1183 if (app_pid == -1)
1157 errExit("fork"); 1184 errExit("fork");
diff --git a/src/firejail/shutdown.c b/src/firejail/shutdown.c
index be20cd353..743a256a4 100644
--- a/src/firejail/shutdown.c
+++ b/src/firejail/shutdown.c
@@ -26,20 +26,11 @@
26void shut(pid_t pid) { 26void shut(pid_t pid) {
27 EUID_ASSERT(); 27 EUID_ASSERT();
28 28
29 pid_t parent = pid;
30 // if the pid is that of a firejail process, use the pid of a child process inside the sandbox
31 EUID_ROOT(); 29 EUID_ROOT();
32 char *comm = pid_proc_comm(pid); 30 char *comm = pid_proc_comm(pid);
33 EUID_USER(); 31 EUID_USER();
34 if (comm) { 32 if (comm) {
35 if (strcmp(comm, "firejail") == 0) { 33 if (strcmp(comm, "firejail") != 0) {
36 pid_t child;
37 if (find_child(pid, &child) == 0) {
38 pid = child;
39 printf("Switching to pid %u, the first child process inside the sandbox\n", (unsigned) pid);
40 }
41 }
42 else {
43 fprintf(stderr, "Error: this is not a firejail sandbox\n"); 34 fprintf(stderr, "Error: this is not a firejail sandbox\n");
44 exit(1); 35 exit(1);
45 } 36 }
@@ -58,7 +49,6 @@ void shut(pid_t pid) {
58 } 49 }
59 } 50 }
60 51
61 EUID_ROOT();
62 printf("Sending SIGTERM to %u\n", pid); 52 printf("Sending SIGTERM to %u\n", pid);
63 kill(pid, SIGTERM); 53 kill(pid, SIGTERM);
64 54
@@ -94,14 +84,16 @@ void shut(pid_t pid) {
94 84
95 // force SIGKILL 85 // force SIGKILL
96 if (!killdone) { 86 if (!killdone) {
97 // kill the process and also the parent 87 // kill the process and its child
88 pid_t child;
89 if (find_child(pid, &child) == 0) {
90 printf("Sending SIGKILL to %u\n", child);
91 kill(child, SIGKILL);
92 }
98 printf("Sending SIGKILL to %u\n", pid); 93 printf("Sending SIGKILL to %u\n", pid);
99 kill(pid, SIGKILL); 94 kill(pid, SIGKILL);
100 if (parent != pid) {
101 printf("Sending SIGKILL to %u\n", parent);
102 kill(parent, SIGKILL);
103 }
104 } 95 }
105 96
106 delete_run_files(parent); 97 EUID_ROOT();
98 delete_run_files(pid);
107} 99}
diff --git a/src/firejail/util.c b/src/firejail/util.c
index 8c474f966..b1fba4226 100644
--- a/src/firejail/util.c
+++ b/src/firejail/util.c
@@ -454,7 +454,6 @@ void trim_trailing_slash_or_dot(char *path) {
454 assert(path); 454 assert(path);
455 455
456 char *end = strchr(path, '\0'); 456 char *end = strchr(path, '\0');
457 assert(end);
458 if ((end - path) > 1) { 457 if ((end - path) > 1) {
459 end--; 458 end--;
460 while (*end == '/' || 459 while (*end == '/' ||
@@ -941,9 +940,7 @@ int remove_overlay_directory(void) {
941 // wait for the child to finish 940 // wait for the child to finish
942 waitpid(child, NULL, 0); 941 waitpid(child, NULL, 0);
943 // check if ~/.firejail was deleted 942 // check if ~/.firejail was deleted
944 if (stat(path, &s) == -1) 943 if (stat(path, &s) == 0)
945 return 0;
946 else
947 return 1; 944 return 1;
948 } 945 }
949 return 0; 946 return 0;
@@ -1093,8 +1090,13 @@ unsigned extract_timeout(const char *str) {
1093 fprintf(stderr, "Error: invalid timeout, please use a hh:mm:ss format\n"); 1090 fprintf(stderr, "Error: invalid timeout, please use a hh:mm:ss format\n");
1094 exit(1); 1091 exit(1);
1095 } 1092 }
1093 unsigned timeout = h * 3600 + m * 60 + s;
1094 if (timeout == 0) {
1095 fprintf(stderr, "Error: invalid timeout\n");
1096 exit(1);
1097 }
1096 1098
1097 return h * 3600 + m * 60 + s; 1099 return timeout;
1098} 1100}
1099 1101
1100void disable_file_or_dir(const char *fname) { 1102void disable_file_or_dir(const char *fname) {
@@ -1264,6 +1266,37 @@ int invalid_sandbox(const pid_t pid) {
1264 return 0; 1266 return 0;
1265} 1267}
1266 1268
1269int has_handler(pid_t pid, int signal) {
1270 if (signal > 0) {
1271 char *fname;
1272 if (asprintf(&fname, "/proc/%d/status", pid) == -1)
1273 errExit("asprintf");
1274 EUID_ROOT();
1275 FILE *fp = fopen(fname, "re");
1276 EUID_USER();
1277 free(fname);
1278 if (fp) {
1279 char buf[BUFLEN];
1280 while (fgets(buf, BUFLEN, fp)) {
1281 if (strncmp(buf, "SigCgt:", 7) == 0) {
1282 char *ptr = buf + 7;
1283 unsigned long long val;
1284 if (sscanf(ptr, "%llx", &val) != 1) {
1285 fprintf(stderr, "Error: cannot read /proc file\n");
1286 exit(1);
1287 }
1288 val >>= (signal - 1);
1289 val &= 1;
1290 fclose(fp);
1291 return val; // 1 if process has a handler for the signal, else 0
1292 }
1293 }
1294 fclose(fp);
1295 }
1296 }
1297 return 0;
1298}
1299
1267void enter_network_namespace(pid_t pid) { 1300void enter_network_namespace(pid_t pid) {
1268 // in case the pid is that of a firejail process, use the pid of the first child process 1301 // in case the pid is that of a firejail process, use the pid of the first child process
1269 pid_t child = switch_to_child(pid); 1302 pid_t child = switch_to_child(pid);