diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/firejail/chroot.c | 40 | ||||
-rw-r--r-- | src/firejail/dbus.c | 14 | ||||
-rw-r--r-- | src/firejail/firejail.h | 4 | ||||
-rw-r--r-- | src/firejail/fs.c | 163 | ||||
-rw-r--r-- | src/firejail/fs_home.c | 21 | ||||
-rw-r--r-- | src/firejail/fs_lib.c | 6 | ||||
-rw-r--r-- | src/firejail/fs_trace.c | 6 | ||||
-rw-r--r-- | src/firejail/fs_whitelist.c | 18 | ||||
-rw-r--r-- | src/firejail/pulseaudio.c | 6 | ||||
-rw-r--r-- | src/firejail/restrict_users.c | 12 | ||||
-rw-r--r-- | src/firejail/util.c | 138 | ||||
-rw-r--r-- | src/firejail/x11.c | 25 |
12 files changed, 218 insertions, 235 deletions
diff --git a/src/firejail/chroot.c b/src/firejail/chroot.c index 757ffb1f7..4125a4130 100644 --- a/src/firejail/chroot.c +++ b/src/firejail/chroot.c | |||
@@ -163,12 +163,8 @@ void fs_chroot(const char *rootdir) { | |||
163 | int fd = openat(parentfd, "dev", O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | 163 | int fd = openat(parentfd, "dev", O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); |
164 | if (fd == -1) | 164 | if (fd == -1) |
165 | errExit("open"); | 165 | errExit("open"); |
166 | char *proc; | 166 | if (bind_mount_path_to_fd("/dev", fd)) |
167 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) | ||
168 | errExit("asprintf"); | ||
169 | if (mount("/dev", proc, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
170 | errExit("mounting /dev"); | 167 | errExit("mounting /dev"); |
171 | free(proc); | ||
172 | close(fd); | 168 | close(fd); |
173 | 169 | ||
174 | #ifdef HAVE_X11 | 170 | #ifdef HAVE_X11 |
@@ -192,11 +188,8 @@ void fs_chroot(const char *rootdir) { | |||
192 | fd = openat(parentfd, "tmp/.X11-unix", O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | 188 | fd = openat(parentfd, "tmp/.X11-unix", O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); |
193 | if (fd == -1) | 189 | if (fd == -1) |
194 | errExit("open"); | 190 | errExit("open"); |
195 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) | 191 | if (bind_mount_path_to_fd("/tmp/.X11-unix", fd)) |
196 | errExit("asprintf"); | ||
197 | if (mount("/tmp/.X11-unix", proc, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
198 | errExit("mounting /tmp/.X11-unix"); | 192 | errExit("mounting /tmp/.X11-unix"); |
199 | free(proc); | ||
200 | close(fd); | 193 | close(fd); |
201 | } | 194 | } |
202 | #endif // HAVE_X11 | 195 | #endif // HAVE_X11 |
@@ -225,19 +218,11 @@ void fs_chroot(const char *rootdir) { | |||
225 | fprintf(stderr, "Error: cannot open %s\n", pulse); | 218 | fprintf(stderr, "Error: cannot open %s\n", pulse); |
226 | exit(1); | 219 | exit(1); |
227 | } | 220 | } |
228 | free(pulse); | 221 | if (bind_mount_by_fd(src, dst)) |
229 | 222 | errExit("mounting pulseaudio") | |
230 | char *proc_src, *proc_dst; | ||
231 | if (asprintf(&proc_src, "/proc/self/fd/%d", src) == -1) | ||
232 | errExit("asprintf"); | ||
233 | if (asprintf(&proc_dst, "/proc/self/fd/%d", dst) == -1) | ||
234 | errExit("asprintf"); | ||
235 | if (mount(proc_src, proc_dst, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
236 | errExit("mount bind"); | ||
237 | free(proc_src); | ||
238 | free(proc_dst); | ||
239 | close(src); | 223 | close(src); |
240 | close(dst); | 224 | close(dst); |
225 | free(pulse); | ||
241 | 226 | ||
242 | // update /etc/machine-id in chroot | 227 | // update /etc/machine-id in chroot |
243 | update_file(parentfd, "etc/machine-id"); | 228 | update_file(parentfd, "etc/machine-id"); |
@@ -256,11 +241,8 @@ void fs_chroot(const char *rootdir) { | |||
256 | fd = openat(parentfd, &RUN_FIREJAIL_LIB_DIR[1], O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | 241 | fd = openat(parentfd, &RUN_FIREJAIL_LIB_DIR[1], O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); |
257 | if (fd == -1) | 242 | if (fd == -1) |
258 | errExit("open"); | 243 | errExit("open"); |
259 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) | 244 | if (bind_mount_path_to_fd(RUN_FIREJAIL_LIB_DIR, fd)) |
260 | errExit("asprintf"); | ||
261 | if (mount(RUN_FIREJAIL_LIB_DIR, proc, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
262 | errExit("mount bind"); | 245 | errExit("mount bind"); |
263 | free(proc); | ||
264 | close(fd); | 246 | close(fd); |
265 | 247 | ||
266 | // create /run/firejail/mnt directory in chroot | 248 | // create /run/firejail/mnt directory in chroot |
@@ -271,11 +253,8 @@ void fs_chroot(const char *rootdir) { | |||
271 | fd = openat(parentfd, &RUN_MNT_DIR[1], O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | 253 | fd = openat(parentfd, &RUN_MNT_DIR[1], O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); |
272 | if (fd == -1) | 254 | if (fd == -1) |
273 | errExit("open"); | 255 | errExit("open"); |
274 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) | 256 | if (bind_mount_path_to_fd(RUN_MNT_DIR, fd)) |
275 | errExit("asprintf"); | ||
276 | if (mount(RUN_MNT_DIR, proc, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
277 | errExit("mount bind"); | 257 | errExit("mount bind"); |
278 | free(proc); | ||
279 | close(fd); | 258 | close(fd); |
280 | 259 | ||
281 | // update chroot resolv.conf | 260 | // update chroot resolv.conf |
@@ -289,11 +268,8 @@ void fs_chroot(const char *rootdir) { | |||
289 | if (mkdir(oroot, 0755) == -1) | 268 | if (mkdir(oroot, 0755) == -1) |
290 | errExit("mkdir"); | 269 | errExit("mkdir"); |
291 | // mount the chroot dir on top of /run/firejail/mnt/oroot in order to reuse the apparmor rules for overlay | 270 | // mount the chroot dir on top of /run/firejail/mnt/oroot in order to reuse the apparmor rules for overlay |
292 | if (asprintf(&proc, "/proc/self/fd/%d", parentfd) == -1) | 271 | if (bind_mount_fd_to_path(parentfd, oroot)) |
293 | errExit("asprintf"); | ||
294 | if (mount(proc, oroot, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
295 | errExit("mounting rootdir oroot"); | 272 | errExit("mounting rootdir oroot"); |
296 | free(proc); | ||
297 | close(parentfd); | 273 | close(parentfd); |
298 | // chroot into the new directory | 274 | // chroot into the new directory |
299 | if (arg_debug) | 275 | if (arg_debug) |
diff --git a/src/firejail/dbus.c b/src/firejail/dbus.c index b8aa2c974..bfa28fcba 100644 --- a/src/firejail/dbus.c +++ b/src/firejail/dbus.c | |||
@@ -258,12 +258,8 @@ static char *find_user_socket_by_format(char *format) { | |||
258 | if (asprintf(&dbus_user_socket, format, (int) getuid()) == -1) | 258 | if (asprintf(&dbus_user_socket, format, (int) getuid()) == -1) |
259 | errExit("asprintf"); | 259 | errExit("asprintf"); |
260 | struct stat s; | 260 | struct stat s; |
261 | if (stat(dbus_user_socket, &s) == -1) { | 261 | if (lstat(dbus_user_socket, &s) == -1) |
262 | if (errno == ENOENT) | 262 | goto fail; |
263 | goto fail; | ||
264 | return NULL; | ||
265 | errExit("stat"); | ||
266 | } | ||
267 | if (!S_ISSOCK(s.st_mode)) | 263 | if (!S_ISSOCK(s.st_mode)) |
268 | goto fail; | 264 | goto fail; |
269 | return dbus_user_socket; | 265 | return dbus_user_socket; |
@@ -426,12 +422,8 @@ static void socket_overlay(char *socket_path, char *proxy_path) { | |||
426 | errno = ENOTSOCK; | 422 | errno = ENOTSOCK; |
427 | errExit("mounting DBus proxy socket"); | 423 | errExit("mounting DBus proxy socket"); |
428 | } | 424 | } |
429 | char *proxy_fd_path; | 425 | if (bind_mount_fd_to_path(fd, socket_path)) |
430 | if (asprintf(&proxy_fd_path, "/proc/self/fd/%d", fd) == -1) | ||
431 | errExit("asprintf"); | ||
432 | if (mount(proxy_path, socket_path, NULL, MS_BIND | MS_REC, NULL) == -1) | ||
433 | errExit("mount bind"); | 426 | errExit("mount bind"); |
434 | free(proxy_fd_path); | ||
435 | close(fd); | 427 | close(fd); |
436 | } | 428 | } |
437 | 429 | ||
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index a5c44739e..ab6e44a8a 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h | |||
@@ -526,6 +526,10 @@ unsigned extract_timeout(const char *str); | |||
526 | void disable_file_or_dir(const char *fname); | 526 | void disable_file_or_dir(const char *fname); |
527 | void disable_file_path(const char *path, const char *file); | 527 | void disable_file_path(const char *path, const char *file); |
528 | int safer_openat(int dirfd, const char *path, int flags); | 528 | int safer_openat(int dirfd, const char *path, int flags); |
529 | int remount_by_fd(int dst, unsigned long mountflags); | ||
530 | int bind_mount_by_fd(int src, int dst); | ||
531 | int bind_mount_path_to_fd(const char *srcname, int dst); | ||
532 | int bind_mount_fd_to_path(int src, const char *destname); | ||
529 | int has_handler(pid_t pid, int signal); | 533 | int has_handler(pid_t pid, int signal); |
530 | void enter_network_namespace(pid_t pid); | 534 | void enter_network_namespace(pid_t pid); |
531 | int read_pid(const char *name, pid_t *pid); | 535 | int read_pid(const char *name, pid_t *pid); |
diff --git a/src/firejail/fs.c b/src/firejail/fs.c index 09de11de9..eaf23e603 100644 --- a/src/firejail/fs.c +++ b/src/firejail/fs.c | |||
@@ -54,16 +54,9 @@ static char *opstr[] = { | |||
54 | [MOUNT_RDWR_NOCHECK] = "read-write", | 54 | [MOUNT_RDWR_NOCHECK] = "read-write", |
55 | }; | 55 | }; |
56 | 56 | ||
57 | typedef enum { | ||
58 | UNSUCCESSFUL, | ||
59 | SUCCESSFUL | ||
60 | } LAST_DISABLE_OPERATION; | ||
61 | LAST_DISABLE_OPERATION last_disable = UNSUCCESSFUL; | ||
62 | |||
63 | static void disable_file(OPERATION op, const char *filename) { | 57 | static void disable_file(OPERATION op, const char *filename) { |
64 | assert(filename); | 58 | assert(filename); |
65 | assert(op <OPERATION_MAX); | 59 | assert(op <OPERATION_MAX); |
66 | last_disable = UNSUCCESSFUL; | ||
67 | 60 | ||
68 | // Resolve all symlinks | 61 | // Resolve all symlinks |
69 | char* fname = realpath(filename, NULL); | 62 | char* fname = realpath(filename, NULL); |
@@ -71,20 +64,22 @@ static void disable_file(OPERATION op, const char *filename) { | |||
71 | return; | 64 | return; |
72 | } | 65 | } |
73 | if (fname == NULL && errno == EACCES) { | 66 | if (fname == NULL && errno == EACCES) { |
74 | if (arg_debug) | ||
75 | printf("Debug: no access to file %s, forcing mount\n", filename); | ||
76 | // realpath and stat functions will fail on FUSE filesystems | 67 | // realpath and stat functions will fail on FUSE filesystems |
77 | // they don't seem to like a uid of 0 | 68 | // they don't seem to like a uid of 0 |
78 | // force mounting | 69 | // force mounting |
79 | int rv = mount(RUN_RO_DIR, filename, "none", MS_BIND, "mode=400,gid=0"); | 70 | int fd = open(filename, O_PATH|O_CLOEXEC); |
80 | if (rv == 0) | 71 | if (fd < 0) { |
81 | last_disable = SUCCESSFUL; | 72 | if (arg_debug) |
82 | else { | 73 | printf("Warning (blacklisting): cannot open %s: %s\n", filename, strerror(errno)); |
83 | rv = mount(RUN_RO_FILE, filename, "none", MS_BIND, "mode=400,gid=0"); | 74 | return; |
84 | if (rv == 0) | ||
85 | last_disable = SUCCESSFUL; | ||
86 | } | 75 | } |
87 | if (last_disable == SUCCESSFUL) { | 76 | |
77 | int err = bind_mount_path_to_fd(RUN_RO_DIR, fd); | ||
78 | if (err < 0) | ||
79 | err = bind_mount_path_to_fd(RUN_RO_FILE, fd); | ||
80 | close(fd); | ||
81 | |||
82 | if (err == 0) { | ||
88 | if (arg_debug) | 83 | if (arg_debug) |
89 | printf("Disable %s\n", filename); | 84 | printf("Disable %s\n", filename); |
90 | if (op == BLACKLIST_FILE) | 85 | if (op == BLACKLIST_FILE) |
@@ -92,21 +87,18 @@ static void disable_file(OPERATION op, const char *filename) { | |||
92 | else | 87 | else |
93 | fs_logger2("blacklist-nolog", filename); | 88 | fs_logger2("blacklist-nolog", filename); |
94 | } | 89 | } |
95 | else { | 90 | else if (arg_debug) |
96 | if (arg_debug) | 91 | printf("Warning (blacklisting): cannot mount on %s\n", filename); |
97 | printf("Warning (blacklisting): %s is an invalid file, skipping...\n", filename); | ||
98 | } | ||
99 | 92 | ||
100 | return; | 93 | return; |
101 | } | 94 | } |
102 | 95 | ||
103 | // if the file is not present, do nothing | 96 | // if the file is not present, do nothing |
97 | assert(fname); | ||
104 | struct stat s; | 98 | struct stat s; |
105 | if (fname == NULL) | 99 | if (stat(fname, &s) < 0) { |
106 | return; | ||
107 | if (stat(fname, &s) == -1) { | ||
108 | if (arg_debug) | 100 | if (arg_debug) |
109 | fwarning("%s does not exist, skipping...\n", fname); | 101 | printf("Warning (blacklisting): cannot access %s: %s\n", fname, strerror(errno)); |
110 | free(fname); | 102 | free(fname); |
111 | return; | 103 | return; |
112 | } | 104 | } |
@@ -115,8 +107,10 @@ static void disable_file(OPERATION op, const char *filename) { | |||
115 | // we migth have a file found in ${PATH} pointing to /usr/bin/firejail | 107 | // we migth have a file found in ${PATH} pointing to /usr/bin/firejail |
116 | // blacklisting it here will end up breaking situations like user clicks on a link in Thunderbird | 108 | // blacklisting it here will end up breaking situations like user clicks on a link in Thunderbird |
117 | // and expects Firefox to open in the same sandbox | 109 | // and expects Firefox to open in the same sandbox |
118 | if (strcmp(BINDIR "/firejail", fname) == 0) | 110 | if (strcmp(BINDIR "/firejail", fname) == 0) { |
111 | free(fname); | ||
119 | return; | 112 | return; |
113 | } | ||
120 | 114 | ||
121 | // modify the file | 115 | // modify the file |
122 | if (op == BLACKLIST_FILE || op == BLACKLIST_NOLOG) { | 116 | if (op == BLACKLIST_FILE || op == BLACKLIST_NOLOG) { |
@@ -141,15 +135,23 @@ static void disable_file(OPERATION op, const char *filename) { | |||
141 | printf(" - no logging\n"); | 135 | printf(" - no logging\n"); |
142 | } | 136 | } |
143 | 137 | ||
138 | int fd = open(fname, O_PATH|O_CLOEXEC); | ||
139 | if (fd < 0) { | ||
140 | if (arg_debug) | ||
141 | printf("Warning (blacklisting): cannot open %s: %s\n", fname, strerror(errno)); | ||
142 | free(fname); | ||
143 | return; | ||
144 | } | ||
144 | if (S_ISDIR(s.st_mode)) { | 145 | if (S_ISDIR(s.st_mode)) { |
145 | if (mount(RUN_RO_DIR, fname, "none", MS_BIND, "mode=400,gid=0") < 0) | 146 | if (bind_mount_path_to_fd(RUN_RO_DIR, fd) < 0) |
146 | errExit("disable file"); | 147 | errExit("disable file"); |
147 | } | 148 | } |
148 | else { | 149 | else { |
149 | if (mount(RUN_RO_FILE, fname, "none", MS_BIND, "mode=400,gid=0") < 0) | 150 | if (bind_mount_path_to_fd(RUN_RO_FILE, fd) < 0) |
150 | errExit("disable file"); | 151 | errExit("disable file"); |
151 | } | 152 | } |
152 | last_disable = SUCCESSFUL; | 153 | close(fd); |
154 | |||
153 | if (op == BLACKLIST_FILE) | 155 | if (op == BLACKLIST_FILE) |
154 | fs_logger2("blacklist", fname); | 156 | fs_logger2("blacklist", fname); |
155 | else | 157 | else |
@@ -158,7 +160,6 @@ static void disable_file(OPERATION op, const char *filename) { | |||
158 | } | 160 | } |
159 | else if (op == MOUNT_READONLY || op == MOUNT_RDWR || op == MOUNT_NOEXEC) { | 161 | else if (op == MOUNT_READONLY || op == MOUNT_RDWR || op == MOUNT_NOEXEC) { |
160 | fs_remount_rec(fname, op); | 162 | fs_remount_rec(fname, op); |
161 | // todo: last_disable = SUCCESSFUL; | ||
162 | } | 163 | } |
163 | else if (op == MOUNT_TMPFS) { | 164 | else if (op == MOUNT_TMPFS) { |
164 | if (S_ISDIR(s.st_mode)) { | 165 | if (S_ISDIR(s.st_mode)) { |
@@ -171,7 +172,6 @@ static void disable_file(OPERATION op, const char *filename) { | |||
171 | } | 172 | } |
172 | fs_tmpfs(fname, getuid()); | 173 | fs_tmpfs(fname, getuid()); |
173 | selinux_relabel_path(fname, fname); | 174 | selinux_relabel_path(fname, fname); |
174 | last_disable = SUCCESSFUL; | ||
175 | } | 175 | } |
176 | else | 176 | else |
177 | fwarning("%s is not a directory; cannot mount a tmpfs on top of it.\n", fname); | 177 | fwarning("%s is not a directory; cannot mount a tmpfs on top of it.\n", fname); |
@@ -493,35 +493,38 @@ static void fs_remount_simple(const char *path, OPERATION op) { | |||
493 | assert(path); | 493 | assert(path); |
494 | 494 | ||
495 | // open path without following symbolic links | 495 | // open path without following symbolic links |
496 | int fd1 = safer_openat(-1, path, O_PATH|O_NOFOLLOW|O_CLOEXEC); | 496 | int fd = safer_openat(-1, path, O_PATH|O_NOFOLLOW|O_CLOEXEC); |
497 | if (fd1 == -1) | 497 | if (fd < 0) |
498 | goto out; | 498 | goto out; |
499 | struct stat s1; | 499 | |
500 | if (fstat(fd1, &s1) == -1) { | 500 | struct stat s; |
501 | if (fstat(fd, &s) < 0) { | ||
501 | // fstat can fail with EACCES if path is a FUSE mount, | 502 | // fstat can fail with EACCES if path is a FUSE mount, |
502 | // mounted without 'allow_root' or 'allow_other' | 503 | // mounted without 'allow_root' or 'allow_other' |
503 | if (errno != EACCES) | 504 | if (errno != EACCES) |
504 | errExit("fstat"); | 505 | errExit("fstat"); |
505 | close(fd1); | 506 | close(fd); |
506 | goto out; | 507 | goto out; |
507 | } | 508 | } |
508 | // get mount flags | 509 | // get mount flags |
509 | struct statvfs buf; | 510 | struct statvfs buf; |
510 | if (fstatvfs(fd1, &buf) == -1) | 511 | if (fstatvfs(fd, &buf) < 0) { |
511 | errExit("fstatvfs"); | 512 | close(fd); |
513 | goto out; | ||
514 | } | ||
512 | unsigned long flags = buf.f_flag; | 515 | unsigned long flags = buf.f_flag; |
513 | 516 | ||
514 | // read-write option | 517 | // read-write option |
515 | if (op == MOUNT_RDWR || op == MOUNT_RDWR_NOCHECK) { | 518 | if (op == MOUNT_RDWR || op == MOUNT_RDWR_NOCHECK) { |
516 | // nothing to do if there is no read-only flag | 519 | // nothing to do if there is no read-only flag |
517 | if ((flags & MS_RDONLY) == 0) { | 520 | if ((flags & MS_RDONLY) == 0) { |
518 | close(fd1); | 521 | close(fd); |
519 | return; | 522 | return; |
520 | } | 523 | } |
521 | // allow only user owned directories, except the user is root | 524 | // allow only user owned directories, except the user is root |
522 | if (op != MOUNT_RDWR_NOCHECK && getuid() != 0 && s1.st_uid != getuid()) { | 525 | if (op != MOUNT_RDWR_NOCHECK && getuid() != 0 && s.st_uid != getuid()) { |
523 | fwarning("you are not allowed to change %s to read-write\n", path); | 526 | fwarning("you are not allowed to change %s to read-write\n", path); |
524 | close(fd1); | 527 | close(fd); |
525 | return; | 528 | return; |
526 | } | 529 | } |
527 | flags &= ~MS_RDONLY; | 530 | flags &= ~MS_RDONLY; |
@@ -530,7 +533,7 @@ static void fs_remount_simple(const char *path, OPERATION op) { | |||
530 | else if (op == MOUNT_NOEXEC) { | 533 | else if (op == MOUNT_NOEXEC) { |
531 | // nothing to do if path is mounted noexec already | 534 | // nothing to do if path is mounted noexec already |
532 | if ((flags & (MS_NOEXEC|MS_NODEV|MS_NOSUID)) == (MS_NOEXEC|MS_NODEV|MS_NOSUID)) { | 535 | if ((flags & (MS_NOEXEC|MS_NODEV|MS_NOSUID)) == (MS_NOEXEC|MS_NODEV|MS_NOSUID)) { |
533 | close(fd1); | 536 | close(fd); |
534 | return; | 537 | return; |
535 | } | 538 | } |
536 | flags |= MS_NOEXEC|MS_NODEV|MS_NOSUID; | 539 | flags |= MS_NOEXEC|MS_NODEV|MS_NOSUID; |
@@ -539,7 +542,7 @@ static void fs_remount_simple(const char *path, OPERATION op) { | |||
539 | else if (op == MOUNT_READONLY) { | 542 | else if (op == MOUNT_READONLY) { |
540 | // nothing to do if path is mounted read-only already | 543 | // nothing to do if path is mounted read-only already |
541 | if ((flags & MS_RDONLY) == MS_RDONLY) { | 544 | if ((flags & MS_RDONLY) == MS_RDONLY) { |
542 | close(fd1); | 545 | close(fd); |
543 | return; | 546 | return; |
544 | } | 547 | } |
545 | flags |= MS_RDONLY; | 548 | flags |= MS_RDONLY; |
@@ -549,29 +552,33 @@ static void fs_remount_simple(const char *path, OPERATION op) { | |||
549 | 552 | ||
550 | if (arg_debug) | 553 | if (arg_debug) |
551 | printf("Mounting %s %s\n", opstr[op], path); | 554 | printf("Mounting %s %s\n", opstr[op], path); |
555 | |||
556 | // make path a mount point: | ||
552 | // mount --bind path path | 557 | // mount --bind path path |
553 | char *proc; | 558 | int err = bind_mount_by_fd(fd, fd); |
554 | if (asprintf(&proc, "/proc/self/fd/%d", fd1) == -1) | 559 | if (err) { |
555 | errExit("asprintf"); | 560 | close(fd); |
556 | if (mount(proc, proc, NULL, MS_BIND|MS_REC, NULL) < 0) | 561 | goto out; |
557 | errExit("mount"); | 562 | } |
558 | free(proc); | ||
559 | 563 | ||
560 | // mount --bind -o remount,ro path | 564 | // remount the mount point |
561 | // need to open path again without following symbolic links | 565 | // need to open path again |
562 | int fd2 = safer_openat(-1, path, O_PATH|O_NOFOLLOW|O_CLOEXEC); | 566 | int fd2 = safer_openat(-1, path, O_PATH|O_NOFOLLOW|O_CLOEXEC); |
563 | if (fd2 == -1) | 567 | close(fd); // earliest timepoint to close fd |
564 | errExit("open"); | 568 | if (fd2 < 0) |
569 | goto out; | ||
570 | |||
571 | // device and inode number should be the same | ||
565 | struct stat s2; | 572 | struct stat s2; |
566 | if (fstat(fd2, &s2) == -1) | 573 | if (fstat(fd2, &s2) < 0) |
567 | errExit("fstat"); | 574 | errExit("fstat"); |
568 | // device and inode number should be the same | 575 | if (s.st_dev != s2.st_dev || s.st_ino != s2.st_ino) |
569 | if (s1.st_dev != s2.st_dev || s1.st_ino != s2.st_ino) | ||
570 | errLogExit("invalid %s mount", opstr[op]); | 576 | errLogExit("invalid %s mount", opstr[op]); |
571 | if (asprintf(&proc, "/proc/self/fd/%d", fd2) == -1) | 577 | |
572 | errExit("asprintf"); | 578 | err = remount_by_fd(fd2, flags); |
573 | if (mount(NULL, proc, NULL, flags|MS_BIND|MS_REMOUNT, NULL) < 0) | 579 | close(fd2); |
574 | errExit("mount"); | 580 | if (err) |
581 | goto out; | ||
575 | 582 | ||
576 | // run a sanity check on /proc/self/mountinfo and confirm that target of the last | 583 | // run a sanity check on /proc/self/mountinfo and confirm that target of the last |
577 | // mount operation was path; if there are other mount points contained inside path, | 584 | // mount operation was path; if there are other mount points contained inside path, |
@@ -582,10 +589,8 @@ static void fs_remount_simple(const char *path, OPERATION op) { | |||
582 | (*(mptr->dir + len) != '\0' && *(mptr->dir + len) != '/')) | 589 | (*(mptr->dir + len) != '\0' && *(mptr->dir + len) != '/')) |
583 | && strcmp(path, "/") != 0) // support read-only=/ | 590 | && strcmp(path, "/") != 0) // support read-only=/ |
584 | errLogExit("invalid %s mount", opstr[op]); | 591 | errLogExit("invalid %s mount", opstr[op]); |
592 | |||
585 | fs_logger2(opstr[op], path); | 593 | fs_logger2(opstr[op], path); |
586 | free(proc); | ||
587 | close(fd1); | ||
588 | close(fd2); | ||
589 | return; | 594 | return; |
590 | 595 | ||
591 | out: | 596 | out: |
@@ -743,8 +748,6 @@ void fs_proc_sys_dev_boot(void) { | |||
743 | 748 | ||
744 | // disable various ipc sockets in /run/user | 749 | // disable various ipc sockets in /run/user |
745 | if (!arg_writable_run_user) { | 750 | if (!arg_writable_run_user) { |
746 | struct stat s; | ||
747 | |||
748 | char *fname; | 751 | char *fname; |
749 | if (asprintf(&fname, "/run/user/%d", getuid()) == -1) | 752 | if (asprintf(&fname, "/run/user/%d", getuid()) == -1) |
750 | errExit("asprintf"); | 753 | errExit("asprintf"); |
@@ -755,8 +758,7 @@ void fs_proc_sys_dev_boot(void) { | |||
755 | errExit("asprintf"); | 758 | errExit("asprintf"); |
756 | if (create_empty_dir_as_user(fnamegpg, 0700)) | 759 | if (create_empty_dir_as_user(fnamegpg, 0700)) |
757 | fs_logger2("create", fnamegpg); | 760 | fs_logger2("create", fnamegpg); |
758 | if (stat(fnamegpg, &s) == 0) | 761 | disable_file(BLACKLIST_FILE, fnamegpg); |
759 | disable_file(BLACKLIST_FILE, fnamegpg); | ||
760 | free(fnamegpg); | 762 | free(fnamegpg); |
761 | 763 | ||
762 | // disable /run/user/{uid}/systemd | 764 | // disable /run/user/{uid}/systemd |
@@ -765,8 +767,7 @@ void fs_proc_sys_dev_boot(void) { | |||
765 | errExit("asprintf"); | 767 | errExit("asprintf"); |
766 | if (create_empty_dir_as_user(fnamesysd, 0755)) | 768 | if (create_empty_dir_as_user(fnamesysd, 0755)) |
767 | fs_logger2("create", fnamesysd); | 769 | fs_logger2("create", fnamesysd); |
768 | if (stat(fnamesysd, &s) == 0) | 770 | disable_file(BLACKLIST_FILE, fnamesysd); |
769 | disable_file(BLACKLIST_FILE, fnamesysd); | ||
770 | free(fnamesysd); | 771 | free(fnamesysd); |
771 | } | 772 | } |
772 | free(fname); | 773 | free(fname); |
@@ -781,26 +782,18 @@ void fs_proc_sys_dev_boot(void) { | |||
781 | 782 | ||
782 | // disable firejail configuration in ~/.config/firejail | 783 | // disable firejail configuration in ~/.config/firejail |
783 | void disable_config(void) { | 784 | void disable_config(void) { |
784 | struct stat s; | ||
785 | |||
786 | char *fname; | 785 | char *fname; |
787 | if (asprintf(&fname, "%s/.config/firejail", cfg.homedir) == -1) | 786 | if (asprintf(&fname, "%s/.config/firejail", cfg.homedir) == -1) |
788 | errExit("asprintf"); | 787 | errExit("asprintf"); |
789 | if (stat(fname, &s) == 0) | 788 | disable_file(BLACKLIST_FILE, fname); |
790 | disable_file(BLACKLIST_FILE, fname); | ||
791 | free(fname); | 789 | free(fname); |
792 | 790 | ||
793 | // disable run time information | 791 | // disable run time information |
794 | if (stat(RUN_FIREJAIL_NETWORK_DIR, &s) == 0) | 792 | disable_file(BLACKLIST_FILE, RUN_FIREJAIL_NETWORK_DIR); |
795 | disable_file(BLACKLIST_FILE, RUN_FIREJAIL_NETWORK_DIR); | 793 | disable_file(BLACKLIST_FILE, RUN_FIREJAIL_BANDWIDTH_DIR); |
796 | if (stat(RUN_FIREJAIL_BANDWIDTH_DIR, &s) == 0) | 794 | disable_file(BLACKLIST_FILE, RUN_FIREJAIL_NAME_DIR); |
797 | disable_file(BLACKLIST_FILE, RUN_FIREJAIL_BANDWIDTH_DIR); | 795 | disable_file(BLACKLIST_FILE, RUN_FIREJAIL_PROFILE_DIR); |
798 | if (stat(RUN_FIREJAIL_NAME_DIR, &s) == 0) | 796 | disable_file(BLACKLIST_FILE, RUN_FIREJAIL_X11_DIR); |
799 | disable_file(BLACKLIST_FILE, RUN_FIREJAIL_NAME_DIR); | ||
800 | if (stat(RUN_FIREJAIL_PROFILE_DIR, &s) == 0) | ||
801 | disable_file(BLACKLIST_FILE, RUN_FIREJAIL_PROFILE_DIR); | ||
802 | if (stat(RUN_FIREJAIL_X11_DIR, &s) == 0) | ||
803 | disable_file(BLACKLIST_FILE, RUN_FIREJAIL_X11_DIR); | ||
804 | } | 797 | } |
805 | 798 | ||
806 | 799 | ||
@@ -1241,8 +1234,8 @@ void fs_private_tmp(void) { | |||
1241 | 1234 | ||
1242 | // whitelist x11 directory | 1235 | // whitelist x11 directory |
1243 | profile_add("whitelist /tmp/.X11-unix"); | 1236 | profile_add("whitelist /tmp/.X11-unix"); |
1244 | // read-only x11 directory | 1237 | // read-only x11 directory |
1245 | profile_add("read-only /tmp/.X11-unix"); | 1238 | profile_add("read-only /tmp/.X11-unix"); |
1246 | 1239 | ||
1247 | // whitelist any pulse* file in /tmp directory | 1240 | // whitelist any pulse* file in /tmp directory |
1248 | // some distros use PulseAudio sockets under /tmp instead of the socket in /urn/user | 1241 | // some distros use PulseAudio sockets under /tmp instead of the socket in /urn/user |
diff --git a/src/firejail/fs_home.c b/src/firejail/fs_home.c index f61d43c29..ec6dab947 100644 --- a/src/firejail/fs_home.c +++ b/src/firejail/fs_home.c | |||
@@ -287,17 +287,9 @@ void fs_private_homedir(void) { | |||
287 | exit(1); | 287 | exit(1); |
288 | } | 288 | } |
289 | // mount via the links in /proc/self/fd | 289 | // mount via the links in /proc/self/fd |
290 | char *proc_src, *proc_dst; | 290 | if (bind_mount_by_fd(src, dst)) |
291 | if (asprintf(&proc_src, "/proc/self/fd/%d", src) == -1) | ||
292 | errExit("asprintf"); | ||
293 | if (asprintf(&proc_dst, "/proc/self/fd/%d", dst) == -1) | ||
294 | errExit("asprintf"); | ||
295 | if (mount(proc_src, proc_dst, NULL, MS_NOSUID | MS_NODEV | MS_BIND | MS_REC, NULL) < 0) | ||
296 | errExit("mount bind"); | 291 | errExit("mount bind"); |
297 | free(proc_src); | 292 | |
298 | free(proc_dst); | ||
299 | close(src); | ||
300 | close(dst); | ||
301 | // check /proc/self/mountinfo to confirm the mount is ok | 293 | // check /proc/self/mountinfo to confirm the mount is ok |
302 | MountData *mptr = get_last_mount(); | 294 | MountData *mptr = get_last_mount(); |
303 | size_t len = strlen(homedir); | 295 | size_t len = strlen(homedir); |
@@ -305,6 +297,8 @@ void fs_private_homedir(void) { | |||
305 | (*(mptr->dir + len) != '\0' && *(mptr->dir + len) != '/')) | 297 | (*(mptr->dir + len) != '\0' && *(mptr->dir + len) != '/')) |
306 | errLogExit("invalid private mount"); | 298 | errLogExit("invalid private mount"); |
307 | 299 | ||
300 | close(src); | ||
301 | close(dst); | ||
308 | fs_logger3("mount-bind", private_homedir, homedir); | 302 | fs_logger3("mount-bind", private_homedir, homedir); |
309 | fs_logger2("whitelist", homedir); | 303 | fs_logger2("whitelist", homedir); |
310 | // preserve mode and ownership | 304 | // preserve mode and ownership |
@@ -590,13 +584,10 @@ void fs_private_home_list(void) { | |||
590 | exit(1); | 584 | exit(1); |
591 | } | 585 | } |
592 | // mount using the file descriptor | 586 | // mount using the file descriptor |
593 | char *proc; | 587 | if (bind_mount_path_to_fd(RUN_HOME_DIR, fd)) |
594 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) | ||
595 | errExit("asprintf"); | ||
596 | if (mount(RUN_HOME_DIR, proc, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
597 | errExit("mount bind"); | 588 | errExit("mount bind"); |
598 | free(proc); | ||
599 | close(fd); | 589 | close(fd); |
590 | |||
600 | // check /proc/self/mountinfo to confirm the mount is ok | 591 | // check /proc/self/mountinfo to confirm the mount is ok |
601 | MountData *mptr = get_last_mount(); | 592 | MountData *mptr = get_last_mount(); |
602 | if (strcmp(mptr->dir, homedir) != 0 || strcmp(mptr->fstype, "tmpfs") != 0) | 593 | if (strcmp(mptr->dir, homedir) != 0 || strcmp(mptr->fstype, "tmpfs") != 0) |
diff --git a/src/firejail/fs_lib.c b/src/firejail/fs_lib.c index 5df356d04..8369e6259 100644 --- a/src/firejail/fs_lib.c +++ b/src/firejail/fs_lib.c | |||
@@ -203,7 +203,7 @@ void fslib_mount_libs(const char *full_path, unsigned user) { | |||
203 | } | 203 | } |
204 | 204 | ||
205 | if (arg_debug || arg_debug_private_lib) | 205 | if (arg_debug || arg_debug_private_lib) |
206 | printf(" fslib_mount_libs %s (parse as %s)\n", full_path, user ? "user" : "root"); | 206 | printf(" fslib_mount_libs %s\n", full_path); |
207 | // create an empty RUN_LIB_FILE and allow the user to write to it | 207 | // create an empty RUN_LIB_FILE and allow the user to write to it |
208 | unlink(RUN_LIB_FILE); // in case is there | 208 | unlink(RUN_LIB_FILE); // in case is there |
209 | create_empty_file_as_root(RUN_LIB_FILE, 0644); | 209 | create_empty_file_as_root(RUN_LIB_FILE, 0644); |
@@ -212,7 +212,7 @@ void fslib_mount_libs(const char *full_path, unsigned user) { | |||
212 | 212 | ||
213 | // run fldd to extract the list of files | 213 | // run fldd to extract the list of files |
214 | if (arg_debug || arg_debug_private_lib) | 214 | if (arg_debug || arg_debug_private_lib) |
215 | printf(" running fldd %s\n", full_path); | 215 | printf(" running fldd %s as %s\n", full_path, user ? "user" : "root"); |
216 | unsigned mask; | 216 | unsigned mask; |
217 | if (user) | 217 | if (user) |
218 | mask = SBOX_USER; | 218 | mask = SBOX_USER; |
@@ -298,7 +298,7 @@ static void install_list_entry(const char *lib) { | |||
298 | //printf("glob %s\n", globbuf.gl_pathv[j]); | 298 | //printf("glob %s\n", globbuf.gl_pathv[j]); |
299 | // GLOB_NOCHECK - no pattern matched returns the original pattern; try to load it anyway | 299 | // GLOB_NOCHECK - no pattern matched returns the original pattern; try to load it anyway |
300 | 300 | ||
301 | // foobar/* includes foobar/. and foobar/.. | 301 | // foobar/* expands to foobar/. and foobar/.. |
302 | const char *base = gnu_basename(globbuf.gl_pathv[j]); | 302 | const char *base = gnu_basename(globbuf.gl_pathv[j]); |
303 | if (strcmp(base, ".") == 0 || strcmp(base, "..") == 0) | 303 | if (strcmp(base, ".") == 0 || strcmp(base, "..") == 0) |
304 | continue; | 304 | continue; |
diff --git a/src/firejail/fs_trace.c b/src/firejail/fs_trace.c index 1fc38361e..475a391ec 100644 --- a/src/firejail/fs_trace.c +++ b/src/firejail/fs_trace.c | |||
@@ -71,12 +71,8 @@ void fs_tracefile(void) { | |||
71 | // mount using the symbolic link in /proc/self/fd | 71 | // mount using the symbolic link in /proc/self/fd |
72 | if (arg_debug) | 72 | if (arg_debug) |
73 | printf("Bind mount %s to %s\n", arg_tracefile, RUN_TRACE_FILE); | 73 | printf("Bind mount %s to %s\n", arg_tracefile, RUN_TRACE_FILE); |
74 | char *proc; | 74 | if (bind_mount_fd_to_path(fd, RUN_TRACE_FILE)) |
75 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) | ||
76 | errExit("asprintf"); | ||
77 | if (mount(proc, RUN_TRACE_FILE, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
78 | errExit("mount bind " RUN_TRACE_FILE); | 75 | errExit("mount bind " RUN_TRACE_FILE); |
79 | free(proc); | ||
80 | close(fd); | 76 | close(fd); |
81 | // now that RUN_TRACE_FILE is user-writable, mount it noexec | 77 | // now that RUN_TRACE_FILE is user-writable, mount it noexec |
82 | fs_remount(RUN_TRACE_FILE, MOUNT_NOEXEC, 0); | 78 | fs_remount(RUN_TRACE_FILE, MOUNT_NOEXEC, 0); |
diff --git a/src/firejail/fs_whitelist.c b/src/firejail/fs_whitelist.c index 258f023f6..ec3fe4c5b 100644 --- a/src/firejail/fs_whitelist.c +++ b/src/firejail/fs_whitelist.c | |||
@@ -195,15 +195,7 @@ static void whitelist_file(int dirfd, const char *relpath, const char *path) { | |||
195 | 195 | ||
196 | if (arg_debug || arg_debug_whitelists) | 196 | if (arg_debug || arg_debug_whitelists) |
197 | printf("Whitelisting %s\n", path); | 197 | printf("Whitelisting %s\n", path); |
198 | 198 | if (bind_mount_by_fd(fd, fd3)) | |
199 | // in order to make this mount resilient against symlink attacks, use | ||
200 | // magic links in /proc/self/fd instead of mounting the paths directly | ||
201 | char *proc_src, *proc_dst; | ||
202 | if (asprintf(&proc_src, "/proc/self/fd/%d", fd) == -1) | ||
203 | errExit("asprintf"); | ||
204 | if (asprintf(&proc_dst, "/proc/self/fd/%d", fd3) == -1) | ||
205 | errExit("asprintf"); | ||
206 | if (mount(proc_src, proc_dst, NULL, MS_BIND | MS_REC, NULL) < 0) | ||
207 | errExit("mount bind"); | 199 | errExit("mount bind"); |
208 | // check the last mount operation | 200 | // check the last mount operation |
209 | MountData *mptr = get_last_mount(); // will do exit(1) if the mount cannot be found | 201 | MountData *mptr = get_last_mount(); // will do exit(1) if the mount cannot be found |
@@ -221,8 +213,6 @@ static void whitelist_file(int dirfd, const char *relpath, const char *path) { | |||
221 | // - there should be more than one '/' char in dest string | 213 | // - there should be more than one '/' char in dest string |
222 | if (mptr->dir == strrchr(mptr->dir, '/')) | 214 | if (mptr->dir == strrchr(mptr->dir, '/')) |
223 | errLogExit("invalid whitelist mount"); | 215 | errLogExit("invalid whitelist mount"); |
224 | free(proc_src); | ||
225 | free(proc_dst); | ||
226 | close(fd); | 216 | close(fd); |
227 | close(fd3); | 217 | close(fd3); |
228 | fs_logger2("whitelist", path); | 218 | fs_logger2("whitelist", path); |
@@ -341,12 +331,8 @@ static void tmpfs_topdirs(const TopDir *topdirs) { | |||
341 | // restore /run/firejail directory | 331 | // restore /run/firejail directory |
342 | if (mkdir(RUN_FIREJAIL_DIR, 0755) == -1) | 332 | if (mkdir(RUN_FIREJAIL_DIR, 0755) == -1) |
343 | errExit("mkdir"); | 333 | errExit("mkdir"); |
344 | char *proc; | 334 | if (bind_mount_fd_to_path(fd, RUN_FIREJAIL_DIR)) |
345 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) | ||
346 | errExit("asprintf"); | ||
347 | if (mount(proc, RUN_FIREJAIL_DIR, NULL, MS_BIND | MS_REC, NULL) < 0) | ||
348 | errExit("mount bind"); | 335 | errExit("mount bind"); |
349 | free(proc); | ||
350 | close(fd); | 336 | close(fd); |
351 | fs_logger2("whitelist", RUN_FIREJAIL_DIR); | 337 | fs_logger2("whitelist", RUN_FIREJAIL_DIR); |
352 | 338 | ||
diff --git a/src/firejail/pulseaudio.c b/src/firejail/pulseaudio.c index 1b01a71c6..be0f5aea6 100644 --- a/src/firejail/pulseaudio.c +++ b/src/firejail/pulseaudio.c | |||
@@ -158,17 +158,13 @@ void pulseaudio_init(void) { | |||
158 | // mount via the link in /proc/self/fd | 158 | // mount via the link in /proc/self/fd |
159 | if (arg_debug) | 159 | if (arg_debug) |
160 | printf("Mounting %s on %s\n", RUN_PULSE_DIR, homeusercfg); | 160 | printf("Mounting %s on %s\n", RUN_PULSE_DIR, homeusercfg); |
161 | char *proc; | 161 | if (bind_mount_path_to_fd(RUN_PULSE_DIR, fd)) |
162 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) | ||
163 | errExit("asprintf"); | ||
164 | if (mount(RUN_PULSE_DIR, proc, "none", MS_BIND, NULL) < 0) | ||
165 | errExit("mount pulseaudio"); | 162 | errExit("mount pulseaudio"); |
166 | // check /proc/self/mountinfo to confirm the mount is ok | 163 | // check /proc/self/mountinfo to confirm the mount is ok |
167 | MountData *mptr = get_last_mount(); | 164 | MountData *mptr = get_last_mount(); |
168 | if (strcmp(mptr->dir, homeusercfg) != 0 || strcmp(mptr->fstype, "tmpfs") != 0) | 165 | if (strcmp(mptr->dir, homeusercfg) != 0 || strcmp(mptr->fstype, "tmpfs") != 0) |
169 | errLogExit("invalid pulseaudio mount"); | 166 | errLogExit("invalid pulseaudio mount"); |
170 | fs_logger2("tmpfs", homeusercfg); | 167 | fs_logger2("tmpfs", homeusercfg); |
171 | free(proc); | ||
172 | close(fd); | 168 | close(fd); |
173 | 169 | ||
174 | char *p; | 170 | char *p; |
diff --git a/src/firejail/restrict_users.c b/src/firejail/restrict_users.c index 892244b5f..6f17231a4 100644 --- a/src/firejail/restrict_users.c +++ b/src/firejail/restrict_users.c | |||
@@ -104,12 +104,8 @@ static void sanitize_home(void) { | |||
104 | selinux_relabel_path(cfg.homedir, cfg.homedir); | 104 | selinux_relabel_path(cfg.homedir, cfg.homedir); |
105 | 105 | ||
106 | // bring back real user home directory | 106 | // bring back real user home directory |
107 | char *proc; | 107 | if (bind_mount_fd_to_path(fd, cfg.homedir)) |
108 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) | ||
109 | errExit("asprintf"); | ||
110 | if (mount(proc, cfg.homedir, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
111 | errExit("mount bind"); | 108 | errExit("mount bind"); |
112 | free(proc); | ||
113 | close(fd); | 109 | close(fd); |
114 | 110 | ||
115 | if (!arg_private) | 111 | if (!arg_private) |
@@ -154,12 +150,8 @@ static void sanitize_run(void) { | |||
154 | selinux_relabel_path(runuser, runuser); | 150 | selinux_relabel_path(runuser, runuser); |
155 | 151 | ||
156 | // bring back real run/user/$UID directory | 152 | // bring back real run/user/$UID directory |
157 | char *proc; | 153 | if (bind_mount_fd_to_path(fd, runuser)) |
158 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) | ||
159 | errExit("asprintf"); | ||
160 | if (mount(proc, runuser, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
161 | errExit("mount bind"); | 154 | errExit("mount bind"); |
162 | free(proc); | ||
163 | close(fd); | 155 | close(fd); |
164 | 156 | ||
165 | fs_logger2("whitelist", runuser); | 157 | fs_logger2("whitelist", runuser); |
diff --git a/src/firejail/util.c b/src/firejail/util.c index dc9fe2449..44f1cbd02 100644 --- a/src/firejail/util.c +++ b/src/firejail/util.c | |||
@@ -394,11 +394,11 @@ void touch_file_as_user(const char *fname, mode_t mode) { | |||
394 | // drop privileges | 394 | // drop privileges |
395 | drop_privs(0); | 395 | drop_privs(0); |
396 | 396 | ||
397 | FILE *fp = fopen(fname, "wx"); | 397 | int fd = open(fname, O_RDONLY|O_CREAT|O_EXCL|O_CLOEXEC, S_IRUSR | S_IWUSR); |
398 | if (fp) { | 398 | if (fd > -1) { |
399 | fprintf(fp, "\n"); | 399 | int err = fchmod(fd, mode); |
400 | SET_PERMS_STREAM(fp, -1, -1, mode); | 400 | (void) err; |
401 | fclose(fp); | 401 | close(fd); |
402 | } | 402 | } |
403 | else | 403 | else |
404 | fwarning("cannot create %s\n", fname); | 404 | fwarning("cannot create %s\n", fname); |
@@ -899,27 +899,29 @@ int remove_overlay_directory(void) { | |||
899 | errExit("asprintf"); | 899 | errExit("asprintf"); |
900 | 900 | ||
901 | if (lstat(path, &s) == 0) { | 901 | if (lstat(path, &s) == 0) { |
902 | // deal with obvious problems such as symlinks and root ownership | ||
903 | if (!S_ISDIR(s.st_mode)) { | ||
904 | if (S_ISLNK(s.st_mode)) | ||
905 | fprintf(stderr, "Error: %s is a symbolic link\n", path); | ||
906 | else | ||
907 | fprintf(stderr, "Error: %s is not a directory\n", path); | ||
908 | exit(1); | ||
909 | } | ||
910 | if (s.st_uid != getuid()) { | ||
911 | fprintf(stderr, "Error: %s is not owned by the current user\n", path); | ||
912 | exit(1); | ||
913 | } | ||
914 | |||
915 | pid_t child = fork(); | 902 | pid_t child = fork(); |
916 | if (child < 0) | 903 | if (child < 0) |
917 | errExit("fork"); | 904 | errExit("fork"); |
918 | if (child == 0) { | 905 | if (child == 0) { |
919 | // open ~/.firejail, fails if there is any symlink | 906 | // open ~/.firejail |
920 | int fd = safer_openat(-1, path, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | 907 | int fd = safer_openat(-1, path, O_PATH|O_NOFOLLOW|O_CLOEXEC); |
921 | if (fd == -1) | 908 | if (fd == -1) { |
922 | errExit("safer_openat"); | 909 | fprintf(stderr, "Error: cannot open %s\n", path); |
910 | _exit(1); | ||
911 | } | ||
912 | if (fstat(fd, &s) == -1) | ||
913 | errExit("fstat"); | ||
914 | if (!S_ISDIR(s.st_mode)) { | ||
915 | if (S_ISLNK(s.st_mode)) | ||
916 | fprintf(stderr, "Error: %s is a symbolic link\n", path); | ||
917 | else | ||
918 | fprintf(stderr, "Error: %s is not a directory\n", path); | ||
919 | _exit(1); | ||
920 | } | ||
921 | if (s.st_uid != getuid()) { | ||
922 | fprintf(stderr, "Error: %s is not owned by the current user\n", path); | ||
923 | _exit(1); | ||
924 | } | ||
923 | // chdir to ~/.firejail | 925 | // chdir to ~/.firejail |
924 | if (fchdir(fd) == -1) | 926 | if (fchdir(fd) == -1) |
925 | errExit("fchdir"); | 927 | errExit("fchdir"); |
@@ -988,8 +990,8 @@ int create_empty_dir_as_user(const char *dir, mode_t mode) { | |||
988 | drop_privs(0); | 990 | drop_privs(0); |
989 | 991 | ||
990 | if (mkdir(dir, mode) == 0) { | 992 | if (mkdir(dir, mode) == 0) { |
991 | if (chmod(dir, mode) == -1) | 993 | int err = chmod(dir, mode); |
992 | {;} // do nothing | 994 | (void) err; |
993 | } | 995 | } |
994 | else if (arg_debug) | 996 | else if (arg_debug) |
995 | printf("Directory %s not created: %s\n", dir, strerror(errno)); | 997 | printf("Directory %s not created: %s\n", dir, strerror(errno)); |
@@ -1110,20 +1112,32 @@ unsigned extract_timeout(const char *str) { | |||
1110 | } | 1112 | } |
1111 | 1113 | ||
1112 | void disable_file_or_dir(const char *fname) { | 1114 | void disable_file_or_dir(const char *fname) { |
1115 | assert(fname); | ||
1116 | |||
1117 | int fd = open(fname, O_PATH|O_CLOEXEC); | ||
1118 | if (fd < 0) | ||
1119 | return; | ||
1120 | |||
1113 | struct stat s; | 1121 | struct stat s; |
1114 | if (stat(fname, &s) != -1) { | 1122 | if (fstat(fd, &s) < 0) { // FUSE |
1115 | if (arg_debug) | 1123 | if (errno != EACCES) |
1116 | printf("blacklist %s\n", fname); | 1124 | errExit("fstat"); |
1117 | if (is_dir(fname)) { | 1125 | close(fd); |
1118 | if (mount(RUN_RO_DIR, fname, "none", MS_BIND, "mode=400,gid=0") < 0) | 1126 | return; |
1127 | } | ||
1128 | |||
1129 | if (arg_debug) | ||
1130 | printf("blacklist %s\n", fname); | ||
1131 | if (S_ISDIR(s.st_mode)) { | ||
1132 | if (bind_mount_path_to_fd(RUN_RO_DIR, fd) < 0) | ||
1119 | errExit("disable directory"); | 1133 | errExit("disable directory"); |
1120 | } | ||
1121 | else { | ||
1122 | if (mount(RUN_RO_FILE, fname, "none", MS_BIND, "mode=400,gid=0") < 0) | ||
1123 | errExit("disable file"); | ||
1124 | } | ||
1125 | fs_logger2("blacklist", fname); | ||
1126 | } | 1134 | } |
1135 | else { | ||
1136 | if (bind_mount_path_to_fd(RUN_RO_FILE, fd) < 0) | ||
1137 | errExit("disable file"); | ||
1138 | } | ||
1139 | close(fd); | ||
1140 | fs_logger2("blacklist", fname); | ||
1127 | } | 1141 | } |
1128 | 1142 | ||
1129 | void disable_file_path(const char *path, const char *file) { | 1143 | void disable_file_path(const char *path, const char *file) { |
@@ -1210,6 +1224,60 @@ int safer_openat(int dirfd, const char *path, int flags) { | |||
1210 | return fd; | 1224 | return fd; |
1211 | } | 1225 | } |
1212 | 1226 | ||
1227 | int remount_by_fd(int dst, unsigned long mountflags) { | ||
1228 | char *proc; | ||
1229 | if (asprintf(&proc, "/proc/self/fd/%d", dst) < 0) | ||
1230 | errExit("asprintf"); | ||
1231 | |||
1232 | int rv = mount(NULL, proc, NULL, mountflags|MS_BIND|MS_REMOUNT, NULL); | ||
1233 | if (rv < 0 && arg_debug) | ||
1234 | printf("Failed mount: %s\n", strerror(errno)); | ||
1235 | |||
1236 | free(proc); | ||
1237 | return rv; | ||
1238 | } | ||
1239 | |||
1240 | int bind_mount_by_fd(int src, int dst) { | ||
1241 | char *proc_src, *proc_dst; | ||
1242 | if (asprintf(&proc_src, "/proc/self/fd/%d", src) < 0 || | ||
1243 | asprintf(&proc_dst, "/proc/self/fd/%d", dst) < 0) | ||
1244 | errExit("asprintf"); | ||
1245 | |||
1246 | int rv = mount(proc_src, proc_dst, NULL, MS_BIND|MS_REC, NULL); | ||
1247 | if (rv < 0 && arg_debug) | ||
1248 | printf("Failed mount: %s\n", strerror(errno)); | ||
1249 | |||
1250 | free(proc_src); | ||
1251 | free(proc_dst); | ||
1252 | return rv; | ||
1253 | } | ||
1254 | |||
1255 | int bind_mount_fd_to_path(int src, const char *destname) { | ||
1256 | char *proc; | ||
1257 | if (asprintf(&proc, "/proc/self/fd/%d", src) < 0) | ||
1258 | errExit("asprintf"); | ||
1259 | |||
1260 | int rv = mount(proc, destname, NULL, MS_BIND|MS_REC, NULL); | ||
1261 | if (rv < 0 && arg_debug) | ||
1262 | printf("Failed mount: %s\n", strerror(errno)); | ||
1263 | |||
1264 | free(proc); | ||
1265 | return rv; | ||
1266 | } | ||
1267 | |||
1268 | int bind_mount_path_to_fd(const char *srcname, int dst) { | ||
1269 | char *proc; | ||
1270 | if (asprintf(&proc, "/proc/self/fd/%d", dst) < 0) | ||
1271 | errExit("asprintf"); | ||
1272 | |||
1273 | int rv = mount(srcname, proc, NULL, MS_BIND|MS_REC, NULL); | ||
1274 | if (rv < 0 && arg_debug) | ||
1275 | printf("Failed mount: %s\n", strerror(errno)); | ||
1276 | |||
1277 | free(proc); | ||
1278 | return rv; | ||
1279 | } | ||
1280 | |||
1213 | int has_handler(pid_t pid, int signal) { | 1281 | int has_handler(pid_t pid, int signal) { |
1214 | if (signal > 0 && signal <= SIGRTMAX) { | 1282 | if (signal > 0 && signal <= SIGRTMAX) { |
1215 | char *fname; | 1283 | char *fname; |
diff --git a/src/firejail/x11.c b/src/firejail/x11.c index f4f093138..afe77e246 100644 --- a/src/firejail/x11.c +++ b/src/firejail/x11.c | |||
@@ -1276,12 +1276,7 @@ void x11_xorg(void) { | |||
1276 | // mount via the link in /proc/self/fd | 1276 | // mount via the link in /proc/self/fd |
1277 | if (arg_debug) | 1277 | if (arg_debug) |
1278 | printf("Mounting %s on %s\n", tmpfname, dest); | 1278 | printf("Mounting %s on %s\n", tmpfname, dest); |
1279 | char *proc_src, *proc_dst; | 1279 | if (bind_mount_by_fd(src, dst)) { |
1280 | if (asprintf(&proc_src, "/proc/self/fd/%d", src) == -1) | ||
1281 | errExit("asprintf"); | ||
1282 | if (asprintf(&proc_dst, "/proc/self/fd/%d", dst) == -1) | ||
1283 | errExit("asprintf"); | ||
1284 | if (mount(proc_src, proc_dst, NULL, MS_BIND, NULL) == -1) { | ||
1285 | fprintf(stderr, "Error: cannot mount the new .Xauthority file\n"); | 1280 | fprintf(stderr, "Error: cannot mount the new .Xauthority file\n"); |
1286 | exit(1); | 1281 | exit(1); |
1287 | } | 1282 | } |
@@ -1289,8 +1284,6 @@ void x11_xorg(void) { | |||
1289 | MountData *mptr = get_last_mount(); | 1284 | MountData *mptr = get_last_mount(); |
1290 | if (strcmp(mptr->dir, dest) != 0 || strcmp(mptr->fstype, "tmpfs") != 0) | 1285 | if (strcmp(mptr->dir, dest) != 0 || strcmp(mptr->fstype, "tmpfs") != 0) |
1291 | errLogExit("invalid .Xauthority mount"); | 1286 | errLogExit("invalid .Xauthority mount"); |
1292 | free(proc_src); | ||
1293 | free(proc_dst); | ||
1294 | close(src); | 1287 | close(src); |
1295 | close(dst); | 1288 | close(dst); |
1296 | 1289 | ||
@@ -1336,6 +1329,8 @@ void fs_x11(void) { | |||
1336 | return; | 1329 | return; |
1337 | } | 1330 | } |
1338 | 1331 | ||
1332 | // the mount source is under control of the user, so be careful and | ||
1333 | // mount without following symbolic links, using a file descriptor | ||
1339 | char *x11file; | 1334 | char *x11file; |
1340 | if (asprintf(&x11file, "/tmp/.X11-unix/X%d", display) == -1) | 1335 | if (asprintf(&x11file, "/tmp/.X11-unix/X%d", display) == -1) |
1341 | errExit("asprintf"); | 1336 | errExit("asprintf"); |
@@ -1344,10 +1339,10 @@ void fs_x11(void) { | |||
1344 | free(x11file); | 1339 | free(x11file); |
1345 | return; | 1340 | return; |
1346 | } | 1341 | } |
1347 | struct stat x11stat; | 1342 | struct stat s3; |
1348 | if (fstat(src, &x11stat) < 0) | 1343 | if (fstat(src, &s3) < 0) |
1349 | errExit("fstat"); | 1344 | errExit("fstat"); |
1350 | if (!S_ISSOCK(x11stat.st_mode)) { | 1345 | if (!S_ISSOCK(s3.st_mode)) { |
1351 | close(src); | 1346 | close(src); |
1352 | free(x11file); | 1347 | free(x11file); |
1353 | return; | 1348 | return; |
@@ -1367,14 +1362,8 @@ void fs_x11(void) { | |||
1367 | if (dst < 0) | 1362 | if (dst < 0) |
1368 | errExit("open"); | 1363 | errExit("open"); |
1369 | 1364 | ||
1370 | char *proc_src, *proc_dst; | 1365 | if (bind_mount_by_fd(src, dst)) |
1371 | if (asprintf(&proc_src, "/proc/self/fd/%d", src) == -1 || | ||
1372 | asprintf(&proc_dst, "/proc/self/fd/%d", dst) == -1) | ||
1373 | errExit("asprintf"); | ||
1374 | if (mount(proc_src, proc_dst, NULL, MS_BIND | MS_REC, NULL) < 0) | ||
1375 | errExit("mount bind"); | 1366 | errExit("mount bind"); |
1376 | free(proc_src); | ||
1377 | free(proc_dst); | ||
1378 | close(src); | 1367 | close(src); |
1379 | close(dst); | 1368 | close(dst); |
1380 | fs_logger2("whitelist", x11file); | 1369 | fs_logger2("whitelist", x11file); |