aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar smitsohu <smitsohu@gmail.com>2021-05-08 00:06:14 +0200
committerLibravatar smitsohu <smitsohu@gmail.com>2021-05-08 16:35:20 +0200
commit9e7cad06c86b64a8608e690b8c637c33ce18c6c3 (patch)
treebefe7d31f10d4b8f92366ff43ed093fd31e0133b
parenttweak (diff)
downloadfirejail-9e7cad06c86b64a8608e690b8c637c33ce18c6c3.tar.gz
firejail-9e7cad06c86b64a8608e690b8c637c33ce18c6c3.tar.zst
firejail-9e7cad06c86b64a8608e690b8c637c33ce18c6c3.zip
add /run whitelist support
-rw-r--r--src/firejail/fs_whitelist.c125
1 files changed, 84 insertions, 41 deletions
diff --git a/src/firejail/fs_whitelist.c b/src/firejail/fs_whitelist.c
index c58b8e786..c7dbe6496 100644
--- a/src/firejail/fs_whitelist.c
+++ b/src/firejail/fs_whitelist.c
@@ -114,7 +114,7 @@ static void whitelist_file(int dirfd, const char *topdir, const char *relpath, c
114 // top level directory 114 // top level directory
115 // as the top level directory was opened before mounting the tmpfs 115 // as the top level directory was opened before mounting the tmpfs
116 // we still have full access to all directory contents 116 // we still have full access to all directory contents
117 // take care to no follow symbolic links 117 // take care to not follow symbolic links
118 int fd = safer_openat(dirfd, relpath, O_PATH|O_NOFOLLOW|O_CLOEXEC); 118 int fd = safer_openat(dirfd, relpath, O_PATH|O_NOFOLLOW|O_CLOEXEC);
119 if (fd == -1) { 119 if (fd == -1) {
120 if (arg_debug || arg_debug_whitelists) 120 if (arg_debug || arg_debug_whitelists)
@@ -307,51 +307,93 @@ static void globbing(const char *pattern) {
307} 307}
308 308
309// mount tmpfs on all top level directories 309// mount tmpfs on all top level directories
310static void tmpfs_topdirs(TopDir *topdirs) { 310static void tmpfs_topdirs(const TopDir *topdirs) {
311 // process user home directory first 311 int tmpfs_home = 0;
312 int tmpfs_runuser = 0;
313
312 int i; 314 int i;
313 for (i = 0; i < TOP_MAX && topdirs[i].path; i++) { 315 for (i = 0; i < TOP_MAX && topdirs[i].path; i++) {
316 // do user home and /run/user/$UID last
314 if (strcmp(topdirs[i].path, cfg.homedir) == 0) { 317 if (strcmp(topdirs[i].path, cfg.homedir) == 0) {
315 fs_private(); 318 tmpfs_home = 1;
316 break; 319 continue;
320 }
321 if (strcmp(topdirs[i].path, runuser) == 0) {
322 tmpfs_runuser = 1;
323 continue;
317 } 324 }
318 }
319 325
320 for (i = 0; i < TOP_MAX && topdirs[i].path; i++) { 326 // special case /run
321 if (strcmp(topdirs[i].path, cfg.homedir) != 0) { 327 // open /run/firejail, so it can be restored right after mounting the tmpfs
322 // mount the tmpfs 328 int fd = -1;
323 fs_tmpfs(topdirs[i].path, 0); 329 if (strcmp(topdirs[i].path, "/run") == 0) {
324 selinux_relabel_path(topdirs[i].path, topdirs[i].path); 330 fd = open(RUN_FIREJAIL_DIR, O_PATH|O_CLOEXEC);
331 if (fd == -1)
332 errExit("open");
333 }
334
335 // mount tmpfs
336 fs_tmpfs(topdirs[i].path, 0);
337
338 // init tmpfs
339 if (strcmp(topdirs[i].path, "/run") == 0) {
340 // restore /run/firejail directory
341 if (mkdir(RUN_FIREJAIL_DIR, 0755) == -1)
342 errExit("mkdir");
343 char *proc;
344 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
345 errExit("asprintf");
346 if (mount(proc, RUN_FIREJAIL_DIR, NULL, MS_BIND | MS_REC, NULL) < 0)
347 errExit("mount bind");
348 free(proc);
349 close(fd);
350 fs_logger2("whitelist", RUN_FIREJAIL_DIR);
325 351
326 // init tmpfs 352 // restore /run/user/$UID directory
353 // get path relative to /run
354 const char *rel = runuser + 5;
355 whitelist_file(topdirs[i].fd, topdirs[i].path, rel, runuser);
356 }
357 else if (strcmp(topdirs[i].path, "/tmp") == 0) {
327 // fix pam-tmpdir (#2685) 358 // fix pam-tmpdir (#2685)
328 if (strcmp(topdirs[i].path, "/tmp") == 0) { 359 const char *env = env_get("TMP");
329 const char *env = env_get("TMP"); 360 if (env) {
330 if (env) { 361 char *pamtmpdir;
331 char *pamtmpdir; 362 if (asprintf(&pamtmpdir, "/tmp/user/%u", getuid()) == -1)
332 if (asprintf(&pamtmpdir, "/tmp/user/%u", getuid()) == -1) 363 errExit("asprintf");
333 errExit("asprintf"); 364 if (strcmp(env, pamtmpdir) == 0) {
334 if (strcmp(env, pamtmpdir) == 0) { 365 // create empty user-owned /tmp/user/$UID directory
335 // create empty user-owned /tmp/user/$UID directory 366 mkdir_attr("/tmp/user", 0711, 0, 0);
336 mkdir_attr("/tmp/user", 0711, 0, 0); 367 selinux_relabel_path("/tmp/user", "/tmp/user");
337 selinux_relabel_path("/tmp/user", "/tmp/user"); 368 fs_logger("mkdir /tmp/user");
338 fs_logger("mkdir /tmp/user"); 369 mkdir_attr(pamtmpdir, 0700, getuid(), 0);
339 mkdir_attr(pamtmpdir, 0700, getuid(), 0); 370 selinux_relabel_path(pamtmpdir, pamtmpdir);
340 selinux_relabel_path(pamtmpdir, pamtmpdir); 371 fs_logger2("mkdir", pamtmpdir);
341 fs_logger2("mkdir", pamtmpdir);
342 }
343 free(pamtmpdir);
344 } 372 }
373 free(pamtmpdir);
345 } 374 }
375 }
346 376
347 // bring back user home directory if it is masked by the tmpfs 377 // restore user home directory if it is masked by the tmpfs
348 size_t topdir_len = strlen(topdirs[i].path); 378 // creates path owned by root
349 if (strncmp(topdirs[i].path, cfg.homedir, topdir_len) == 0 && cfg.homedir[topdir_len] == '/') { 379 size_t topdir_len = strlen(topdirs[i].path);
350 // get path relative to top level directory 380 if (strncmp(topdirs[i].path, cfg.homedir, topdir_len) == 0 && cfg.homedir[topdir_len] == '/') {
351 const char *rel = cfg.homedir + topdir_len + 1; 381 // get path relative to top level directory
352 whitelist_file(topdirs[i].fd, topdirs[i].path, rel, cfg.homedir); 382 const char *rel = cfg.homedir + topdir_len + 1;
353 } 383 whitelist_file(topdirs[i].fd, topdirs[i].path, rel, cfg.homedir);
354 } 384 }
385
386 selinux_relabel_path(topdirs[i].path, topdirs[i].path);
387 }
388
389 // user home directory
390 if (tmpfs_home)
391 fs_private(); // checks owner if outside /home
392
393 // /run/user/$UID directory
394 if (tmpfs_runuser) {
395 fs_tmpfs(runuser, 0);
396 selinux_relabel_path(runuser, runuser);
355 } 397 }
356} 398}
357 399
@@ -372,10 +414,9 @@ static int reject_topdir(const char *dir) {
372static TopDir *add_topdir(const char *dir, TopDir *topdirs, const char *path) { 414static TopDir *add_topdir(const char *dir, TopDir *topdirs, const char *path) {
373 assert(dir && path); 415 assert(dir && path);
374 416
375 // /proc, /run and /sys are not allowed 417 // /proc and /sys are not allowed
376 if (strcmp(dir, "/") == 0 || 418 if (strcmp(dir, "/") == 0 ||
377 strcmp(dir, "/proc") == 0 || 419 strcmp(dir, "/proc") == 0 ||
378 strcmp(dir, "/run") == 0 ||
379 strcmp(dir, "/sys") == 0) 420 strcmp(dir, "/sys") == 0)
380 whitelist_error(path); 421 whitelist_error(path);
381 422
@@ -448,14 +489,16 @@ static char *extract_topdir(const char *path) {
448 if (!dup) 489 if (!dup)
449 errExit("strdup"); 490 errExit("strdup");
450 491
451 // user home is treated as top level directory 492 // user home directory can be anywhere; disconnect user home
493 // whitelisting from top level directory whitelisting
494 // by treating user home as separate whitelist top level directory
452 if (strncmp(dup, cfg.homedir, homedir_len) == 0 && dup[homedir_len] == '/') 495 if (strncmp(dup, cfg.homedir, homedir_len) == 0 && dup[homedir_len] == '/')
453 dup[homedir_len] = '\0'; 496 dup[homedir_len] = '\0';
454 // whitelisting in /run and /sys is not allowed, 497 // /run/user/$UID is treated as top level directory
455 // but /run/user/$UID and /sys/module are exceptions
456 // and are treated as top level directories here
457 else if (strncmp(dup, runuser, runuser_len) == 0 && dup[runuser_len] == '/') 498 else if (strncmp(dup, runuser, runuser_len) == 0 && dup[runuser_len] == '/')
458 dup[runuser_len] = '\0'; 499 dup[runuser_len] = '\0';
500 // whitelisting in /sys is not allowed, but /sys/module is an exception
501 // and is treated as top level directory here
459 else if (strncmp(dup, "/sys/module", 11) == 0 && dup[11] == '/') 502 else if (strncmp(dup, "/sys/module", 11) == 0 && dup[11] == '/')
460 dup[11] = '\0'; 503 dup[11] = '\0';
461 // treat /usr subdirectories as top level directories 504 // treat /usr subdirectories as top level directories