diff options
-rw-r--r-- | src/firejail/firejail.h | 1 | ||||
-rw-r--r-- | src/firejail/fs.c | 88 | ||||
-rw-r--r-- | src/firejail/fs_whitelist.c | 1 | ||||
-rw-r--r-- | src/firejail/join.c | 22 | ||||
-rw-r--r-- | src/firejail/main.c | 48 | ||||
-rw-r--r-- | src/firejail/mountinfo.c | 3 | ||||
-rw-r--r-- | src/firejail/sandbox.c | 53 | ||||
-rw-r--r-- | src/firejail/shutdown.c | 26 | ||||
-rw-r--r-- | src/firejail/util.c | 43 |
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); | |||
551 | void disable_file_path(const char *path, const char *file); | 551 | void disable_file_path(const char *path, const char *file); |
552 | int safe_fd(const char *path, int flags); | 552 | int safe_fd(const char *path, int flags); |
553 | int invalid_sandbox(const pid_t pid); | 553 | int invalid_sandbox(const pid_t pid); |
554 | int has_handler(pid_t pid, int signal); | ||
554 | void enter_network_namespace(pid_t pid); | 555 | void 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 | ||
37 | static int mount_warning = 0; // remember if warning was printed already | 38 | static int mount_warning = 0; |
38 | static void fs_rdwr(const char *dir); | 39 | static void fs_rdwr(const char *dir); |
39 | static void fs_rdwr_rec(const char *dir); | 40 | static 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) { | |||
1215 | void fs_check_chroot_dir(const char *rootdir) { | 1215 | void 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 | ||
1377 | error1: | 1352 | error1: |
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); |
1380 | error2: | 1360 | error2: |
1381 | perror("open"); | ||
1382 | fprintf(stderr, "Error: cannot open /%s in chroot directory\n", dir); | ||
1383 | exit(1); | ||
1384 | error3: | ||
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) { | |||
119 | static void whitelist_path(ProfileEntry *entry) { | 119 | static 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 | ||
32 | static int apply_caps = 0; | 32 | static int apply_caps = 0; |
33 | static uint64_t caps = 0; | 33 | static uint64_t caps = 0; |
34 | static int apply_seccomp = 0; | ||
35 | static unsigned display = 0; | 34 | static 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 | ||
43 | static 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 | ||
46 | static void extract_x11_display(pid_t pid) { | 53 | static 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 | ||
160 | static void my_handler(int s){ | 160 | static 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 | |||
173 | static 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. |
205 | char **build_mount_array(const int mount_id, const char *path) { | 204 | char **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 | ||
54 | static int monitored_pid = 0; | 54 | static int monitored_pid = 0; |
55 | static void sandbox_handler(int sig){ | 55 | static 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 | ||
94 | static 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 | ||
96 | static void set_caps(void) { | 112 | static 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 | ||
129 | static void save_nonewprivs(void) { | 144 | static void save_nonewprivs(void) { |
@@ -235,9 +250,17 @@ static void chk_chroot(void) { | |||
235 | } | 250 | } |
236 | 251 | ||
237 | static int monitor_application(pid_t app_pid) { | 252 | static 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 @@ | |||
26 | void shut(pid_t pid) { | 26 | void 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 | ||
1100 | void disable_file_or_dir(const char *fname) { | 1102 | void 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 | ||
1269 | int 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 | |||
1267 | void enter_network_namespace(pid_t pid) { | 1300 | void 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); |