diff options
-rw-r--r-- | src/firejail/firejail.h | 2 | ||||
-rw-r--r-- | src/firejail/fs.c | 38 | ||||
-rw-r--r-- | src/firejail/mountinfo.c | 71 |
3 files changed, 63 insertions, 48 deletions
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index 2a7d88575..e1ba8f48a 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h | |||
@@ -563,7 +563,7 @@ typedef struct { | |||
563 | 563 | ||
564 | // mountinfo.c | 564 | // mountinfo.c |
565 | MountData *get_last_mount(void); | 565 | MountData *get_last_mount(void); |
566 | int get_mount_id(const char *path); | 566 | int get_mount_id(int fd); |
567 | char **build_mount_array(const int mount_id, const char *path); | 567 | char **build_mount_array(const int mount_id, const char *path); |
568 | 568 | ||
569 | // fs_var.c | 569 | // fs_var.c |
diff --git a/src/firejail/fs.c b/src/firejail/fs.c index 6d01b5e5d..3144156a3 100644 --- a/src/firejail/fs.c +++ b/src/firejail/fs.c | |||
@@ -633,34 +633,30 @@ out: | |||
633 | } | 633 | } |
634 | 634 | ||
635 | // remount recursively; requires a resolved path | 635 | // remount recursively; requires a resolved path |
636 | static void fs_remount_rec(const char *dir, OPERATION op) { | 636 | static void fs_remount_rec(const char *path, OPERATION op) { |
637 | EUID_ASSERT(); | 637 | EUID_ASSERT(); |
638 | assert(dir); | 638 | assert(op < OPERATION_MAX); |
639 | assert(path); | ||
639 | 640 | ||
640 | struct stat s; | 641 | // no need to search /proc/self/mountinfo for submounts if not a directory |
641 | if (stat(dir, &s) != 0) | 642 | int fd = open(path, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); |
642 | return; | 643 | if (fd < 0) { |
643 | if (!S_ISDIR(s.st_mode)) { | 644 | fs_remount_simple(path, op); |
644 | // no need to search in /proc/self/mountinfo for submounts if not a directory | ||
645 | fs_remount_simple(dir, op); | ||
646 | return; | 645 | return; |
647 | } | 646 | } |
648 | // get mount point of the directory | 647 | |
649 | int mountid = get_mount_id(dir); | 648 | // get mount id of the directory |
650 | if (mountid == -1) | 649 | int mountid = get_mount_id(fd); |
651 | return; | 650 | close(fd); |
652 | if (mountid == -2) { | 651 | if (mountid < 0) { |
653 | // falling back to a simple remount on old kernels | 652 | // falling back to a simple remount |
654 | static int mount_warning = 0; | 653 | fwarning("%s %s not applied recursively\n", opstr[op], path); |
655 | if (!mount_warning) { | 654 | fs_remount_simple(path, op); |
656 | fwarning("read-only, read-write and noexec options are not applied recursively\n"); | ||
657 | mount_warning = 1; | ||
658 | } | ||
659 | fs_remount_simple(dir, op); | ||
660 | return; | 655 | return; |
661 | } | 656 | } |
657 | |||
662 | // build array with all mount points that need to get remounted | 658 | // build array with all mount points that need to get remounted |
663 | char **arr = build_mount_array(mountid, dir); | 659 | char **arr = build_mount_array(mountid, path); |
664 | assert(arr); | 660 | assert(arr); |
665 | // remount | 661 | // remount |
666 | char **tmp = arr; | 662 | char **tmp = arr; |
diff --git a/src/firejail/mountinfo.c b/src/firejail/mountinfo.c index 64a94bd84..304f80eee 100644 --- a/src/firejail/mountinfo.c +++ b/src/firejail/mountinfo.c | |||
@@ -19,6 +19,7 @@ | |||
19 | */ | 19 | */ |
20 | 20 | ||
21 | #include "firejail.h" | 21 | #include "firejail.h" |
22 | #include <errno.h> | ||
22 | 23 | ||
23 | #include <fcntl.h> | 24 | #include <fcntl.h> |
24 | #ifndef O_PATH | 25 | #ifndef O_PATH |
@@ -151,53 +152,71 @@ MountData *get_last_mount(void) { | |||
151 | return &mdata; | 152 | return &mdata; |
152 | } | 153 | } |
153 | 154 | ||
154 | // Extract the mount id from /proc/self/fdinfo and return it. | 155 | // Returns mount id, or -1 if fd refers to a procfs or sysfs file |
155 | int get_mount_id(const char *path) { | 156 | static int get_mount_id_from_handle(int fd) { |
156 | EUID_ASSERT(); | 157 | EUID_ASSERT(); |
157 | assert(path); | ||
158 | 158 | ||
159 | int fd = open(path, O_PATH|O_CLOEXEC); | 159 | char *proc; |
160 | if (fd == -1) | 160 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) |
161 | return -1; | 161 | errExit("asprintf"); |
162 | struct file_handle *fh = malloc(sizeof *fh); | ||
163 | if (!fh) | ||
164 | errExit("malloc"); | ||
165 | fh->handle_bytes = 0; | ||
166 | |||
167 | int rv = -1; | ||
168 | int tmp; | ||
169 | if (name_to_handle_at(-1, proc, fh, &tmp, AT_SYMLINK_FOLLOW) != -1) { | ||
170 | fprintf(stderr, "Error: unexpected result from name_to_handle_at\n"); | ||
171 | exit(1); | ||
172 | } | ||
173 | if (errno == EOVERFLOW && fh->handle_bytes) | ||
174 | rv = tmp; | ||
175 | |||
176 | free(proc); | ||
177 | free(fh); | ||
178 | return rv; | ||
179 | } | ||
180 | |||
181 | // Returns mount id, or -1 on kernels < 3.15 | ||
182 | static int get_mount_id_from_fdinfo(int fd) { | ||
183 | EUID_ASSERT(); | ||
184 | int rv = -1; | ||
162 | 185 | ||
163 | char *fdinfo; | 186 | char *proc; |
164 | if (asprintf(&fdinfo, "/proc/self/fdinfo/%d", fd) == -1) | 187 | if (asprintf(&proc, "/proc/self/fdinfo/%d", fd) == -1) |
165 | errExit("asprintf"); | 188 | errExit("asprintf"); |
166 | EUID_ROOT(); | 189 | EUID_ROOT(); |
167 | FILE *fp = fopen(fdinfo, "re"); | 190 | FILE *fp = fopen(proc, "re"); |
168 | EUID_USER(); | 191 | EUID_USER(); |
169 | free(fdinfo); | ||
170 | if (!fp) | 192 | if (!fp) |
171 | goto errexit; | 193 | goto errexit; |
172 | 194 | ||
173 | // read the file | ||
174 | char buf[MAX_BUF]; | 195 | char buf[MAX_BUF]; |
175 | if (fgets(buf, MAX_BUF, fp) == NULL) | 196 | while (fgets(buf, MAX_BUF, fp)) { |
176 | goto errexit; | ||
177 | do { | ||
178 | if (strncmp(buf, "mnt_id:", 7) == 0) { | 197 | if (strncmp(buf, "mnt_id:", 7) == 0) { |
179 | char *ptr = buf + 7; | 198 | if (sscanf(buf + 7, "%d", &rv) != 1) |
180 | while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t')) { | ||
181 | ptr++; | ||
182 | } | ||
183 | if (*ptr == '\0') | ||
184 | goto errexit; | 199 | goto errexit; |
185 | fclose(fp); | 200 | break; |
186 | close(fd); | ||
187 | return atoi(ptr); | ||
188 | } | 201 | } |
189 | } while (fgets(buf, MAX_BUF, fp)); | 202 | } |
190 | 203 | ||
191 | // fallback, kernels older than 3.15 don't expose the mount id in this place | 204 | free(proc); |
192 | fclose(fp); | 205 | fclose(fp); |
193 | close(fd); | 206 | return rv; |
194 | return -2; | ||
195 | 207 | ||
196 | errexit: | 208 | errexit: |
197 | fprintf(stderr, "Error: cannot read proc file\n"); | 209 | fprintf(stderr, "Error: cannot read proc file\n"); |
198 | exit(1); | 210 | exit(1); |
199 | } | 211 | } |
200 | 212 | ||
213 | int get_mount_id(int fd) { | ||
214 | int rv = get_mount_id_from_fdinfo(fd); | ||
215 | if (rv < 0) | ||
216 | rv = get_mount_id_from_handle(fd); | ||
217 | return rv; | ||
218 | } | ||
219 | |||
201 | // Check /proc/self/mountinfo if path contains any mounts points. | 220 | // Check /proc/self/mountinfo if path contains any mounts points. |
202 | // Returns an array that can be iterated over for recursive remounting. | 221 | // Returns an array that can be iterated over for recursive remounting. |
203 | char **build_mount_array(const int mount_id, const char *path) { | 222 | char **build_mount_array(const int mount_id, const char *path) { |