diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/firejail/fs_whitelist.c | 125 |
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 |
310 | static void tmpfs_topdirs(TopDir *topdirs) { | 310 | static 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) { | |||
372 | static TopDir *add_topdir(const char *dir, TopDir *topdirs, const char *path) { | 414 | static 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 |