diff options
author | smitsohu <smitsohu@gmail.com> | 2018-11-04 19:03:49 +0100 |
---|---|---|
committer | smitsohu <smitsohu@gmail.com> | 2018-11-04 19:03:49 +0100 |
commit | 35d2aec567c0e9c9b5c79678b1dc762916d5492c (patch) | |
tree | 40335d4a7320d830cc5f1717fb8afd6e95532330 | |
parent | mount appimages nodev,nosuid (diff) | |
download | firejail-35d2aec567c0e9c9b5c79678b1dc762916d5492c.tar.gz firejail-35d2aec567c0e9c9b5c79678b1dc762916d5492c.tar.zst firejail-35d2aec567c0e9c9b5c79678b1dc762916d5492c.zip |
recursive remounts: add fallback for old kernels, some improvements
* vanilla kernels before 3.15 don't expose a mount id in /proc/pid/fdinfo files.
This is still relevant on Ubuntu 14.04 with 3.13 kernel, CentOS 7 doesn't
have this problem. In this case fall back to simple a remount and print
a warning.
* drop euid switching as it doesn't really serve a purpose here (paths are not opened
in reading or writing mode, and we are not doing anything with it) and potentially
causes problems when suid programs are sandboxed
* more rigorous error handling
-rw-r--r-- | src/firejail/fs.c | 41 | ||||
-rw-r--r-- | src/firejail/mountinfo.c | 44 |
2 files changed, 57 insertions, 28 deletions
diff --git a/src/firejail/fs.c b/src/firejail/fs.c index eda46d127..38af1fccf 100644 --- a/src/firejail/fs.c +++ b/src/firejail/fs.c | |||
@@ -34,7 +34,7 @@ | |||
34 | //#define TEST_NO_BLACKLIST_MATCHING | 34 | //#define TEST_NO_BLACKLIST_MATCHING |
35 | 35 | ||
36 | 36 | ||
37 | 37 | static int mount_warning = 0; // remember if warning was printed already | |
38 | static void fs_rdwr(const char *dir); | 38 | static void fs_rdwr(const char *dir); |
39 | static void fs_rdwr_rec(const char *dir); | 39 | static void fs_rdwr_rec(const char *dir); |
40 | 40 | ||
@@ -482,18 +482,23 @@ void fs_rdonly(const char *dir) { | |||
482 | // remount directory read-only recursively | 482 | // remount directory read-only recursively |
483 | void fs_rdonly_rec(const char *dir) { | 483 | void fs_rdonly_rec(const char *dir) { |
484 | assert(dir); | 484 | assert(dir); |
485 | EUID_USER(); | ||
486 | // get mount point of the directory | 485 | // get mount point of the directory |
487 | int mountid = get_mount_id(dir); | 486 | int mountid = get_mount_id(dir); |
488 | if (mountid == -1) { | 487 | if (mountid == -1) |
489 | EUID_ROOT(); | 488 | return; |
489 | if (mountid == -2) { | ||
490 | // falling back to a simple remount on old kernels | ||
491 | if (!mount_warning) { | ||
492 | fwarning("read-only, read-write and noexec options are not applied recursively\n"); | ||
493 | mount_warning = 1; | ||
494 | } | ||
495 | fs_rdonly(dir); | ||
490 | return; | 496 | return; |
491 | } | 497 | } |
492 | // build array with all mount points that need to get remounted | 498 | // build array with all mount points that need to get remounted |
493 | char **arr = build_mount_array(mountid, dir); | 499 | char **arr = build_mount_array(mountid, dir); |
494 | assert(arr); | 500 | assert(arr); |
495 | // remount | 501 | // remount |
496 | EUID_ROOT(); | ||
497 | char **tmp = arr; | 502 | char **tmp = arr; |
498 | while (*tmp) { | 503 | while (*tmp) { |
499 | fs_rdonly(*tmp); | 504 | fs_rdonly(*tmp); |
@@ -540,18 +545,23 @@ static void fs_rdwr(const char *dir) { | |||
540 | // remount directory read-write recursively | 545 | // remount directory read-write recursively |
541 | static void fs_rdwr_rec(const char *dir) { | 546 | static void fs_rdwr_rec(const char *dir) { |
542 | assert(dir); | 547 | assert(dir); |
543 | EUID_USER(); | ||
544 | // get mount point of the directory | 548 | // get mount point of the directory |
545 | int mountid = get_mount_id(dir); | 549 | int mountid = get_mount_id(dir); |
546 | if (mountid == -1) { | 550 | if (mountid == -1) |
547 | EUID_ROOT(); | 551 | return; |
552 | if (mountid == -2) { | ||
553 | // falling back to a simple remount on old kernels | ||
554 | if (!mount_warning) { | ||
555 | fwarning("read-only, read-write and noexec options are not applied recursively\n"); | ||
556 | mount_warning = 1; | ||
557 | } | ||
558 | fs_rdwr(dir); | ||
548 | return; | 559 | return; |
549 | } | 560 | } |
550 | // build array with all mount points that need to get remounted | 561 | // build array with all mount points that need to get remounted |
551 | char **arr = build_mount_array(mountid, dir); | 562 | char **arr = build_mount_array(mountid, dir); |
552 | assert(arr); | 563 | assert(arr); |
553 | // remount | 564 | // remount |
554 | EUID_ROOT(); | ||
555 | char **tmp = arr; | 565 | char **tmp = arr; |
556 | while (*tmp) { | 566 | while (*tmp) { |
557 | fs_rdwr(*tmp); | 567 | fs_rdwr(*tmp); |
@@ -586,18 +596,23 @@ void fs_noexec(const char *dir) { | |||
586 | // remount directory noexec, nodev, nosuid recursively | 596 | // remount directory noexec, nodev, nosuid recursively |
587 | void fs_noexec_rec(const char *dir) { | 597 | void fs_noexec_rec(const char *dir) { |
588 | assert(dir); | 598 | assert(dir); |
589 | EUID_USER(); | ||
590 | // get mount point of the directory | 599 | // get mount point of the directory |
591 | int mountid = get_mount_id(dir); | 600 | int mountid = get_mount_id(dir); |
592 | if (mountid == -1) { | 601 | if (mountid == -1) |
593 | EUID_ROOT(); | 602 | return; |
603 | if (mountid == -2) { | ||
604 | // falling back to a simple remount on old kernels | ||
605 | if (!mount_warning) { | ||
606 | fwarning("read-only, read-write and noexec options are not applied recursively\n"); | ||
607 | mount_warning = 1; | ||
608 | } | ||
609 | fs_noexec(dir); | ||
594 | return; | 610 | return; |
595 | } | 611 | } |
596 | // build array with all mount points that need to get remounted | 612 | // build array with all mount points that need to get remounted |
597 | char **arr = build_mount_array(mountid, dir); | 613 | char **arr = build_mount_array(mountid, dir); |
598 | assert(arr); | 614 | assert(arr); |
599 | // remount | 615 | // remount |
600 | EUID_ROOT(); | ||
601 | char **tmp = arr; | 616 | char **tmp = arr; |
602 | while (*tmp) { | 617 | while (*tmp) { |
603 | fs_noexec(*tmp); | 618 | fs_noexec(*tmp); |
diff --git a/src/firejail/mountinfo.c b/src/firejail/mountinfo.c index b7e6c6fdd..ab1e501a7 100644 --- a/src/firejail/mountinfo.c +++ b/src/firejail/mountinfo.c | |||
@@ -153,7 +153,6 @@ MountData *get_last_mount(void) { | |||
153 | 153 | ||
154 | // Extract the mount id from /proc/self/fdinfo and return it. | 154 | // Extract the mount id from /proc/self/fdinfo and return it. |
155 | int get_mount_id(const char *path) { | 155 | int get_mount_id(const char *path) { |
156 | EUID_ASSERT(); | ||
157 | int fd = open(path, O_PATH|O_CLOEXEC); | 156 | int fd = open(path, O_PATH|O_CLOEXEC); |
158 | if (fd == -1) | 157 | if (fd == -1) |
159 | return -1; | 158 | return -1; |
@@ -161,32 +160,41 @@ int get_mount_id(const char *path) { | |||
161 | char *fdinfo; | 160 | char *fdinfo; |
162 | if (asprintf(&fdinfo, "/proc/self/fdinfo/%d", fd) == -1) | 161 | if (asprintf(&fdinfo, "/proc/self/fdinfo/%d", fd) == -1) |
163 | errExit("asprintf"); | 162 | errExit("asprintf"); |
164 | EUID_ROOT(); | ||
165 | FILE *fp = fopen(fdinfo, "re"); | 163 | FILE *fp = fopen(fdinfo, "re"); |
166 | EUID_USER(); | 164 | if (!fp) { |
167 | if (!fp) | 165 | perror("fopen"); |
168 | goto errexit; | 166 | fprintf(stderr, "Error: cannot open %s\n", fdinfo); |
167 | exit(1); | ||
168 | } | ||
169 | 169 | ||
170 | // read the file | 170 | // read the file |
171 | char buf[MAX_BUF]; | 171 | char buf[MAX_BUF]; |
172 | while (fgets(buf, MAX_BUF, fp)) { | 172 | if (fgets(buf, MAX_BUF, fp) == NULL) { |
173 | fprintf(stderr, "Error: cannot read %s\n", fdinfo); | ||
174 | exit(1); | ||
175 | } | ||
176 | do { | ||
173 | if (strncmp(buf, "mnt_id:", 7) == 0) { | 177 | if (strncmp(buf, "mnt_id:", 7) == 0) { |
174 | char *ptr = buf + 7; | 178 | char *ptr = buf + 7; |
175 | while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t')) { | 179 | while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t')) { |
176 | ptr++; | 180 | ptr++; |
177 | } | 181 | } |
178 | if (*ptr == '\0') | 182 | if (*ptr == '\0') { |
179 | goto errexit; | 183 | fprintf(stderr, "Error: cannot read %s\n", fdinfo); |
184 | exit(1); | ||
185 | } | ||
180 | fclose(fp); | 186 | fclose(fp); |
181 | close(fd); | 187 | close(fd); |
182 | free(fdinfo); | 188 | free(fdinfo); |
183 | return atoi(ptr); | 189 | return atoi(ptr); |
184 | } | 190 | } |
185 | } | 191 | } while (fgets(buf, MAX_BUF, fp)); |
186 | 192 | ||
187 | errexit: | 193 | // fallback, kernels older than 3.15 don't expose the mount id in this place |
188 | fprintf(stderr, "Error: cannot read %s\n", fdinfo); | 194 | fclose(fp); |
189 | exit(1); | 195 | close(fd); |
196 | free(fdinfo); | ||
197 | return -2; | ||
190 | } | 198 | } |
191 | 199 | ||
192 | // Return array with all paths that might need a remount. | 200 | // Return array with all paths that might need a remount. |
@@ -208,7 +216,11 @@ char **build_mount_array(const int mountid, const char *path) { | |||
208 | // read /proc/self/mountinfo | 216 | // read /proc/self/mountinfo |
209 | size_t pathlen = strlen(path); | 217 | size_t pathlen = strlen(path); |
210 | int found = 0; | 218 | int found = 0; |
211 | while (fgets(mbuf, MAX_BUF, fp)) { | 219 | if (fgets(mbuf, MAX_BUF, fp) == NULL) { |
220 | fprintf(stderr, "Error: cannot read /proc/self/mountinfo\n"); | ||
221 | exit(1); | ||
222 | } | ||
223 | do { | ||
212 | // find mount point with mount id | 224 | // find mount point with mount id |
213 | if (!found) { | 225 | if (!found) { |
214 | parse_line(mbuf, &mdata); | 226 | parse_line(mbuf, &mdata); |
@@ -230,7 +242,8 @@ char **build_mount_array(const int mountid, const char *path) { | |||
230 | else | 242 | else |
231 | continue; | 243 | continue; |
232 | } | 244 | } |
233 | // from here on add all mount points below path | 245 | // from here on add all mount points below path, |
246 | // don't remount blacklisted paths | ||
234 | parse_line(mbuf, &mdata); | 247 | parse_line(mbuf, &mdata); |
235 | if (strncmp(mdata.dir, path, pathlen) == 0 && | 248 | if (strncmp(mdata.dir, path, pathlen) == 0 && |
236 | mdata.dir[pathlen] == '/' && | 249 | mdata.dir[pathlen] == '/' && |
@@ -248,7 +261,8 @@ char **build_mount_array(const int mountid, const char *path) { | |||
248 | errExit("strdup"); | 261 | errExit("strdup"); |
249 | cnt++; | 262 | cnt++; |
250 | } | 263 | } |
251 | } | 264 | } while (fgets(mbuf, MAX_BUF, fp)); |
265 | |||
252 | if (cnt == size) { | 266 | if (cnt == size) { |
253 | size++; | 267 | size++; |
254 | rv = realloc(rv, size * sizeof(*rv)); | 268 | rv = realloc(rv, size * sizeof(*rv)); |