aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar smitsohu <smitsohu@gmail.com>2021-06-08 18:24:22 +0200
committerLibravatar GitHub <noreply@github.com>2021-06-08 18:24:22 +0200
commit5e30eb49355f5620cc2f99100a0890cfed02ccd4 (patch)
tree0852f5402f9785dc1ebdd236eb31a9c40b02adf4
parentfixup 9678da00301562464464099b9d7cfd76424fbb23 (diff)
parentadd more EUID switching (diff)
downloadfirejail-5e30eb49355f5620cc2f99100a0890cfed02ccd4.tar.gz
firejail-5e30eb49355f5620cc2f99100a0890cfed02ccd4.tar.zst
firejail-5e30eb49355f5620cc2f99100a0890cfed02ccd4.zip
Merge pull request #4349 from smitsohu/misc
Misc hardening + refactoring
-rw-r--r--src/fcopy/main.c17
-rw-r--r--src/firejail/checkcfg.c3
-rw-r--r--src/firejail/chroot.c40
-rw-r--r--src/firejail/dbus.c14
-rw-r--r--src/firejail/dhcp.c9
-rw-r--r--src/firejail/firejail.h7
-rw-r--r--src/firejail/fs.c210
-rw-r--r--src/firejail/fs_dev.c2
-rw-r--r--src/firejail/fs_home.c63
-rw-r--r--src/firejail/fs_lib.c13
-rw-r--r--src/firejail/fs_trace.c6
-rw-r--r--src/firejail/fs_var.c4
-rw-r--r--src/firejail/fs_whitelist.c19
-rw-r--r--src/firejail/macros.c1
-rw-r--r--src/firejail/main.c10
-rw-r--r--src/firejail/mountinfo.c5
-rw-r--r--src/firejail/paths.c2
-rw-r--r--src/firejail/pulseaudio.c6
-rw-r--r--src/firejail/restrict_users.c22
-rw-r--r--src/firejail/sbox.c2
-rw-r--r--src/firejail/selinux.c10
-rw-r--r--src/firejail/util.c227
-rw-r--r--src/firejail/x11.c40
-rw-r--r--src/lib/ldd_utils.c2
24 files changed, 426 insertions, 308 deletions
diff --git a/src/fcopy/main.c b/src/fcopy/main.c
index 869549821..31810de9a 100644
--- a/src/fcopy/main.c
+++ b/src/fcopy/main.c
@@ -19,11 +19,15 @@
19 */ 19 */
20 20
21#include "../include/common.h" 21#include "../include/common.h"
22#include <fcntl.h>
23#include <ftw.h> 22#include <ftw.h>
24#include <errno.h> 23#include <errno.h>
25#include <pwd.h> 24#include <pwd.h>
26 25
26#include <fcntl.h>
27#ifndef O_PATH
28#define O_PATH 010000000
29#endif
30
27#if HAVE_SELINUX 31#if HAVE_SELINUX
28#include <sys/stat.h> 32#include <sys/stat.h>
29#include <sys/types.h> 33#include <sys/types.h>
@@ -55,7 +59,7 @@ static void selinux_relabel_path(const char *path, const char *inside_path) {
55 assert(path); 59 assert(path);
56 assert(inside_path); 60 assert(inside_path);
57#if HAVE_SELINUX 61#if HAVE_SELINUX
58 char procfs_path[64]; 62 char procfs_path[64];
59 char *fcon = NULL; 63 char *fcon = NULL;
60 int fd; 64 int fd;
61 struct stat st; 65 struct stat st;
@@ -69,20 +73,23 @@ static void selinux_relabel_path(const char *path, const char *inside_path) {
69 if (!label_hnd) 73 if (!label_hnd)
70 label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0); 74 label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
71 75
76 if (!label_hnd)
77 errExit("selabel_open");
78
72 /* Open the file as O_PATH, to pin it while we determine and adjust the label */ 79 /* Open the file as O_PATH, to pin it while we determine and adjust the label */
73 fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH); 80 fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH);
74 if (fd < 0) 81 if (fd < 0)
75 return; 82 return;
76 if (fstat(fd, &st) < 0) 83 if (fstat(fd, &st) < 0)
77 goto close; 84 goto close;
78 85
79 if (selabel_lookup_raw(label_hnd, &fcon, inside_path, st.st_mode) == 0) { 86 if (selabel_lookup_raw(label_hnd, &fcon, inside_path, st.st_mode) == 0) {
80 sprintf(procfs_path, "/proc/self/fd/%i", fd); 87 sprintf(procfs_path, "/proc/self/fd/%i", fd);
81 if (arg_debug) 88 if (arg_debug)
82 printf("Relabeling %s as %s (%s)\n", path, inside_path, fcon); 89 printf("Relabeling %s as %s (%s)\n", path, inside_path, fcon);
83 90
84 setfilecon_raw(procfs_path, fcon); 91 setfilecon_raw(procfs_path, fcon);
85 } 92 }
86 freecon(fcon); 93 freecon(fcon);
87 close: 94 close:
88 close(fd); 95 close(fd);
diff --git a/src/firejail/checkcfg.c b/src/firejail/checkcfg.c
index d7690a4fc..f3ab0a6d8 100644
--- a/src/firejail/checkcfg.c
+++ b/src/firejail/checkcfg.c
@@ -134,8 +134,7 @@ int checkcfg(int val) {
134 *end = '\0'; 134 *end = '\0';
135 135
136 // is the file present? 136 // is the file present?
137 struct stat s; 137 if (access(fname, F_OK) == -1) {
138 if (stat(fname, &s) == -1) {
139 fprintf(stderr, "Error: netfilter-default file %s not available\n", fname); 138 fprintf(stderr, "Error: netfilter-default file %s not available\n", fname);
140 exit(1); 139 exit(1);
141 } 140 }
diff --git a/src/firejail/chroot.c b/src/firejail/chroot.c
index 757ffb1f7..05d89a866 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/dhcp.c b/src/firejail/dhcp.c
index 5bcdcad37..47dd39ac0 100644
--- a/src/firejail/dhcp.c
+++ b/src/firejail/dhcp.c
@@ -153,14 +153,11 @@ void dhcp_start(void) {
153 if (!any_dhcp()) 153 if (!any_dhcp())
154 return; 154 return;
155 155
156 char *dhclient_path = RUN_MNT_DIR "/dhclient";; 156 char *dhclient_path = RUN_MNT_DIR "/dhclient";
157 struct stat s; 157 struct stat s;
158 if (stat(dhclient_path, &s) == -1) { 158 if (stat(dhclient_path, &s) == -1) {
159 dhclient_path = "/usr/sbin/dhclient"; 159 fprintf(stderr, "Error: %s was not found.\n", dhclient_path);
160 if (stat(dhclient_path, &s) == -1) { 160 exit(1);
161 fprintf(stderr, "Error: dhclient was not found.\n");
162 exit(1);
163 }
164 } 161 }
165 162
166 sbox_run(SBOX_ROOT| SBOX_SECCOMP, 4, PATH_FCOPY, "--follow-link", dhclient_path, RUN_MNT_DIR); 163 sbox_run(SBOX_ROOT| SBOX_SECCOMP, 4, PATH_FCOPY, "--follow-link", dhclient_path, RUN_MNT_DIR);
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h
index a5c44739e..10133142a 100644
--- a/src/firejail/firejail.h
+++ b/src/firejail/firejail.h
@@ -503,6 +503,9 @@ void copy_file_from_user_to_root(const char *srcname, const char *destname, uid_
503void touch_file_as_user(const char *fname, mode_t mode); 503void touch_file_as_user(const char *fname, mode_t mode);
504int is_dir(const char *fname); 504int is_dir(const char *fname);
505int is_link(const char *fname); 505int is_link(const char *fname);
506char *realpath_as_user(const char *fname);
507int stat_as_user(const char *fname, struct stat *s);
508int lstat_as_user(const char *fname, struct stat *s);
506void trim_trailing_slash_or_dot(char *path); 509void trim_trailing_slash_or_dot(char *path);
507char *line_remove_spaces(const char *buf); 510char *line_remove_spaces(const char *buf);
508char *split_comma(char *str); 511char *split_comma(char *str);
@@ -526,6 +529,10 @@ unsigned extract_timeout(const char *str);
526void disable_file_or_dir(const char *fname); 529void disable_file_or_dir(const char *fname);
527void disable_file_path(const char *path, const char *file); 530void disable_file_path(const char *path, const char *file);
528int safer_openat(int dirfd, const char *path, int flags); 531int safer_openat(int dirfd, const char *path, int flags);
532int remount_by_fd(int dst, unsigned long mountflags);
533int bind_mount_by_fd(int src, int dst);
534int bind_mount_path_to_fd(const char *srcname, int dst);
535int bind_mount_fd_to_path(int src, const char *destname);
529int has_handler(pid_t pid, int signal); 536int has_handler(pid_t pid, int signal);
530void enter_network_namespace(pid_t pid); 537void enter_network_namespace(pid_t pid);
531int read_pid(const char *name, pid_t *pid); 538int read_pid(const char *name, pid_t *pid);
diff --git a/src/firejail/fs.c b/src/firejail/fs.c
index 09de11de9..01182bd2c 100644
--- a/src/firejail/fs.c
+++ b/src/firejail/fs.c
@@ -54,16 +54,10 @@ 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; 60 EUID_ASSERT();
67 61
68 // Resolve all symlinks 62 // Resolve all symlinks
69 char* fname = realpath(filename, NULL); 63 char* fname = realpath(filename, NULL);
@@ -71,20 +65,24 @@ static void disable_file(OPERATION op, const char *filename) {
71 return; 65 return;
72 } 66 }
73 if (fname == NULL && errno == EACCES) { 67 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 68 // realpath and stat functions will fail on FUSE filesystems
77 // they don't seem to like a uid of 0 69 // they don't seem to like a uid of 0
78 // force mounting 70 // force mounting
79 int rv = mount(RUN_RO_DIR, filename, "none", MS_BIND, "mode=400,gid=0"); 71 int fd = open(filename, O_PATH|O_CLOEXEC);
80 if (rv == 0) 72 if (fd < 0) {
81 last_disable = SUCCESSFUL; 73 if (arg_debug)
82 else { 74 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"); 75 return;
84 if (rv == 0)
85 last_disable = SUCCESSFUL;
86 } 76 }
87 if (last_disable == SUCCESSFUL) { 77
78 EUID_ROOT();
79 int err = bind_mount_path_to_fd(RUN_RO_DIR, fd);
80 if (err < 0)
81 err = bind_mount_path_to_fd(RUN_RO_FILE, fd);
82 EUID_USER();
83 close(fd);
84
85 if (err == 0) {
88 if (arg_debug) 86 if (arg_debug)
89 printf("Disable %s\n", filename); 87 printf("Disable %s\n", filename);
90 if (op == BLACKLIST_FILE) 88 if (op == BLACKLIST_FILE)
@@ -92,21 +90,18 @@ static void disable_file(OPERATION op, const char *filename) {
92 else 90 else
93 fs_logger2("blacklist-nolog", filename); 91 fs_logger2("blacklist-nolog", filename);
94 } 92 }
95 else { 93 else if (arg_debug)
96 if (arg_debug) 94 printf("Warning (blacklisting): cannot mount on %s\n", filename);
97 printf("Warning (blacklisting): %s is an invalid file, skipping...\n", filename);
98 }
99 95
100 return; 96 return;
101 } 97 }
102 98
103 // if the file is not present, do nothing 99 // if the file is not present, do nothing
100 assert(fname);
104 struct stat s; 101 struct stat s;
105 if (fname == NULL) 102 if (stat(fname, &s) < 0) {
106 return;
107 if (stat(fname, &s) == -1) {
108 if (arg_debug) 103 if (arg_debug)
109 fwarning("%s does not exist, skipping...\n", fname); 104 printf("Warning (blacklisting): cannot access %s: %s\n", fname, strerror(errno));
110 free(fname); 105 free(fname);
111 return; 106 return;
112 } 107 }
@@ -115,8 +110,10 @@ static void disable_file(OPERATION op, const char *filename) {
115 // we migth have a file found in ${PATH} pointing to /usr/bin/firejail 110 // 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 111 // 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 112 // and expects Firefox to open in the same sandbox
118 if (strcmp(BINDIR "/firejail", fname) == 0) 113 if (strcmp(BINDIR "/firejail", fname) == 0) {
114 free(fname);
119 return; 115 return;
116 }
120 117
121 // modify the file 118 // modify the file
122 if (op == BLACKLIST_FILE || op == BLACKLIST_NOLOG) { 119 if (op == BLACKLIST_FILE || op == BLACKLIST_NOLOG) {
@@ -141,15 +138,25 @@ static void disable_file(OPERATION op, const char *filename) {
141 printf(" - no logging\n"); 138 printf(" - no logging\n");
142 } 139 }
143 140
141 int fd = open(fname, O_PATH|O_CLOEXEC);
142 if (fd < 0) {
143 if (arg_debug)
144 printf("Warning (blacklisting): cannot open %s: %s\n", fname, strerror(errno));
145 free(fname);
146 return;
147 }
148 EUID_ROOT();
144 if (S_ISDIR(s.st_mode)) { 149 if (S_ISDIR(s.st_mode)) {
145 if (mount(RUN_RO_DIR, fname, "none", MS_BIND, "mode=400,gid=0") < 0) 150 if (bind_mount_path_to_fd(RUN_RO_DIR, fd) < 0)
146 errExit("disable file"); 151 errExit("disable file");
147 } 152 }
148 else { 153 else {
149 if (mount(RUN_RO_FILE, fname, "none", MS_BIND, "mode=400,gid=0") < 0) 154 if (bind_mount_path_to_fd(RUN_RO_FILE, fd) < 0)
150 errExit("disable file"); 155 errExit("disable file");
151 } 156 }
152 last_disable = SUCCESSFUL; 157 EUID_USER();
158 close(fd);
159
153 if (op == BLACKLIST_FILE) 160 if (op == BLACKLIST_FILE)
154 fs_logger2("blacklist", fname); 161 fs_logger2("blacklist", fname);
155 else 162 else
@@ -158,7 +165,6 @@ static void disable_file(OPERATION op, const char *filename) {
158 } 165 }
159 else if (op == MOUNT_READONLY || op == MOUNT_RDWR || op == MOUNT_NOEXEC) { 166 else if (op == MOUNT_READONLY || op == MOUNT_RDWR || op == MOUNT_NOEXEC) {
160 fs_remount_rec(fname, op); 167 fs_remount_rec(fname, op);
161 // todo: last_disable = SUCCESSFUL;
162 } 168 }
163 else if (op == MOUNT_TMPFS) { 169 else if (op == MOUNT_TMPFS) {
164 if (S_ISDIR(s.st_mode)) { 170 if (S_ISDIR(s.st_mode)) {
@@ -169,9 +175,10 @@ static void disable_file(OPERATION op, const char *filename) {
169 exit(1); 175 exit(1);
170 } 176 }
171 } 177 }
178 // fs_tmpfs returns with EUID 0
172 fs_tmpfs(fname, getuid()); 179 fs_tmpfs(fname, getuid());
173 selinux_relabel_path(fname, fname); 180 selinux_relabel_path(fname, fname);
174 last_disable = SUCCESSFUL; 181 EUID_USER();
175 } 182 }
176 else 183 else
177 fwarning("%s is not a directory; cannot mount a tmpfs on top of it.\n", fname); 184 fwarning("%s is not a directory; cannot mount a tmpfs on top of it.\n", fname);
@@ -191,6 +198,7 @@ static int *nbcheck = NULL;
191// Treat pattern as a shell glob pattern and blacklist matching files 198// Treat pattern as a shell glob pattern and blacklist matching files
192static void globbing(OPERATION op, const char *pattern, const char *noblacklist[], size_t noblacklist_len) { 199static void globbing(OPERATION op, const char *pattern, const char *noblacklist[], size_t noblacklist_len) {
193 assert(pattern); 200 assert(pattern);
201 EUID_ASSERT();
194 202
195#ifdef TEST_NO_BLACKLIST_MATCHING 203#ifdef TEST_NO_BLACKLIST_MATCHING
196 if (nbcheck_start == 0) { 204 if (nbcheck_start == 0) {
@@ -264,6 +272,7 @@ void fs_blacklist(void) {
264 if (noblacklist == NULL) 272 if (noblacklist == NULL)
265 errExit("failed allocating memory for noblacklist entries"); 273 errExit("failed allocating memory for noblacklist entries");
266 274
275 EUID_USER();
267 while (entry) { 276 while (entry) {
268 OPERATION op = OPERATION_MAX; 277 OPERATION op = OPERATION_MAX;
269 char *ptr; 278 char *ptr;
@@ -294,11 +303,13 @@ void fs_blacklist(void) {
294 if (arg_debug) 303 if (arg_debug)
295 printf("Mount-bind %s on top of %s\n", dname1, dname2); 304 printf("Mount-bind %s on top of %s\n", dname1, dname2);
296 // preserve dname2 mode and ownership 305 // preserve dname2 mode and ownership
306 // EUID_ROOT(); - option not accessible to non-root users
297 if (mount(dname1, dname2, NULL, MS_BIND|MS_REC, NULL) < 0) 307 if (mount(dname1, dname2, NULL, MS_BIND|MS_REC, NULL) < 0)
298 errExit("mount bind"); 308 errExit("mount bind");
299 /* coverity[toctou] */ 309 /* coverity[toctou] */
300 if (set_perms(dname2, s.st_uid, s.st_gid,s.st_mode)) 310 if (set_perms(dname2, s.st_uid, s.st_gid,s.st_mode))
301 errExit("set_perms"); 311 errExit("set_perms");
312 // EUID_USER();
302 313
303 entry = entry->next; 314 entry = entry->next;
304 continue; 315 continue;
@@ -376,16 +387,12 @@ void fs_blacklist(void) {
376 op = MOUNT_TMPFS; 387 op = MOUNT_TMPFS;
377 } 388 }
378 else if (strncmp(entry->data, "mkdir ", 6) == 0) { 389 else if (strncmp(entry->data, "mkdir ", 6) == 0) {
379 EUID_USER();
380 fs_mkdir(entry->data + 6); 390 fs_mkdir(entry->data + 6);
381 EUID_ROOT();
382 entry = entry->next; 391 entry = entry->next;
383 continue; 392 continue;
384 } 393 }
385 else if (strncmp(entry->data, "mkfile ", 7) == 0) { 394 else if (strncmp(entry->data, "mkfile ", 7) == 0) {
386 EUID_USER();
387 fs_mkfile(entry->data + 7); 395 fs_mkfile(entry->data + 7);
388 EUID_ROOT();
389 entry = entry->next; 396 entry = entry->next;
390 continue; 397 continue;
391 } 398 }
@@ -441,6 +448,8 @@ void fs_blacklist(void) {
441 for (i = 0; i < noblacklist_c; i++) 448 for (i = 0; i < noblacklist_c; i++)
442 free(noblacklist[i]); 449 free(noblacklist[i]);
443 free(noblacklist); 450 free(noblacklist);
451
452 EUID_ROOT();
444} 453}
445 454
446//*********************************************** 455//***********************************************
@@ -449,6 +458,7 @@ void fs_blacklist(void) {
449 458
450// mount a writable tmpfs on directory; requires a resolved path 459// mount a writable tmpfs on directory; requires a resolved path
451void fs_tmpfs(const char *dir, unsigned check_owner) { 460void fs_tmpfs(const char *dir, unsigned check_owner) {
461 EUID_USER();
452 assert(dir); 462 assert(dir);
453 if (arg_debug) 463 if (arg_debug)
454 printf("Mounting tmpfs on %s, check owner: %s\n", dir, (check_owner)? "yes": "no"); 464 printf("Mounting tmpfs on %s, check owner: %s\n", dir, (check_owner)? "yes": "no");
@@ -473,6 +483,7 @@ void fs_tmpfs(const char *dir, unsigned check_owner) {
473 errExit("fstatvfs"); 483 errExit("fstatvfs");
474 unsigned long flags = buf.f_flag & ~(MS_RDONLY|MS_BIND); 484 unsigned long flags = buf.f_flag & ~(MS_RDONLY|MS_BIND);
475 // mount via the symbolic link in /proc/self/fd 485 // mount via the symbolic link in /proc/self/fd
486 EUID_ROOT();
476 char *proc; 487 char *proc;
477 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) 488 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
478 errExit("asprintf"); 489 errExit("asprintf");
@@ -490,38 +501,42 @@ void fs_tmpfs(const char *dir, unsigned check_owner) {
490 501
491// remount path, preserving other mount flags; requires a resolved path 502// remount path, preserving other mount flags; requires a resolved path
492static void fs_remount_simple(const char *path, OPERATION op) { 503static void fs_remount_simple(const char *path, OPERATION op) {
504 EUID_ASSERT();
493 assert(path); 505 assert(path);
494 506
495 // open path without following symbolic links 507 // open path without following symbolic links
496 int fd1 = safer_openat(-1, path, O_PATH|O_NOFOLLOW|O_CLOEXEC); 508 int fd = safer_openat(-1, path, O_PATH|O_NOFOLLOW|O_CLOEXEC);
497 if (fd1 == -1) 509 if (fd < 0)
498 goto out; 510 goto out;
499 struct stat s1; 511
500 if (fstat(fd1, &s1) == -1) { 512 struct stat s;
513 if (fstat(fd, &s) < 0) {
501 // fstat can fail with EACCES if path is a FUSE mount, 514 // fstat can fail with EACCES if path is a FUSE mount,
502 // mounted without 'allow_root' or 'allow_other' 515 // mounted without 'allow_root' or 'allow_other'
503 if (errno != EACCES) 516 if (errno != EACCES)
504 errExit("fstat"); 517 errExit("fstat");
505 close(fd1); 518 close(fd);
506 goto out; 519 goto out;
507 } 520 }
508 // get mount flags 521 // get mount flags
509 struct statvfs buf; 522 struct statvfs buf;
510 if (fstatvfs(fd1, &buf) == -1) 523 if (fstatvfs(fd, &buf) < 0) {
511 errExit("fstatvfs"); 524 close(fd);
525 goto out;
526 }
512 unsigned long flags = buf.f_flag; 527 unsigned long flags = buf.f_flag;
513 528
514 // read-write option 529 // read-write option
515 if (op == MOUNT_RDWR || op == MOUNT_RDWR_NOCHECK) { 530 if (op == MOUNT_RDWR || op == MOUNT_RDWR_NOCHECK) {
516 // nothing to do if there is no read-only flag 531 // nothing to do if there is no read-only flag
517 if ((flags & MS_RDONLY) == 0) { 532 if ((flags & MS_RDONLY) == 0) {
518 close(fd1); 533 close(fd);
519 return; 534 return;
520 } 535 }
521 // allow only user owned directories, except the user is root 536 // allow only user owned directories, except the user is root
522 if (op != MOUNT_RDWR_NOCHECK && getuid() != 0 && s1.st_uid != getuid()) { 537 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); 538 fwarning("you are not allowed to change %s to read-write\n", path);
524 close(fd1); 539 close(fd);
525 return; 540 return;
526 } 541 }
527 flags &= ~MS_RDONLY; 542 flags &= ~MS_RDONLY;
@@ -530,7 +545,7 @@ static void fs_remount_simple(const char *path, OPERATION op) {
530 else if (op == MOUNT_NOEXEC) { 545 else if (op == MOUNT_NOEXEC) {
531 // nothing to do if path is mounted noexec already 546 // nothing to do if path is mounted noexec already
532 if ((flags & (MS_NOEXEC|MS_NODEV|MS_NOSUID)) == (MS_NOEXEC|MS_NODEV|MS_NOSUID)) { 547 if ((flags & (MS_NOEXEC|MS_NODEV|MS_NOSUID)) == (MS_NOEXEC|MS_NODEV|MS_NOSUID)) {
533 close(fd1); 548 close(fd);
534 return; 549 return;
535 } 550 }
536 flags |= MS_NOEXEC|MS_NODEV|MS_NOSUID; 551 flags |= MS_NOEXEC|MS_NODEV|MS_NOSUID;
@@ -539,7 +554,7 @@ static void fs_remount_simple(const char *path, OPERATION op) {
539 else if (op == MOUNT_READONLY) { 554 else if (op == MOUNT_READONLY) {
540 // nothing to do if path is mounted read-only already 555 // nothing to do if path is mounted read-only already
541 if ((flags & MS_RDONLY) == MS_RDONLY) { 556 if ((flags & MS_RDONLY) == MS_RDONLY) {
542 close(fd1); 557 close(fd);
543 return; 558 return;
544 } 559 }
545 flags |= MS_RDONLY; 560 flags |= MS_RDONLY;
@@ -549,29 +564,37 @@ static void fs_remount_simple(const char *path, OPERATION op) {
549 564
550 if (arg_debug) 565 if (arg_debug)
551 printf("Mounting %s %s\n", opstr[op], path); 566 printf("Mounting %s %s\n", opstr[op], path);
567
568 // make path a mount point:
552 // mount --bind path path 569 // mount --bind path path
553 char *proc; 570 EUID_ROOT();
554 if (asprintf(&proc, "/proc/self/fd/%d", fd1) == -1) 571 int err = bind_mount_by_fd(fd, fd);
555 errExit("asprintf"); 572 EUID_USER();
556 if (mount(proc, proc, NULL, MS_BIND|MS_REC, NULL) < 0) 573 if (err) {
557 errExit("mount"); 574 close(fd);
558 free(proc); 575 goto out;
576 }
559 577
560 // mount --bind -o remount,ro path 578 // remount the mount point
561 // need to open path again without following symbolic links 579 // need to open path again
562 int fd2 = safer_openat(-1, path, O_PATH|O_NOFOLLOW|O_CLOEXEC); 580 int fd2 = safer_openat(-1, path, O_PATH|O_NOFOLLOW|O_CLOEXEC);
563 if (fd2 == -1) 581 close(fd); // earliest timepoint to close fd
564 errExit("open"); 582 if (fd2 < 0)
583 goto out;
584
585 // device and inode number should be the same
565 struct stat s2; 586 struct stat s2;
566 if (fstat(fd2, &s2) == -1) 587 if (fstat(fd2, &s2) < 0)
567 errExit("fstat"); 588 errExit("fstat");
568 // device and inode number should be the same 589 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]); 590 errLogExit("invalid %s mount", opstr[op]);
571 if (asprintf(&proc, "/proc/self/fd/%d", fd2) == -1) 591
572 errExit("asprintf"); 592 EUID_ROOT();
573 if (mount(NULL, proc, NULL, flags|MS_BIND|MS_REMOUNT, NULL) < 0) 593 err = remount_by_fd(fd2, flags);
574 errExit("mount"); 594 EUID_USER();
595 close(fd2);
596 if (err)
597 goto out;
575 598
576 // run a sanity check on /proc/self/mountinfo and confirm that target of the last 599 // 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, 600 // mount operation was path; if there are other mount points contained inside path,
@@ -582,10 +605,8 @@ static void fs_remount_simple(const char *path, OPERATION op) {
582 (*(mptr->dir + len) != '\0' && *(mptr->dir + len) != '/')) 605 (*(mptr->dir + len) != '\0' && *(mptr->dir + len) != '/'))
583 && strcmp(path, "/") != 0) // support read-only=/ 606 && strcmp(path, "/") != 0) // support read-only=/
584 errLogExit("invalid %s mount", opstr[op]); 607 errLogExit("invalid %s mount", opstr[op]);
608
585 fs_logger2(opstr[op], path); 609 fs_logger2(opstr[op], path);
586 free(proc);
587 close(fd1);
588 close(fd2);
589 return; 610 return;
590 611
591out: 612out:
@@ -594,7 +615,9 @@ out:
594 615
595// remount recursively; requires a resolved path 616// remount recursively; requires a resolved path
596static void fs_remount_rec(const char *dir, OPERATION op) { 617static void fs_remount_rec(const char *dir, OPERATION op) {
618 EUID_ASSERT();
597 assert(dir); 619 assert(dir);
620
598 struct stat s; 621 struct stat s;
599 if (stat(dir, &s) != 0) 622 if (stat(dir, &s) != 0)
600 return; 623 return;
@@ -632,6 +655,9 @@ static void fs_remount_rec(const char *dir, OPERATION op) {
632// resolve a path and remount it 655// resolve a path and remount it
633void fs_remount(const char *path, OPERATION op, int rec) { 656void fs_remount(const char *path, OPERATION op, int rec) {
634 assert(path); 657 assert(path);
658 assert(geteuid() == 0);
659 EUID_USER();
660
635 char *rpath = realpath(path, NULL); 661 char *rpath = realpath(path, NULL);
636 if (rpath) { 662 if (rpath) {
637 if (rec) 663 if (rec)
@@ -640,10 +666,12 @@ void fs_remount(const char *path, OPERATION op, int rec) {
640 fs_remount_simple(rpath, op); 666 fs_remount_simple(rpath, op);
641 free(rpath); 667 free(rpath);
642 } 668 }
669 EUID_ROOT();
643} 670}
644 671
645// Disable /mnt, /media, /run/mount and /run/media access 672// Disable /mnt, /media, /run/mount and /run/media access
646void fs_mnt(const int enforce) { 673void fs_mnt(const int enforce) {
674 EUID_USER();
647 if (enforce) { 675 if (enforce) {
648 // disable-mnt set in firejail.config 676 // disable-mnt set in firejail.config
649 // overriding with noblacklist is not possible in this case 677 // overriding with noblacklist is not possible in this case
@@ -653,13 +681,12 @@ void fs_mnt(const int enforce) {
653 disable_file(BLACKLIST_FILE, "/run/media"); 681 disable_file(BLACKLIST_FILE, "/run/media");
654 } 682 }
655 else { 683 else {
656 EUID_USER();
657 profile_add("blacklist /mnt"); 684 profile_add("blacklist /mnt");
658 profile_add("blacklist /media"); 685 profile_add("blacklist /media");
659 profile_add("blacklist /run/mount"); 686 profile_add("blacklist /run/mount");
660 profile_add("blacklist /run/media"); 687 profile_add("blacklist /run/media");
661 EUID_ROOT();
662 } 688 }
689 EUID_ROOT();
663} 690}
664 691
665 692
@@ -674,7 +701,6 @@ void fs_proc_sys_dev_boot(void) {
674 errExit("mounting /proc/sys"); 701 errExit("mounting /proc/sys");
675 fs_logger("read-only /proc/sys"); 702 fs_logger("read-only /proc/sys");
676 703
677
678 /* Mount a version of /sys that describes the network namespace */ 704 /* Mount a version of /sys that describes the network namespace */
679 if (arg_debug) 705 if (arg_debug)
680 printf("Remounting /sys directory\n"); 706 printf("Remounting /sys directory\n");
@@ -689,13 +715,13 @@ void fs_proc_sys_dev_boot(void) {
689 else 715 else
690 fs_logger("remount /sys"); 716 fs_logger("remount /sys");
691 717
718 EUID_USER();
719
692 disable_file(BLACKLIST_FILE, "/sys/firmware"); 720 disable_file(BLACKLIST_FILE, "/sys/firmware");
693 disable_file(BLACKLIST_FILE, "/sys/hypervisor"); 721 disable_file(BLACKLIST_FILE, "/sys/hypervisor");
694 { // allow user access to some directories in /sys/ by specifying 'noblacklist' option 722 { // allow user access to some directories in /sys/ by specifying 'noblacklist' option
695 EUID_USER();
696 profile_add("blacklist /sys/fs"); 723 profile_add("blacklist /sys/fs");
697 profile_add("blacklist /sys/module"); 724 profile_add("blacklist /sys/module");
698 EUID_ROOT();
699 } 725 }
700 disable_file(BLACKLIST_FILE, "/sys/power"); 726 disable_file(BLACKLIST_FILE, "/sys/power");
701 disable_file(BLACKLIST_FILE, "/sys/kernel/debug"); 727 disable_file(BLACKLIST_FILE, "/sys/kernel/debug");
@@ -739,12 +765,8 @@ void fs_proc_sys_dev_boot(void) {
739 // disable /dev/port 765 // disable /dev/port
740 disable_file(BLACKLIST_FILE, "/dev/port"); 766 disable_file(BLACKLIST_FILE, "/dev/port");
741 767
742
743
744 // disable various ipc sockets in /run/user 768 // disable various ipc sockets in /run/user
745 if (!arg_writable_run_user) { 769 if (!arg_writable_run_user) {
746 struct stat s;
747
748 char *fname; 770 char *fname;
749 if (asprintf(&fname, "/run/user/%d", getuid()) == -1) 771 if (asprintf(&fname, "/run/user/%d", getuid()) == -1)
750 errExit("asprintf"); 772 errExit("asprintf");
@@ -755,8 +777,7 @@ void fs_proc_sys_dev_boot(void) {
755 errExit("asprintf"); 777 errExit("asprintf");
756 if (create_empty_dir_as_user(fnamegpg, 0700)) 778 if (create_empty_dir_as_user(fnamegpg, 0700))
757 fs_logger2("create", fnamegpg); 779 fs_logger2("create", fnamegpg);
758 if (stat(fnamegpg, &s) == 0) 780 disable_file(BLACKLIST_FILE, fnamegpg);
759 disable_file(BLACKLIST_FILE, fnamegpg);
760 free(fnamegpg); 781 free(fnamegpg);
761 782
762 // disable /run/user/{uid}/systemd 783 // disable /run/user/{uid}/systemd
@@ -765,8 +786,7 @@ void fs_proc_sys_dev_boot(void) {
765 errExit("asprintf"); 786 errExit("asprintf");
766 if (create_empty_dir_as_user(fnamesysd, 0755)) 787 if (create_empty_dir_as_user(fnamesysd, 0755))
767 fs_logger2("create", fnamesysd); 788 fs_logger2("create", fnamesysd);
768 if (stat(fnamesysd, &s) == 0) 789 disable_file(BLACKLIST_FILE, fnamesysd);
769 disable_file(BLACKLIST_FILE, fnamesysd);
770 free(fnamesysd); 790 free(fnamesysd);
771 } 791 }
772 free(fname); 792 free(fname);
@@ -777,30 +797,26 @@ void fs_proc_sys_dev_boot(void) {
777 disable_file(BLACKLIST_FILE, "/dev/kmsg"); 797 disable_file(BLACKLIST_FILE, "/dev/kmsg");
778 disable_file(BLACKLIST_FILE, "/proc/kmsg"); 798 disable_file(BLACKLIST_FILE, "/proc/kmsg");
779 } 799 }
800
801 EUID_ROOT();
780} 802}
781 803
782// disable firejail configuration in ~/.config/firejail 804// disable firejail configuration in ~/.config/firejail
783void disable_config(void) { 805void disable_config(void) {
784 struct stat s; 806 EUID_USER();
785
786 char *fname; 807 char *fname;
787 if (asprintf(&fname, "%s/.config/firejail", cfg.homedir) == -1) 808 if (asprintf(&fname, "%s/.config/firejail", cfg.homedir) == -1)
788 errExit("asprintf"); 809 errExit("asprintf");
789 if (stat(fname, &s) == 0) 810 disable_file(BLACKLIST_FILE, fname);
790 disable_file(BLACKLIST_FILE, fname);
791 free(fname); 811 free(fname);
792 812
793 // disable run time information 813 // disable run time information
794 if (stat(RUN_FIREJAIL_NETWORK_DIR, &s) == 0) 814 disable_file(BLACKLIST_FILE, RUN_FIREJAIL_NETWORK_DIR);
795 disable_file(BLACKLIST_FILE, RUN_FIREJAIL_NETWORK_DIR); 815 disable_file(BLACKLIST_FILE, RUN_FIREJAIL_BANDWIDTH_DIR);
796 if (stat(RUN_FIREJAIL_BANDWIDTH_DIR, &s) == 0) 816 disable_file(BLACKLIST_FILE, RUN_FIREJAIL_NAME_DIR);
797 disable_file(BLACKLIST_FILE, RUN_FIREJAIL_BANDWIDTH_DIR); 817 disable_file(BLACKLIST_FILE, RUN_FIREJAIL_PROFILE_DIR);
798 if (stat(RUN_FIREJAIL_NAME_DIR, &s) == 0) 818 disable_file(BLACKLIST_FILE, RUN_FIREJAIL_X11_DIR);
799 disable_file(BLACKLIST_FILE, RUN_FIREJAIL_NAME_DIR); 819 EUID_ROOT();
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} 820}
805 821
806 822
@@ -862,6 +878,7 @@ void fs_basic_fs(void) {
862#ifdef HAVE_OVERLAYFS 878#ifdef HAVE_OVERLAYFS
863char *fs_check_overlay_dir(const char *subdirname, int allow_reuse) { 879char *fs_check_overlay_dir(const char *subdirname, int allow_reuse) {
864 assert(subdirname); 880 assert(subdirname);
881 EUID_ASSERT();
865 struct stat s; 882 struct stat s;
866 char *dirname; 883 char *dirname;
867 884
@@ -1221,6 +1238,7 @@ void fs_overlayfs(void) {
1221 1238
1222// this function is called from sandbox.c before blacklist/whitelist functions 1239// this function is called from sandbox.c before blacklist/whitelist functions
1223void fs_private_tmp(void) { 1240void fs_private_tmp(void) {
1241 EUID_ASSERT();
1224 if (arg_debug) 1242 if (arg_debug)
1225 printf("Generate private-tmp whitelist commands\n"); 1243 printf("Generate private-tmp whitelist commands\n");
1226 1244
@@ -1241,8 +1259,8 @@ void fs_private_tmp(void) {
1241 1259
1242 // whitelist x11 directory 1260 // whitelist x11 directory
1243 profile_add("whitelist /tmp/.X11-unix"); 1261 profile_add("whitelist /tmp/.X11-unix");
1244 // read-only x11 directory 1262 // read-only x11 directory
1245 profile_add("read-only /tmp/.X11-unix"); 1263 profile_add("read-only /tmp/.X11-unix");
1246 1264
1247 // whitelist any pulse* file in /tmp directory 1265 // whitelist any pulse* file in /tmp directory
1248 // some distros use PulseAudio sockets under /tmp instead of the socket in /urn/user 1266 // some distros use PulseAudio sockets under /tmp instead of the socket in /urn/user
diff --git a/src/firejail/fs_dev.c b/src/firejail/fs_dev.c
index 8c2870a4d..8cc3ecc62 100644
--- a/src/firejail/fs_dev.c
+++ b/src/firejail/fs_dev.c
@@ -187,8 +187,10 @@ static void mount_dev_shm(void) {
187static void process_dev_shm(void) { 187static void process_dev_shm(void) {
188 // Jack audio keeps an Unix socket under (/dev/shm/jack_default_1000_0 or /dev/shm/jack/...) 188 // Jack audio keeps an Unix socket under (/dev/shm/jack_default_1000_0 or /dev/shm/jack/...)
189 // looking for jack socket 189 // looking for jack socket
190 EUID_USER();
190 glob_t globbuf; 191 glob_t globbuf;
191 int globerr = glob(RUN_DEV_DIR "/shm/jack*", GLOB_NOSORT, NULL, &globbuf); 192 int globerr = glob(RUN_DEV_DIR "/shm/jack*", GLOB_NOSORT, NULL, &globbuf);
193 EUID_ROOT();
192 if (globerr && !arg_keep_dev_shm) { 194 if (globerr && !arg_keep_dev_shm) {
193 empty_dev_shm(); 195 empty_dev_shm();
194 return; 196 return;
diff --git a/src/firejail/fs_home.c b/src/firejail/fs_home.c
index 4bcefa443..eab952eb8 100644
--- a/src/firejail/fs_home.c
+++ b/src/firejail/fs_home.c
@@ -42,15 +42,14 @@ static void skel(const char *homedir, uid_t u, gid_t g) {
42 // copy skel files 42 // copy skel files
43 if (asprintf(&fname, "%s/.zshrc", homedir) == -1) 43 if (asprintf(&fname, "%s/.zshrc", homedir) == -1)
44 errExit("asprintf"); 44 errExit("asprintf");
45 struct stat s;
46 // don't copy it if we already have the file 45 // don't copy it if we already have the file
47 if (stat(fname, &s) == 0) 46 if (access(fname, F_OK) == 0)
48 return; 47 return;
49 if (is_link(fname)) { // stat on dangling symlinks fails, try again using lstat 48 if (is_link(fname)) { // access(3) on dangling symlinks fails, try again using lstat
50 fprintf(stderr, "Error: invalid %s file\n", fname); 49 fprintf(stderr, "Error: invalid %s file\n", fname);
51 exit(1); 50 exit(1);
52 } 51 }
53 if (stat("/etc/skel/.zshrc", &s) == 0) { 52 if (access("/etc/skel/.zshrc", R_OK) == 0) {
54 copy_file_as_user("/etc/skel/.zshrc", fname, u, g, 0644); // regular user 53 copy_file_as_user("/etc/skel/.zshrc", fname, u, g, 0644); // regular user
55 fs_logger("clone /etc/skel/.zshrc"); 54 fs_logger("clone /etc/skel/.zshrc");
56 fs_logger2("clone", fname); 55 fs_logger2("clone", fname);
@@ -67,16 +66,14 @@ static void skel(const char *homedir, uid_t u, gid_t g) {
67 // copy skel files 66 // copy skel files
68 if (asprintf(&fname, "%s/.cshrc", homedir) == -1) 67 if (asprintf(&fname, "%s/.cshrc", homedir) == -1)
69 errExit("asprintf"); 68 errExit("asprintf");
70 struct stat s;
71
72 // don't copy it if we already have the file 69 // don't copy it if we already have the file
73 if (stat(fname, &s) == 0) 70 if (access(fname, F_OK) == 0)
74 return; 71 return;
75 if (is_link(fname)) { // stat on dangling symlinks fails, try again using lstat 72 if (is_link(fname)) { // access(3) on dangling symlinks fails, try again using lstat
76 fprintf(stderr, "Error: invalid %s file\n", fname); 73 fprintf(stderr, "Error: invalid %s file\n", fname);
77 exit(1); 74 exit(1);
78 } 75 }
79 if (stat("/etc/skel/.cshrc", &s) == 0) { 76 if (access("/etc/skel/.cshrc", R_OK) == 0) {
80 copy_file_as_user("/etc/skel/.cshrc", fname, u, g, 0644); // regular user 77 copy_file_as_user("/etc/skel/.cshrc", fname, u, g, 0644); // regular user
81 fs_logger("clone /etc/skel/.cshrc"); 78 fs_logger("clone /etc/skel/.cshrc");
82 fs_logger2("clone", fname); 79 fs_logger2("clone", fname);
@@ -93,15 +90,14 @@ static void skel(const char *homedir, uid_t u, gid_t g) {
93 // copy skel files 90 // copy skel files
94 if (asprintf(&fname, "%s/.bashrc", homedir) == -1) 91 if (asprintf(&fname, "%s/.bashrc", homedir) == -1)
95 errExit("asprintf"); 92 errExit("asprintf");
96 struct stat s;
97 // don't copy it if we already have the file 93 // don't copy it if we already have the file
98 if (stat(fname, &s) == 0) 94 if (access(fname, F_OK) == 0)
99 return; 95 return;
100 if (is_link(fname)) { // stat on dangling symlinks fails, try again using lstat 96 if (is_link(fname)) { // access(3) on dangling symlinks fails, try again using lstat
101 fprintf(stderr, "Error: invalid %s file\n", fname); 97 fprintf(stderr, "Error: invalid %s file\n", fname);
102 exit(1); 98 exit(1);
103 } 99 }
104 if (stat("/etc/skel/.bashrc", &s) == 0) { 100 if (access("/etc/skel/.bashrc", R_OK) == 0) {
105 copy_file_as_user("/etc/skel/.bashrc", fname, u, g, 0644); // regular user 101 copy_file_as_user("/etc/skel/.bashrc", fname, u, g, 0644); // regular user
106 fs_logger("clone /etc/skel/.bashrc"); 102 fs_logger("clone /etc/skel/.bashrc");
107 fs_logger2("clone", fname); 103 fs_logger2("clone", fname);
@@ -122,8 +118,8 @@ static int store_xauthority(void) {
122 errExit("asprintf"); 118 errExit("asprintf");
123 119
124 struct stat s; 120 struct stat s;
125 if (stat(src, &s) == 0) { 121 if (lstat_as_user(src, &s) == 0) {
126 if (is_link(src)) { 122 if (S_ISLNK(s.st_mode)) {
127 fwarning("invalid .Xauthority file\n"); 123 fwarning("invalid .Xauthority file\n");
128 free(src); 124 free(src);
129 return 0; 125 return 0;
@@ -161,11 +157,11 @@ static int store_asoundrc(void) {
161 errExit("asprintf"); 157 errExit("asprintf");
162 158
163 struct stat s; 159 struct stat s;
164 if (stat(src, &s) == 0) { 160 if (lstat_as_user(src, &s) == 0) {
165 if (is_link(src)) { 161 if (S_ISLNK(s.st_mode)) {
166 // make sure the real path of the file is inside the home directory 162 // make sure the real path of the file is inside the home directory
167 /* coverity[toctou] */ 163 /* coverity[toctou] */
168 char* rp = realpath(src, NULL); 164 char *rp = realpath_as_user(src);
169 if (!rp) { 165 if (!rp) {
170 fprintf(stderr, "Error: Cannot access %s\n", src); 166 fprintf(stderr, "Error: Cannot access %s\n", src);
171 exit(1); 167 exit(1);
@@ -234,6 +230,7 @@ static void copy_asoundrc(void) {
234 } 230 }
235 231
236 copy_file_as_user(src, dest, getuid(), getgid(), S_IRUSR | S_IWUSR); // regular user 232 copy_file_as_user(src, dest, getuid(), getgid(), S_IRUSR | S_IWUSR); // regular user
233 selinux_relabel_path(dest, src);
237 fs_logger2("clone", dest); 234 fs_logger2("clone", dest);
238 free(dest); 235 free(dest);
239 236
@@ -262,6 +259,7 @@ void fs_private_homedir(void) {
262 if (arg_debug) 259 if (arg_debug)
263 printf("Mount-bind %s on top of %s\n", private_homedir, homedir); 260 printf("Mount-bind %s on top of %s\n", private_homedir, homedir);
264 // get file descriptors for homedir and private_homedir, fails if there is any symlink 261 // get file descriptors for homedir and private_homedir, fails if there is any symlink
262 EUID_USER();
265 int src = safer_openat(-1, private_homedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); 263 int src = safer_openat(-1, private_homedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
266 if (src == -1) 264 if (src == -1)
267 errExit("opening private directory"); 265 errExit("opening private directory");
@@ -286,17 +284,10 @@ void fs_private_homedir(void) {
286 exit(1); 284 exit(1);
287 } 285 }
288 // mount via the links in /proc/self/fd 286 // mount via the links in /proc/self/fd
289 char *proc_src, *proc_dst; 287 EUID_ROOT();
290 if (asprintf(&proc_src, "/proc/self/fd/%d", src) == -1) 288 if (bind_mount_by_fd(src, dst))
291 errExit("asprintf");
292 if (asprintf(&proc_dst, "/proc/self/fd/%d", dst) == -1)
293 errExit("asprintf");
294 if (mount(proc_src, proc_dst, NULL, MS_NOSUID | MS_NODEV | MS_BIND | MS_REC, NULL) < 0)
295 errExit("mount bind"); 289 errExit("mount bind");
296 free(proc_src); 290
297 free(proc_dst);
298 close(src);
299 close(dst);
300 // check /proc/self/mountinfo to confirm the mount is ok 291 // check /proc/self/mountinfo to confirm the mount is ok
301 MountData *mptr = get_last_mount(); 292 MountData *mptr = get_last_mount();
302 size_t len = strlen(homedir); 293 size_t len = strlen(homedir);
@@ -304,6 +295,8 @@ void fs_private_homedir(void) {
304 (*(mptr->dir + len) != '\0' && *(mptr->dir + len) != '/')) 295 (*(mptr->dir + len) != '\0' && *(mptr->dir + len) != '/'))
305 errLogExit("invalid private mount"); 296 errLogExit("invalid private mount");
306 297
298 close(src);
299 close(dst);
307 fs_logger3("mount-bind", private_homedir, homedir); 300 fs_logger3("mount-bind", private_homedir, homedir);
308 fs_logger2("whitelist", homedir); 301 fs_logger2("whitelist", homedir);
309// preserve mode and ownership 302// preserve mode and ownership
@@ -438,6 +431,7 @@ void fs_check_private_cwd(const char *dir) {
438// --private-home 431// --private-home
439//*********************************************************************************** 432//***********************************************************************************
440static char *check_dir_or_file(const char *name) { 433static char *check_dir_or_file(const char *name) {
434 EUID_ASSERT();
441 assert(name); 435 assert(name);
442 436
443 // basic checks 437 // basic checks
@@ -498,6 +492,7 @@ errexit:
498} 492}
499 493
500static void duplicate(char *name) { 494static void duplicate(char *name) {
495 EUID_ASSERT();
501 char *fname = check_dir_or_file(name); 496 char *fname = check_dir_or_file(name);
502 497
503 if (arg_debug) 498 if (arg_debug)
@@ -553,10 +548,10 @@ void fs_private_home_list(void) {
553 selinux_relabel_path(RUN_HOME_DIR, homedir); 548 selinux_relabel_path(RUN_HOME_DIR, homedir);
554 fs_logger_print(); // save the current log 549 fs_logger_print(); // save the current log
555 550
551 // copy the list of files in the new home directory
552 EUID_USER();
556 if (arg_debug) 553 if (arg_debug)
557 printf("Copying files in the new home:\n"); 554 printf("Copying files in the new home:\n");
558
559 // copy the list of files in the new home directory
560 char *dlist = strdup(cfg.home_private_keep); 555 char *dlist = strdup(cfg.home_private_keep);
561 if (!dlist) 556 if (!dlist)
562 errExit("strdup"); 557 errExit("strdup");
@@ -589,13 +584,11 @@ void fs_private_home_list(void) {
589 exit(1); 584 exit(1);
590 } 585 }
591 // mount using the file descriptor 586 // mount using the file descriptor
592 char *proc; 587 EUID_ROOT();
593 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) 588 if (bind_mount_path_to_fd(RUN_HOME_DIR, fd))
594 errExit("asprintf");
595 if (mount(RUN_HOME_DIR, proc, NULL, MS_BIND|MS_REC, NULL) < 0)
596 errExit("mount bind"); 589 errExit("mount bind");
597 free(proc);
598 close(fd); 590 close(fd);
591
599 // check /proc/self/mountinfo to confirm the mount is ok 592 // check /proc/self/mountinfo to confirm the mount is ok
600 MountData *mptr = get_last_mount(); 593 MountData *mptr = get_last_mount();
601 if (strcmp(mptr->dir, homedir) != 0 || strcmp(mptr->fstype, "tmpfs") != 0) 594 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..9d7a17cf3 100644
--- a/src/firejail/fs_lib.c
+++ b/src/firejail/fs_lib.c
@@ -178,8 +178,7 @@ void fslib_mount(const char *full_path) {
178 178
179 if (*full_path == '\0' || 179 if (*full_path == '\0' ||
180 !valid_full_path(full_path) || 180 !valid_full_path(full_path) ||
181 access(full_path, F_OK) != 0 || 181 stat_as_user(full_path, &s) != 0 ||
182 stat(full_path, &s) != 0 ||
183 s.st_uid != 0) 182 s.st_uid != 0)
184 return; 183 return;
185 184
@@ -203,7 +202,7 @@ void fslib_mount_libs(const char *full_path, unsigned user) {
203 } 202 }
204 203
205 if (arg_debug || arg_debug_private_lib) 204 if (arg_debug || arg_debug_private_lib)
206 printf(" fslib_mount_libs %s (parse as %s)\n", full_path, user ? "user" : "root"); 205 printf(" fslib_mount_libs %s\n", full_path);
207 // create an empty RUN_LIB_FILE and allow the user to write to it 206 // create an empty RUN_LIB_FILE and allow the user to write to it
208 unlink(RUN_LIB_FILE); // in case is there 207 unlink(RUN_LIB_FILE); // in case is there
209 create_empty_file_as_root(RUN_LIB_FILE, 0644); 208 create_empty_file_as_root(RUN_LIB_FILE, 0644);
@@ -212,7 +211,7 @@ void fslib_mount_libs(const char *full_path, unsigned user) {
212 211
213 // run fldd to extract the list of files 212 // run fldd to extract the list of files
214 if (arg_debug || arg_debug_private_lib) 213 if (arg_debug || arg_debug_private_lib)
215 printf(" running fldd %s\n", full_path); 214 printf(" running fldd %s as %s\n", full_path, user ? "user" : "root");
216 unsigned mask; 215 unsigned mask;
217 if (user) 216 if (user)
218 mask = SBOX_USER; 217 mask = SBOX_USER;
@@ -246,7 +245,7 @@ static void load_library(const char *fname) {
246 245
247 // existing file owned by root 246 // existing file owned by root
248 struct stat s; 247 struct stat s;
249 if (!access(fname, F_OK) && stat(fname, &s) == 0 && s.st_uid == 0) { 248 if (stat_as_user(fname, &s) == 0 && s.st_uid == 0) {
250 // load directories, regular 64 bit libraries, and 64 bit executables 249 // load directories, regular 64 bit libraries, and 64 bit executables
251 if (S_ISDIR(s.st_mode)) 250 if (S_ISDIR(s.st_mode))
252 fslib_mount(fname); 251 fslib_mount(fname);
@@ -286,19 +285,21 @@ static void install_list_entry(const char *lib) {
286#define DO_GLOBBING 285#define DO_GLOBBING
287#ifdef DO_GLOBBING 286#ifdef DO_GLOBBING
288 // globbing 287 // globbing
288 EUID_USER();
289 glob_t globbuf; 289 glob_t globbuf;
290 int globerr = glob(fname, GLOB_NOCHECK | GLOB_NOSORT | GLOB_PERIOD, NULL, &globbuf); 290 int globerr = glob(fname, GLOB_NOCHECK | GLOB_NOSORT | GLOB_PERIOD, NULL, &globbuf);
291 if (globerr) { 291 if (globerr) {
292 fprintf(stderr, "Error: failed to glob private-lib pattern %s\n", fname); 292 fprintf(stderr, "Error: failed to glob private-lib pattern %s\n", fname);
293 exit(1); 293 exit(1);
294 } 294 }
295 EUID_ROOT();
295 size_t j; 296 size_t j;
296 for (j = 0; j < globbuf.gl_pathc; j++) { 297 for (j = 0; j < globbuf.gl_pathc; j++) {
297 assert(globbuf.gl_pathv[j]); 298 assert(globbuf.gl_pathv[j]);
298//printf("glob %s\n", globbuf.gl_pathv[j]); 299//printf("glob %s\n", globbuf.gl_pathv[j]);
299 // GLOB_NOCHECK - no pattern matched returns the original pattern; try to load it anyway 300 // GLOB_NOCHECK - no pattern matched returns the original pattern; try to load it anyway
300 301
301 // foobar/* includes foobar/. and foobar/.. 302 // foobar/* expands to foobar/. and foobar/..
302 const char *base = gnu_basename(globbuf.gl_pathv[j]); 303 const char *base = gnu_basename(globbuf.gl_pathv[j]);
303 if (strcmp(base, ".") == 0 || strcmp(base, "..") == 0) 304 if (strcmp(base, ".") == 0 || strcmp(base, "..") == 0)
304 continue; 305 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_var.c b/src/firejail/fs_var.c
index bae3d6df0..20e262d80 100644
--- a/src/firejail/fs_var.c
+++ b/src/firejail/fs_var.c
@@ -323,4 +323,8 @@ void fs_var_utmp(void) {
323 if (mount(RUN_UTMP_FILE, UTMP_FILE, NULL, MS_BIND|MS_NOSUID|MS_NOEXEC | MS_NODEV | MS_REC, NULL) < 0) 323 if (mount(RUN_UTMP_FILE, UTMP_FILE, NULL, MS_BIND|MS_NOSUID|MS_NOEXEC | MS_NODEV | MS_REC, NULL) < 0)
324 errExit("mount bind utmp"); 324 errExit("mount bind utmp");
325 fs_logger2("create", UTMP_FILE); 325 fs_logger2("create", UTMP_FILE);
326
327 // blacklist RUN_UTMP_FILE
328 if (mount(RUN_RO_FILE, RUN_UTMP_FILE, NULL, MS_BIND, "mode=400,gid=0") < 0)
329 errExit("mount bind");
326} 330}
diff --git a/src/firejail/fs_whitelist.c b/src/firejail/fs_whitelist.c
index 258f023f6..370035a4d 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);
@@ -267,6 +257,7 @@ static void whitelist_symlink(const char *link, const char *target) {
267} 257}
268 258
269static void globbing(const char *pattern) { 259static void globbing(const char *pattern) {
260 EUID_ASSERT();
270 assert(pattern); 261 assert(pattern);
271 262
272 // globbing 263 // globbing
@@ -341,12 +332,8 @@ static void tmpfs_topdirs(const TopDir *topdirs) {
341 // restore /run/firejail directory 332 // restore /run/firejail directory
342 if (mkdir(RUN_FIREJAIL_DIR, 0755) == -1) 333 if (mkdir(RUN_FIREJAIL_DIR, 0755) == -1)
343 errExit("mkdir"); 334 errExit("mkdir");
344 char *proc; 335 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"); 336 errExit("mount bind");
349 free(proc);
350 close(fd); 337 close(fd);
351 fs_logger2("whitelist", RUN_FIREJAIL_DIR); 338 fs_logger2("whitelist", RUN_FIREJAIL_DIR);
352 339
diff --git a/src/firejail/macros.c b/src/firejail/macros.c
index bcac1feb4..cd29d8f85 100644
--- a/src/firejail/macros.c
+++ b/src/firejail/macros.c
@@ -149,6 +149,7 @@ static char *resolve_xdg(const char *var) {
149 149
150// returns mallocated memory 150// returns mallocated memory
151static char *resolve_hardcoded(char *entries[]) { 151static char *resolve_hardcoded(char *entries[]) {
152 EUID_ASSERT();
152 char *fname; 153 char *fname;
153 struct stat s; 154 struct stat s;
154 155
diff --git a/src/firejail/main.c b/src/firejail/main.c
index 7ec2d6114..12ac01de7 100644
--- a/src/firejail/main.c
+++ b/src/firejail/main.c
@@ -862,12 +862,11 @@ static void run_cmd_and_exit(int i, int argc, char **argv) {
862char *guess_shell(void) { 862char *guess_shell(void) {
863 const char *shell; 863 const char *shell;
864 char *retval; 864 char *retval;
865 struct stat s;
866 865
867 shell = env_get("SHELL"); 866 shell = env_get("SHELL");
868 if (shell) { 867 if (shell) {
869 invalid_filename(shell, 0); // no globbing 868 invalid_filename(shell, 0); // no globbing
870 if (!is_dir(shell) && strstr(shell, "..") == NULL && stat(shell, &s) == 0 && access(shell, X_OK) == 0 && 869 if (access(shell, X_OK) == 0 && !is_dir(shell) && strstr(shell, "..") == NULL &&
871 strcmp(shell, PATH_FIREJAIL) != 0) 870 strcmp(shell, PATH_FIREJAIL) != 0)
872 goto found; 871 goto found;
873 } 872 }
@@ -878,12 +877,15 @@ char *guess_shell(void) {
878 int i = 0; 877 int i = 0;
879 while (shells[i] != NULL) { 878 while (shells[i] != NULL) {
880 // access call checks as real UID/GID, not as effective UID/GID 879 // access call checks as real UID/GID, not as effective UID/GID
881 if (stat(shells[i], &s) == 0 && access(shells[i], X_OK) == 0) { 880 if (access(shells[i], X_OK) == 0) {
882 shell = shells[i]; 881 shell = shells[i];
883 break; 882 goto found;
884 } 883 }
885 i++; 884 i++;
886 } 885 }
886
887 return NULL;
888
887 found: 889 found:
888 retval = strdup(shell); 890 retval = strdup(shell);
889 if (!retval) 891 if (!retval)
diff --git a/src/firejail/mountinfo.c b/src/firejail/mountinfo.c
index a700729d3..64a94bd84 100644
--- a/src/firejail/mountinfo.c
+++ b/src/firejail/mountinfo.c
@@ -22,7 +22,7 @@
22 22
23#include <fcntl.h> 23#include <fcntl.h>
24#ifndef O_PATH 24#ifndef O_PATH
25# define O_PATH 010000000 25#define O_PATH 010000000
26#endif 26#endif
27 27
28#define MAX_BUF 4096 28#define MAX_BUF 4096
@@ -153,6 +153,7 @@ MountData *get_last_mount(void) {
153 153
154// Extract the mount id from /proc/self/fdinfo and return it. 154// Extract the mount id from /proc/self/fdinfo and return it.
155int get_mount_id(const char *path) { 155int get_mount_id(const char *path) {
156 EUID_ASSERT();
156 assert(path); 157 assert(path);
157 158
158 int fd = open(path, O_PATH|O_CLOEXEC); 159 int fd = open(path, O_PATH|O_CLOEXEC);
@@ -162,7 +163,9 @@ int get_mount_id(const char *path) {
162 char *fdinfo; 163 char *fdinfo;
163 if (asprintf(&fdinfo, "/proc/self/fdinfo/%d", fd) == -1) 164 if (asprintf(&fdinfo, "/proc/self/fdinfo/%d", fd) == -1)
164 errExit("asprintf"); 165 errExit("asprintf");
166 EUID_ROOT();
165 FILE *fp = fopen(fdinfo, "re"); 167 FILE *fp = fopen(fdinfo, "re");
168 EUID_USER();
166 free(fdinfo); 169 free(fdinfo);
167 if (!fp) 170 if (!fp)
168 goto errexit; 171 goto errexit;
diff --git a/src/firejail/paths.c b/src/firejail/paths.c
index b800fa944..d58a9d272 100644
--- a/src/firejail/paths.c
+++ b/src/firejail/paths.c
@@ -136,7 +136,7 @@ int program_in_path(const char *program) {
136 // ('x' permission means something different for directories). 136 // ('x' permission means something different for directories).
137 // exec follows symlinks, so use stat, not lstat. 137 // exec follows symlinks, so use stat, not lstat.
138 struct stat st; 138 struct stat st;
139 if (stat(scratch, &st)) { 139 if (stat_as_user(scratch, &st)) {
140 perror(scratch); 140 perror(scratch);
141 exit(1); 141 exit(1);
142 } 142 }
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 53e395b89..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);
@@ -246,6 +238,11 @@ static void sanitize_passwd(void) {
246 // mount-bind tne new password file 238 // mount-bind tne new password file
247 if (mount(RUN_PASSWD_FILE, "/etc/passwd", "none", MS_BIND, "mode=400,gid=0") < 0) 239 if (mount(RUN_PASSWD_FILE, "/etc/passwd", "none", MS_BIND, "mode=400,gid=0") < 0)
248 errExit("mount"); 240 errExit("mount");
241
242 // blacklist RUN_PASSWD_FILE
243 if (mount(RUN_RO_FILE, RUN_PASSWD_FILE, "none", MS_BIND, "mode=400,gid=0") < 0)
244 errExit("mount");
245
249 fs_logger("create /etc/passwd"); 246 fs_logger("create /etc/passwd");
250 247
251 return; 248 return;
@@ -376,6 +373,11 @@ static void sanitize_group(void) {
376 // mount-bind tne new group file 373 // mount-bind tne new group file
377 if (mount(RUN_GROUP_FILE, "/etc/group", "none", MS_BIND, "mode=400,gid=0") < 0) 374 if (mount(RUN_GROUP_FILE, "/etc/group", "none", MS_BIND, "mode=400,gid=0") < 0)
378 errExit("mount"); 375 errExit("mount");
376
377 // blacklist RUN_GROUP_FILE
378 if (mount(RUN_RO_FILE, RUN_GROUP_FILE, "none", MS_BIND, "mode=400,gid=0") < 0)
379 errExit("mount");
380
379 fs_logger("create /etc/group"); 381 fs_logger("create /etc/group");
380 382
381 return; 383 return;
diff --git a/src/firejail/sbox.c b/src/firejail/sbox.c
index d2c0bcc19..37111324a 100644
--- a/src/firejail/sbox.c
+++ b/src/firejail/sbox.c
@@ -265,7 +265,6 @@ int sbox_run(unsigned filtermask, int num, ...) {
265} 265}
266 266
267int sbox_run_v(unsigned filtermask, char * const arg[]) { 267int sbox_run_v(unsigned filtermask, char * const arg[]) {
268 EUID_ROOT();
269 assert(arg); 268 assert(arg);
270 269
271 if (arg_debug) { 270 if (arg_debug) {
@@ -285,6 +284,7 @@ int sbox_run_v(unsigned filtermask, char * const arg[]) {
285 if (child < 0) 284 if (child < 0)
286 errExit("fork"); 285 errExit("fork");
287 if (child == 0) { 286 if (child == 0) {
287 EUID_ROOT();
288 sbox_do_exec_v(filtermask, arg); 288 sbox_do_exec_v(filtermask, arg);
289 } 289 }
290 290
diff --git a/src/firejail/selinux.c b/src/firejail/selinux.c
index 06189d7f6..6969e7a3d 100644
--- a/src/firejail/selinux.c
+++ b/src/firejail/selinux.c
@@ -19,10 +19,13 @@
19*/ 19*/
20#if HAVE_SELINUX 20#if HAVE_SELINUX
21#include "firejail.h" 21#include "firejail.h"
22
23#include <sys/types.h> 22#include <sys/types.h>
24#include <sys/stat.h> 23#include <sys/stat.h>
24
25#include <fcntl.h> 25#include <fcntl.h>
26#ifndef O_PATH
27#define O_PATH 010000000
28#endif
26 29
27#include <selinux/context.h> 30#include <selinux/context.h>
28#include <selinux/label.h> 31#include <selinux/label.h>
@@ -52,8 +55,9 @@ void selinux_relabel_path(const char *path, const char *inside_path)
52 if (!label_hnd) 55 if (!label_hnd)
53 errExit("selabel_open"); 56 errExit("selabel_open");
54 57
55 /* Open the file as O_PATH, to pin it while we determine and adjust the label */ 58 /* Open the file as O_PATH, to pin it while we determine and adjust the label
56 fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH); 59 * Defeat symlink races by not allowing symbolic links */
60 fd = safer_openat(-1, path, O_NOFOLLOW|O_CLOEXEC|O_PATH);
57 if (fd < 0) 61 if (fd < 0)
58 return; 62 return;
59 if (fstat(fd, &st) < 0) 63 if (fstat(fd, &st) < 0)
diff --git a/src/firejail/util.c b/src/firejail/util.c
index dc9fe2449..b8643ff60 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);
@@ -417,6 +417,13 @@ int is_dir(const char *fname) {
417 if (*fname == '\0') 417 if (*fname == '\0')
418 return 0; 418 return 0;
419 419
420 int called_as_root = 0;
421 if (geteuid() == 0)
422 called_as_root = 1;
423
424 if (called_as_root)
425 EUID_USER();
426
420 // if fname doesn't end in '/', add one 427 // if fname doesn't end in '/', add one
421 int rv; 428 int rv;
422 struct stat s; 429 struct stat s;
@@ -432,6 +439,9 @@ int is_dir(const char *fname) {
432 free(tmp); 439 free(tmp);
433 } 440 }
434 441
442 if (called_as_root)
443 EUID_ROOT();
444
435 if (rv == -1) 445 if (rv == -1)
436 return 0; 446 return 0;
437 447
@@ -447,6 +457,14 @@ int is_link(const char *fname) {
447 if (*fname == '\0') 457 if (*fname == '\0')
448 return 0; 458 return 0;
449 459
460 int called_as_root = 0;
461 if (geteuid() == 0)
462 called_as_root = 1;
463
464 if (called_as_root)
465 EUID_USER();
466
467 // remove trailing '/' if any
450 char *tmp = strdup(fname); 468 char *tmp = strdup(fname);
451 if (!tmp) 469 if (!tmp)
452 errExit("strdup"); 470 errExit("strdup");
@@ -456,9 +474,66 @@ int is_link(const char *fname) {
456 ssize_t rv = readlink(tmp, &c, 1); 474 ssize_t rv = readlink(tmp, &c, 1);
457 free(tmp); 475 free(tmp);
458 476
477 if (called_as_root)
478 EUID_ROOT();
479
459 return (rv != -1); 480 return (rv != -1);
460} 481}
461 482
483char *realpath_as_user(const char *fname) {
484 assert(fname);
485
486 int called_as_root = 0;
487 if (geteuid() == 0)
488 called_as_root = 1;
489
490 if (called_as_root)
491 EUID_USER();
492
493 char *rv = realpath(fname, NULL);
494
495 if (called_as_root)
496 EUID_ROOT();
497
498 return rv;
499}
500
501int stat_as_user(const char *fname, struct stat *s) {
502 assert(fname);
503
504 int called_as_root = 0;
505 if (geteuid() == 0)
506 called_as_root = 1;
507
508 if (called_as_root)
509 EUID_USER();
510
511 int rv = stat(fname, s);
512
513 if (called_as_root)
514 EUID_ROOT();
515
516 return rv;
517}
518
519int lstat_as_user(const char *fname, struct stat *s) {
520 assert(fname);
521
522 int called_as_root = 0;
523 if (geteuid() == 0)
524 called_as_root = 1;
525
526 if (called_as_root)
527 EUID_USER();
528
529 int rv = lstat(fname, s);
530
531 if (called_as_root)
532 EUID_ROOT();
533
534 return rv;
535}
536
462// remove all slashes and single dots from the end of a path 537// remove all slashes and single dots from the end of a path
463// for example /foo/bar///././. -> /foo/bar 538// for example /foo/bar///././. -> /foo/bar
464void trim_trailing_slash_or_dot(char *path) { 539void trim_trailing_slash_or_dot(char *path) {
@@ -891,35 +966,37 @@ static int remove_callback(const char *fpath, const struct stat *sb, int typefla
891 966
892int remove_overlay_directory(void) { 967int remove_overlay_directory(void) {
893 EUID_ASSERT(); 968 EUID_ASSERT();
894 struct stat s;
895 sleep(1); 969 sleep(1);
896 970
897 char *path; 971 char *path;
898 if (asprintf(&path, "%s/.firejail", cfg.homedir) == -1) 972 if (asprintf(&path, "%s/.firejail", cfg.homedir) == -1)
899 errExit("asprintf"); 973 errExit("asprintf");
900 974
901 if (lstat(path, &s) == 0) { 975 if (access(path, F_OK) == 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(); 976 pid_t child = fork();
916 if (child < 0) 977 if (child < 0)
917 errExit("fork"); 978 errExit("fork");
918 if (child == 0) { 979 if (child == 0) {
919 // open ~/.firejail, fails if there is any symlink 980 // open ~/.firejail
920 int fd = safer_openat(-1, path, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); 981 int fd = safer_openat(-1, path, O_PATH|O_NOFOLLOW|O_CLOEXEC);
921 if (fd == -1) 982 if (fd == -1) {
922 errExit("safer_openat"); 983 fprintf(stderr, "Error: cannot open %s\n", path);
984 _exit(1);
985 }
986 struct stat s;
987 if (fstat(fd, &s) == -1)
988 errExit("fstat");
989 if (!S_ISDIR(s.st_mode)) {
990 if (S_ISLNK(s.st_mode))
991 fprintf(stderr, "Error: %s is a symbolic link\n", path);
992 else
993 fprintf(stderr, "Error: %s is not a directory\n", path);
994 _exit(1);
995 }
996 if (s.st_uid != getuid()) {
997 fprintf(stderr, "Error: %s is not owned by the current user\n", path);
998 _exit(1);
999 }
923 // chdir to ~/.firejail 1000 // chdir to ~/.firejail
924 if (fchdir(fd) == -1) 1001 if (fchdir(fd) == -1)
925 errExit("fchdir"); 1002 errExit("fchdir");
@@ -942,7 +1019,7 @@ int remove_overlay_directory(void) {
942 // wait for the child to finish 1019 // wait for the child to finish
943 waitpid(child, NULL, 0); 1020 waitpid(child, NULL, 0);
944 // check if ~/.firejail was deleted 1021 // check if ~/.firejail was deleted
945 if (stat(path, &s) == 0) 1022 if (access(path, F_OK) == 0)
946 return 1; 1023 return 1;
947 } 1024 }
948 return 0; 1025 return 0;
@@ -975,9 +1052,8 @@ void flush_stdin(void) {
975int create_empty_dir_as_user(const char *dir, mode_t mode) { 1052int create_empty_dir_as_user(const char *dir, mode_t mode) {
976 assert(dir); 1053 assert(dir);
977 mode &= 07777; 1054 mode &= 07777;
978 struct stat s;
979 1055
980 if (stat(dir, &s)) { 1056 if (access(dir, F_OK) != 0) {
981 if (arg_debug) 1057 if (arg_debug)
982 printf("Creating empty %s directory\n", dir); 1058 printf("Creating empty %s directory\n", dir);
983 pid_t child = fork(); 1059 pid_t child = fork();
@@ -988,8 +1064,8 @@ int create_empty_dir_as_user(const char *dir, mode_t mode) {
988 drop_privs(0); 1064 drop_privs(0);
989 1065
990 if (mkdir(dir, mode) == 0) { 1066 if (mkdir(dir, mode) == 0) {
991 if (chmod(dir, mode) == -1) 1067 int err = chmod(dir, mode);
992 {;} // do nothing 1068 (void) err;
993 } 1069 }
994 else if (arg_debug) 1070 else if (arg_debug)
995 printf("Directory %s not created: %s\n", dir, strerror(errno)); 1071 printf("Directory %s not created: %s\n", dir, strerror(errno));
@@ -999,7 +1075,7 @@ int create_empty_dir_as_user(const char *dir, mode_t mode) {
999 _exit(0); 1075 _exit(0);
1000 } 1076 }
1001 waitpid(child, NULL, 0); 1077 waitpid(child, NULL, 0);
1002 if (stat(dir, &s) == 0) 1078 if (access(dir, F_OK) == 0)
1003 return 1; 1079 return 1;
1004 } 1080 }
1005 return 0; 1081 return 0;
@@ -1110,20 +1186,35 @@ unsigned extract_timeout(const char *str) {
1110} 1186}
1111 1187
1112void disable_file_or_dir(const char *fname) { 1188void disable_file_or_dir(const char *fname) {
1189 assert(fname);
1190 assert(geteuid() == 0);
1191
1192 EUID_USER();
1193 int fd = open(fname, O_PATH|O_CLOEXEC);
1194 EUID_ROOT();
1195 if (fd < 0)
1196 return;
1197
1113 struct stat s; 1198 struct stat s;
1114 if (stat(fname, &s) != -1) { 1199 if (fstat(fd, &s) < 0) { // FUSE
1115 if (arg_debug) 1200 if (errno != EACCES)
1116 printf("blacklist %s\n", fname); 1201 errExit("fstat");
1117 if (is_dir(fname)) { 1202 close(fd);
1118 if (mount(RUN_RO_DIR, fname, "none", MS_BIND, "mode=400,gid=0") < 0) 1203 return;
1204 }
1205
1206 if (arg_debug)
1207 printf("blacklist %s\n", fname);
1208 if (S_ISDIR(s.st_mode)) {
1209 if (bind_mount_path_to_fd(RUN_RO_DIR, fd) < 0)
1119 errExit("disable directory"); 1210 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 } 1211 }
1212 else {
1213 if (bind_mount_path_to_fd(RUN_RO_FILE, fd) < 0)
1214 errExit("disable file");
1215 }
1216 close(fd);
1217 fs_logger2("blacklist", fname);
1127} 1218}
1128 1219
1129void disable_file_path(const char *path, const char *file) { 1220void disable_file_path(const char *path, const char *file) {
@@ -1210,6 +1301,60 @@ int safer_openat(int dirfd, const char *path, int flags) {
1210 return fd; 1301 return fd;
1211} 1302}
1212 1303
1304int remount_by_fd(int dst, unsigned long mountflags) {
1305 char *proc;
1306 if (asprintf(&proc, "/proc/self/fd/%d", dst) < 0)
1307 errExit("asprintf");
1308
1309 int rv = mount(NULL, proc, NULL, mountflags|MS_BIND|MS_REMOUNT, NULL);
1310 if (rv < 0 && arg_debug)
1311 printf("Failed mount: %s\n", strerror(errno));
1312
1313 free(proc);
1314 return rv;
1315}
1316
1317int bind_mount_by_fd(int src, int dst) {
1318 char *proc_src, *proc_dst;
1319 if (asprintf(&proc_src, "/proc/self/fd/%d", src) < 0 ||
1320 asprintf(&proc_dst, "/proc/self/fd/%d", dst) < 0)
1321 errExit("asprintf");
1322
1323 int rv = mount(proc_src, proc_dst, NULL, MS_BIND|MS_REC, NULL);
1324 if (rv < 0 && arg_debug)
1325 printf("Failed mount: %s\n", strerror(errno));
1326
1327 free(proc_src);
1328 free(proc_dst);
1329 return rv;
1330}
1331
1332int bind_mount_fd_to_path(int src, const char *destname) {
1333 char *proc;
1334 if (asprintf(&proc, "/proc/self/fd/%d", src) < 0)
1335 errExit("asprintf");
1336
1337 int rv = mount(proc, destname, NULL, MS_BIND|MS_REC, NULL);
1338 if (rv < 0 && arg_debug)
1339 printf("Failed mount: %s\n", strerror(errno));
1340
1341 free(proc);
1342 return rv;
1343}
1344
1345int bind_mount_path_to_fd(const char *srcname, int dst) {
1346 char *proc;
1347 if (asprintf(&proc, "/proc/self/fd/%d", dst) < 0)
1348 errExit("asprintf");
1349
1350 int rv = mount(srcname, proc, NULL, MS_BIND|MS_REC, NULL);
1351 if (rv < 0 && arg_debug)
1352 printf("Failed mount: %s\n", strerror(errno));
1353
1354 free(proc);
1355 return rv;
1356}
1357
1213int has_handler(pid_t pid, int signal) { 1358int has_handler(pid_t pid, int signal) {
1214 if (signal > 0 && signal <= SIGRTMAX) { 1359 if (signal > 0 && signal <= SIGRTMAX) {
1215 char *fname; 1360 char *fname;
diff --git a/src/firejail/x11.c b/src/firejail/x11.c
index f4f093138..09956b903 100644
--- a/src/firejail/x11.c
+++ b/src/firejail/x11.c
@@ -204,7 +204,6 @@ static int random_display_number(void) {
204void x11_start_xvfb(int argc, char **argv) { 204void x11_start_xvfb(int argc, char **argv) {
205 EUID_ASSERT(); 205 EUID_ASSERT();
206 int i; 206 int i;
207 struct stat s;
208 pid_t jail = 0; 207 pid_t jail = 0;
209 pid_t server = 0; 208 pid_t server = 0;
210 209
@@ -348,7 +347,7 @@ void x11_start_xvfb(int argc, char **argv) {
348 // wait for x11 server to start 347 // wait for x11 server to start
349 while (++n < 10) { 348 while (++n < 10) {
350 sleep(1); 349 sleep(1);
351 if (stat(fname, &s) == 0) 350 if (access(fname, F_OK) == 0)
352 break; 351 break;
353 }; 352 };
354 353
@@ -427,7 +426,6 @@ static char *extract_setting(int argc, char **argv, const char *argument) {
427void x11_start_xephyr(int argc, char **argv) { 426void x11_start_xephyr(int argc, char **argv) {
428 EUID_ASSERT(); 427 EUID_ASSERT();
429 int i; 428 int i;
430 struct stat s;
431 pid_t jail = 0; 429 pid_t jail = 0;
432 pid_t server = 0; 430 pid_t server = 0;
433 431
@@ -586,7 +584,7 @@ void x11_start_xephyr(int argc, char **argv) {
586 // wait for x11 server to start 584 // wait for x11 server to start
587 while (++n < 10) { 585 while (++n < 10) {
588 sleep(1); 586 sleep(1);
589 if (stat(fname, &s) == 0) 587 if (access(fname, F_OK) == 0)
590 break; 588 break;
591 }; 589 };
592 590
@@ -701,7 +699,6 @@ static char * get_title_arg_str() {
701static void __attribute__((noreturn)) x11_start_xpra_old(int argc, char **argv, int display, char *display_str) { 699static void __attribute__((noreturn)) x11_start_xpra_old(int argc, char **argv, int display, char *display_str) {
702 EUID_ASSERT(); 700 EUID_ASSERT();
703 int i; 701 int i;
704 struct stat s;
705 pid_t client = 0; 702 pid_t client = 0;
706 pid_t server = 0; 703 pid_t server = 0;
707 704
@@ -818,7 +815,7 @@ static void __attribute__((noreturn)) x11_start_xpra_old(int argc, char **argv,
818 // wait for x11 server to start 815 // wait for x11 server to start
819 while (++n < 10) { 816 while (++n < 10) {
820 sleep(1); 817 sleep(1);
821 if (stat(fname, &s) == 0) 818 if (access(fname, F_OK) == 0)
822 break; 819 break;
823 } 820 }
824 821
@@ -1231,9 +1228,9 @@ void x11_xorg(void) {
1231 char *dest; 1228 char *dest;
1232 if (asprintf(&dest, "%s/.Xauthority", cfg.homedir) == -1) 1229 if (asprintf(&dest, "%s/.Xauthority", cfg.homedir) == -1)
1233 errExit("asprintf"); 1230 errExit("asprintf");
1234 if (lstat(dest, &s) == -1) { 1231 if (access(dest, F_OK) == -1) {
1235 touch_file_as_user(dest, 0600); 1232 touch_file_as_user(dest, 0600);
1236 if (stat(dest, &s) == -1) { 1233 if (access(dest, F_OK) == -1) {
1237 fprintf(stderr, "Error: cannot create %s\n", dest); 1234 fprintf(stderr, "Error: cannot create %s\n", dest);
1238 exit(1); 1235 exit(1);
1239 } 1236 }
@@ -1276,12 +1273,7 @@ void x11_xorg(void) {
1276 // mount via the link in /proc/self/fd 1273 // mount via the link in /proc/self/fd
1277 if (arg_debug) 1274 if (arg_debug)
1278 printf("Mounting %s on %s\n", tmpfname, dest); 1275 printf("Mounting %s on %s\n", tmpfname, dest);
1279 char *proc_src, *proc_dst; 1276 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"); 1277 fprintf(stderr, "Error: cannot mount the new .Xauthority file\n");
1286 exit(1); 1278 exit(1);
1287 } 1279 }
@@ -1289,8 +1281,6 @@ void x11_xorg(void) {
1289 MountData *mptr = get_last_mount(); 1281 MountData *mptr = get_last_mount();
1290 if (strcmp(mptr->dir, dest) != 0 || strcmp(mptr->fstype, "tmpfs") != 0) 1282 if (strcmp(mptr->dir, dest) != 0 || strcmp(mptr->fstype, "tmpfs") != 0)
1291 errLogExit("invalid .Xauthority mount"); 1283 errLogExit("invalid .Xauthority mount");
1292 free(proc_src);
1293 free(proc_dst);
1294 close(src); 1284 close(src);
1295 close(dst); 1285 close(dst);
1296 1286
@@ -1299,7 +1289,7 @@ void x11_xorg(void) {
1299 // blacklist user .Xauthority file if it is not masked already 1289 // blacklist user .Xauthority file if it is not masked already
1300 const char *envar = env_get("XAUTHORITY"); 1290 const char *envar = env_get("XAUTHORITY");
1301 if (envar) { 1291 if (envar) {
1302 char *rp = realpath(envar, NULL); 1292 char *rp = realpath_as_user(envar);
1303 if (rp) { 1293 if (rp) {
1304 if (strcmp(rp, dest) != 0) 1294 if (strcmp(rp, dest) != 0)
1305 disable_file_or_dir(rp); 1295 disable_file_or_dir(rp);
@@ -1336,6 +1326,8 @@ void fs_x11(void) {
1336 return; 1326 return;
1337 } 1327 }
1338 1328
1329 // the mount source is under control of the user, so be careful and
1330 // mount without following symbolic links, using a file descriptor
1339 char *x11file; 1331 char *x11file;
1340 if (asprintf(&x11file, "/tmp/.X11-unix/X%d", display) == -1) 1332 if (asprintf(&x11file, "/tmp/.X11-unix/X%d", display) == -1)
1341 errExit("asprintf"); 1333 errExit("asprintf");
@@ -1344,10 +1336,10 @@ void fs_x11(void) {
1344 free(x11file); 1336 free(x11file);
1345 return; 1337 return;
1346 } 1338 }
1347 struct stat x11stat; 1339 struct stat s3;
1348 if (fstat(src, &x11stat) < 0) 1340 if (fstat(src, &s3) < 0)
1349 errExit("fstat"); 1341 errExit("fstat");
1350 if (!S_ISSOCK(x11stat.st_mode)) { 1342 if (!S_ISSOCK(s3.st_mode)) {
1351 close(src); 1343 close(src);
1352 free(x11file); 1344 free(x11file);
1353 return; 1345 return;
@@ -1367,14 +1359,8 @@ void fs_x11(void) {
1367 if (dst < 0) 1359 if (dst < 0)
1368 errExit("open"); 1360 errExit("open");
1369 1361
1370 char *proc_src, *proc_dst; 1362 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"); 1363 errExit("mount bind");
1376 free(proc_src);
1377 free(proc_dst);
1378 close(src); 1364 close(src);
1379 close(dst); 1365 close(dst);
1380 fs_logger2("whitelist", x11file); 1366 fs_logger2("whitelist", x11file);
diff --git a/src/lib/ldd_utils.c b/src/lib/ldd_utils.c
index cd60d74e4..c5dde85b0 100644
--- a/src/lib/ldd_utils.c
+++ b/src/lib/ldd_utils.c
@@ -50,7 +50,7 @@ int is_lib_64(const char *exe) {
50 unsigned char buf[EI_NIDENT]; 50 unsigned char buf[EI_NIDENT];
51 ssize_t len = 0; 51 ssize_t len = 0;
52 while (len < EI_NIDENT) { 52 while (len < EI_NIDENT) {
53 ssize_t sz = read(fd, buf, EI_NIDENT); 53 ssize_t sz = read(fd, buf + len, EI_NIDENT - len);
54 if (sz <= 0) 54 if (sz <= 0)
55 goto doexit; 55 goto doexit;
56 len += sz; 56 len += sz;