aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar smitsohu <smitsohu@gmail.com>2021-06-08 12:27:04 +0200
committerLibravatar smitsohu <smitsohu@gmail.com>2021-06-08 15:53:47 +0200
commit0ad36056e3fe9baec2c7d69444f1eaf1648490a5 (patch)
tree40ed62102afa70e046fa3aea216b01e75d975158
parentmisc (diff)
downloadfirejail-0ad36056e3fe9baec2c7d69444f1eaf1648490a5.tar.gz
firejail-0ad36056e3fe9baec2c7d69444f1eaf1648490a5.tar.zst
firejail-0ad36056e3fe9baec2c7d69444f1eaf1648490a5.zip
refactor mounting
-rw-r--r--src/firejail/chroot.c40
-rw-r--r--src/firejail/dbus.c14
-rw-r--r--src/firejail/firejail.h4
-rw-r--r--src/firejail/fs.c163
-rw-r--r--src/firejail/fs_home.c21
-rw-r--r--src/firejail/fs_lib.c6
-rw-r--r--src/firejail/fs_trace.c6
-rw-r--r--src/firejail/fs_whitelist.c18
-rw-r--r--src/firejail/pulseaudio.c6
-rw-r--r--src/firejail/restrict_users.c12
-rw-r--r--src/firejail/util.c138
-rw-r--r--src/firejail/x11.c25
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);
526void disable_file_or_dir(const char *fname); 526void disable_file_or_dir(const char *fname);
527void disable_file_path(const char *path, const char *file); 527void disable_file_path(const char *path, const char *file);
528int safer_openat(int dirfd, const char *path, int flags); 528int safer_openat(int dirfd, const char *path, int flags);
529int remount_by_fd(int dst, unsigned long mountflags);
530int bind_mount_by_fd(int src, int dst);
531int bind_mount_path_to_fd(const char *srcname, int dst);
532int bind_mount_fd_to_path(int src, const char *destname);
529int has_handler(pid_t pid, int signal); 533int has_handler(pid_t pid, int signal);
530void enter_network_namespace(pid_t pid); 534void enter_network_namespace(pid_t pid);
531int read_pid(const char *name, pid_t *pid); 535int 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
57typedef enum {
58 UNSUCCESSFUL,
59 SUCCESSFUL
60} LAST_DISABLE_OPERATION;
61LAST_DISABLE_OPERATION last_disable = UNSUCCESSFUL;
62
63static void disable_file(OPERATION op, const char *filename) { 57static 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
591out: 596out:
@@ -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
783void disable_config(void) { 784void 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
1112void disable_file_or_dir(const char *fname) { 1114void 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
1129void disable_file_path(const char *path, const char *file) { 1143void 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
1227int 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
1240int 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
1255int 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
1268int 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
1213int has_handler(pid_t pid, int signal) { 1281int 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);