diff options
author | 2021-06-08 18:24:22 +0200 | |
---|---|---|
committer | 2021-06-08 18:24:22 +0200 | |
commit | 5e30eb49355f5620cc2f99100a0890cfed02ccd4 (patch) | |
tree | 0852f5402f9785dc1ebdd236eb31a9c40b02adf4 /src | |
parent | fixup 9678da00301562464464099b9d7cfd76424fbb23 (diff) | |
parent | add more EUID switching (diff) | |
download | firejail-5e30eb49355f5620cc2f99100a0890cfed02ccd4.tar.gz firejail-5e30eb49355f5620cc2f99100a0890cfed02ccd4.tar.zst firejail-5e30eb49355f5620cc2f99100a0890cfed02ccd4.zip |
Merge pull request #4349 from smitsohu/misc
Misc hardening + refactoring
Diffstat (limited to 'src')
-rw-r--r-- | src/fcopy/main.c | 17 | ||||
-rw-r--r-- | src/firejail/checkcfg.c | 3 | ||||
-rw-r--r-- | src/firejail/chroot.c | 40 | ||||
-rw-r--r-- | src/firejail/dbus.c | 14 | ||||
-rw-r--r-- | src/firejail/dhcp.c | 9 | ||||
-rw-r--r-- | src/firejail/firejail.h | 7 | ||||
-rw-r--r-- | src/firejail/fs.c | 210 | ||||
-rw-r--r-- | src/firejail/fs_dev.c | 2 | ||||
-rw-r--r-- | src/firejail/fs_home.c | 63 | ||||
-rw-r--r-- | src/firejail/fs_lib.c | 13 | ||||
-rw-r--r-- | src/firejail/fs_trace.c | 6 | ||||
-rw-r--r-- | src/firejail/fs_var.c | 4 | ||||
-rw-r--r-- | src/firejail/fs_whitelist.c | 19 | ||||
-rw-r--r-- | src/firejail/macros.c | 1 | ||||
-rw-r--r-- | src/firejail/main.c | 10 | ||||
-rw-r--r-- | src/firejail/mountinfo.c | 5 | ||||
-rw-r--r-- | src/firejail/paths.c | 2 | ||||
-rw-r--r-- | src/firejail/pulseaudio.c | 6 | ||||
-rw-r--r-- | src/firejail/restrict_users.c | 22 | ||||
-rw-r--r-- | src/firejail/sbox.c | 2 | ||||
-rw-r--r-- | src/firejail/selinux.c | 10 | ||||
-rw-r--r-- | src/firejail/util.c | 227 | ||||
-rw-r--r-- | src/firejail/x11.c | 40 | ||||
-rw-r--r-- | src/lib/ldd_utils.c | 2 |
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_ | |||
503 | void touch_file_as_user(const char *fname, mode_t mode); | 503 | void touch_file_as_user(const char *fname, mode_t mode); |
504 | int is_dir(const char *fname); | 504 | int is_dir(const char *fname); |
505 | int is_link(const char *fname); | 505 | int is_link(const char *fname); |
506 | char *realpath_as_user(const char *fname); | ||
507 | int stat_as_user(const char *fname, struct stat *s); | ||
508 | int lstat_as_user(const char *fname, struct stat *s); | ||
506 | void trim_trailing_slash_or_dot(char *path); | 509 | void trim_trailing_slash_or_dot(char *path); |
507 | char *line_remove_spaces(const char *buf); | 510 | char *line_remove_spaces(const char *buf); |
508 | char *split_comma(char *str); | 511 | char *split_comma(char *str); |
@@ -526,6 +529,10 @@ unsigned extract_timeout(const char *str); | |||
526 | void disable_file_or_dir(const char *fname); | 529 | void disable_file_or_dir(const char *fname); |
527 | void disable_file_path(const char *path, const char *file); | 530 | void disable_file_path(const char *path, const char *file); |
528 | int safer_openat(int dirfd, const char *path, int flags); | 531 | int safer_openat(int dirfd, const char *path, int flags); |
532 | int remount_by_fd(int dst, unsigned long mountflags); | ||
533 | int bind_mount_by_fd(int src, int dst); | ||
534 | int bind_mount_path_to_fd(const char *srcname, int dst); | ||
535 | int bind_mount_fd_to_path(int src, const char *destname); | ||
529 | int has_handler(pid_t pid, int signal); | 536 | int has_handler(pid_t pid, int signal); |
530 | void enter_network_namespace(pid_t pid); | 537 | void enter_network_namespace(pid_t pid); |
531 | int read_pid(const char *name, pid_t *pid); | 538 | int 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 | ||
57 | typedef enum { | ||
58 | UNSUCCESSFUL, | ||
59 | SUCCESSFUL | ||
60 | } LAST_DISABLE_OPERATION; | ||
61 | LAST_DISABLE_OPERATION last_disable = UNSUCCESSFUL; | ||
62 | |||
63 | static void disable_file(OPERATION op, const char *filename) { | 57 | static void disable_file(OPERATION op, const char *filename) { |
64 | assert(filename); | 58 | assert(filename); |
65 | assert(op <OPERATION_MAX); | 59 | assert(op <OPERATION_MAX); |
66 | last_disable = UNSUCCESSFUL; | 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 |
192 | static void globbing(OPERATION op, const char *pattern, const char *noblacklist[], size_t noblacklist_len) { | 199 | static 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 |
451 | void fs_tmpfs(const char *dir, unsigned check_owner) { | 460 | void 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 |
492 | static void fs_remount_simple(const char *path, OPERATION op) { | 503 | static 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 | ||
591 | out: | 612 | out: |
@@ -594,7 +615,9 @@ out: | |||
594 | 615 | ||
595 | // remount recursively; requires a resolved path | 616 | // remount recursively; requires a resolved path |
596 | static void fs_remount_rec(const char *dir, OPERATION op) { | 617 | static 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 |
633 | void fs_remount(const char *path, OPERATION op, int rec) { | 656 | void 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 |
646 | void fs_mnt(const int enforce) { | 673 | void 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 |
783 | void disable_config(void) { | 805 | void 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 |
863 | char *fs_check_overlay_dir(const char *subdirname, int allow_reuse) { | 879 | char *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 |
1223 | void fs_private_tmp(void) { | 1240 | void 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) { | |||
187 | static void process_dev_shm(void) { | 187 | static 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 | //*********************************************************************************** |
440 | static char *check_dir_or_file(const char *name) { | 433 | static 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 | ||
500 | static void duplicate(char *name) { | 494 | static 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 | ||
269 | static void globbing(const char *pattern) { | 259 | static 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 |
151 | static char *resolve_hardcoded(char *entries[]) { | 151 | static 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) { | |||
862 | char *guess_shell(void) { | 862 | char *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. |
155 | int get_mount_id(const char *path) { | 155 | int 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 | ||
267 | int sbox_run_v(unsigned filtermask, char * const arg[]) { | 267 | int 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 | ||
483 | char *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 | |||
501 | int 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 | |||
519 | int 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 |
464 | void trim_trailing_slash_or_dot(char *path) { | 539 | void 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 | ||
892 | int remove_overlay_directory(void) { | 967 | int 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) { | |||
975 | int create_empty_dir_as_user(const char *dir, mode_t mode) { | 1052 | int 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 | ||
1112 | void disable_file_or_dir(const char *fname) { | 1188 | void 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 | ||
1129 | void disable_file_path(const char *path, const char *file) { | 1220 | void 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 | ||
1304 | int 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 | |||
1317 | int 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 | |||
1332 | int 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 | |||
1345 | int 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 | |||
1213 | int has_handler(pid_t pid, int signal) { | 1358 | int 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) { | |||
204 | void x11_start_xvfb(int argc, char **argv) { | 204 | void 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) { | |||
427 | void x11_start_xephyr(int argc, char **argv) { | 426 | void 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() { | |||
701 | static void __attribute__((noreturn)) x11_start_xpra_old(int argc, char **argv, int display, char *display_str) { | 699 | static 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; |