diff options
author | smitsohu <smitsohu@gmail.com> | 2021-02-12 16:20:43 +0100 |
---|---|---|
committer | smitsohu <smitsohu@gmail.com> | 2021-02-12 16:20:43 +0100 |
commit | 105946495fb744558a4830591b857cdb6fc0111d (patch) | |
tree | 9875700956b790c44ea61545045bd2143cf6a5ff | |
parent | chroot hardening (diff) | |
download | firejail-105946495fb744558a4830591b857cdb6fc0111d.tar.gz firejail-105946495fb744558a4830591b857cdb6fc0111d.tar.zst firejail-105946495fb744558a4830591b857cdb6fc0111d.zip |
remount hardening
-rw-r--r-- | src/firejail/fs.c | 48 |
1 files changed, 26 insertions, 22 deletions
diff --git a/src/firejail/fs.c b/src/firejail/fs.c index b1c509b30..972ee8def 100644 --- a/src/firejail/fs.c +++ b/src/firejail/fs.c | |||
@@ -487,27 +487,26 @@ void fs_tmpfs(const char *dir, unsigned check_owner) { | |||
487 | close(fd); | 487 | close(fd); |
488 | } | 488 | } |
489 | 489 | ||
490 | // remount path, but preserve existing mount flags; requires a resolved path | 490 | // remount path, preserving other mount flags; requires a resolved path |
491 | static void fs_remount_simple(const char *path, OPERATION op) { | 491 | static void fs_remount_simple(const char *path, OPERATION op) { |
492 | struct stat s1, s2; | ||
492 | assert(path); | 493 | assert(path); |
493 | 494 | ||
494 | // open path without following symbolic links | 495 | // open path without following symbolic links |
495 | int fd = safe_fd(path, O_PATH|O_NOFOLLOW|O_CLOEXEC); | 496 | int fd1 = safe_fd(path, O_PATH|O_NOFOLLOW|O_CLOEXEC); |
496 | if (fd == -1) | 497 | if (fd1 == -1) |
497 | goto out; | 498 | goto out; |
498 | // identify file owner | 499 | if (fstat(fd1, &s1) == -1) { |
499 | struct stat s; | ||
500 | if (fstat(fd, &s) == -1) { | ||
501 | // fstat can fail with EACCES if path is a FUSE mount, | 500 | // fstat can fail with EACCES if path is a FUSE mount, |
502 | // mounted without 'allow_root' or 'allow_other' | 501 | // mounted without 'allow_root' or 'allow_other' |
503 | if (errno != EACCES) | 502 | if (errno != EACCES) |
504 | errExit("fstat"); | 503 | errExit("fstat"); |
505 | close(fd); | 504 | close(fd1); |
506 | goto out; | 505 | goto out; |
507 | } | 506 | } |
508 | // get mount flags | 507 | // get mount flags |
509 | struct statvfs buf; | 508 | struct statvfs buf; |
510 | if (fstatvfs(fd, &buf) == -1) | 509 | if (fstatvfs(fd1, &buf) == -1) |
511 | errExit("fstatvfs"); | 510 | errExit("fstatvfs"); |
512 | unsigned long flags = buf.f_flag; | 511 | unsigned long flags = buf.f_flag; |
513 | 512 | ||
@@ -515,13 +514,13 @@ static void fs_remount_simple(const char *path, OPERATION op) { | |||
515 | if (op == MOUNT_RDWR || op == MOUNT_RDWR_NOCHECK) { | 514 | if (op == MOUNT_RDWR || op == MOUNT_RDWR_NOCHECK) { |
516 | // nothing to do if there is no read-only flag | 515 | // nothing to do if there is no read-only flag |
517 | if ((flags & MS_RDONLY) == 0) { | 516 | if ((flags & MS_RDONLY) == 0) { |
518 | close(fd); | 517 | close(fd1); |
519 | return; | 518 | return; |
520 | } | 519 | } |
521 | // allow only user owned directories, except the user is root | 520 | // allow only user owned directories, except the user is root |
522 | if (op == MOUNT_RDWR && getuid() != 0 && s.st_uid != getuid()) { | 521 | if (op != MOUNT_RDWR_NOCHECK && getuid() != 0 && s1.st_uid != getuid()) { |
523 | fwarning("you are not allowed to change %s to read-write\n", path); | 522 | fwarning("you are not allowed to change %s to read-write\n", path); |
524 | close(fd); | 523 | close(fd1); |
525 | return; | 524 | return; |
526 | } | 525 | } |
527 | flags &= ~MS_RDONLY; | 526 | flags &= ~MS_RDONLY; |
@@ -530,7 +529,7 @@ static void fs_remount_simple(const char *path, OPERATION op) { | |||
530 | else if (op == MOUNT_NOEXEC) { | 529 | else if (op == MOUNT_NOEXEC) { |
531 | // nothing to do if path is mounted noexec already | 530 | // nothing to do if path is mounted noexec already |
532 | if ((flags & (MS_NOEXEC|MS_NODEV|MS_NOSUID)) == (MS_NOEXEC|MS_NODEV|MS_NOSUID)) { | 531 | if ((flags & (MS_NOEXEC|MS_NODEV|MS_NOSUID)) == (MS_NOEXEC|MS_NODEV|MS_NOSUID)) { |
533 | close(fd); | 532 | close(fd1); |
534 | return; | 533 | return; |
535 | } | 534 | } |
536 | flags |= MS_NOEXEC|MS_NODEV|MS_NOSUID; | 535 | flags |= MS_NOEXEC|MS_NODEV|MS_NOSUID; |
@@ -539,7 +538,7 @@ static void fs_remount_simple(const char *path, OPERATION op) { | |||
539 | else if (op == MOUNT_READONLY) { | 538 | else if (op == MOUNT_READONLY) { |
540 | // nothing to do if path is mounted read-only already | 539 | // nothing to do if path is mounted read-only already |
541 | if ((flags & MS_RDONLY) == MS_RDONLY) { | 540 | if ((flags & MS_RDONLY) == MS_RDONLY) { |
542 | close(fd); | 541 | close(fd1); |
543 | return; | 542 | return; |
544 | } | 543 | } |
545 | flags |= MS_RDONLY; | 544 | flags |= MS_RDONLY; |
@@ -549,21 +548,25 @@ static void fs_remount_simple(const char *path, OPERATION op) { | |||
549 | 548 | ||
550 | if (arg_debug) | 549 | if (arg_debug) |
551 | printf("Mounting %s %s\n", opstr[op], path); | 550 | printf("Mounting %s %s\n", opstr[op], path); |
552 | // mount --bind /bin /bin | 551 | // mount --bind path path |
553 | char *proc; | 552 | char *proc; |
554 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) | 553 | if (asprintf(&proc, "/proc/self/fd/%d", fd1) == -1) |
555 | errExit("asprintf"); | 554 | errExit("asprintf"); |
556 | if (mount(proc, proc, NULL, MS_BIND|MS_REC, NULL) < 0) | 555 | if (mount(proc, proc, NULL, MS_BIND|MS_REC, NULL) < 0) |
557 | errExit("mount"); | 556 | errExit("mount"); |
558 | free(proc); | 557 | free(proc); |
559 | close(fd); | ||
560 | 558 | ||
561 | // mount --bind -o remount,ro /bin | 559 | // mount --bind -o remount,ro path |
562 | // we need to open path again | 560 | // need to open path again without following symbolic links |
563 | fd = safe_fd(path, O_PATH|O_NOFOLLOW|O_CLOEXEC); | 561 | int fd2 = safe_fd(path, O_PATH|O_NOFOLLOW|O_CLOEXEC); |
564 | if (fd == -1) | 562 | if (fd2 == -1) |
565 | errExit("open"); | 563 | errExit("open"); |
566 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) | 564 | if (fstat(fd2, &s2) == -1) |
565 | errExit("fstat"); | ||
566 | // device and inode number should be the same | ||
567 | if (s1.st_dev != s2.st_dev || s1.st_ino != s2.st_ino) | ||
568 | errLogExit("invalid %s mount", opstr[op]); | ||
569 | if (asprintf(&proc, "/proc/self/fd/%d", fd2) == -1) | ||
567 | errExit("asprintf"); | 570 | errExit("asprintf"); |
568 | if (mount(NULL, proc, NULL, flags|MS_BIND|MS_REMOUNT, NULL) < 0) | 571 | if (mount(NULL, proc, NULL, flags|MS_BIND|MS_REMOUNT, NULL) < 0) |
569 | errExit("mount"); | 572 | errExit("mount"); |
@@ -579,7 +582,8 @@ static void fs_remount_simple(const char *path, OPERATION op) { | |||
579 | errLogExit("invalid %s mount", opstr[op]); | 582 | errLogExit("invalid %s mount", opstr[op]); |
580 | fs_logger2(opstr[op], path); | 583 | fs_logger2(opstr[op], path); |
581 | free(proc); | 584 | free(proc); |
582 | close(fd); | 585 | close(fd1); |
586 | close(fd2); | ||
583 | return; | 587 | return; |
584 | 588 | ||
585 | out: | 589 | out: |