diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/firejail/firejail.h | 2 | ||||
-rw-r--r-- | src/firejail/fs.c | 108 | ||||
-rw-r--r-- | src/man/firejail.txt | 2 |
3 files changed, 56 insertions, 56 deletions
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index c66904c1b..a4aa20667 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h | |||
@@ -439,6 +439,8 @@ void preproc_clean_run(void); | |||
439 | // fs.c | 439 | // fs.c |
440 | // blacklist files or directories by mounting empty files on top of them | 440 | // blacklist files or directories by mounting empty files on top of them |
441 | void fs_blacklist(void); | 441 | void fs_blacklist(void); |
442 | // mount a writable tmpfs | ||
443 | void fs_tmpfs(const char *dir, unsigned check_owner); | ||
442 | // remount a directory read-only | 444 | // remount a directory read-only |
443 | void fs_rdonly(const char *dir); | 445 | void fs_rdonly(const char *dir); |
444 | void fs_rdonly_rec(const char *dir); | 446 | void fs_rdonly_rec(const char *dir); |
diff --git a/src/firejail/fs.c b/src/firejail/fs.c index ff920b913..49074f525 100644 --- a/src/firejail/fs.c +++ b/src/firejail/fs.c | |||
@@ -162,18 +162,8 @@ static void disable_file(OPERATION op, const char *filename) { | |||
162 | } | 162 | } |
163 | else if (op == MOUNT_TMPFS) { | 163 | else if (op == MOUNT_TMPFS) { |
164 | if (S_ISDIR(s.st_mode)) { | 164 | if (S_ISDIR(s.st_mode)) { |
165 | if (arg_debug) | 165 | fs_tmpfs(fname, 0); |
166 | printf("Mounting tmpfs on %s\n", fname); | ||
167 | // preserve owner and mode for the directory | ||
168 | if (mount("tmpfs", fname, "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, 0) < 0) | ||
169 | errExit("mounting tmpfs"); | ||
170 | /* coverity[toctou] */ | ||
171 | if (chown(fname, s.st_uid, s.st_gid) == -1) | ||
172 | errExit("mounting tmpfs chown"); | ||
173 | if (chmod(fname, s.st_mode) == -1) | ||
174 | errExit("mounting tmpfs chmod"); | ||
175 | last_disable = SUCCESSFUL; | 166 | last_disable = SUCCESSFUL; |
176 | fs_logger2("tmpfs", fname); | ||
177 | } | 167 | } |
178 | else | 168 | else |
179 | fwarning("%s is not a directory; cannot mount a tmpfs on top of it.\n", fname); | 169 | fwarning("%s is not a directory; cannot mount a tmpfs on top of it.\n", fname); |
@@ -456,6 +446,49 @@ static int get_mount_flags(const char *path, unsigned long *flags) { | |||
456 | // - functions need fully resolved paths | 446 | // - functions need fully resolved paths |
457 | //*********************************************** | 447 | //*********************************************** |
458 | 448 | ||
449 | // mount a writable tmpfs on directory | ||
450 | void fs_tmpfs(const char *dir, unsigned check_owner) { | ||
451 | assert(dir); | ||
452 | // get a file descriptor for dir, fails if there is any symlink | ||
453 | int fd = safe_fd(dir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | ||
454 | if (fd == -1) | ||
455 | errExit("safe_fd"); | ||
456 | struct stat s; | ||
457 | if (fstat(fd, &s) == -1) | ||
458 | errExit("fstat"); | ||
459 | if (check_owner && s.st_uid != getuid()) { | ||
460 | fwarning("no tmpfs mounted on %s: not owned by the current user\n", dir); | ||
461 | close(fd); | ||
462 | return; | ||
463 | } | ||
464 | if (arg_debug) | ||
465 | printf("Mounting tmpfs on %s\n", dir); | ||
466 | // preserve ownership, mode | ||
467 | char *options; | ||
468 | if (asprintf(&options, "mode=%o,uid=%u,gid=%u", s.st_mode & 07777, s.st_uid, s.st_gid) == -1) | ||
469 | errExit("asprintf"); | ||
470 | // preserve some mount flags | ||
471 | struct statvfs buf; | ||
472 | if (fstatvfs(fd, &buf) == -1) | ||
473 | errExit("fstatvfs"); | ||
474 | unsigned long flags = buf.f_flag & // remove read-only flag | ||
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 | ||
477 | char *proc; | ||
478 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) | ||
479 | errExit("asprintf"); | ||
480 | if (mount("tmpfs", proc, "tmpfs", flags|MS_NOSUID|MS_NODEV, options) < 0) | ||
481 | errExit("mounting tmpfs"); | ||
482 | // check the last mount operation | ||
483 | MountData *mdata = get_last_mount(); | ||
484 | if (strcmp(mdata->fstype, "tmpfs") != 0 || strcmp(mdata->dir, dir) != 0) | ||
485 | errLogExit("invalid tmpfs mount"); | ||
486 | fs_logger2("tmpfs", dir); | ||
487 | free(options); | ||
488 | free(proc); | ||
489 | close(fd); | ||
490 | } | ||
491 | |||
459 | // remount directory read-only | 492 | // remount directory read-only |
460 | void fs_rdonly(const char *dir) { | 493 | void fs_rdonly(const char *dir) { |
461 | assert(dir); | 494 | assert(dir); |
@@ -1584,8 +1617,6 @@ void fs_private_tmp(void) { | |||
1584 | } | 1617 | } |
1585 | } | 1618 | } |
1586 | closedir(dir); | 1619 | closedir(dir); |
1587 | |||
1588 | |||
1589 | } | 1620 | } |
1590 | 1621 | ||
1591 | // this function is called from sandbox.c before blacklist/whitelist functions | 1622 | // this function is called from sandbox.c before blacklist/whitelist functions |
@@ -1595,53 +1626,20 @@ void fs_private_cache(void) { | |||
1595 | errExit("asprintf"); | 1626 | errExit("asprintf"); |
1596 | // check if ~/.cache is a valid destination | 1627 | // check if ~/.cache is a valid destination |
1597 | struct stat s; | 1628 | struct stat s; |
1598 | if (is_link(cache)) { | 1629 | if (lstat(cache, &s) == -1) { |
1599 | fwarning("user .cache is a symbolic link, tmpfs not mounted\n"); | 1630 | fwarning("cannot find %s, tmpfs not mounted\n", cache); |
1600 | free(cache); | ||
1601 | return; | ||
1602 | } | ||
1603 | if (stat(cache, &s) == -1 || !S_ISDIR(s.st_mode)) { | ||
1604 | fwarning("no user .cache directory found, tmpfs not mounted\n"); | ||
1605 | free(cache); | 1631 | free(cache); |
1606 | return; | 1632 | return; |
1607 | } | 1633 | } |
1608 | if (s.st_uid != getuid()) { | 1634 | if (!S_ISDIR(s.st_mode)) { |
1609 | fwarning("user .cache is not owned by current user, tmpfs not mounted\n"); | 1635 | if (S_ISLNK(s.st_mode)) |
1636 | fwarning("%s is a symbolic link, tmpfs not mounted\n", cache); | ||
1637 | else | ||
1638 | fwarning("%s is not a directory; cannot mount a tmpfs on top of it\n", cache); | ||
1610 | free(cache); | 1639 | free(cache); |
1611 | return; | 1640 | return; |
1612 | } | 1641 | } |
1613 | 1642 | // do the mount | |
1614 | if (arg_debug) | 1643 | fs_tmpfs(cache, getuid()); // check ownership of ~/.cache |
1615 | printf("Mounting tmpfs on %s\n", cache); | ||
1616 | // get a file descriptor for ~/.cache, fails if there is any symlink | ||
1617 | int fd = safe_fd(cache, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | ||
1618 | if (fd == -1) | ||
1619 | errExit("safe_fd"); | ||
1620 | // confirm that actual mount destination is owned by the user | ||
1621 | if (fstat(fd, &s) == -1 || s.st_uid != getuid()) | ||
1622 | errExit("fstat"); | ||
1623 | |||
1624 | // mount a tmpfs on ~/.cache via the symbolic link in /proc/self/fd | ||
1625 | char *proc; | ||
1626 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) | ||
1627 | errExit("asprintf"); | ||
1628 | if (mount("tmpfs", proc, "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, 0) < 0) | ||
1629 | errExit("mounting tmpfs"); | ||
1630 | fs_logger2("tmpfs", cache); | ||
1631 | free(proc); | ||
1632 | close(fd); | ||
1633 | // check the last mount operation | ||
1634 | MountData *mdata = get_last_mount(); | ||
1635 | assert(mdata); | ||
1636 | if (strcmp(mdata->fstype, "tmpfs") != 0 || strcmp(mdata->dir, cache) != 0) | ||
1637 | errLogExit("invalid .cache mount"); | ||
1638 | |||
1639 | // get a new file descriptor for ~/.cache, the old directory is masked by the tmpfs | ||
1640 | fd = safe_fd(cache, O_RDONLY|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | ||
1641 | if (fd == -1) | ||
1642 | errExit("safe_fd"); | ||
1643 | free(cache); | 1644 | free(cache); |
1644 | // restore permissions | ||
1645 | SET_PERMS_FD(fd, s.st_uid, s.st_gid, s.st_mode); | ||
1646 | close(fd); | ||
1647 | } | 1645 | } |
diff --git a/src/man/firejail.txt b/src/man/firejail.txt index 42495f52c..66663be35 100644 --- a/src/man/firejail.txt +++ b/src/man/firejail.txt | |||
@@ -2058,7 +2058,7 @@ Kill the sandbox automatically after the time has elapsed. The time is specified | |||
2058 | $ firejail \-\-timeout=01:30:00 firefox | 2058 | $ firejail \-\-timeout=01:30:00 firefox |
2059 | .TP | 2059 | .TP |
2060 | \fB\-\-tmpfs=dirname | 2060 | \fB\-\-tmpfs=dirname |
2061 | Mount a tmpfs filesystem on directory dirname. This option is available only when running the sandbox as root. | 2061 | Mount a writable tmpfs filesystem on directory dirname. This option is available only when running the sandbox as root. |
2062 | File globbing is supported, see \fBFILE GLOBBING\fR section for more details. | 2062 | File globbing is supported, see \fBFILE GLOBBING\fR section for more details. |
2063 | .br | 2063 | .br |
2064 | 2064 | ||