aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/firejail/firejail.h1
-rw-r--r--src/firejail/fs.c81
-rw-r--r--src/firejail/join.c21
-rw-r--r--src/firejail/main.c48
-rw-r--r--src/firejail/sandbox.c51
-rw-r--r--src/firejail/util.c31
6 files changed, 160 insertions, 73 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..ce2b1a8bc 100644
--- a/src/firejail/fs.c
+++ b/src/firejail/fs.c
@@ -30,6 +30,7 @@
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
@@ -1215,9 +1216,8 @@ void fs_overlayfs(void) {
1215void fs_check_chroot_dir(const char *rootdir) { 1216void fs_check_chroot_dir(const char *rootdir) {
1216 EUID_ASSERT(); 1217 EUID_ASSERT();
1217 assert(rootdir); 1218 assert(rootdir);
1219 char *dir = EMPTY_STRING;
1218 struct stat s; 1220 struct stat s;
1219 int fd = -1;
1220 int parentfd = -1;
1221 1221
1222 char *overlay; 1222 char *overlay;
1223 if (asprintf(&overlay, "%s/.firejail", cfg.homedir) == -1) 1223 if (asprintf(&overlay, "%s/.firejail", cfg.homedir) == -1)
@@ -1229,7 +1229,7 @@ void fs_check_chroot_dir(const char *rootdir) {
1229 free(overlay); 1229 free(overlay);
1230 1230
1231 // fails if there is any symlink or if rootdir is not a directory 1231 // 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); 1232 int parentfd = safe_fd(rootdir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
1233 if (parentfd == -1) { 1233 if (parentfd == -1) {
1234 fprintf(stderr, "Error: invalid chroot directory %s\n", rootdir); 1234 fprintf(stderr, "Error: invalid chroot directory %s\n", rootdir);
1235 exit(1); 1235 exit(1);
@@ -1248,78 +1248,58 @@ void fs_check_chroot_dir(const char *rootdir) {
1248 } 1248 }
1249 1249
1250 // check /dev 1250 // check /dev
1251 char *dir = "dev"; 1251 dir = "dev";
1252 fd = openat(parentfd, dir, O_PATH|O_CLOEXEC); 1252 int fd = openat(parentfd, dir, O_PATH|O_CLOEXEC);
1253 if (fd == -1) { 1253 if (fd == -1)
1254 if (errno == ENOENT) 1254 goto error1;
1255 goto error1;
1256 else
1257 goto error2;
1258 }
1259 if (fstat(fd, &s) == -1) 1255 if (fstat(fd, &s) == -1)
1260 errExit("fstat"); 1256 errExit("fstat");
1261 if (!S_ISDIR(s.st_mode) || s.st_uid != 0) 1257 if (!S_ISDIR(s.st_mode) || s.st_uid != 0)
1262 goto error3; 1258 goto error2;
1263 close(fd); 1259 close(fd);
1264 1260
1265 // check /var/tmp 1261 // check /var/tmp
1266 dir = "var/tmp"; 1262 dir = "var/tmp";
1267 fd = openat(parentfd, dir, O_PATH|O_CLOEXEC); 1263 fd = openat(parentfd, dir, O_PATH|O_CLOEXEC);
1268 if (fd == -1) { 1264 if (fd == -1)
1269 if (errno == ENOENT) 1265 goto error1;
1270 goto error1;
1271 else
1272 goto error2;
1273 }
1274 if (fstat(fd, &s) == -1) 1266 if (fstat(fd, &s) == -1)
1275 errExit("fstat"); 1267 errExit("fstat");
1276 if (!S_ISDIR(s.st_mode) || s.st_uid != 0) 1268 if (!S_ISDIR(s.st_mode) || s.st_uid != 0)
1277 goto error3; 1269 goto error2;
1278 close(fd); 1270 close(fd);
1279 1271
1280 // check /proc 1272 // check /proc
1281 dir = "proc"; 1273 dir = "proc";
1282 fd = openat(parentfd, dir, O_PATH|O_CLOEXEC); 1274 fd = openat(parentfd, dir, O_PATH|O_CLOEXEC);
1283 if (fd == -1) { 1275 if (fd == -1)
1284 if (errno == ENOENT) 1276 goto error1;
1285 goto error1;
1286 else
1287 goto error2;
1288 }
1289 if (fstat(fd, &s) == -1) 1277 if (fstat(fd, &s) == -1)
1290 errExit("fstat"); 1278 errExit("fstat");
1291 if (!S_ISDIR(s.st_mode) || s.st_uid != 0) 1279 if (!S_ISDIR(s.st_mode) || s.st_uid != 0)
1292 goto error3; 1280 goto error2;
1293 close(fd); 1281 close(fd);
1294 1282
1295 // check /tmp 1283 // check /tmp
1296 dir = "tmp"; 1284 dir = "tmp";
1297 fd = openat(parentfd, dir, O_PATH|O_CLOEXEC); 1285 fd = openat(parentfd, dir, O_PATH|O_CLOEXEC);
1298 if (fd == -1) { 1286 if (fd == -1)
1299 if (errno == ENOENT) 1287 goto error1;
1300 goto error1;
1301 else
1302 goto error2;
1303 }
1304 if (fstat(fd, &s) == -1) 1288 if (fstat(fd, &s) == -1)
1305 errExit("fstat"); 1289 errExit("fstat");
1306 if (!S_ISDIR(s.st_mode) || s.st_uid != 0) 1290 if (!S_ISDIR(s.st_mode) || s.st_uid != 0)
1307 goto error3; 1291 goto error2;
1308 close(fd); 1292 close(fd);
1309 1293
1310 // check /etc 1294 // check /etc
1311 dir = "etc"; 1295 dir = "etc";
1312 fd = openat(parentfd, dir, O_PATH|O_CLOEXEC); 1296 fd = openat(parentfd, dir, O_PATH|O_CLOEXEC);
1313 if (fd == -1) { 1297 if (fd == -1)
1314 if (errno == ENOENT) 1298 goto error1;
1315 goto error1;
1316 else
1317 goto error2;
1318 }
1319 if (fstat(fd, &s) == -1) 1299 if (fstat(fd, &s) == -1)
1320 errExit("fstat"); 1300 errExit("fstat");
1321 if (!S_ISDIR(s.st_mode) || s.st_uid != 0) 1301 if (!S_ISDIR(s.st_mode) || s.st_uid != 0)
1322 goto error3; 1302 goto error2;
1323 if (((S_IWGRP|S_IWOTH) & s.st_mode) != 0) { 1303 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"); 1304 fprintf(stderr, "Error: only root user should be given write permission on chroot /etc\n");
1325 exit(1); 1305 exit(1);
@@ -1358,16 +1338,12 @@ void fs_check_chroot_dir(const char *rootdir) {
1358 if (getenv("FIREJAIL_X11")) { 1338 if (getenv("FIREJAIL_X11")) {
1359 dir = "tmp/.X11-unix"; 1339 dir = "tmp/.X11-unix";
1360 fd = openat(parentfd, dir, O_PATH|O_CLOEXEC); 1340 fd = openat(parentfd, dir, O_PATH|O_CLOEXEC);
1361 if (fd == -1) { 1341 if (fd == -1)
1362 if (errno == ENOENT) 1342 goto error1;
1363 goto error1;
1364 else
1365 goto error2;
1366 }
1367 if (fstat(fd, &s) == -1) 1343 if (fstat(fd, &s) == -1)
1368 errExit("fstat"); 1344 errExit("fstat");
1369 if (!S_ISDIR(s.st_mode) || s.st_uid != 0) 1345 if (!S_ISDIR(s.st_mode) || s.st_uid != 0)
1370 goto error3; 1346 goto error2;
1371 close(fd); 1347 close(fd);
1372 } 1348 }
1373 1349
@@ -1375,13 +1351,14 @@ void fs_check_chroot_dir(const char *rootdir) {
1375 return; 1351 return;
1376 1352
1377error1: 1353error1:
1378 fprintf(stderr, "Error: cannot find /%s in chroot directory\n", dir); 1354 if (errno == ENOENT)
1355 fprintf(stderr, "Error: cannot find /%s in chroot directory\n", dir);
1356 else {
1357 perror("open");
1358 fprintf(stderr, "Error: cannot open /%s in chroot directory\n", dir);
1359 }
1379 exit(1); 1360 exit(1);
1380error2: 1361error2:
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); 1362 fprintf(stderr, "Error: chroot /%s should be a directory owned by root\n", dir);
1386 exit(1); 1363 exit(1);
1387} 1364}
diff --git a/src/firejail/join.c b/src/firejail/join.c
index 4259644f7..d05a4a465 100644
--- a/src/firejail/join.c
+++ b/src/firejail/join.c
@@ -41,7 +41,15 @@ static void signal_handler(int sig){
41 exit(sig); 41 exit(sig);
42} 42}
43 43
44 44static void install_handler(void) {
45 struct sigaction sga;
46
47 // handle SIGTERM
48 sigemptyset(&sga.sa_mask);
49 sga.sa_handler = signal_handler;
50 sga.sa_flags = 0;
51 sigaction(SIGTERM, &sga, NULL);
52}
45 53
46static void extract_x11_display(pid_t pid) { 54static void extract_x11_display(pid_t pid) {
47 char *fname; 55 char *fname;
@@ -465,8 +473,19 @@ void join(pid_t pid, int argc, char **argv, int index) {
465 } 473 }
466 474
467 int status = 0; 475 int status = 0;
476 //*****************************
477 // following code is signal-safe
478
479 install_handler();
480
468 // wait for the child to finish 481 // wait for the child to finish
469 waitpid(child, &status, 0); 482 waitpid(child, &status, 0);
483
484 // restore default signal action
485 signal(SIGTERM, SIG_DFL);
486
487 // end of signal-safe code
488 //*****************************
470 flush_stdin(); 489 flush_stdin();
471 490
472 if (WIFEXITED(status)) { 491 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/sandbox.c b/src/firejail/sandbox.c
index 475779f47..ba9a36250 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"))) {
diff --git a/src/firejail/util.c b/src/firejail/util.c
index 8c474f966..6b30ab5fe 100644
--- a/src/firejail/util.c
+++ b/src/firejail/util.c
@@ -1264,6 +1264,37 @@ int invalid_sandbox(const pid_t pid) {
1264 return 0; 1264 return 0;
1265} 1265}
1266 1266
1267int has_handler(pid_t pid, int signal) {
1268 if (signal > 0) {
1269 char *fname;
1270 if (asprintf(&fname, "/proc/%d/status", pid) == -1)
1271 errExit("asprintf");
1272 EUID_ROOT();
1273 FILE *fp = fopen(fname, "re");
1274 EUID_USER();
1275 free(fname);
1276 if (fp) {
1277 char buf[BUFLEN];
1278 while (fgets(buf, BUFLEN, fp)) {
1279 if (strncmp(buf, "SigCgt:", 7) == 0) {
1280 char *ptr = buf + 7;
1281 unsigned long long val;
1282 if (sscanf(ptr, "%llx", &val) != 1) {
1283 fprintf(stderr, "Error: cannot read /proc file\n");
1284 exit(1);
1285 }
1286 val >>= (signal - 1);
1287 val &= 1;
1288 fclose(fp);
1289 return val; // 1 if process has a handler for the signal, else 0
1290 }
1291 }
1292 fclose(fp);
1293 }
1294 }
1295 return 0;
1296}
1297
1267void enter_network_namespace(pid_t pid) { 1298void 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 1299 // 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); 1300 pid_t child = switch_to_child(pid);